* merge in matts r6917 so it doesn't colide with ongoing changes.
[citadel.git] / webcit / preferences.c
1 /*
2  * $Id$
3  *
4  * Manage user preferences with a little help from the Citadel server.
5  *
6  */
7
8 #include "webcit.h"
9 #include "webserver.h"
10 #include "groupdav.h"
11
12
13 HashList *PreferenceHooks;
14
15 typedef struct _Prefs {
16         long Type;
17         const char *Setting;
18         const char *PrefStr;
19 } Prefs;
20
21 void RegisterPreference(const char *Setting, const char *PrefStr, long Type)
22 {
23         Prefs *Newpref = (Prefs*) malloc(sizeof(Prefs));
24         Newpref->Setting = Setting;
25         Newpref->PrefStr = PrefStr;
26         Newpref->Type = Type;
27         Put(PreferenceHooks, Setting, strlen(Setting), Newpref, NULL);
28 }
29
30 const char *PrefGetLocalStr(const char *Setting, long len)
31 {
32         void *hash_value;
33         if (GetHash(PreferenceHooks, Setting, len, &hash_value) != 0) {
34                 Prefs *Newpref = (Prefs*) hash_value;
35                 return _(Newpref->PrefStr);
36
37         }
38         return "";
39 }
40
41 #ifdef DBG_PREFS_HASH
42 inline const char *PrintPref(void *Prefstr)
43 {
44         return ChrPtr(Prefstr);
45 }
46 #endif
47
48
49 void ParsePref(HashList **List, StrBuf *ReadBuf)
50 {
51         StrBuf *Key;
52         StrBuf *Data = NULL;
53         StrBuf *LastData = NULL;
54                                 
55         Key = NewStrBuf();
56         while (StrBuf_ServGetln(ReadBuf), 
57                strcmp(ChrPtr(ReadBuf), "000")) 
58         {
59                 if ((ChrPtr(ReadBuf)[0] == ' ') &&
60                     (Data != NULL)) {
61                         StrBufAppendBuf(Data, ReadBuf, 1);
62                 }
63                 else {
64                         LastData = Data = NewStrBuf();
65                         StrBufExtract_token(Key, ReadBuf, 0, '|');
66                         StrBufExtract_token(Data, ReadBuf, 1, '|');
67                         if (!IsEmptyStr(ChrPtr(Key)))
68                         {
69                                 Put(*List, 
70                                     ChrPtr(Key), StrLength(Key), 
71                                     Data, 
72                                     HFreeStrBuf);
73                         }
74                         else 
75                         {
76                                 FreeStrBuf(&Data);
77                                 LastData = NULL;
78                         }
79                 }
80         }
81         FreeStrBuf(&Key);
82 }
83
84
85 /*
86  * display preferences dialog
87  */
88 void load_preferences(void) 
89 {
90         StrBuf *ReadBuf;
91         char buf[SIZ];
92         long msgnum = 0L;
93         
94         serv_printf("GOTO %s", USERCONFIGROOM);
95         serv_getln(buf, sizeof buf);
96         if (buf[0] != '2') return;
97         
98         serv_puts("MSGS ALL|0|1");
99         serv_getln(buf, sizeof buf);
100         if (buf[0] == '8') {
101                 serv_puts("subj|__ WebCit Preferences __");
102                 serv_puts("000");
103         }
104         while (serv_getln(buf, sizeof buf), strcmp(buf, "000")) {
105                 msgnum = atol(buf);
106         }
107
108         if (msgnum > 0L) {
109                 serv_printf("MSG0 %ld", msgnum);
110                 serv_getln(buf, sizeof buf);
111                 if (buf[0] == '1') {
112                         ReadBuf = NewStrBuf();
113                         while (StrBuf_ServGetln(ReadBuf),
114                                (strcmp(ChrPtr(ReadBuf), "text") && 
115                                 strcmp(ChrPtr(ReadBuf), "000"))) {
116                         }
117                         if (!strcmp(ChrPtr(ReadBuf), "text")) {
118                                 ParsePref(&WC->hash_prefs, ReadBuf);
119                         }
120                 }
121                 FreeStrBuf(&ReadBuf);
122         }
123
124         /* Go back to the room we're supposed to be in */
125         serv_printf("GOTO %s", ChrPtr(WC->wc_roomname));
126         serv_getln(buf, sizeof buf);
127 }
128
129 /**
130  * \brief Goto the user's configuration room, creating it if necessary.
131  * \return 0 on success or nonzero upon failure.
132  */
133 int goto_config_room(void) {
134         char buf[SIZ];
135
136         serv_printf("GOTO %s", USERCONFIGROOM);
137         serv_getln(buf, sizeof buf);
138         if (buf[0] != '2') { /* try to create the config room if not there */
139                 serv_printf("CRE8 1|%s|4|0", USERCONFIGROOM);
140                 serv_getln(buf, sizeof buf);
141                 serv_printf("GOTO %s", USERCONFIGROOM);
142                 serv_getln(buf, sizeof buf);
143                 if (buf[0] != '2') return(1);
144         }
145         return(0);
146 }
147
148 void WritePrefsToServer(HashList *Hash)
149 {
150         long len;
151         HashPos *HashPos;
152         void *Value;
153         const char *Key;
154         StrBuf *Buf;
155         StrBuf *SubBuf = NULL;
156         
157         Hash = WC->hash_prefs;
158 #ifdef DBG_PREFS_HASH
159         dbg_PrintHash(Hash, PrintPref, NULL);
160 #endif
161         HashPos = GetNewHashPos(Hash, 0);
162         while (GetNextHashPos(Hash, HashPos, &len, &Key, &Value)!=0)
163         {
164                 size_t nchars;
165                 Buf = (StrBuf*) Value;
166                 if (Buf == NULL)
167                         continue;
168                 nchars = StrLength(Buf);
169                 if (nchars > 80){
170                         int n = 0;
171                         size_t offset, nchars;
172                         if (SubBuf == NULL)
173                                 SubBuf = NewStrBuf();
174                         nchars = 1;
175                         offset = 0;
176                         while (nchars > 0) {
177                                 if (n == 0)
178                                         nchars = 70;
179                                 else 
180                                         nchars = 80;
181                                 
182                                 nchars = StrBufSub(SubBuf, Buf, offset, nchars);
183                                 
184                                 if (n == 0)
185                                         serv_printf("%s|%s", Key, ChrPtr(SubBuf));
186                                 else
187                                         serv_printf(" %s", ChrPtr(SubBuf));
188                                 
189                                 offset += nchars;
190                                 nchars = StrLength(Buf) - offset;
191                                 n++;
192                         }
193                         
194                 }
195                 else
196                         serv_printf("%s|%s", Key, ChrPtr(Buf));
197                 
198         }
199         if (SubBuf != NULL)
200                 FreeStrBuf(&SubBuf);
201         DeleteHashPos(&HashPos);
202 }
203
204 /**
205  * \brief save the modifications
206  */
207 void save_preferences(void) {
208         char buf[SIZ];
209         long msgnum = 0L;
210         
211         if (goto_config_room() != 0) return;    /* oh well. */
212         serv_puts("MSGS ALL|0|1");
213         serv_getln(buf, sizeof buf);
214         if (buf[0] == '8') {
215                 serv_puts("subj|__ WebCit Preferences __");
216                 serv_puts("000");
217         }
218         while (serv_getln(buf, sizeof buf), strcmp(buf, "000")) {
219                 msgnum = atol(buf);
220         }
221
222         if (msgnum > 0L) {
223                 serv_printf("DELE %ld", msgnum);
224                 serv_getln(buf, sizeof buf);
225         }
226
227         serv_printf("ENT0 1||0|1|__ WebCit Preferences __|");
228         serv_getln(buf, sizeof buf);
229         if (buf[0] == '4') {
230
231                 WritePrefsToServer(WC->hash_prefs);
232                 serv_puts("");
233                 serv_puts("000");
234         }
235
236         /** Go back to the room we're supposed to be in */
237         serv_printf("GOTO %s", ChrPtr(WC->wc_roomname));
238         serv_getln(buf, sizeof buf);
239 }
240
241 /**
242  * \brief query the actual setting of key in the citadel database
243  * \param key config key to query
244  * \param keylen length of the key string
245  * \param value StrBuf-value to the key to get
246  * \returns found?
247  */
248 int get_PREFERENCE(const char *key, size_t keylen, StrBuf **value)
249 {
250         void *hash_value = NULL;
251 #ifdef DBG_PREFS_HASH
252         dbg_PrintHash(WC->hash_prefs, PrintPref, NULL);
253 #endif
254         if (GetHash(WC->hash_prefs, key, keylen, &hash_value) == 0) {
255                 *value = NULL;
256                 return 0;
257         }
258         else {
259                 *value = NULL;
260                 *value = (StrBuf*) hash_value;
261                 return 1;
262         }
263 }
264
265 /**
266  * \brief       Write a key into the webcit preferences database for this user
267  *
268  * \params      key             key whichs value is to be modified
269  * \param keylen length of the key string
270  * \param       value           value to set
271  * \param       save_to_server  1 = flush all data to the server, 0 = cache it for now
272  */
273 void set_PREFERENCE(const char *key, size_t keylen, StrBuf *value, int save_to_server) {
274         
275         Put(WC->hash_prefs, key, keylen, value, HFreeStrBuf);
276         
277         if (save_to_server) save_preferences();
278 }
279
280 int get_PREF_LONG(const char *key, size_t keylen, long *value, long Default)
281 {
282         int ret;
283         StrBuf *val;
284         ret = get_PREFERENCE(key, keylen, &val);
285         if (ret) {
286                 *value = atol(ChrPtr(val));
287         }
288         else {
289                 *value = Default;
290         }
291
292         return ret;
293 }
294
295
296 void set_PREF_LONG(const char *key, size_t keylen, long value, int save_to_server)
297 {
298         StrBuf *val;
299         if (get_PREFERENCE(key, keylen, &val)) {
300                 StrBufPrintf(val, "%ld", value);
301         }
302         else {
303                 val = NewStrBuf();
304                 StrBufPrintf(val, "%ld", value);
305                 set_PREFERENCE(key, keylen, val, save_to_server);
306         }
307 }
308
309
310
311 int get_PREF_YESNO(const char *key, size_t keylen, int *value, int Default)
312 {
313         int ret;
314         StrBuf *val;
315         ret = get_PREFERENCE(key, keylen, &val);
316         if (ret) {
317                 *value = strcmp(ChrPtr(val), "yes") == 0;
318         }
319         else {
320                 *value = Default;
321         }
322
323         return ret;
324 }
325
326 void set_PREF_YESNO(const char *key, size_t keylen, int value, int save_to_server)
327 {
328         StrBuf *val;
329         if (get_PREFERENCE(key, keylen, &val)) {
330                 StrBufPrintf(val, "%s", (value)?"yes":"no");
331         }
332         else {
333                 val = NewStrBuf();
334                 StrBufPrintf(val, "%s", (value)?"yes":"no");
335                 set_PREFERENCE(key, keylen, val, save_to_server);
336         }
337 }
338
339 StrBuf *get_ROOM_PREFS(const char *key, size_t keylen)
340 {
341         StrBuf *pref_name, *pref_value;
342         
343         pref_name = NewStrBuf ();
344         StrBufPrintf(pref_name, "%s %s", key, ChrPtr(WC->wc_roomname));
345         get_pref(pref_name, &pref_value);
346         FreeStrBuf(&pref_name);
347         return pref_value;
348 }
349
350 void set_ROOM_PREFS(const char *key, size_t keylen, StrBuf *value, int save_to_server)
351 {
352         StrBuf *pref_name;
353         
354         pref_name = NewStrBuf ();
355         StrBufPrintf(pref_name, "%s %s", key, ChrPtr(WC->wc_roomname));
356         set_PREFERENCE(ChrPtr(pref_name), StrLength(pref_name), value, save_to_server);
357         FreeStrBuf(&pref_name);
358 }
359
360 /*
361  * Offer to make any page the user's "start page."
362  */
363 void offer_start_page(StrBuf *Target, WCTemplputParams *TP)
364 {
365         wprintf("<a href=\"change_start_page?startpage=");
366         urlescputs(WC->this_page);
367         wprintf("\">");
368         wprintf(_("Make this my start page"));
369         wprintf("</a>");
370 #ifdef TECH_PREVIEW
371         wprintf("<br/><a href=\"rss?room=");
372         urlescputs(ChrPtr(WC->wc_roomname));
373         wprintf("\" title=\"RSS 2.0 feed for ");
374         escputs(ChrPtr(WC->wc_roomname));
375         wprintf("\"><img alt=\"RSS\" border=\"0\" src=\"static/xml_button.gif\"/></a>\n");
376 #endif
377 }
378
379
380 /*
381  * Change the user's start page
382  */
383 void change_start_page(void) {
384
385         if (bstr("startpage") == NULL) {
386                 safestrncpy(WC->ImportantMessage,
387                         _("You no longer have a start page selected."),
388                         sizeof WC->ImportantMessage);
389                 display_main_menu();
390                 return;
391         }
392
393         set_preference("startpage", NewStrBufPlain(bstr("startpage"), -1), 1);
394
395         output_headers(1, 1, 0, 0, 0, 0);
396         do_template("newstartpage", NULL);
397         wDumpContent(1);
398 }
399
400
401
402 /**
403  * \brief Commit new preferences and settings
404  */
405 void set_preferences(void)
406 {
407         long fmt;
408         StrBuf *buf, *encBuf;
409         int *time_format_cache;
410         
411          time_format_cache = &(WC->time_format_cache);
412
413          if (!havebstr("change_button")) {
414                  safestrncpy(WC->ImportantMessage, 
415                          _("Cancelled.  No settings were changed."),
416                          sizeof WC->ImportantMessage);
417                  display_main_menu();
418                  return;
419          }
420
421          /**
422           * Set the last argument to 1 only for the final setting, so
423           * we don't send the prefs file to the server repeatedly
424           */
425          set_preference("roomlistview", NewStrBufPlain(bstr("roomlistview"), -1), 0);
426          fmt = lbstr("calhourformat");
427          set_pref_long("calhourformat", fmt, 0);
428          if (fmt == 24) 
429                  *time_format_cache = WC_TIMEFORMAT_24;
430          else
431                  *time_format_cache = WC_TIMEFORMAT_AMPM;
432
433          set_pref_long("weekstart", lbstr("weekstart"), 0);
434          set_pref_yesno("use_sig", yesbstr("use_sig"), 0);
435          set_pref_long("daystart", lbstr("daystart"), 0);
436          set_pref_long("dayend", lbstr("dayend"), 0);
437          set_preference("default_header_charset", NewStrBufPlain(bstr("default_header_charset"), -1), 0);
438          set_preference("emptyfloors", NewStrBufPlain(bstr("emptyfloors"), -1), 0);
439          set_preference("defaultfrom", NewStrBufDup(sbstr("defaultfrom")), 0);
440          set_preference("defaultname", NewStrBufDup(sbstr("defaultname")), 0);
441          set_preference("defaulthandle", NewStrBufDup(sbstr("defaulthandle")), 0);
442          set_preference("mailbox", NewStrBufDup(sbstr("mailbox")), 0);
443
444
445         buf = NewStrBufPlain(bstr("signature"), -1);
446         encBuf = NewStrBuf();
447         StrBufEUid_escapize(encBuf, buf);
448         set_preference("signature", encBuf, 1);
449         FreeStrBuf(&buf);
450
451         display_main_menu();
452 }
453
454
455 #define PRF_STRING 1
456 #define PRF_INT 2
457 #define PRF_QP_STRING 3
458 #define PRF_YESNO 4
459
460
461 void tmplput_CFG_Value(StrBuf *Target, WCTemplputParams *TP)
462 {
463         StrBuf *Setting;
464         if (get_PREFERENCE(TKEY(0), &Setting))
465         StrBufAppendTemplate(Target, TP, Setting, 1);
466 }
467
468 void tmplput_CFG_Descr(StrBuf *Target, WCTemplputParams *TP)
469 {
470         const char *SettingStr;
471         SettingStr = PrefGetLocalStr(TKEY(0));
472         if (SettingStr != NULL) 
473                 StrBufAppendBufPlain(Target, SettingStr, -1, 0);
474 }
475 void tmplput_CFG_RoomValue(StrBuf *Target, WCTemplputParams *TP)
476 {
477   StrBuf *pref = get_ROOM_PREFS(TKEY(0));
478   if (pref != NULL) 
479     StrBufAppendBuf(Target, pref, 0);
480 }
481 int ConditionalHasRoomPreference(WCTemplateToken *Tokens, void *Context, int ContextType) {
482   if (get_ROOM_PREFS(Tokens->Params[0]->Start, Tokens->Params[0]->len) != NULL) 
483     return 1;
484   
485   return 0;
486 }
487 void CfgZoneTempl(StrBuf *TemplBuffer, WCTemplputParams *TP)
488 {
489         StrBuf *Zone = (StrBuf*) CTX;
490
491         SVPutBuf("ZONENAME", Zone, 1);
492 }
493
494 int ConditionalPreference(StrBuf *Target, WCTemplputParams *TP)
495 {
496         StrBuf *Pref;
497
498         if (!get_PREFERENCE(TKEY(2), &Pref)) 
499                 return 0;
500         
501         if (TP->Tokens->nParameters == 3) {
502                 return 1;
503         }
504         else if (TP->Tokens->Params[3]->Type == TYPE_STR)
505                 return ((TP->Tokens->Params[3]->len == StrLength(Pref)) &&
506                         (strcmp(TP->Tokens->Params[3]->Start, ChrPtr(Pref)) == 0));
507         else 
508                 return (StrTol(Pref) == TP->Tokens->Params[3]->lvalue);
509 }
510
511 int ConditionalHasPreference(StrBuf *Target, WCTemplputParams *TP)
512 {
513         StrBuf *Pref;
514
515         if (!get_PREFERENCE(TKEY(2), &Pref) || 
516             (Pref == NULL)) 
517                 return 0;
518         else 
519                 return 1;
520 }
521
522 HashList *GetGVEAHash(StrBuf *Target, WCTemplputParams *TP)
523 {
524         StrBuf *Rcp;
525         HashList *List = NULL;
526         int Done = 0;
527         int i, n = 1;
528         char N[64];
529
530         Rcp = NewStrBuf();
531         serv_puts("GVEA");
532         StrBuf_ServGetln(Rcp);
533         if (GetServerStatus(Rcp, NULL) == 1) {
534                 FlushStrBuf(Rcp);
535                 List = NewHash(1, NULL);
536                 while (!Done && (StrBuf_ServGetln(Rcp)>=0)) {
537                         if ( (StrLength(Rcp)==3) && 
538                              !strcmp(ChrPtr(Rcp), "000")) 
539                         {
540                                 Done = 1;
541                         }
542                         else {
543                                 i = snprintf(N, sizeof(N), "%d", n);
544                                 StrBufTrim(Rcp);
545                                 Put(List, N, i, Rcp, HFreeStrBuf);
546                                 Rcp = NewStrBuf();
547                         }
548                         n++;
549                 }
550         }
551         FreeStrBuf(&Rcp);
552         return List;
553 }
554 void DeleteGVEAHash(HashList **KillMe)
555 {
556         DeleteHash(KillMe);
557 }
558
559 HashList *GetGVSNHash(StrBuf *Target, WCTemplputParams *TP)
560 {
561         StrBuf *Rcp;
562         HashList *List = NULL;
563         int Done = 0;
564         int i, n = 1;
565         char N[64];
566
567         Rcp = NewStrBuf();
568         serv_puts("GVSN");
569         StrBuf_ServGetln(Rcp);
570         if (GetServerStatus(Rcp, NULL) == 1) {
571                 FlushStrBuf(Rcp);
572                 List = NewHash(1, NULL);
573                 while (!Done && (StrBuf_ServGetln(Rcp)>=0)) {
574                         if ( (StrLength(Rcp)==3) && 
575                              !strcmp(ChrPtr(Rcp), "000")) 
576                         {
577                                 Done = 1;
578                         }
579                         else {
580                                 i = snprintf(N, sizeof(N), "%d", n);
581                                 StrBufTrim(Rcp);
582                                 Put(List, N, i, Rcp, HFreeStrBuf);
583                                 Rcp = NewStrBuf();
584                         }
585                         n++;
586                 }
587         }
588         FreeStrBuf(&Rcp);
589         return List;
590 }
591 void DeleteGVSNHash(HashList **KillMe)
592 {
593         DeleteHash(KillMe);
594 }
595
596
597 void 
598 InitModule_PREFERENCES
599 (void)
600 {
601         WebcitAddUrlHandler(HKEY("set_preferences"), set_preferences, 0);
602         WebcitAddUrlHandler(HKEY("change_start_page"), change_start_page, 0);
603
604         RegisterPreference("roomlistview",_("Room list view"),PRF_STRING);
605         RegisterPreference("calhourformat",_("Time format"), PRF_INT);
606         RegisterPreference("daystart", _("Calendar day view begins at:"), PRF_INT);
607         RegisterPreference("dayend", _("Calendar day view ends at:"), PRF_INT);
608         RegisterPreference("weekstart",_("Week starts on:"), PRF_INT);
609
610         RegisterPreference("use_sig",_("Attach signature to email messages?"), PRF_YESNO);
611         RegisterPreference("signature",_("Use this signature:"),PRF_QP_STRING);
612         RegisterPreference("default_header_charset", _("Default character set for email headers:") ,PRF_STRING);
613         RegisterPreference("emptyfloors", _("Show empty floors"), PRF_YESNO);
614         RegisterPreference("defaultfrom", _("Preferred email address"), PRF_STRING);
615         RegisterPreference("defaultname", _("Preferred display name for email messages"), PRF_STRING);
616         RegisterPreference("defaulthandle", _("Preferred display name for bulletin board posts"), PRF_STRING);
617         RegisterNamespace("OFFERSTARTPAGE", 0, 0, offer_start_page, CTX_NONE);
618         RegisterPreference("mailbox",_("Mailbox view mode"), PRF_STRING);
619         RegisterNamespace("PREF:ROOM:VALUE", 1, 2, tmplput_CFG_RoomValue,  CTX_NONE);
620         RegisterNamespace("PREF:VALUE", 1, 2, tmplput_CFG_Value, CTX_NONE);
621         RegisterNamespace("PREF:DESCR", 1, 1, tmplput_CFG_Descr, CTX_NONE);
622         RegisterIterator("PREF:ZONE", 0, ZoneHash, NULL, CfgZoneTempl, NULL, CTX_PREF, CTX_NONE, IT_NOFLAG);
623
624         RegisterConditional(HKEY("COND:PREF"), 4, ConditionalPreference, CTX_NONE);
625         RegisterConditional(HKEY("COND:PREF:SET"), 4, ConditionalHasPreference, CTX_NONE);
626         RegisterConditional(HKEY("COND:ROOM:SET"), 4, ConditionalHasRoomPreference, CTX_NONE);
627         
628         RegisterIterator("PREF:VALID:EMAIL:ADDR", 0, NULL, 
629                          GetGVEAHash, NULL, DeleteGVEAHash, CTX_STRBUF, CTX_NONE, IT_NOFLAG);
630         RegisterIterator("PREF:VALID:EMAIL:NAME", 0, NULL, 
631                          GetGVSNHash, NULL, DeleteGVSNHash, CTX_STRBUF, CTX_NONE, IT_NOFLAG);
632
633 }
634 /*@}*/