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