4 * Manage user preferences with a little help from the Citadel server.
12 HashList
*PreferenceHooks
;
14 typedef struct _PrefDef
{
22 typedef struct _Preference
{
32 void DestroyPrefDef(void *vPrefDef
)
34 PrefDef
*Prefdef
= (PrefDef
*) vPrefDef
;
35 FreeStrBuf(&Prefdef
->Setting
);
36 FreeStrBuf(&Prefdef
->OnLoadName
);
40 void DestroyPreference(void *vPref
)
42 Preference
*Pref
= (Preference
*) vPref
;
43 FreeStrBuf(&Pref
->Key
);
44 FreeStrBuf(&Pref
->Val
);
45 FreeStrBuf(&Pref
->DeQPed
);
49 void _RegisterPreference(const char *Setting
, long SettingLen
,
53 const char *OnLoadName
)
55 PrefDef
*Newpref
= (PrefDef
*) malloc(sizeof(PrefDef
));
56 Newpref
->Setting
= NewStrBufPlain(Setting
, SettingLen
);
57 Newpref
->PrefStr
= PrefStr
;
59 Newpref
->OnLoad
= OnLoad
;
60 if (Newpref
->OnLoad
!= NULL
) {
61 Newpref
->OnLoadName
= NewStrBufPlain(OnLoadName
, -1);
64 Newpref
->OnLoadName
= NULL
;
65 Put(PreferenceHooks
, Setting
, SettingLen
, Newpref
, DestroyPrefDef
);
68 const char *PrefGetLocalStr(const char *Setting
, long len
)
71 if (GetHash(PreferenceHooks
, Setting
, len
, &hash_value
) != 0) {
72 PrefDef
*Newpref
= (PrefDef
*) hash_value
;
73 return _(Newpref
->PrefStr
);
80 inline const char *PrintPref(void *vPref
)
82 Preference
*Pref
= (Preference
*) vPref
;
83 if (Pref
->DeQPed
!= NULL
)
84 return ChrPtr(Pref
->DeQPed
);
86 return ChrPtr(Pref
->Val
);
90 void GetPrefTypes(HashList
*List
)
100 It
= GetNewHashPos(List
, 0);
101 while (GetNextHashPos(List
, It
, &len
, &Key
, &vSetting
))
103 Pref
= (Preference
*) vSetting
;
104 if (GetHash(PreferenceHooks
, SKEY(Pref
->Key
), &vPrefDef
) &&
107 PrefType
= (PrefDef
*) vPrefDef
;
108 Pref
->Type
= PrefType
;
110 lprintf(1, "Loading [%s]with type [%ld] [\"%s\"]\n",
115 switch (Pref
->Type
->Type
)
121 Pref
->lval
= StrTol(Pref
->Val
);
125 Pref
->DeQPed
= NewStrBufPlain(NULL
, StrLength(Pref
->Val
));
126 StrBufEUid_unescapize(Pref
->DeQPed
, Pref
->Val
);
130 Pref
->lval
= strcmp(ChrPtr(Pref
->Val
), "yes") == 0;
135 if (PrefType
->OnLoad
!= NULL
){
137 lprintf(1, "Loading with: -> %s(\"%s\", %ld)\n",
138 ChrPtr(PrefType
->OnLoadName
),
141 PrefType
->OnLoad(Pref
->Val
, Pref
->lval
);
148 void ParsePref(HashList
**List
, StrBuf
*ReadBuf
)
150 Preference
*Data
= NULL
;
151 Preference
*LastData
= NULL
;
153 while (StrBuf_ServGetln(ReadBuf
),
154 strcmp(ChrPtr(ReadBuf
), "000"))
156 if ((ChrPtr(ReadBuf
)[0] == ' ') &&
158 StrBufAppendBuf(Data
->Val
, ReadBuf
, 1);
161 LastData
= Data
= malloc(sizeof(Preference
));
162 memset(Data
, 0, sizeof(Preference
));
163 Data
->Key
= NewStrBuf();
164 Data
->Val
= NewStrBuf();
165 StrBufExtract_token(Data
->Key
, ReadBuf
, 0, '|');
166 StrBufExtract_token(Data
->Val
, ReadBuf
, 1, '|');
167 if (!IsEmptyStr(ChrPtr(Data
->Key
)))
177 lprintf(1, "ignoring spurious preference line: [%s]\n",
179 DestroyPreference(Data
);
189 * display preferences dialog
191 void load_preferences(void)
197 if (goto_config_room() != 0) return; /* oh well. */
199 ReadBuf
= NewStrBuf();
200 serv_puts("MSGS ALL|0|1");
201 StrBuf_ServGetln(ReadBuf
);
202 if (GetServerStatus(ReadBuf
, NULL
) == 8) {
203 serv_puts("subj|__ WebCit Preferences __");
206 while (serv_getln(buf
, sizeof buf
), strcmp(buf
, "000")) {
211 serv_printf("MSG0 %ld", msgnum
);
212 StrBuf_ServGetln(ReadBuf
);
213 if (GetServerStatus(ReadBuf
, NULL
) == 1) {
214 while (StrBuf_ServGetln(ReadBuf
),
215 (strcmp(ChrPtr(ReadBuf
), "text") &&
216 strcmp(ChrPtr(ReadBuf
), "000"))) {
218 if (!strcmp(ChrPtr(ReadBuf
), "text")) {
219 ParsePref(&WC
->hash_prefs
, ReadBuf
);
224 /* Go back to the room we're supposed to be in */
225 serv_printf("GOTO %s", ChrPtr(WC
->wc_roomname
));
226 StrBuf_ServGetln(ReadBuf
);
227 GetServerStatus(ReadBuf
, NULL
);
228 FreeStrBuf(&ReadBuf
);
232 * \brief Goto the user's configuration room, creating it if necessary.
233 * \return 0 on success or nonzero upon failure.
235 int goto_config_room(void) {
238 serv_printf("GOTO %s", USERCONFIGROOM
);
239 serv_getln(buf
, sizeof buf
);
240 if (buf
[0] != '2') { /* try to create the config room if not there */
241 serv_printf("CRE8 1|%s|4|0", USERCONFIGROOM
);
242 serv_getln(buf
, sizeof buf
);
243 serv_printf("GOTO %s", USERCONFIGROOM
);
244 serv_getln(buf
, sizeof buf
);
245 if (buf
[0] != '2') return(1);
250 void WritePrefsToServer(HashList
*Hash
)
257 StrBuf
*SubBuf
= NULL
;
259 Hash
= WC
->hash_prefs
;
260 #ifdef DBG_PREFS_HASH
261 dbg_PrintHash(Hash
, PrintPref
, NULL
);
263 HashPos
= GetNewHashPos(Hash
, 0);
264 while (GetNextHashPos(Hash
, HashPos
, &len
, &Key
, &vPref
)!=0)
269 Pref
= (Preference
*) vPref
;
270 nchars
= StrLength(Pref
->Val
);
273 size_t offset
, nchars
;
275 SubBuf
= NewStrBuf();
284 nchars
= StrBufSub(SubBuf
, Pref
->Val
, offset
, nchars
);
287 serv_printf("%s|%s", ChrPtr(Pref
->Key
), ChrPtr(SubBuf
));
289 serv_printf(" %s", ChrPtr(SubBuf
));
292 nchars
= StrLength(Pref
->Val
) - offset
;
298 serv_printf("%s|%s", ChrPtr(Pref
->Key
), ChrPtr(Pref
->Val
));
302 DeleteHashPos(&HashPos
);
306 * \brief save the modifications
308 void save_preferences(void)
313 if (goto_config_room() != 0) return; /* oh well. */
314 serv_puts("MSGS ALL|0|1");
315 serv_getln(buf
, sizeof buf
);
317 serv_puts("subj|__ WebCit Preferences __");
320 while (serv_getln(buf
, sizeof buf
), strcmp(buf
, "000")) {
325 serv_printf("DELE %ld", msgnum
);
326 serv_getln(buf
, sizeof buf
);
329 serv_printf("ENT0 1||0|1|__ WebCit Preferences __|");
330 serv_getln(buf
, sizeof buf
);
333 WritePrefsToServer(WC
->hash_prefs
);
338 /** Go back to the room we're supposed to be in */
339 serv_printf("GOTO %s", ChrPtr(WC
->wc_roomname
));
340 serv_getln(buf
, sizeof buf
);
344 * \brief query the actual setting of key in the citadel database
345 * \param key config key to query
346 * \param keylen length of the key string
347 * \param value StrBuf-value to the key to get
350 int get_pref_backend(const char *key
, size_t keylen
, Preference
**Pref
)
352 void *hash_value
= NULL
;
353 #ifdef DBG_PREFS_HASH
354 dbg_PrintHash(WC
->hash_prefs
, PrintPref
, NULL
);
356 if (GetHash(WC
->hash_prefs
, key
, keylen
, &hash_value
) == 0) {
361 *Pref
= (Preference
*) hash_value
;
366 int get_PREFERENCE(const char *key
, size_t keylen
, StrBuf
**value
)
371 Ret
= get_pref_backend(key
, keylen
, &Pref
);
380 * \brief Write a key into the webcit preferences database for this user
382 * \params key key whichs value is to be modified
383 * \param keylen length of the key string
384 * \param value value to set
385 * \param save_to_server 1 = flush all data to the server, 0 = cache it for now
387 void set_preference_backend(const char *key
, size_t keylen
,
397 Pref
= (Preference
*) malloc(sizeof(Preference
));
398 memset(Pref
, 0, sizeof(Preference
));
399 Pref
->Key
= NewStrBufPlain(key
, keylen
);
401 if ((PrefType
== NULL
) &&
402 GetHash(PreferenceHooks
, SKEY(Pref
->Key
), &vPrefDef
) &&
404 PrefType
= (PrefDef
*) vPrefDef
;
406 if (PrefType
!= NULL
)
408 Pref
->Type
= PrefType
;
409 if (Pref
->Type
->Type
!= lPrefType
)
410 lprintf(1, "warning: saving preference with wrong type [%s] %ld != %ld \n",
411 key
, Pref
->Type
->Type
, lPrefType
);
412 switch (Pref
->Type
->Type
)
421 if (Pref
->Val
== NULL
)
422 Pref
->Val
= NewStrBufPlain(NULL
, 64);
423 StrBufPrintf(Pref
->Val
, "%ld", lvalue
);
427 Pref
->DeQPed
= value
;
428 Pref
->Val
= NewStrBufPlain(NULL
, StrLength(Pref
->DeQPed
) * 3);
429 StrBufEUid_escapize(Pref
->Val
, Pref
->DeQPed
);
435 Pref
->Val
= NewStrBufPlain(HKEY("yes"));
437 Pref
->Val
= NewStrBufPlain(HKEY("no"));
441 if (Pref
->Type
->OnLoad
!= NULL
)
442 Pref
->Type
->OnLoad(Pref
->Val
, Pref
->lval
);
454 if (Pref
->Val
== NULL
)
455 Pref
->Val
= NewStrBufPlain(NULL
, 64);
456 StrBufPrintf(Pref
->Val
, "%ld", lvalue
);
460 Pref
->DeQPed
= value
;
461 Pref
->Val
= NewStrBufPlain(NULL
, StrLength(Pref
->DeQPed
) * 3);
462 StrBufEUid_escapize(Pref
->Val
, Pref
->DeQPed
);
468 Pref
->Val
= NewStrBufPlain(HKEY("yes"));
470 Pref
->Val
= NewStrBufPlain(HKEY("no"));
475 Put(WC
->hash_prefs
, key
, keylen
, Pref
, DestroyPreference
);
477 if (save_to_server
) WC
->SavePrefsToServer
= 1;
480 void set_PREFERENCE(const char *key
, size_t keylen
, StrBuf
*value
, int save_to_server
)
482 set_preference_backend(key
, keylen
, 0, value
, PRF_STRING
, save_to_server
, NULL
);
485 int get_PREF_LONG(const char *key
, size_t keylen
, long *value
, long Default
)
490 Ret
= get_pref_backend(key
, keylen
, &Pref
);
499 *value
= Pref
->lval
= atol(ChrPtr(Pref
->Val
));
506 void set_PREF_LONG(const char *key
, size_t keylen
, long value
, int save_to_server
)
508 set_preference_backend(key
, keylen
, value
, NULL
, PRF_INT
, save_to_server
, NULL
);
511 int get_PREF_YESNO(const char *key
, size_t keylen
, int *value
, int Default
)
516 Ret
= get_pref_backend(key
, keylen
, &Pref
);
525 *value
= Pref
->lval
= strcmp(ChrPtr(Pref
->Val
), "yes") == 0;
531 void set_PREF_YESNO(const char *key
, size_t keylen
, long value
, int save_to_server
)
533 set_preference_backend(key
, keylen
, value
, NULL
, PRF_YESNO
, save_to_server
, NULL
);
536 int get_room_prefs_backend(const char *key
, size_t keylen
,
542 pref_name
= NewStrBufPlain (HKEY("ROOM:"));
543 StrBufAppendBuf(pref_name
, WC
->wc_roomname
, 0);
544 StrBufAppendBufPlain(pref_name
, HKEY(":"), 0);
545 StrBufAppendBufPlain(pref_name
, key
, keylen
, 0);
546 Ret
= get_pref_backend(SKEY(pref_name
), Pref
);
547 FreeStrBuf(&pref_name
);
552 const StrBuf
*get_X_PREFS(const char *key
, size_t keylen
,
553 const char *xkey
, size_t xkeylen
)
559 pref_name
= NewStrBufPlain (HKEY("XPREF:"));
560 StrBufAppendBufPlain(pref_name
, xkey
, xkeylen
, 0);
561 StrBufAppendBufPlain(pref_name
, HKEY(":"), 0);
562 StrBufAppendBufPlain(pref_name
, key
, keylen
, 0);
564 ret
= get_pref_backend(SKEY(pref_name
), &Prf
);
565 FreeStrBuf(&pref_name
);
572 void set_X_PREFS(const char *key
, size_t keylen
, const char *xkey
, size_t xkeylen
, StrBuf
*value
, int save_to_server
)
576 pref_name
= NewStrBufPlain (HKEY("XPREF:"));
577 StrBufAppendBufPlain(pref_name
, xkey
, xkeylen
, 0);
578 StrBufAppendBufPlain(pref_name
, HKEY(":"), 0);
579 StrBufAppendBufPlain(pref_name
, key
, keylen
, 0);
581 set_preference_backend(SKEY(pref_name
), 0, value
, PRF_STRING
, save_to_server
, NULL
);
582 FreeStrBuf(&pref_name
);
586 StrBuf
*get_ROOM_PREFS(const char *key
, size_t keylen
)
591 Ret
= get_room_prefs_backend(key
, keylen
, &Pref
);
600 void set_ROOM_PREFS(const char *key
, size_t keylen
, StrBuf
*value
, int save_to_server
)
604 pref_name
= NewStrBufPlain (HKEY("ROOM:"));
605 StrBufAppendBuf(pref_name
, WC
->wc_roomname
, 0);
606 StrBufAppendBufPlain(pref_name
, HKEY(":"), 0);
607 StrBufAppendBufPlain(pref_name
, key
, keylen
, 0);
608 set_preference_backend(SKEY(pref_name
), 0, value
, PRF_STRING
, save_to_server
, NULL
);
609 FreeStrBuf(&pref_name
);
613 void GetPreferences(HashList
*Setting
)
625 Tmp
= WCC
->hash_prefs
;
626 WCC
->hash_prefs
= Setting
;
628 It
= GetNewHashPos(PreferenceHooks
, 0);
629 while (GetNextHashPos(PreferenceHooks
, It
, &len
, &Key
, &vSetting
)) {
630 PrefType
= (PrefDef
*) vSetting
;
632 if (!HaveBstr(SKEY(PrefType
->Setting
)))
634 switch (PrefType
->Type
) {
636 Buf
= NewStrBufDup(SBstr(SKEY(PrefType
->Setting
)));
637 set_preference_backend(SKEY(PrefType
->Setting
),
645 lval
= LBstr(SKEY(PrefType
->Setting
));
646 set_preference_backend(SKEY(PrefType
->Setting
),
654 Buf
= NewStrBufDup(SBstr(SKEY(PrefType
->Setting
)));
655 set_preference_backend(SKEY(PrefType
->Setting
),
663 lval
= YesBstr(SKEY(PrefType
->Setting
));
664 set_preference_backend(SKEY(PrefType
->Setting
),
673 WCC
->hash_prefs
= Tmp
;
679 * \brief Commit new preferences and settings
681 void set_preferences(void)
683 if (!havebstr("change_button")) {
684 safestrncpy(WC
->ImportantMessage
,
685 _("Cancelled. No settings were changed."),
686 sizeof WC
->ImportantMessage
);
690 GetPreferences(WC
->hash_prefs
);
695 void tmplput_CFG_Value(StrBuf
*Target
, WCTemplputParams
*TP
)
698 if (get_pref_backend(TKEY(0), &Pref
))
700 if (Pref
->Type
== NULL
) {
701 StrBufAppendTemplate(Target
, TP
, Pref
->Val
, 1);
703 switch (Pref
->Type
->Type
)
706 StrBufAppendTemplate(Target
, TP
, Pref
->Val
, 1);
709 if (Pref
->decoded
!= 1) {
710 if (Pref
->Val
== NULL
)
711 Pref
->Val
= NewStrBufPlain(NULL
, 64);
712 StrBufPrintf(Pref
->Val
, "%ld", Pref
->lval
);
715 StrBufAppendTemplate(Target
, TP
, Pref
->Val
, 1);
718 if (Pref
->decoded
!= 1) {
719 if (Pref
->DeQPed
== NULL
)
720 Pref
->DeQPed
= NewStrBufPlain(NULL
, StrLength(Pref
->Val
));
722 StrBufEUid_unescapize(Pref
->DeQPed
, Pref
->Val
);
725 StrBufAppendTemplate(Target
, TP
, Pref
->DeQPed
, 1);
728 if (Pref
->decoded
!= 1) {
729 Pref
->lval
= strcmp(ChrPtr(Pref
->Val
), "yes") == 0;
732 StrBufAppendTemplate(Target
, TP
, Pref
->Val
, 1);
738 void tmplput_CFG_Descr(StrBuf
*Target
, WCTemplputParams
*TP
)
740 const char *SettingStr
;
741 SettingStr
= PrefGetLocalStr(TKEY(0));
742 if (SettingStr
!= NULL
)
743 StrBufAppendBufPlain(Target
, SettingStr
, -1, 0);
745 void tmplput_CFG_RoomValue(StrBuf
*Target
, WCTemplputParams
*TP
)
747 StrBuf
*pref
= get_ROOM_PREFS(TKEY(0));
749 StrBufAppendBuf(Target
, pref
, 0);
751 int ConditionalHasRoomPreference(StrBuf
*Target
, WCTemplputParams
*TP
)
753 if (get_ROOM_PREFS(TP
->Tokens
->Params
[0]->Start
,
754 TP
->Tokens
->Params
[0]->len
) != NULL
)
759 void CfgZoneTempl(StrBuf
*TemplBuffer
, WCTemplputParams
*TP
)
761 StrBuf
*Zone
= (StrBuf
*) CTX
;
763 SVPutBuf("ZONENAME", Zone
, 1);
766 int ConditionalPreference(StrBuf
*Target
, WCTemplputParams
*TP
)
770 if (!get_PREFERENCE(TKEY(2), &Pref
))
773 if (TP
->Tokens
->nParameters
== 3) {
776 else if (TP
->Tokens
->Params
[3]->Type
== TYPE_STR
)
777 return ((TP
->Tokens
->Params
[3]->len
== StrLength(Pref
)) &&
778 (strcmp(TP
->Tokens
->Params
[3]->Start
, ChrPtr(Pref
)) == 0));
780 return (StrTol(Pref
) == TP
->Tokens
->Params
[3]->lvalue
);
783 int ConditionalHasPreference(StrBuf
*Target
, WCTemplputParams
*TP
)
787 if (!get_PREFERENCE(TKEY(2), &Pref
) ||
795 /********************************************************************************
796 * preferences stored discrete in citserver
797 ********************************************************************************/
798 HashList
*GetGVEAHash(StrBuf
*Target
, WCTemplputParams
*TP
)
801 HashList
*List
= NULL
;
808 StrBuf_ServGetln(Rcp
);
809 if (GetServerStatus(Rcp
, NULL
) == 1) {
811 List
= NewHash(1, NULL
);
812 while (!Done
&& (StrBuf_ServGetln(Rcp
)>=0)) {
813 if ( (StrLength(Rcp
)==3) &&
814 !strcmp(ChrPtr(Rcp
), "000"))
819 i
= snprintf(N
, sizeof(N
), "%d", n
);
821 Put(List
, N
, i
, Rcp
, HFreeStrBuf
);
830 void DeleteGVEAHash(HashList
**KillMe
)
835 HashList
*GetGVSNHash(StrBuf
*Target
, WCTemplputParams
*TP
)
838 HashList
*List
= NULL
;
845 StrBuf_ServGetln(Rcp
);
846 if (GetServerStatus(Rcp
, NULL
) == 1) {
848 List
= NewHash(1, NULL
);
849 while (!Done
&& (StrBuf_ServGetln(Rcp
)>=0)) {
850 if ( (StrLength(Rcp
)==3) &&
851 !strcmp(ChrPtr(Rcp
), "000"))
856 i
= snprintf(N
, sizeof(N
), "%d", n
);
858 Put(List
, N
, i
, Rcp
, HFreeStrBuf
);
867 void DeleteGVSNHash(HashList
**KillMe
)
876 * Offer to make any page the user's "start page."
878 void offer_start_page(StrBuf
*Target
, WCTemplputParams
*TP
)
880 wprintf("<a href=\"change_start_page?startpage=");
881 urlescputs(ChrPtr(WC
->this_page
));
883 wprintf(_("Make this my start page"));
886 wprintf("<br/><a href=\"rss?room=");
887 urlescputs(ChrPtr(WC
->wc_roomname
));
888 wprintf("\" title=\"RSS 2.0 feed for ");
889 escputs(ChrPtr(WC
->wc_roomname
));
890 wprintf("\"><img alt=\"RSS\" border=\"0\" src=\"static/xml_button.gif\"/></a>\n");
896 * Change the user's start page
898 void change_start_page(void)
900 if (!havebstr("startpage")) {
901 set_preference_backend(HKEY("startpage"),
903 NewStrBufPlain(HKEY("")),
907 safestrncpy(WC
->ImportantMessage
,
908 _("You no longer have a start page selected."),
909 sizeof( WC
->ImportantMessage
));
914 set_preference_backend(HKEY("startpage"),
916 NewStrBufDup(sbstr("startpage")),
921 output_headers(1, 1, 0, 0, 0, 0);
922 do_template("newstartpage", NULL
);
928 InitModule_PREFERENCES
931 WebcitAddUrlHandler(HKEY("set_preferences"), set_preferences
, 0);
932 WebcitAddUrlHandler(HKEY("change_start_page"), change_start_page
, 0);
935 RegisterNamespace("OFFERSTARTPAGE", 0, 0, offer_start_page
, CTX_NONE
);
936 RegisterNamespace("PREF:ROOM:VALUE", 1, 2, tmplput_CFG_RoomValue
, CTX_NONE
);
937 RegisterNamespace("PREF:VALUE", 1, 2, tmplput_CFG_Value
, CTX_NONE
);
938 RegisterNamespace("PREF:DESCR", 1, 1, tmplput_CFG_Descr
, CTX_NONE
);
939 RegisterIterator("PREF:ZONE", 0, ZoneHash
, NULL
, CfgZoneTempl
, NULL
, CTX_PREF
, CTX_NONE
, IT_NOFLAG
);
941 RegisterConditional(HKEY("COND:PREF"), 4, ConditionalPreference
, CTX_NONE
);
942 RegisterConditional(HKEY("COND:PREF:SET"), 4, ConditionalHasPreference
, CTX_NONE
);
943 RegisterConditional(HKEY("COND:ROOM:SET"), 4, ConditionalHasRoomPreference
, CTX_NONE
);
945 RegisterIterator("PREF:VALID:EMAIL:ADDR", 0, NULL
,
946 GetGVEAHash
, NULL
, DeleteGVEAHash
, CTX_STRBUF
, CTX_NONE
, IT_NOFLAG
);
947 RegisterIterator("PREF:VALID:EMAIL:NAME", 0, NULL
,
948 GetGVSNHash
, NULL
, DeleteGVSNHash
, CTX_STRBUF
, CTX_NONE
, IT_NOFLAG
);