8118e6eb8792518d513ba7e6a738911729660b4d
[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", 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", 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, 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, 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, int nArgs, WCTemplateToken *Token, void *Context, int ContextType) {
364         wprintf("<a href=\"change_start_page?startpage=");
365         urlescputs(WC->this_page);
366         wprintf("\">");
367         wprintf(_("Make this my start page"));
368         wprintf("</a>");
369 #ifdef TECH_PREVIEW
370         wprintf("<br/><a href=\"rss?room=");
371         urlescputs(WC->wc_roomname);
372         wprintf("\" title=\"RSS 2.0 feed for ");
373         escputs(WC->wc_roomname);
374         wprintf("\"><img alt=\"RSS\" border=\"0\" src=\"static/xml_button.gif\"/></a>\n");
375 #endif
376 }
377
378
379 /*
380  * Change the user's start page
381  */
382 void change_start_page(void) {
383
384         if (bstr("startpage") == NULL) {
385                 safestrncpy(WC->ImportantMessage,
386                         _("You no longer have a start page selected."),
387                         sizeof WC->ImportantMessage);
388                 display_main_menu();
389                 return;
390         }
391
392         set_preference("startpage", NewStrBufPlain(bstr("startpage"), -1), 1);
393
394         output_headers(1, 1, 0, 0, 0, 0);
395         do_template("newstartpage", NULL);
396         wDumpContent(1);
397 }
398
399
400
401 /**
402  * \brief Commit new preferences and settings
403  */
404 void set_preferences(void)
405 {
406         long fmt;
407         StrBuf *buf, *encBuf;
408         int *time_format_cache;
409         
410          time_format_cache = &(WC->time_format_cache);
411
412          if (!havebstr("change_button")) {
413                  safestrncpy(WC->ImportantMessage, 
414                          _("Cancelled.  No settings were changed."),
415                          sizeof WC->ImportantMessage);
416                  display_main_menu();
417                  return;
418          }
419
420          /**
421           * Set the last argument to 1 only for the final setting, so
422           * we don't send the prefs file to the server repeatedly
423           */
424          set_preference("roomlistview", NewStrBufPlain(bstr("roomlistview"), -1), 0);
425          fmt = lbstr("calhourformat");
426          set_pref_long("calhourformat", fmt, 0);
427          if (fmt == 24) 
428                  *time_format_cache = WC_TIMEFORMAT_24;
429          else
430                  *time_format_cache = WC_TIMEFORMAT_AMPM;
431
432          set_pref_long("weekstart", lbstr("weekstart"), 0);
433          set_pref_yesno("use_sig", yesbstr("use_sig"), 0);
434          set_pref_long("daystart", lbstr("daystart"), 0);
435          set_pref_long("dayend", lbstr("dayend"), 0);
436          set_preference("default_header_charset", NewStrBufPlain(bstr("default_header_charset"), -1), 0);
437          set_preference("emptyfloors", NewStrBufPlain(bstr("emptyfloors"), -1), 0);
438          set_preference("defaultfrom", NewStrBufDup(sbstr("defaultfrom")), 0);
439          set_preference("defaultname", NewStrBufDup(sbstr("defaultname")), 0);
440          set_preference("defaulthandle", NewStrBufDup(sbstr("defaulthandle")), 0);
441
442
443         buf = NewStrBufPlain(bstr("signature"), -1);
444         encBuf = NewStrBuf();
445         StrBufEUid_escapize(encBuf, buf);
446         set_preference("signature", encBuf, 1);
447         FreeStrBuf(&buf);
448
449         display_main_menu();
450 }
451
452
453 #define PRF_STRING 1
454 #define PRF_INT 2
455 #define PRF_QP_STRING 3
456 #define PRF_YESNO 4
457
458
459 void tmplput_CFG_Value(StrBuf *Target, int nArgs, WCTemplateToken *Tokens, void *Context, int ContextType)
460 {
461         StrBuf *Setting;
462         if (get_PREFERENCE(TKEY(0), &Setting))
463         StrBufAppendTemplate(Target, nArgs, Tokens, Context, ContextType, Setting, 1);
464 }
465
466 void tmplput_CFG_Descr(StrBuf *Target, int nArgs, WCTemplateToken *Tokens, void *Context, int ContextType)
467 {
468         const char *SettingStr;
469         SettingStr = PrefGetLocalStr(TKEY(0));
470         if (SettingStr != NULL) 
471                 StrBufAppendBufPlain(Target, SettingStr, -1, 0);
472 }
473
474
475 void CfgZoneTempl(StrBuf *TemplBuffer, void *vContext, WCTemplateToken *Token)
476 {
477         StrBuf *Zone = (StrBuf*) vContext;
478
479         SVPutBuf("ZONENAME", Zone, 1);
480 }
481
482 int ConditionalPreference(WCTemplateToken *Tokens, void *Context, int ContextType)
483 {
484         StrBuf *Pref;
485
486         if (!get_PREFERENCE(TKEY(2), &Pref)) 
487                 return 0;
488         
489         if (Tokens->nParameters == 3) {
490                 return 1;
491         }
492         else if (Tokens->Params[3]->Type == TYPE_STR)
493                 return ((Tokens->Params[3]->len == StrLength(Pref)) &&
494                         (strcmp(Tokens->Params[3]->Start, ChrPtr(Pref)) == 0));
495         else 
496                 return (StrTol(Pref) == Tokens->Params[3]->lvalue);
497 }
498
499 int ConditionalHazePreference(WCTemplateToken *Tokens, void *Context, int ContextType)
500 {
501         StrBuf *Pref;
502
503         if (!get_PREFERENCE(TKEY(2), &Pref) || 
504             (Pref == NULL)) 
505                 return 0;
506         else 
507                 return 1;
508 }
509
510 HashList *GetGVEAHash(StrBuf *Target, int nArgs, WCTemplateToken *Tokens, void *Context, int ContextType)
511 {
512         StrBuf *Rcp;
513         HashList *List = NULL;
514         int Done = 0;
515         int i, n = 1;
516         char N[64];
517
518         Rcp = NewStrBuf();
519         serv_puts("GVEA");
520         StrBuf_ServGetln(Rcp);
521         if (ChrPtr(Rcp)[0] == '1') {
522                 FlushStrBuf(Rcp);
523                 List = NewHash(1, NULL);
524                 while (!Done && (StrBuf_ServGetln(Rcp)>=0)) {
525                         if ( (StrLength(Rcp)==3) && 
526                              !strcmp(ChrPtr(Rcp), "000")) 
527                         {
528                                 Done = 1;
529                         }
530                         else {
531                                 i = snprintf(N, sizeof(N), "%d", n);
532                                 StrBufTrim(Rcp);
533                                 Put(List, N, i, Rcp, HFreeStrBuf);
534                                 Rcp = NewStrBuf();
535                         }
536                         n++;
537                 }
538         }
539         FreeStrBuf(&Rcp);
540         return List;
541 }
542 void DeleteGVEAHash(HashList **KillMe)
543 {
544         DeleteHash(KillMe);
545 }
546
547 HashList *GetGVSNHash(StrBuf *Target, int nArgs, WCTemplateToken *Tokens, void *Context, int ContextType)
548 {
549         StrBuf *Rcp;
550         HashList *List = NULL;
551         int Done = 0;
552         int i, n = 1;
553         char N[64];
554
555         Rcp = NewStrBuf();
556         serv_puts("GVSN");
557         StrBuf_ServGetln(Rcp);
558         if (ChrPtr(Rcp)[0] == '1') {
559                 FlushStrBuf(Rcp);
560                 List = NewHash(1, NULL);
561                 while (!Done && (StrBuf_ServGetln(Rcp)>=0)) {
562                         if ( (StrLength(Rcp)==3) && 
563                              !strcmp(ChrPtr(Rcp), "000")) 
564                         {
565                                 Done = 1;
566                         }
567                         else {
568                                 i = snprintf(N, sizeof(N), "%d", n);
569                                 StrBufTrim(Rcp);
570                                 Put(List, N, i, Rcp, HFreeStrBuf);
571                                 Rcp = NewStrBuf();
572                         }
573                         n++;
574                 }
575         }
576         FreeStrBuf(&Rcp);
577         return List;
578 }
579 void DeleteGVSNHash(HashList **KillMe)
580 {
581         DeleteHash(KillMe);
582 }
583
584
585 void 
586 InitModule_PREFERENCES
587 (void)
588 {
589         WebcitAddUrlHandler(HKEY("set_preferences"), set_preferences, 0);
590         WebcitAddUrlHandler(HKEY("change_start_page"), change_start_page, 0);
591
592         RegisterPreference("roomlistview",_("Room list view"),PRF_STRING);
593         RegisterPreference("calhourformat",_("Time format"), PRF_INT);
594         RegisterPreference("daystart", _("Calendar day view begins at:"), PRF_INT);
595         RegisterPreference("dayend", _("Calendar day view ends at:"), PRF_INT);
596         RegisterPreference("weekstart",_("Week starts on:"), PRF_INT);
597
598         RegisterPreference("use_sig",_("Attach signature to email messages?"), PRF_YESNO);
599         RegisterPreference("signature",_("Use this signature:"),PRF_QP_STRING);
600         RegisterPreference("default_header_charset", _("Default character set for email headers:") ,PRF_STRING);
601         RegisterPreference("emptyfloors", _("Show empty floors"), PRF_YESNO);
602         RegisterPreference("defaultfrom", _("Preferred email address"), PRF_STRING);
603         RegisterPreference("defaultname", _("Preferred display name for email messages"), PRF_STRING);
604         RegisterPreference("defaulthandle", _("Preferred display name for bulletin board posts"), PRF_STRING);
605         
606         RegisterNamespace("PREF:VALUE", 1, 2, tmplput_CFG_Value, CTX_NONE);
607         RegisterNamespace("PREF:DESCR", 1, 1, tmplput_CFG_Descr, CTX_NONE);
608         RegisterIterator("PREF:ZONE", 0, ZoneHash, NULL, CfgZoneTempl, NULL, CTX_PREF, CTX_NONE, IT_NOFLAG);
609
610         RegisterConditional(HKEY("COND:PREF"), 4, ConditionalPreference, CTX_NONE);
611         RegisterConditional(HKEY("COND:PREF:SET"), 4, ConditionalHazePreference, CTX_NONE);
612         
613         RegisterIterator("PREF:VALID:EMAIL:ADDR", 0, NULL, 
614                          GetGVEAHash, NULL, DeleteGVEAHash, CTX_STRBUF, CTX_NONE, IT_NOFLAG);
615         RegisterIterator("PREF:VALID:EMAIL:NAME", 0, NULL, 
616                          GetGVSNHash, NULL, DeleteGVSNHash, CTX_STRBUF, CTX_NONE, IT_NOFLAG);
617
618 }
619 /*@}*/