* migrate bstr backend to strbuf
[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 #ifdef DBG_PREFS_HASH
13 inline const char *PrintPref(void *Prefstr)
14 {
15         return ChrPtr(Prefstr);
16 }
17 #endif
18
19 /*
20  * display preferences dialog
21  */
22 void load_preferences(void) {
23         char buf[SIZ];
24         long msgnum = 0L;
25         StrBuf *ReadBuf;
26         
27         serv_printf("GOTO %s", USERCONFIGROOM);
28         serv_getln(buf, sizeof buf);
29         if (buf[0] != '2') return;
30         
31         serv_puts("MSGS ALL|0|1");
32         serv_getln(buf, sizeof buf);
33         if (buf[0] == '8') {
34                 serv_puts("subj|__ WebCit Preferences __");
35                 serv_puts("000");
36         }
37         while (serv_getln(buf, sizeof buf), strcmp(buf, "000")) {
38                 msgnum = atol(buf);
39         }
40
41         if (msgnum > 0L) {
42                 serv_printf("MSG0 %ld", msgnum);
43                 serv_getln(buf, sizeof buf);
44                 if (buf[0] == '1') {
45                         ReadBuf = NewStrBuf();
46                         while (StrBuf_ServGetln(ReadBuf),
47                                (strcmp(ChrPtr(ReadBuf), "text") && 
48                                 strcmp(ChrPtr(ReadBuf), "000"))) {
49                         }
50                         if (!strcmp(ChrPtr(ReadBuf), "text")) {
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(WC->hash_prefs, 
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                         FreeStrBuf(&ReadBuf);
84                 }
85         }
86
87         /* Go back to the room we're supposed to be in */
88         serv_printf("GOTO %s", WC->wc_roomname);
89         serv_getln(buf, sizeof buf);
90 }
91
92 /**
93  * \brief Goto the user's configuration room, creating it if necessary.
94  * \return 0 on success or nonzero upon failure.
95  */
96 int goto_config_room(void) {
97         char buf[SIZ];
98
99         serv_printf("GOTO %s", USERCONFIGROOM);
100         serv_getln(buf, sizeof buf);
101         if (buf[0] != '2') { /* try to create the config room if not there */
102                 serv_printf("CRE8 1|%s|4|0", USERCONFIGROOM);
103                 serv_getln(buf, sizeof buf);
104                 serv_printf("GOTO %s", USERCONFIGROOM);
105                 serv_getln(buf, sizeof buf);
106                 if (buf[0] != '2') return(1);
107         }
108         return(0);
109 }
110
111 /**
112  * \brief save the modifications
113  */
114 void save_preferences(void) {
115         char buf[SIZ];
116         long msgnum = 0L;
117         
118         if (goto_config_room() != 0) return;    /* oh well. */
119         serv_puts("MSGS ALL|0|1");
120         serv_getln(buf, sizeof buf);
121         if (buf[0] == '8') {
122                 serv_puts("subj|__ WebCit Preferences __");
123                 serv_puts("000");
124         }
125         while (serv_getln(buf, sizeof buf), strcmp(buf, "000")) {
126                 msgnum = atol(buf);
127         }
128
129         if (msgnum > 0L) {
130                 serv_printf("DELE %ld", msgnum);
131                 serv_getln(buf, sizeof buf);
132         }
133
134         serv_printf("ENT0 1||0|1|__ WebCit Preferences __|");
135         serv_getln(buf, sizeof buf);
136         if (buf[0] == '4') {
137                 long len;
138                 HashPos *HashPos;
139                 HashList *Hash;
140                 void *Value;
141                 char *Key;
142                 StrBuf *Buf;
143                 StrBuf *SubBuf = NULL;
144                 
145                 Hash = WC->hash_prefs;
146 #ifdef DBG_PREFS_HASH
147                 dbg_PrintHash(Hash, PrintPref, NULL);
148 #endif
149                 HashPos = GetNewHashPos();
150                 while (GetNextHashPos(Hash, HashPos, &len, &Key, &Value)!=0)
151                 {
152                         size_t nchars;
153                         Buf = (StrBuf*) Value;
154                         if (Buf == NULL)
155                                 continue;
156                         nchars = StrLength(Buf);
157                         if (nchars > 80){
158                                 int n = 0;
159                                 size_t offset, nchars;
160                                 if (SubBuf == NULL)
161                                         SubBuf = NewStrBuf();
162                                 nchars = 1;
163                                 offset = 0;
164                                 while (nchars > 0) {
165                                         if (n == 0)
166                                                 nchars = 70;
167                                         else 
168                                                 nchars = 80;
169
170                                         nchars = StrBufSub(SubBuf, Buf, offset, nchars);
171                                         
172                                         if (n == 0)
173                                                 serv_printf("%s|%s", Key, ChrPtr(SubBuf));
174                                         else
175                                                 serv_printf(" %s", ChrPtr(SubBuf));
176
177                                         offset += nchars;
178                                         nchars = StrLength(Buf) - offset;
179                                         n++;
180                                 }
181                                 
182                         }
183                         else
184                                 serv_printf("%s|%s", Key, ChrPtr(Buf));
185                         
186                 }
187                 if (SubBuf != NULL)
188                         FreeStrBuf(&SubBuf);
189                 serv_puts("");
190                 serv_puts("000");
191                 DeleteHashPos(&HashPos);
192         }
193
194         /** Go back to the room we're supposed to be in */
195         serv_printf("GOTO %s", WC->wc_roomname);
196         serv_getln(buf, sizeof buf);
197 }
198
199 /**
200  * \brief query the actual setting of key in the citadel database
201  * \param key config key to query
202  * \param keylen length of the key string
203  * \param value StrBuf-value to the key to get
204  * \returns found?
205  */
206 int get_PREFERENCE(const char *key, size_t keylen, StrBuf **value)
207 {
208         void *hash_value = NULL;
209 #ifdef DBG_PREFS_HASH
210         dbg_PrintHash(WC->hash_prefs, PrintPref, NULL);
211 #endif
212         if (GetHash(WC->hash_prefs, key, keylen, &hash_value) == 0) {
213                 *value = NULL;
214                 return 0;
215         }
216         else {
217                 *value = NULL;
218                 *value = (StrBuf*) hash_value;
219                 return 1;
220         }
221 }
222
223 /**
224  * \brief       Write a key into the webcit preferences database for this user
225  *
226  * \params      key             key whichs value is to be modified
227  * \param keylen length of the key string
228  * \param       value           value to set
229  * \param       save_to_server  1 = flush all data to the server, 0 = cache it for now
230  */
231 void set_PREFERENCE(const char *key, size_t keylen, StrBuf *value, int save_to_server) {
232         
233         Put(WC->hash_prefs, key, keylen, value, HFreeStrBuf);
234         
235         if (save_to_server) save_preferences();
236 }
237
238 int get_PREF_LONG(const char *key, size_t keylen, long *value, long Default)
239 {
240         int ret;
241         StrBuf *val;
242         ret = get_PREFERENCE(key, keylen, &val);
243         if (ret) {
244                 *value = atol(ChrPtr(val));
245         }
246         else {
247                 *value = Default;
248         }
249
250         return ret;
251 }
252
253
254 void set_PREF_LONG(const char *key, size_t keylen, long value, int save_to_server)
255 {
256         StrBuf *val;
257         if (get_PREFERENCE(key, keylen, &val)) {
258                 StrBufPrintf(val, "%ld", value);
259         }
260         else {
261                 val = NewStrBuf();
262                 StrBufPrintf(val, "%ld", value);
263                 set_PREFERENCE(key, keylen, val, save_to_server);
264         }
265 }
266
267
268
269 int get_PREF_YESNO(const char *key, size_t keylen, int *value, int Default)
270 {
271         int ret;
272         StrBuf *val;
273         ret = get_PREFERENCE(key, keylen, &val);
274         if (ret) {
275                 *value = strcmp(ChrPtr(val), "yes") == 0;
276         }
277         else {
278                 *value = Default;
279         }
280
281         return ret;
282 }
283
284 void set_PREF_YESNO(const char *key, size_t keylen, int value, int save_to_server)
285 {
286         StrBuf *val;
287         if (get_PREFERENCE(key, keylen, &val)) {
288                 StrBufPrintf(val, "%s", (value)?"yes":"no");
289         }
290         else {
291                 val = NewStrBuf();
292                 StrBufPrintf(val, "%s", (value)?"yes":"no");
293                 set_PREFERENCE(key, keylen, val, save_to_server);
294         }
295 }
296
297 /** 
298  * \brief display form for changing your preferences and settings
299  */
300 void display_preferences(void)
301 {
302         output_headers(1, 1, 1, 0, 0, 0);
303         StrBuf *ebuf = NULL;
304         int i;
305         long DayEnd, DayStart, WeekStart;
306         int UseSig, ShowEmptyFloors;
307         int time_format;
308         time_t tt;
309         struct tm tm;
310         char daylabel[32];
311         StrBuf *Buf;
312         StrBuf *Signature;
313
314         time_format = get_time_format_cached ();
315
316         wprintf("<div class=\"box\">\n");
317         wprintf("<div class=\"boxlabel\">");
318         wprintf(_("Preferences and settings"));
319         wprintf("</div>");
320
321         wprintf("<div class=\"boxcontent\">");
322
323         /** begin form */
324         wprintf("<form name=\"prefform\" action=\"set_preferences\" "
325                 "method=\"post\">\n");
326         wprintf("<input type=\"hidden\" name=\"nonce\" value=\"%d\">\n", WC->nonce);
327
328         /** begin table */
329         wprintf("<table class=\"altern\">\n");
330
331         /**
332          * Room list view
333          */
334         get_preference("roomlistview", &Buf);
335         wprintf("<tr class=\"even\"><td>");
336         wprintf(_("Room list view"));
337         wprintf("</td><td>");
338
339         wprintf("<input type=\"radio\" name=\"roomlistview\" VALUE=\"folders\"");
340         if (!strcasecmp(ChrPtr(Buf), "folders")) wprintf(" checked");
341         wprintf(">");
342         wprintf(_("Tree (folders) view"));
343         wprintf("</input>&nbsp;&nbsp;&nbsp;");
344
345         wprintf("<input type=\"radio\" name=\"roomlistview\" VALUE=\"rooms\"");
346         if (IsEmptyStr(ChrPtr(Buf)) || !strcasecmp(ChrPtr(Buf), "rooms")) wprintf(" checked");
347         wprintf(">");
348         wprintf(_("Table (rooms) view"));
349         wprintf("</input>\n");
350
351         wprintf("</td></tr>\n");
352
353         /**
354          * Time hour format
355          */
356
357         wprintf("<tr class=\"odd\"><td>");
358         wprintf(_("Time format"));
359         wprintf("</td><td>");
360
361         wprintf("<input type=\"radio\" name=\"calhourformat\" VALUE=\"12\"");
362         if (time_format == WC_TIMEFORMAT_AMPM) 
363                 wprintf(" checked");
364         wprintf(">");
365         wprintf(_("12 hour (am/pm)"));
366         wprintf("</input>&nbsp;&nbsp;&nbsp;");
367
368         wprintf("<input type=\"radio\" name=\"calhourformat\" VALUE=\"24\"");
369         if (time_format == WC_TIMEFORMAT_24)
370                 wprintf(" checked");
371         wprintf(">");
372         wprintf(_("24 hour"));
373         wprintf("</input>\n");
374
375         wprintf("</td></tr>\n");
376
377         /**
378          * Calendar day view -- day start time
379          */
380         get_pref_long("daystart", &DayStart, 8);
381
382         wprintf("<tr class=\"even\"><td>");
383         wprintf(_("Calendar day view begins at:"));
384         wprintf("</td><td>");
385
386         wprintf("<select name=\"daystart\" size=\"1\">\n");
387         for (i=0; i<=23; ++i) {
388
389                 if (time_format == WC_TIMEFORMAT_24) {
390                         wprintf("<option %s value=\"%d\">%d:00</option>\n",
391                                 ((DayStart == i) ? "selected" : ""),
392                                 i, i
393                         );
394                 }
395                 else {
396                         wprintf("<option %s value=\"%d\">%s</option>\n",
397                                 ((DayStart == i) ? "selected" : ""),
398                                 i, hourname[i]
399                         );
400                 }
401
402         }
403         wprintf("</select>\n");
404         wprintf("</td></tr>\n");
405
406         /**
407          * Calendar day view -- day end time
408          */
409         get_pref_long("dayend", &DayEnd, 17);
410
411         wprintf("<tr class=\"odd\"><td>");
412         wprintf(_("Calendar day view ends at:"));
413         wprintf("</td><td>");
414
415         wprintf("<select name=\"dayend\" size=\"1\">\n");
416         for (i=0; i<=23; ++i) {
417
418                 if (time_format == WC_TIMEFORMAT_24) {
419                         wprintf("<option %s value=\"%d\">%d:00</option>\n",
420                                 ((DayEnd == i) ? "selected" : ""),
421                                 i, i
422                         );
423                 }
424                 else {
425                         wprintf("<option %s value=\"%d\">%s</option>\n",
426                                 ((DayEnd == i) ? "selected" : ""),
427                                 i, hourname[i]
428                         );
429                 }
430
431         }
432         wprintf("</select>\n");
433         wprintf("</td></tr>\n");
434
435         /**
436          * Day of week to begin calendar month view
437          */
438         get_pref_long("weekstart", &WeekStart, 17);
439         wprintf("<tr class=\"even\"><td>");
440         wprintf(_("Week starts on:"));
441         wprintf("</td><td>");
442
443         wprintf("<select name=\"weekstart\" size=\"1\">\n");
444
445         for (i=0; i<=1; ++i) {
446                 tt = time(NULL);
447                 localtime_r(&tt, &tm);
448                 tm.tm_wday = i;
449                 wc_strftime(daylabel, sizeof daylabel, "%A", &tm);
450
451                 wprintf("<option %s value=\"%d\">%s</option>\n",
452                         ((WeekStart == i) ? "selected" : ""),
453                         i, daylabel
454                 );
455         }
456
457         wprintf("</select>\n");
458         wprintf("</td></tr>\n");
459
460         /**
461          * Signature
462          */
463         get_pref_yesno("use_sig", &UseSig, 0);
464         wprintf("<tr class=\"odd\"><td>");
465         wprintf(_("Attach signature to email messages?"));
466         wprintf("</td><td>");
467
468         wprintf("       <script type=\"text/javascript\">                                       "
469                 "       function show_or_hide_sigbox() {                                        "
470                 "               if ( $F('yes_sig') ) {                                          "
471                 "                       $('signature_box').style.display = 'inline';            "
472                 "               }                                                               "
473                 "               else {                                                          "
474                 "                       $('signature_box').style.display = 'none';              "
475                 "               }                                                               "
476                 "       }                                                                       "
477                 "       </script>                                                               "
478         );
479
480         wprintf("<input type=\"radio\" id=\"no_sig\" name=\"use_sig\" VALUE=\"no\"");
481         if (!UseSig) wprintf(" checked");
482         wprintf(" onChange=\"show_or_hide_sigbox();\" >");
483         wprintf(_("No signature"));
484         wprintf("</input>&nbsp,&nbsp;&nbsp;\n");
485
486         wprintf("<input type=\"radio\" id=\"yes_sig\" name=\"use_sig\" VALUE=\"yes\"");
487         if (UseSig) wprintf(" checked");
488         wprintf(" onChange=\"show_or_hide_sigbox();\" >");
489         wprintf(_("Use this signature:"));
490         wprintf("<div id=\"signature_box\">"
491                 "<br><textarea name=\"signature\" cols=\"40\" rows=\"5\">"
492         );
493
494         get_preference("signature", &Signature);
495         ebuf = NewStrBuf();
496         StrBufEUid_unescapize(ebuf, Signature);
497         escputs((char*)ChrPtr(ebuf));///TODO
498         FreeStrBuf(&ebuf);
499         wprintf("</textarea>"
500                 "</div>"
501         );
502
503         wprintf("</input>\n");
504
505         wprintf("</td></tr>\n");
506
507         wprintf("       <script type=\"text/javascript\">       "
508                 "       show_or_hide_sigbox();                  "
509                 "       </script>                               "
510         );
511
512         /** Character set to assume is in use for improperly encoded headers */
513         if (!get_preference("default_header_charset", &Buf)) {
514                 Buf = NewStrBuf();////TODO: freeme!
515                 StrBufPrintf(Buf, "%s", "UTF-8");
516         }
517         wprintf("<tr class=\"even\"><td>");
518         wprintf(_("Default character set for email headers:"));
519         wprintf("</td><td>");
520         wprintf("<input type=\"text\" NAME=\"default_header_charset\" MAXLENGTH=\"32\" VALUE=\"");
521         escputs((char*)ChrPtr(Buf)); // here shouldn't be bad chars, so...
522         wprintf("\">");
523         wprintf("</td></tr>");
524
525         /**
526          * Show empty floors?
527          */
528
529         get_pref_yesno("emptyfloors", &ShowEmptyFloors, 0);
530         wprintf("<tr class=\"odd\"><td>");
531         wprintf(_("Show empty floors"));
532         wprintf("</td><td>");
533
534         wprintf("<input type=\"radio\" name=\"emptyfloors\" VALUE=\"yes\"");
535         if (ShowEmptyFloors) wprintf(" checked");
536         wprintf(">");
537         wprintf(_("Yes"));
538         wprintf("</input>&nbsp;&nbsp;&nbsp;");
539
540         wprintf("<input type=\"radio\" name=\"emptyfloors\" VALUE=\"no\"");
541         if (!ShowEmptyFloors) wprintf(" checked");
542         wprintf(">");
543         wprintf(_("No"));
544         wprintf("</input>\n");
545
546         wprintf("</td></tr>\n");
547
548         /** end table */
549         wprintf("</table>\n");
550
551         /** submit buttons */
552         wprintf("<div class=\"buttons\"> ");
553         wprintf("<input type=\"submit\" name=\"change_button\" value=\"%s\">"
554                 "&nbsp;"
555                 "<INPUT type=\"submit\" name=\"cancel_button\" value=\"%s\">\n",
556                 _("Change"),
557                 _("Cancel")
558         );
559         wprintf("</div>\n");
560
561         /** end form */
562         wprintf("</form>\n");
563         wprintf("</div>\n");
564         wDumpContent(1);
565 }
566
567 /**
568  * \brief Commit new preferences and settings
569  */
570 void set_preferences(void)
571 {
572         long fmt;
573         StrBuf *buf, *encBuf;
574         int *time_format_cache;
575         
576         time_format_cache = &(WC->time_format_cache);
577
578         if (!havebstr("change_button")) {
579                 safestrncpy(WC->ImportantMessage, 
580                         _("Cancelled.  No settings were changed."),
581                         sizeof WC->ImportantMessage);
582                 display_main_menu();
583                 return;
584         }
585
586         /**
587          * Set the last argument to 1 only for the final setting, so
588          * we don't send the prefs file to the server repeatedly
589          */
590         set_preference("roomlistview", NewStrBufPlain(bstr("roomlistview"), -1), 0);
591         fmt = lbstr("calhourformat");
592         set_pref_long("calhourformat", fmt, 0);
593         if (fmt == 24) 
594                 *time_format_cache = WC_TIMEFORMAT_24;
595         else
596                 *time_format_cache = WC_TIMEFORMAT_AMPM;
597
598         set_pref_long("weekstart", lbstr("weekstart"), 0);
599         set_pref_yesno("use_sig", yesbstr("use_sig"), 0);
600         set_pref_long("daystart", lbstr("daystart"), 0);
601         set_pref_long("dayend", lbstr("dayend"), 0);
602         set_preference("default_header_charset", NewStrBufPlain(bstr("default_header_charset"), -1), 0);
603         set_preference("emptyfloors", NewStrBufPlain(bstr("emptyfloors"), -1), 0);
604
605         buf = NewStrBufPlain(bstr("signature"), -1);
606         encBuf = NewStrBuf();
607         StrBufEUid_escapize(encBuf, buf);
608         set_preference("signature", encBuf, 1);
609
610         display_main_menu();
611 }
612
613
614 void 
615 InitModule_PREFERENCES
616 (void)
617 {
618         WebcitAddUrlHandler(HKEY("display_preferences"), display_preferences, 0);
619         WebcitAddUrlHandler(HKEY("set_preferences"), set_preferences, 0);
620 }
621 /*@}*/