4 * Manage user preferences with a little help from the Citadel server.
13 HashList *PreferenceHooks;
15 typedef struct _Prefs {
21 void RegisterPreference(const char *Setting, const char *PrefStr, long Type)
23 Prefs *Newpref = (Prefs*) malloc(sizeof(Prefs));
24 Newpref->Setting = Setting;
25 Newpref->PrefStr = PrefStr;
27 Put(PreferenceHooks, Setting, strlen(Setting), Newpref, NULL);
30 const char *PrefGetLocalStr(const char *Setting, long len)
33 if (GetHash(PreferenceHooks, Setting, len, &hash_value) != 0) {
34 Prefs *Newpref = (Prefs*) hash_value;
35 return _(Newpref->PrefStr);
42 inline const char *PrintPref(void *Prefstr)
44 return ChrPtr(Prefstr);
49 void ParsePref(HashList **List, StrBuf *ReadBuf)
53 StrBuf *LastData = NULL;
56 while (StrBuf_ServGetln(ReadBuf),
57 strcmp(ChrPtr(ReadBuf), "000"))
59 if ((ChrPtr(ReadBuf)[0] == ' ') &&
61 StrBufAppendBuf(Data, ReadBuf, 1);
64 LastData = Data = NewStrBuf();
65 StrBufExtract_token(Key, ReadBuf, 0, '|');
66 StrBufExtract_token(Data, ReadBuf, 1, '|');
67 if (!IsEmptyStr(ChrPtr(Key)))
70 ChrPtr(Key), StrLength(Key),
86 * display preferences dialog
88 void load_preferences(void)
94 serv_printf("GOTO %s", USERCONFIGROOM);
95 serv_getln(buf, sizeof buf);
96 if (buf[0] != '2') return;
98 serv_puts("MSGS ALL|0|1");
99 serv_getln(buf, sizeof buf);
101 serv_puts("subj|__ WebCit Preferences __");
104 while (serv_getln(buf, sizeof buf), strcmp(buf, "000")) {
109 serv_printf("MSG0 %ld", msgnum);
110 serv_getln(buf, sizeof buf);
112 ReadBuf = NewStrBuf();
113 while (StrBuf_ServGetln(ReadBuf),
114 (strcmp(ChrPtr(ReadBuf), "text") &&
115 strcmp(ChrPtr(ReadBuf), "000"))) {
117 if (!strcmp(ChrPtr(ReadBuf), "text")) {
118 ParsePref(&WC->hash_prefs, ReadBuf);
121 FreeStrBuf(&ReadBuf);
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);
130 * \brief Goto the user's configuration room, creating it if necessary.
131 * \return 0 on success or nonzero upon failure.
133 int goto_config_room(void) {
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);
148 void WritePrefsToServer(HashList *Hash)
155 StrBuf *SubBuf = NULL;
157 Hash = WC->hash_prefs;
158 #ifdef DBG_PREFS_HASH
159 dbg_PrintHash(Hash, PrintPref, NULL);
161 HashPos = GetNewHashPos(Hash, 0);
162 while (GetNextHashPos(Hash, HashPos, &len, &Key, &Value)!=0)
165 Buf = (StrBuf*) Value;
168 nchars = StrLength(Buf);
171 size_t offset, nchars;
173 SubBuf = NewStrBuf();
182 nchars = StrBufSub(SubBuf, Buf, offset, nchars);
185 serv_printf("%s|%s", Key, ChrPtr(SubBuf));
187 serv_printf(" %s", ChrPtr(SubBuf));
190 nchars = StrLength(Buf) - offset;
196 serv_printf("%s|%s", Key, ChrPtr(Buf));
201 DeleteHashPos(&HashPos);
205 * \brief save the modifications
207 void save_preferences(void) {
211 if (goto_config_room() != 0) return; /* oh well. */
212 serv_puts("MSGS ALL|0|1");
213 serv_getln(buf, sizeof buf);
215 serv_puts("subj|__ WebCit Preferences __");
218 while (serv_getln(buf, sizeof buf), strcmp(buf, "000")) {
223 serv_printf("DELE %ld", msgnum);
224 serv_getln(buf, sizeof buf);
227 serv_printf("ENT0 1||0|1|__ WebCit Preferences __|");
228 serv_getln(buf, sizeof buf);
231 WritePrefsToServer(WC->hash_prefs);
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);
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
248 int get_PREFERENCE(const char *key, size_t keylen, StrBuf **value)
250 void *hash_value = NULL;
251 #ifdef DBG_PREFS_HASH
252 dbg_PrintHash(WC->hash_prefs, PrintPref, NULL);
254 if (GetHash(WC->hash_prefs, key, keylen, &hash_value) == 0) {
260 *value = (StrBuf*) hash_value;
266 * \brief Write a key into the webcit preferences database for this user
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
273 void set_PREFERENCE(const char *key, size_t keylen, StrBuf *value, int save_to_server) {
275 Put(WC->hash_prefs, key, keylen, value, HFreeStrBuf);
277 if (save_to_server) save_preferences();
280 int get_PREF_LONG(const char *key, size_t keylen, long *value, long Default)
284 ret = get_PREFERENCE(key, keylen, &val);
286 *value = atol(ChrPtr(val));
296 void set_PREF_LONG(const char *key, size_t keylen, long value, int save_to_server)
299 if (get_PREFERENCE(key, keylen, &val)) {
300 StrBufPrintf(val, "%ld", value);
304 StrBufPrintf(val, "%ld", value);
305 set_PREFERENCE(key, keylen, val, save_to_server);
311 int get_PREF_YESNO(const char *key, size_t keylen, int *value, int Default)
315 ret = get_PREFERENCE(key, keylen, &val);
317 *value = strcmp(ChrPtr(val), "yes") == 0;
326 void set_PREF_YESNO(const char *key, size_t keylen, int value, int save_to_server)
329 if (get_PREFERENCE(key, keylen, &val)) {
330 StrBufPrintf(val, "%s", (value)?"yes":"no");
334 StrBufPrintf(val, "%s", (value)?"yes":"no");
335 set_PREFERENCE(key, keylen, val, save_to_server);
339 StrBuf *get_ROOM_PREFS(const char *key, size_t keylen)
341 StrBuf *pref_name, *pref_value;
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);
350 void set_ROOM_PREFS(const char *key, size_t keylen, StrBuf *value, int save_to_server)
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);
361 * \brief display form for changing your preferences and settings
363 void display_preferences(void)
365 output_headers(1, 1, 1, 0, 0, 0);
368 long DayEnd, DayStart, WeekStart;
369 int UseSig, ShowEmptyFloors;
377 time_format = get_time_format_cached ();
379 wprintf("<div class=\"box\">\n");
380 wprintf("<div class=\"boxlabel\">");
381 wprintf(_("Preferences and settings"));
384 wprintf("<div class=\"boxcontent\">");
387 wprintf("<form name=\"prefform\" action=\"set_preferences\" "
388 "method=\"post\">\n");
389 wprintf("<input type=\"hidden\" name=\"nonce\" value=\"%d\">\n", WC->nonce);
392 wprintf("<table class=\"altern\">\n");
397 get_preference("roomlistview", &Buf);
398 wprintf("<tr class=\"even\"><td>");
399 wprintf(PrefGetLocalStr(HKEY("roomlistview")));
400 wprintf("</td><td>");
402 wprintf("<input type=\"radio\" name=\"roomlistview\" VALUE=\"folders\"");
403 if (!strcasecmp(ChrPtr(Buf), "folders")) wprintf(" checked");
405 wprintf(_("Tree (folders) view"));
406 wprintf("</input> ");
408 wprintf("<input type=\"radio\" name=\"roomlistview\" VALUE=\"rooms\"");
409 if (IsEmptyStr(ChrPtr(Buf)) || !strcasecmp(ChrPtr(Buf), "rooms")) wprintf(" checked");
411 wprintf(_("Table (rooms) view"));
412 wprintf("</input>\n");
414 wprintf("</td></tr>\n");
420 wprintf("<tr class=\"odd\"><td>");
421 wprintf(PrefGetLocalStr(HKEY("calhourformat")));
422 wprintf("</td><td>");
424 wprintf("<input type=\"radio\" name=\"calhourformat\" VALUE=\"12\"");
425 if (time_format == WC_TIMEFORMAT_AMPM)
428 wprintf(_("12 hour (am/pm)"));
429 wprintf("</input> ");
431 wprintf("<input type=\"radio\" name=\"calhourformat\" VALUE=\"24\"");
432 if (time_format == WC_TIMEFORMAT_24)
435 wprintf(_("24 hour"));
436 wprintf("</input>\n");
438 wprintf("</td></tr>\n");
441 * Calendar day view -- day start time
443 get_pref_long("daystart", &DayStart, 8);
445 wprintf("<tr class=\"even\"><td>");
446 wprintf(PrefGetLocalStr(HKEY("daystart")));
447 wprintf("</td><td>");
449 wprintf("<select name=\"daystart\" size=\"1\">\n");
450 for (i=0; i<=23; ++i) {
452 if (time_format == WC_TIMEFORMAT_24) {
453 wprintf("<option %s value=\"%d\">%d:00</option>\n",
454 ((DayStart == i) ? "selected" : ""),
459 wprintf("<option %s value=\"%d\">%s</option>\n",
460 ((DayStart == i) ? "selected" : ""),
466 wprintf("</select>\n");
467 wprintf("</td></tr>\n");
470 * Calendar day view -- day end time
472 get_pref_long("dayend", &DayEnd, 17);
474 wprintf("<tr class=\"odd\"><td>");
475 wprintf(PrefGetLocalStr(HKEY("dayend")));
476 wprintf("</td><td>");
478 wprintf("<select name=\"dayend\" size=\"1\">\n");
479 for (i=0; i<=23; ++i) {
481 if (time_format == WC_TIMEFORMAT_24) {
482 wprintf("<option %s value=\"%d\">%d:00</option>\n",
483 ((DayEnd == i) ? "selected" : ""),
488 wprintf("<option %s value=\"%d\">%s</option>\n",
489 ((DayEnd == i) ? "selected" : ""),
495 wprintf("</select>\n");
496 wprintf("</td></tr>\n");
499 * Day of week to begin calendar month view
501 get_pref_long("weekstart", &WeekStart, 17);
502 wprintf("<tr class=\"even\"><td>");
503 wprintf(PrefGetLocalStr(HKEY("weekstart")));
504 wprintf("</td><td>");
506 wprintf("<select name=\"weekstart\" size=\"1\">\n");
508 for (i=0; i<=1; ++i) {
510 localtime_r(&tt, &tm);
512 wc_strftime(daylabel, sizeof daylabel, "%A", &tm);
514 wprintf("<option %s value=\"%d\">%s</option>\n",
515 ((WeekStart == i) ? "selected" : ""),
520 wprintf("</select>\n");
521 wprintf("</td></tr>\n");
526 get_pref_yesno("use_sig", &UseSig, 0);
527 wprintf("<tr class=\"odd\"><td>");
528 wprintf(_("Attach signature to email messages?"));
529 wprintf("</td><td>");
531 wprintf(" <script type=\"text/javascript\"> "
532 " function show_or_hide_sigbox() { "
533 " if ( $F('yes_sig') ) { "
534 " $('signature_box').style.display = 'inline'; "
537 " $('signature_box').style.display = 'none'; "
543 wprintf(PrefGetLocalStr(HKEY("use_sig")));
545 wprintf("<input type=\"radio\" id=\"no_sig\" name=\"use_sig\" VALUE=\"no\"");
546 if (!UseSig) wprintf(" checked");
547 wprintf(" onChange=\"show_or_hide_sigbox();\" >");
548 wprintf(_("No signature"));
549 wprintf("</input> , \n");
551 wprintf("<input type=\"radio\" id=\"yes_sig\" name=\"use_sig\" VALUE=\"yes\"");
552 if (UseSig) wprintf(" checked");
553 wprintf(" onChange=\"show_or_hide_sigbox();\" >");
554 wprintf(PrefGetLocalStr(HKEY("signature")));
555 wprintf("<div id=\"signature_box\">"
556 "<br><textarea name=\"signature\" cols=\"40\" rows=\"5\">"
559 get_preference("signature", &Signature);
561 StrBufEUid_unescapize(ebuf, Signature);
564 wprintf("</textarea>"
568 wprintf("</input>\n");
570 wprintf("</td></tr>\n");
572 wprintf(" <script type=\"text/javascript\"> "
573 " show_or_hide_sigbox(); "
577 /** Character set to assume is in use for improperly encoded headers */
578 if (!get_preference("default_header_charset", &Buf)) {
580 StrBufPrintf(Buf, "%s", "UTF-8");
581 set_preference("default_header_charset", Buf, 0);
583 wprintf("<tr class=\"even\"><td>");
584 wprintf(PrefGetLocalStr(HKEY("default_header_charset")));
585 wprintf("</td><td>");
586 wprintf("<input type=\"text\" NAME=\"default_header_charset\" MAXLENGTH=\"32\" VALUE=\"");
587 StrEscPuts(Buf); // here shouldn't be bad chars, so...
589 wprintf("</td></tr>");
595 get_pref_yesno("emptyfloors", &ShowEmptyFloors, 0);
596 wprintf("<tr class=\"odd\"><td>");
597 wprintf(PrefGetLocalStr(HKEY("emptyfloors")));
598 wprintf("</td><td>");
600 wprintf("<input type=\"radio\" name=\"emptyfloors\" VALUE=\"yes\"");
601 if (ShowEmptyFloors) wprintf(" checked");
604 wprintf("</input> ");
606 wprintf("<input type=\"radio\" name=\"emptyfloors\" VALUE=\"no\"");
607 if (!ShowEmptyFloors) wprintf(" checked");
610 wprintf("</input>\n");
612 wprintf("</td></tr>\n");
615 wprintf("</table>\n");
617 /** submit buttons */
618 wprintf("<div class=\"buttons\"> ");
619 wprintf("<input type=\"submit\" name=\"change_button\" value=\"%s\">"
621 "<INPUT type=\"submit\" name=\"cancel_button\" value=\"%s\">\n",
628 wprintf("</form>\n");
637 * Offer to make any page the user's "start page."
639 void offer_start_page(StrBuf *Target, int nArgs, WCTemplateToken *Token, void *Context, int ContextType) {
640 wprintf("<a href=\"change_start_page?startpage=");
641 urlescputs(WC->this_page);
643 wprintf(_("Make this my start page"));
646 wprintf("<br/><a href=\"rss?room=");
647 urlescputs(WC->wc_roomname);
648 wprintf("\" title=\"RSS 2.0 feed for ");
649 escputs(WC->wc_roomname);
650 wprintf("\"><img alt=\"RSS\" border=\"0\" src=\"static/xml_button.gif\"/></a>\n");
656 * Change the user's start page
658 void change_start_page(void) {
660 if (bstr("startpage") == NULL) {
661 safestrncpy(WC->ImportantMessage,
662 _("You no longer have a start page selected."),
663 sizeof WC->ImportantMessage);
668 set_preference("startpage", NewStrBufPlain(bstr("startpage"), -1), 1);
670 output_headers(1, 1, 0, 0, 0, 0);
671 do_template("newstartpage", NULL);
678 * \brief Commit new preferences and settings
680 void set_preferences(void)
683 StrBuf *buf, *encBuf;
684 int *time_format_cache;
686 time_format_cache = &(WC->time_format_cache);
688 if (!havebstr("change_button")) {
689 safestrncpy(WC->ImportantMessage,
690 _("Cancelled. No settings were changed."),
691 sizeof WC->ImportantMessage);
697 * Set the last argument to 1 only for the final setting, so
698 * we don't send the prefs file to the server repeatedly
700 set_preference("roomlistview", NewStrBufPlain(bstr("roomlistview"), -1), 0);
701 fmt = lbstr("calhourformat");
702 set_pref_long("calhourformat", fmt, 0);
704 *time_format_cache = WC_TIMEFORMAT_24;
706 *time_format_cache = WC_TIMEFORMAT_AMPM;
708 set_pref_long("weekstart", lbstr("weekstart"), 0);
709 set_pref_yesno("use_sig", yesbstr("use_sig"), 0);
710 set_pref_long("daystart", lbstr("daystart"), 0);
711 set_pref_long("dayend", lbstr("dayend"), 0);
712 set_preference("default_header_charset", NewStrBufPlain(bstr("default_header_charset"), -1), 0);
713 set_preference("emptyfloors", NewStrBufPlain(bstr("emptyfloors"), -1), 0);
714 set_preference("defaultfrom", NewStrBufDup(sbstr("defaultfrom")), 0);
715 set_preference("defaultname", NewStrBufDup(sbstr("defaultname")), 0);
716 set_preference("defaulthandle", NewStrBufDup(sbstr("defaulthandle")), 0);
719 buf = NewStrBufPlain(bstr("signature"), -1);
720 encBuf = NewStrBuf();
721 StrBufEUid_escapize(encBuf, buf);
722 set_preference("signature", encBuf, 1);
731 #define PRF_QP_STRING 3
735 void tmplput_CFG_Value(StrBuf *Target, int nArgs, WCTemplateToken *Tokens, void *Context, int ContextType)
738 if (get_PREFERENCE(Tokens->Params[0]->Start,
739 Tokens->Params[0]->len,
741 StrBufAppendTemplate(Target, nArgs, Tokens, Context, ContextType, Setting, 1);
744 void tmplput_CFG_Descr(StrBuf *Target, int nArgs, WCTemplateToken *Tokens, void *Context, int ContextType)
746 const char *SettingStr;
747 SettingStr = PrefGetLocalStr(Tokens->Params[0]->Start,
748 Tokens->Params[0]->len);
749 if (SettingStr != NULL)
750 StrBufAppendBufPlain(Target, SettingStr, -1, 0);
754 void CfgZoneTempl(StrBuf *TemplBuffer, void *vContext, WCTemplateToken *Token)
756 StrBuf *Zone = (StrBuf*) vContext;
758 SVPutBuf("ZONENAME", Zone, 1);
761 int ConditionalPreference(WCTemplateToken *Tokens, void *Context, int ContextType)
765 if (!get_PREFERENCE(Tokens->Params[2]->Start,
766 Tokens->Params[2]->len,
770 if (Tokens->nParameters == 3) {
773 else if (Tokens->Params[3]->Type == TYPE_STR)
774 return ((Tokens->Params[3]->len == StrLength(Pref)) &&
775 (strcmp(Tokens->Params[3]->Start, ChrPtr(Pref)) == 0));
777 return (StrTol(Pref) == Tokens->Params[3]->lvalue);
780 int ConditionalHazePreference(WCTemplateToken *Tokens, void *Context, int ContextType)
784 if (!get_PREFERENCE(Tokens->Params[2]->Start,
785 Tokens->Params[2]->len,
793 HashList *GetGVEAHash(StrBuf *Target, int nArgs, WCTemplateToken *Tokens, void *Context, int ContextType)
796 HashList *List = NULL;
803 StrBuf_ServGetln(Rcp);
804 if (ChrPtr(Rcp)[0] == '1') {
806 List = NewHash(1, NULL);
807 while (!Done && (StrBuf_ServGetln(Rcp)>=0)) {
808 if ( (StrLength(Rcp)==3) &&
809 !strcmp(ChrPtr(Rcp), "000"))
814 i = snprintf(N, sizeof(N), "%d", n);
816 Put(List, N, i, Rcp, HFreeStrBuf);
825 void DeleteGVEAHash(HashList **KillMe)
830 HashList *GetGVSNHash(StrBuf *Target, int nArgs, WCTemplateToken *Tokens, void *Context, int ContextType)
833 HashList *List = NULL;
840 StrBuf_ServGetln(Rcp);
841 if (ChrPtr(Rcp)[0] == '1') {
843 List = NewHash(1, NULL);
844 while (!Done && (StrBuf_ServGetln(Rcp)>=0)) {
845 if ( (StrLength(Rcp)==3) &&
846 !strcmp(ChrPtr(Rcp), "000"))
851 i = snprintf(N, sizeof(N), "%d", n);
853 Put(List, N, i, Rcp, HFreeStrBuf);
862 void DeleteGVSNHash(HashList **KillMe)
869 InitModule_PREFERENCES
872 WebcitAddUrlHandler(HKEY("display_preferences"), display_preferences, 0);
873 WebcitAddUrlHandler(HKEY("set_preferences"), set_preferences, 0);
874 WebcitAddUrlHandler(HKEY("change_start_page"), change_start_page, 0);
876 RegisterPreference("roomlistview",_("Room list view"),PRF_STRING);
877 RegisterPreference("calhourformat",_("Time format"), PRF_INT);
878 RegisterPreference("daystart", _("Calendar day view begins at:"), PRF_INT);
879 RegisterPreference("dayend", _("Calendar day view ends at:"), PRF_INT);
880 RegisterPreference("weekstart",_("Week starts on:"), PRF_INT);
882 RegisterPreference("use_sig",_("Attach signature to email messages?"), PRF_YESNO);
883 RegisterPreference("signature",_("Use this signature:"),PRF_QP_STRING);
884 RegisterPreference("default_header_charset", _("Default character set for email headers:") ,PRF_STRING);
885 RegisterPreference("emptyfloors", _("Show empty floors"), PRF_YESNO);
886 RegisterPreference("defaultfrom", _("Prefered Email Address"), PRF_STRING);
887 RegisterPreference("defaultname", _("Prefered Email Sendername"), PRF_STRING);
888 RegisterPreference("defaulthandle", _("Prefered Name for posting messages"), PRF_STRING);
891 RegisterNamespace("PREF:VALUE", 1, 2, tmplput_CFG_Value, CTX_NONE);
892 RegisterNamespace("PREF:DESCR", 1, 1, tmplput_CFG_Descr, CTX_NONE);
893 RegisterIterator("PREF:ZONE", 0, ZoneHash, NULL, CfgZoneTempl, NULL, CTX_PREF, CTX_NONE);
895 RegisterConditional(HKEY("COND:PREF"), 4, ConditionalPreference, CTX_NONE);
896 RegisterConditional(HKEY("COND:PREF:SET"), 4, ConditionalHazePreference, CTX_NONE);
898 RegisterIterator("PREF:VALID:EMAIL:ADDR", 0, NULL,
899 GetGVEAHash, NULL, DeleteGVEAHash, CTX_STRBUF, CTX_NONE);
900 RegisterIterator("PREF:VALID:EMAIL:NAME", 0, NULL,
901 GetGVSNHash, NULL, DeleteGVSNHash, CTX_STRBUF, CTX_NONE);