* move some more vars from the session context to strbuf (the use of StrBufAppendTemp...
[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
443
444         buf = NewStrBufPlain(bstr("signature"), -1);
445         encBuf = NewStrBuf();
446         StrBufEUid_escapize(encBuf, buf);
447         set_preference("signature", encBuf, 1);
448         FreeStrBuf(&buf);
449
450         display_main_menu();
451 }
452
453
454 #define PRF_STRING 1
455 #define PRF_INT 2
456 #define PRF_QP_STRING 3
457 #define PRF_YESNO 4
458
459
460 void tmplput_CFG_Value(StrBuf *Target, WCTemplputParams *TP)
461 {
462         StrBuf *Setting;
463         if (get_PREFERENCE(TKEY(0), &Setting))
464         StrBufAppendTemplate(Target, TP, Setting, 1);
465 }
466
467 void tmplput_CFG_Descr(StrBuf *Target, WCTemplputParams *TP)
468 {
469         const char *SettingStr;
470         SettingStr = PrefGetLocalStr(TKEY(0));
471         if (SettingStr != NULL) 
472                 StrBufAppendBufPlain(Target, SettingStr, -1, 0);
473 }
474
475
476 void CfgZoneTempl(StrBuf *TemplBuffer, WCTemplputParams *TP)
477 {
478         StrBuf *Zone = (StrBuf*) CTX;
479
480         SVPutBuf("ZONENAME", Zone, 1);
481 }
482
483 int ConditionalPreference(StrBuf *Target, WCTemplputParams *TP)
484 {
485         StrBuf *Pref;
486
487         if (!get_PREFERENCE(TKEY(2), &Pref)) 
488                 return 0;
489         
490         if (TP->Tokens->nParameters == 3) {
491                 return 1;
492         }
493         else if (TP->Tokens->Params[3]->Type == TYPE_STR)
494                 return ((TP->Tokens->Params[3]->len == StrLength(Pref)) &&
495                         (strcmp(TP->Tokens->Params[3]->Start, ChrPtr(Pref)) == 0));
496         else 
497                 return (StrTol(Pref) == TP->Tokens->Params[3]->lvalue);
498 }
499
500 int ConditionalHazePreference(StrBuf *Target, WCTemplputParams *TP)
501 {
502         StrBuf *Pref;
503
504         if (!get_PREFERENCE(TKEY(2), &Pref) || 
505             (Pref == NULL)) 
506                 return 0;
507         else 
508                 return 1;
509 }
510
511 HashList *GetGVEAHash(StrBuf *Target, WCTemplputParams *TP)
512 {
513         StrBuf *Rcp;
514         HashList *List = NULL;
515         int Done = 0;
516         int i, n = 1;
517         char N[64];
518
519         Rcp = NewStrBuf();
520         serv_puts("GVEA");
521         StrBuf_ServGetln(Rcp);
522         if (GetServerStatus(Rcp, NULL) == 1) {
523                 FlushStrBuf(Rcp);
524                 List = NewHash(1, NULL);
525                 while (!Done && (StrBuf_ServGetln(Rcp)>=0)) {
526                         if ( (StrLength(Rcp)==3) && 
527                              !strcmp(ChrPtr(Rcp), "000")) 
528                         {
529                                 Done = 1;
530                         }
531                         else {
532                                 i = snprintf(N, sizeof(N), "%d", n);
533                                 StrBufTrim(Rcp);
534                                 Put(List, N, i, Rcp, HFreeStrBuf);
535                                 Rcp = NewStrBuf();
536                         }
537                         n++;
538                 }
539         }
540         FreeStrBuf(&Rcp);
541         return List;
542 }
543 void DeleteGVEAHash(HashList **KillMe)
544 {
545         DeleteHash(KillMe);
546 }
547
548 HashList *GetGVSNHash(StrBuf *Target, WCTemplputParams *TP)
549 {
550         StrBuf *Rcp;
551         HashList *List = NULL;
552         int Done = 0;
553         int i, n = 1;
554         char N[64];
555
556         Rcp = NewStrBuf();
557         serv_puts("GVSN");
558         StrBuf_ServGetln(Rcp);
559         if (GetServerStatus(Rcp, NULL) == 1) {
560                 FlushStrBuf(Rcp);
561                 List = NewHash(1, NULL);
562                 while (!Done && (StrBuf_ServGetln(Rcp)>=0)) {
563                         if ( (StrLength(Rcp)==3) && 
564                              !strcmp(ChrPtr(Rcp), "000")) 
565                         {
566                                 Done = 1;
567                         }
568                         else {
569                                 i = snprintf(N, sizeof(N), "%d", n);
570                                 StrBufTrim(Rcp);
571                                 Put(List, N, i, Rcp, HFreeStrBuf);
572                                 Rcp = NewStrBuf();
573                         }
574                         n++;
575                 }
576         }
577         FreeStrBuf(&Rcp);
578         return List;
579 }
580 void DeleteGVSNHash(HashList **KillMe)
581 {
582         DeleteHash(KillMe);
583 }
584
585
586 void 
587 InitModule_PREFERENCES
588 (void)
589 {
590         WebcitAddUrlHandler(HKEY("set_preferences"), set_preferences, 0);
591         WebcitAddUrlHandler(HKEY("change_start_page"), change_start_page, 0);
592
593         RegisterPreference("roomlistview",_("Room list view"),PRF_STRING);
594         RegisterPreference("calhourformat",_("Time format"), PRF_INT);
595         RegisterPreference("daystart", _("Calendar day view begins at:"), PRF_INT);
596         RegisterPreference("dayend", _("Calendar day view ends at:"), PRF_INT);
597         RegisterPreference("weekstart",_("Week starts on:"), PRF_INT);
598
599         RegisterPreference("use_sig",_("Attach signature to email messages?"), PRF_YESNO);
600         RegisterPreference("signature",_("Use this signature:"),PRF_QP_STRING);
601         RegisterPreference("default_header_charset", _("Default character set for email headers:") ,PRF_STRING);
602         RegisterPreference("emptyfloors", _("Show empty floors"), PRF_YESNO);
603         RegisterPreference("defaultfrom", _("Preferred email address"), PRF_STRING);
604         RegisterPreference("defaultname", _("Preferred display name for email messages"), PRF_STRING);
605         RegisterPreference("defaulthandle", _("Preferred display name for bulletin board posts"), PRF_STRING);
606         RegisterNamespace("OFFERSTARTPAGE", 0, 0, offer_start_page, CTX_NONE);
607         RegisterNamespace("PREF:VALUE", 1, 2, tmplput_CFG_Value, CTX_NONE);
608         RegisterNamespace("PREF:DESCR", 1, 1, tmplput_CFG_Descr, CTX_NONE);
609         RegisterIterator("PREF:ZONE", 0, ZoneHash, NULL, CfgZoneTempl, NULL, CTX_PREF, CTX_NONE, IT_NOFLAG);
610
611         RegisterConditional(HKEY("COND:PREF"), 4, ConditionalPreference, CTX_NONE);
612         RegisterConditional(HKEY("COND:PREF:SET"), 4, ConditionalHazePreference, CTX_NONE);
613         
614         RegisterIterator("PREF:VALID:EMAIL:ADDR", 0, NULL, 
615                          GetGVEAHash, NULL, DeleteGVEAHash, CTX_STRBUF, CTX_NONE, IT_NOFLAG);
616         RegisterIterator("PREF:VALID:EMAIL:NAME", 0, NULL, 
617                          GetGVSNHash, NULL, DeleteGVSNHash, CTX_STRBUF, CTX_NONE, IT_NOFLAG);
618
619 }
620 /*@}*/