! fix crash in X-Preferences API; return NULL if not found.
[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 HashList *PreferenceHooks;
13
14 typedef struct _PrefDef {
15         long Type;
16         StrBuf *Setting;
17         const char *PrefStr;
18         PrefEvalFunc OnLoad;
19 } PrefDef;
20
21 typedef struct _Preference {
22         StrBuf *Key;
23         StrBuf *Val;
24         PrefDef *Type;
25
26         long lval;
27         long decoded;
28         StrBuf *DeQPed;
29 }Preference;
30
31 void DestroyPrefDef(void *vPrefDef)
32 {
33         PrefDef *Prefdef = (PrefDef*) vPrefDef;
34         FreeStrBuf(&Prefdef->Setting);
35         free(Prefdef);
36 }
37
38 void DestroyPreference(void *vPref)
39 {
40         Preference *Pref = (Preference*) vPref;
41         FreeStrBuf(&Pref->Key);
42         FreeStrBuf(&Pref->Val);
43         FreeStrBuf(&Pref->DeQPed);
44         free(Pref);
45
46 }
47 void RegisterPreference(const char *Setting, long SettingLen, 
48                         const char *PrefStr, 
49                         long Type, 
50                         PrefEvalFunc OnLoad)
51 {
52         PrefDef *Newpref = (PrefDef*) malloc(sizeof(PrefDef));
53         Newpref->Setting = NewStrBufPlain(Setting, SettingLen);
54         Newpref->PrefStr = PrefStr;
55         Newpref->Type = Type;
56         Newpref->OnLoad = OnLoad;
57         Put(PreferenceHooks, Setting, SettingLen, Newpref, DestroyPrefDef);
58 }
59
60 const char *PrefGetLocalStr(const char *Setting, long len)
61 {
62         void *hash_value;
63         if (GetHash(PreferenceHooks, Setting, len, &hash_value) != 0) {
64                 PrefDef *Newpref = (PrefDef*) hash_value;
65                 return _(Newpref->PrefStr);
66
67         }
68         return "";
69 }
70
71 #ifdef DBG_PREFS_HASH
72 inline const char *PrintPref(void *vPref)
73 {
74         Preference *Pref = (Preference*) vPref;
75         if (Pref->DeQPed != NULL)
76                 return ChrPtr(Pref->DeQPed);
77         else 
78                 return ChrPtr(Pref->Val);
79 }
80 #endif
81
82 void GetPrefTypes(HashList *List)
83 {
84         HashPos *It;
85         long len;
86         const char *Key;
87         void *vSetting;
88         void *vPrefDef;
89         Preference *Setting;
90         PrefDef *PrefType;
91
92         It = GetNewHashPos(List, 0);
93         while (GetNextHashPos(List, It, &len, &Key, &vSetting)) 
94         {
95                 Setting = (Preference*) vSetting;
96                 if (GetHash(PreferenceHooks, SKEY(Setting->Key), &vPrefDef) && 
97                     (vPrefDef != NULL)) 
98                 {
99                         PrefType = (PrefDef*) vPrefDef;
100                         Setting->Type = PrefType;
101                         if (PrefType->OnLoad != NULL)
102                                 PrefType->OnLoad(Setting->Val, Setting->lval);
103                 }
104         }
105 }
106
107 void ParsePref(HashList **List, StrBuf *ReadBuf)
108 {
109         StrBuf *Key;
110         Preference *Data = NULL;
111         Preference *LastData = NULL;
112                                 
113         Key = NewStrBuf();
114         while (StrBuf_ServGetln(ReadBuf), 
115                strcmp(ChrPtr(ReadBuf), "000")) 
116         {
117                 if ((ChrPtr(ReadBuf)[0] == ' ') &&
118                     (Data != NULL)) {
119                         StrBufAppendBuf(Data->Val, ReadBuf, 1);
120                 }
121                 else {
122                         LastData = Data = malloc(sizeof(Preference));
123                         memset(Data, 0, sizeof(Preference));
124                         StrBufExtract_token(Data->Key, ReadBuf, 0, '|');
125                         StrBufExtract_token(Data->Val, ReadBuf, 1, '|');
126                         if (!IsEmptyStr(ChrPtr(Data->Key)))
127                         {
128                                 Put(*List, 
129                                     SKEY(Data->Key),
130                                     Data, 
131                                     DestroyPreference);
132                         }
133                         else 
134                         {
135                                 StrBufTrim(ReadBuf);
136                                 lprintf(1, "ignoring spurious preference line: [%s]\n", 
137                                         ChrPtr(ReadBuf));
138                                 DestroyPreference(Data);
139                                 LastData = NULL;
140                         }
141                 }
142         }
143         GetPrefTypes(*List);
144 }
145
146
147 /*
148  * display preferences dialog
149  */
150 void load_preferences(void) 
151 {
152         StrBuf *ReadBuf;
153         char buf[SIZ];
154         long msgnum = 0L;
155         
156         if (!goto_config_room())
157                 return;
158
159         ReadBuf = NewStrBuf();
160         serv_puts("MSGS ALL|0|1");
161         StrBuf_ServGetln(ReadBuf);
162         if (GetServerStatus(ReadBuf, NULL) == 8) {
163                 serv_puts("subj|__ WebCit Preferences __");
164                 serv_puts("000");
165         }
166         while (serv_getln(buf, sizeof buf), strcmp(buf, "000")) {
167                 msgnum = atol(buf);
168         }
169
170         if (msgnum > 0L) {
171                 serv_printf("MSG0 %ld", msgnum);
172                 StrBuf_ServGetln(ReadBuf);
173                 if (GetServerStatus(ReadBuf, NULL) == 1) {
174                         while (StrBuf_ServGetln(ReadBuf),
175                                (strcmp(ChrPtr(ReadBuf), "text") && 
176                                 strcmp(ChrPtr(ReadBuf), "000"))) {
177                         }
178                         if (!strcmp(ChrPtr(ReadBuf), "text")) {
179                                 ParsePref(&WC->hash_prefs, ReadBuf);
180                         }
181                 }
182         }
183
184         /* Go back to the room we're supposed to be in */
185         serv_printf("GOTO %s", ChrPtr(WC->wc_roomname));
186         StrBuf_ServGetln(ReadBuf);
187         GetServerStatus(ReadBuf, NULL);
188         FreeStrBuf(&ReadBuf);
189 }
190
191 /**
192  * \brief Goto the user's configuration room, creating it if necessary.
193  * \return 0 on success or nonzero upon failure.
194  */
195 int goto_config_room(void) {
196         char buf[SIZ];
197
198         serv_printf("GOTO %s", USERCONFIGROOM);
199         serv_getln(buf, sizeof buf);
200         if (buf[0] != '2') { /* try to create the config room if not there */
201                 serv_printf("CRE8 1|%s|4|0", USERCONFIGROOM);
202                 serv_getln(buf, sizeof buf);
203                 serv_printf("GOTO %s", USERCONFIGROOM);
204                 serv_getln(buf, sizeof buf);
205                 if (buf[0] != '2') return(1);
206         }
207         return(0);
208 }
209
210 void WritePrefsToServer(HashList *Hash)
211 {
212         long len;
213         HashPos *HashPos;
214         void *vPref;
215         const char *Key;
216         Preference *Pref;
217         StrBuf *SubBuf = NULL;
218         
219         Hash = WC->hash_prefs;
220 #ifdef DBG_PREFS_HASH
221         dbg_PrintHash(Hash, PrintPref, NULL);
222 #endif
223         HashPos = GetNewHashPos(Hash, 0);
224         while (GetNextHashPos(Hash, HashPos, &len, &Key, &vPref)!=0)
225         {
226                 size_t nchars;
227                 if (vPref == NULL)
228                         continue;
229                 Pref = (Preference*) vPref;
230                 nchars = StrLength(Pref->Val);
231                 if (nchars > 80){
232                         int n = 0;
233                         size_t offset, nchars;
234                         if (SubBuf == NULL)
235                                 SubBuf = NewStrBuf();
236                         nchars = 1;
237                         offset = 0;
238                         while (nchars > 0) {
239                                 if (n == 0)
240                                         nchars = 70;
241                                 else 
242                                         nchars = 80;
243                                 
244                                 nchars = StrBufSub(SubBuf, Pref->Val, offset, nchars);
245                                 
246                                 if (n == 0)
247                                         serv_printf("%s|%s", ChrPtr(Pref->Key), ChrPtr(SubBuf));
248                                 else
249                                         serv_printf(" %s", ChrPtr(SubBuf));
250                                 
251                                 offset += nchars;
252                                 nchars = StrLength(Pref->Val) - offset;
253                                 n++;
254                         }
255                         
256                 }
257                 else
258                         serv_printf("%s|%s", ChrPtr(Pref->Key), ChrPtr(Pref->Val));
259                 
260         }
261         FreeStrBuf(&SubBuf);
262         DeleteHashPos(&HashPos);
263 }
264
265 /**
266  * \brief save the modifications
267  */
268 void save_preferences(void) 
269 {
270         char buf[SIZ];
271         long msgnum = 0L;
272         
273         if (goto_config_room() != 0) return;    /* oh well. */
274         serv_puts("MSGS ALL|0|1");
275         serv_getln(buf, sizeof buf);
276         if (buf[0] == '8') {
277                 serv_puts("subj|__ WebCit Preferences __");
278                 serv_puts("000");
279         }
280         while (serv_getln(buf, sizeof buf), strcmp(buf, "000")) {
281                 msgnum = atol(buf);
282         }
283
284         if (msgnum > 0L) {
285                 serv_printf("DELE %ld", msgnum);
286                 serv_getln(buf, sizeof buf);
287         }
288
289         serv_printf("ENT0 1||0|1|__ WebCit Preferences __|");
290         serv_getln(buf, sizeof buf);
291         if (buf[0] == '4') {
292
293                 WritePrefsToServer(WC->hash_prefs);
294                 serv_puts("");
295                 serv_puts("000");
296         }
297
298         /** Go back to the room we're supposed to be in */
299         serv_printf("GOTO %s", ChrPtr(WC->wc_roomname));
300         serv_getln(buf, sizeof buf);
301 }
302
303 /**
304  * \brief query the actual setting of key in the citadel database
305  * \param key config key to query
306  * \param keylen length of the key string
307  * \param value StrBuf-value to the key to get
308  * \returns found?
309  */
310 int get_pref_backend(const char *key, size_t keylen, Preference **Pref)
311 {
312         void *hash_value = NULL;
313 #ifdef DBG_PREFS_HASH
314         dbg_PrintHash(WC->hash_prefs, PrintPref, NULL);
315 #endif
316         if (GetHash(WC->hash_prefs, key, keylen, &hash_value) == 0) {
317                 *Pref = NULL;
318                 return 0;
319         }
320         else {
321                 *Pref = (Preference*) hash_value;
322                 return 1;
323         }
324 }
325
326 int get_PREFERENCE(const char *key, size_t keylen, StrBuf **value)
327 {
328         Preference *Pref;
329         int Ret;
330
331         Ret = get_pref_backend(key, keylen, &Pref);
332         if (Ret != 0)
333                 *value = Pref->Val;
334         else
335                 *value = NULL;
336         return Ret;
337 }
338
339 /**
340  * \brief       Write a key into the webcit preferences database for this user
341  *
342  * \params      key             key whichs value is to be modified
343  * \param keylen length of the key string
344  * \param       value           value to set
345  * \param       save_to_server  1 = flush all data to the server, 0 = cache it for now
346  */
347 void set_preference_backend(const char *key, size_t keylen, 
348                             long lvalue, 
349                             StrBuf *value, 
350                             long lPrefType,
351                             int save_to_server, 
352                             PrefDef *PrefType) 
353 {
354         void *vPrefDef;
355         Preference *Pref;
356
357         Pref = (Preference*) malloc(sizeof(Preference));
358         memset(Pref, 0, sizeof(Preference));
359         Pref->Key = NewStrBufPlain(key, keylen);
360
361         if ((PrefType == NULL) &&
362             GetHash(PreferenceHooks, SKEY(Pref->Key), &vPrefDef) && 
363             (vPrefDef != NULL))
364                 PrefType = (PrefDef*) vPrefDef;
365
366         if (PrefType != NULL)
367         {
368                 Pref->Type = PrefType;
369                 if (Pref->Type->Type != lPrefType)
370                         lprintf(1, "warning: saving preference with wrong type [%s] %ld != %ld \n",
371                                 key, Pref->Type->Type, lPrefType);
372                 switch (Pref->Type->Type)
373                 {
374                 case PRF_STRING:
375                         Pref->Val = value;
376                         Pref->decoded = 1;
377                         break;
378                 case PRF_INT:
379                         Pref->lval = lvalue;
380                         Pref->Val = value;
381                         if (Pref->Val == NULL)
382                                 Pref->Val = NewStrBufPlain(NULL, 64);
383                         StrBufPrintf(Pref->Val, "%ld", lvalue);
384                         Pref->decoded = 1;
385                         break;
386                 case PRF_QP_STRING:
387                         Pref->DeQPed = value;
388                         Pref->Val = NewStrBufPlain(NULL, StrLength(Pref->DeQPed) * 3);
389                         StrBufEUid_escapize(Pref->Val, Pref->DeQPed);
390                         Pref->decoded = 1;
391                         break;
392                 case PRF_YESNO:
393                         Pref->lval = lvalue;
394                         if (lvalue) 
395                                 Pref->Val = NewStrBufPlain(HKEY("yes"));
396                         else
397                                 Pref->Val = NewStrBufPlain(HKEY("no"));
398                         Pref->decoded = 1;
399                         break;
400                 }
401                 if (Pref->Type->OnLoad != NULL)
402                         Pref->Type->OnLoad(Pref->Val, Pref->lval);
403         }
404         else {
405                 switch (lPrefType)
406                 {
407                 case PRF_STRING:
408                         Pref->Val = value;
409                         Pref->decoded = 1;
410                         break;
411                 case PRF_INT:
412                         Pref->lval = lvalue;
413                         Pref->Val = value;
414                         if (Pref->Val == NULL)
415                                 Pref->Val = NewStrBufPlain(NULL, 64);
416                         StrBufPrintf(Pref->Val, "%ld", lvalue);
417                         Pref->decoded = 1;
418                         break;
419                 case PRF_QP_STRING:
420                         Pref->DeQPed = value;
421                         Pref->Val = NewStrBufPlain(NULL, StrLength(Pref->DeQPed) * 3);
422                         StrBufEUid_escapize(Pref->Val, Pref->DeQPed);
423                         Pref->decoded = 1;
424                         break;
425                 case PRF_YESNO:
426                         Pref->lval = lvalue;
427                         if (lvalue) 
428                                 Pref->Val = NewStrBufPlain(HKEY("yes"));
429                         else
430                                 Pref->Val = NewStrBufPlain(HKEY("no"));
431                         Pref->decoded = 1;
432                         break;
433                 }
434         }
435         Put(WC->hash_prefs, key, keylen, Pref, DestroyPreference);
436         
437         if (save_to_server) WC->SavePrefsToServer = 1;
438 }
439
440 void set_PREFERENCE(const char *key, size_t keylen, StrBuf *value, int save_to_server) 
441 {
442         set_preference_backend(key, keylen, 0, value, PRF_STRING, save_to_server, NULL);
443 }
444
445 int get_PREF_LONG(const char *key, size_t keylen, long *value, long Default)
446 {
447         Preference *Pref;
448         int Ret;
449
450         Ret = get_pref_backend(key, keylen, &Pref);
451         if (Ret == 0) {
452                 *value = Default;
453                 return 0;
454         }
455
456         if (Pref->decoded)
457                 *value = Pref->lval;
458         else {
459                 *value = Pref->lval = atol(ChrPtr(Pref->Val));
460                 Pref->decoded = 1;
461         }
462         return Ret;
463 }
464
465
466 void set_PREF_LONG(const char *key, size_t keylen, long value, int save_to_server)
467 {
468         set_preference_backend(key, keylen, value, NULL, PRF_INT, save_to_server, NULL);
469 }
470
471 int get_PREF_YESNO(const char *key, size_t keylen, int *value, int Default)
472 {
473         Preference *Pref;
474         int Ret;
475
476         Ret = get_pref_backend(key, keylen, &Pref);
477         if (Ret == 0) {
478                 *value = Default;
479                 return 0;
480         }
481
482         if (Pref->decoded)
483                 *value = Pref->lval;
484         else {
485                 *value = Pref->lval = strcmp(ChrPtr(Pref->Val), "yes") == 0;
486                 Pref->decoded = 1;
487         }
488         return Ret;
489 }
490
491 void set_PREF_YESNO(const char *key, size_t keylen, long value, int save_to_server)
492 {
493         set_preference_backend(key, keylen, value, NULL, PRF_YESNO, save_to_server, NULL);
494 }
495
496 int get_room_prefs_backend(const char *key, size_t keylen, 
497                            Preference **Pref)
498 {
499         StrBuf *pref_name;
500         int Ret;
501
502         pref_name = NewStrBufPlain (HKEY("ROOM:"));
503         StrBufAppendBuf(pref_name, WC->wc_roomname, 0);
504         StrBufAppendBufPlain(pref_name, HKEY(":"), 0);
505         StrBufAppendBufPlain(pref_name, key, keylen, 0);
506         Ret = get_pref_backend(SKEY(pref_name), Pref);
507         FreeStrBuf(&pref_name);
508
509         return Ret;
510 }
511
512 const StrBuf *get_X_PREFS(const char *key, size_t keylen, 
513                           const char *xkey, size_t xkeylen)
514 {
515         int ret;
516         StrBuf *pref_name;
517         Preference *Prf;
518         
519         pref_name = NewStrBufPlain (HKEY("XPREF:"));
520         StrBufAppendBufPlain(pref_name, xkey, xkeylen, 0);
521         StrBufAppendBufPlain(pref_name, HKEY(":"), 0);
522         StrBufAppendBufPlain(pref_name, key, keylen, 0);
523
524         ret = get_pref_backend(SKEY(pref_name), &Prf);
525         FreeStrBuf(&pref_name);
526
527         if (ret)
528                 return Prf->Val;
529         else return NULL;
530 }
531
532 void set_X_PREFS(const char *key, size_t keylen, const char *xkey, size_t xkeylen, StrBuf *value, int save_to_server)
533 {
534         StrBuf *pref_name;
535         
536         pref_name = NewStrBufPlain (HKEY("XPREF:"));
537         StrBufAppendBufPlain(pref_name, xkey, xkeylen, 0);
538         StrBufAppendBufPlain(pref_name, HKEY(":"), 0);
539         StrBufAppendBufPlain(pref_name, key, keylen, 0);
540
541         set_preference_backend(SKEY(pref_name), 0, value, PRF_STRING, save_to_server, NULL);
542         FreeStrBuf(&pref_name);
543 }
544
545
546 StrBuf *get_ROOM_PREFS(const char *key, size_t keylen)
547 {
548         Preference *Pref;
549         int Ret;
550
551         Ret = get_room_prefs_backend(key, keylen, &Pref);
552
553         if (Ret == 0) {
554                 return NULL;
555         }
556         else 
557                 return Pref->Val;
558 }
559
560 void set_ROOM_PREFS(const char *key, size_t keylen, StrBuf *value, int save_to_server)
561 {
562         StrBuf *pref_name;
563         
564         pref_name = NewStrBufPlain (HKEY("ROOM:"));
565         StrBufAppendBuf(pref_name, WC->wc_roomname, 0);
566         StrBufAppendBufPlain(pref_name, HKEY(":"), 0);
567         StrBufAppendBufPlain(pref_name, key, keylen, 0);
568         set_preference_backend(SKEY(pref_name), 0, value, PRF_STRING, save_to_server, NULL);
569         FreeStrBuf(&pref_name);
570 }
571
572
573 void GetPreferences(HashList *Setting)
574 {
575         wcsession *WCC = WC;
576         HashPos *It;
577         long len;
578         const char *Key;
579         void *vSetting;
580         PrefDef *PrefType;
581         StrBuf *Buf;
582         long lval;
583         HashList *Tmp;
584
585         Tmp = WCC->hash_prefs;
586         WCC->hash_prefs = Setting;
587
588         It = GetNewHashPos(PreferenceHooks, 0);
589         while (GetNextHashPos(PreferenceHooks, It, &len, &Key, &vSetting)) {
590                 PrefType = (PrefDef*) vSetting;
591
592                 if (!HaveBstr(SKEY(PrefType->Setting)))
593                         continue;
594                 switch (PrefType->Type) {
595                 case PRF_STRING:
596                         Buf = NewStrBufDup(SBstr(SKEY(PrefType->Setting)));
597                         set_preference_backend(SKEY(PrefType->Setting),
598                                                0, 
599                                                Buf, 
600                                                PRF_STRING,
601                                                1, 
602                                                PrefType);
603                         break;
604                 case PRF_INT:
605                         lval = LBstr(SKEY(PrefType->Setting));
606                         set_preference_backend(SKEY(PrefType->Setting),
607                                                lval, 
608                                                NULL, 
609                                                PRF_INT,
610                                                1, 
611                                                PrefType);
612                         break;
613                 case PRF_QP_STRING:
614                         Buf = NewStrBufDup(SBstr(SKEY(PrefType->Setting)));
615                         set_preference_backend(SKEY(PrefType->Setting),
616                                                0, 
617                                                Buf, 
618                                                PRF_QP_STRING,
619                                                1, 
620                                                PrefType);
621                         break;
622                 case PRF_YESNO:
623                         lval = YesBstr(SKEY(PrefType->Setting));
624                         set_preference_backend(SKEY(PrefType->Setting),
625                                                lval, 
626                                                NULL, 
627                                                PRF_YESNO,
628                                                1, 
629                                                PrefType);
630                         break;
631                 }
632         }
633         WCC->hash_prefs = Tmp;
634 }
635
636
637 /**
638  * \brief Commit new preferences and settings
639  */
640 void set_preferences(void)
641 {       
642         if (!havebstr("change_button")) {
643                 safestrncpy(WC->ImportantMessage, 
644                             _("Cancelled.  No settings were changed."),
645                             sizeof WC->ImportantMessage);
646                 display_main_menu();
647                 return;
648         }
649         GetPreferences(WC->hash_prefs);
650         display_main_menu();
651 }
652
653
654 void tmplput_CFG_Value(StrBuf *Target, WCTemplputParams *TP)
655 {
656         Preference *Pref;
657         if (get_pref_backend(TKEY(0), &Pref))
658         {
659                 if (Pref->Type == NULL) {
660                         StrBufAppendTemplate(Target, TP, Pref->Val, 1);
661                 }
662                 switch (Pref->Type->Type)
663                 {
664                 case PRF_STRING:
665                         StrBufAppendTemplate(Target, TP, Pref->Val, 1);
666                         break;
667                 case PRF_INT:
668                         if (Pref->decoded != 1) {
669                                 if (Pref->Val == NULL)
670                                         Pref->Val = NewStrBufPlain(NULL, 64);
671                                 StrBufPrintf(Pref->Val, "%ld", Pref->lval);
672                                 Pref->decoded = 1;
673                         }
674                         StrBufAppendTemplate(Target, TP, Pref->Val, 1);
675                         break;
676                 case PRF_QP_STRING:
677                         if (Pref->decoded != 1) {
678                                 if (Pref->DeQPed == NULL)
679                                         Pref->DeQPed = NewStrBufPlain(NULL, StrLength(Pref->Val));
680                                         
681                                 StrBufEUid_unescapize(Pref->DeQPed, Pref->Val);
682                                 Pref->decoded = 1;
683                         }
684                         StrBufAppendTemplate(Target, TP, Pref->DeQPed, 1);
685                         break;
686                 case PRF_YESNO:
687                         if (Pref->decoded != 1) {
688                                 Pref->lval = strcmp(ChrPtr(Pref->Val), "yes") == 0;
689                                 Pref->decoded = 1;
690                         }
691                         StrBufAppendTemplate(Target, TP, Pref->Val, 1);
692                         break;
693                 }
694         }
695 }
696
697 void tmplput_CFG_Descr(StrBuf *Target, WCTemplputParams *TP)
698 {
699         const char *SettingStr;
700         SettingStr = PrefGetLocalStr(TKEY(0));
701         if (SettingStr != NULL) 
702                 StrBufAppendBufPlain(Target, SettingStr, -1, 0);
703 }
704 void tmplput_CFG_RoomValue(StrBuf *Target, WCTemplputParams *TP)
705 {
706         StrBuf *pref = get_ROOM_PREFS(TKEY(0));
707         if (pref != NULL) 
708                 StrBufAppendBuf(Target, pref, 0);
709 }
710 int ConditionalHasRoomPreference(StrBuf *Target, WCTemplputParams *TP) 
711 {
712         if (get_ROOM_PREFS(TP->Tokens->Params[0]->Start, 
713                            TP->Tokens->Params[0]->len) != NULL) 
714                 return 1;
715   
716         return 0;
717 }
718 void CfgZoneTempl(StrBuf *TemplBuffer, WCTemplputParams *TP)
719 {
720         StrBuf *Zone = (StrBuf*) CTX;
721
722         SVPutBuf("ZONENAME", Zone, 1);
723 }
724
725 int ConditionalPreference(StrBuf *Target, WCTemplputParams *TP)
726 {
727         StrBuf *Pref;
728
729         if (!get_PREFERENCE(TKEY(2), &Pref)) 
730                 return 0;
731         
732         if (TP->Tokens->nParameters == 3) {
733                 return 1;
734         }
735         else if (TP->Tokens->Params[3]->Type == TYPE_STR)
736                 return ((TP->Tokens->Params[3]->len == StrLength(Pref)) &&
737                         (strcmp(TP->Tokens->Params[3]->Start, ChrPtr(Pref)) == 0));
738         else 
739                 return (StrTol(Pref) == TP->Tokens->Params[3]->lvalue);
740 }
741
742 int ConditionalHasPreference(StrBuf *Target, WCTemplputParams *TP)
743 {
744         StrBuf *Pref;
745
746         if (!get_PREFERENCE(TKEY(2), &Pref) || 
747             (Pref == NULL)) 
748                 return 0;
749         else 
750                 return 1;
751 }
752
753
754 /********************************************************************************
755  *                 preferences stored discrete in citserver
756  ********************************************************************************/
757 HashList *GetGVEAHash(StrBuf *Target, WCTemplputParams *TP)
758 {
759         StrBuf *Rcp;
760         HashList *List = NULL;
761         int Done = 0;
762         int i, n = 1;
763         char N[64];
764
765         Rcp = NewStrBuf();
766         serv_puts("GVEA");
767         StrBuf_ServGetln(Rcp);
768         if (GetServerStatus(Rcp, NULL) == 1) {
769                 FlushStrBuf(Rcp);
770                 List = NewHash(1, NULL);
771                 while (!Done && (StrBuf_ServGetln(Rcp)>=0)) {
772                         if ( (StrLength(Rcp)==3) && 
773                              !strcmp(ChrPtr(Rcp), "000")) 
774                         {
775                                 Done = 1;
776                         }
777                         else {
778                                 i = snprintf(N, sizeof(N), "%d", n);
779                                 StrBufTrim(Rcp);
780                                 Put(List, N, i, Rcp, HFreeStrBuf);
781                                 Rcp = NewStrBuf();
782                         }
783                         n++;
784                 }
785         }
786         FreeStrBuf(&Rcp);
787         return List;
788 }
789 void DeleteGVEAHash(HashList **KillMe)
790 {
791         DeleteHash(KillMe);
792 }
793
794 HashList *GetGVSNHash(StrBuf *Target, WCTemplputParams *TP)
795 {
796         StrBuf *Rcp;
797         HashList *List = NULL;
798         int Done = 0;
799         int i, n = 1;
800         char N[64];
801
802         Rcp = NewStrBuf();
803         serv_puts("GVSN");
804         StrBuf_ServGetln(Rcp);
805         if (GetServerStatus(Rcp, NULL) == 1) {
806                 FlushStrBuf(Rcp);
807                 List = NewHash(1, NULL);
808                 while (!Done && (StrBuf_ServGetln(Rcp)>=0)) {
809                         if ( (StrLength(Rcp)==3) && 
810                              !strcmp(ChrPtr(Rcp), "000")) 
811                         {
812                                 Done = 1;
813                         }
814                         else {
815                                 i = snprintf(N, sizeof(N), "%d", n);
816                                 StrBufTrim(Rcp);
817                                 Put(List, N, i, Rcp, HFreeStrBuf);
818                                 Rcp = NewStrBuf();
819                         }
820                         n++;
821                 }
822         }
823         FreeStrBuf(&Rcp);
824         return List;
825 }
826 void DeleteGVSNHash(HashList **KillMe)
827 {
828         DeleteHash(KillMe);
829 }
830
831
832
833
834 /*
835  * Offer to make any page the user's "start page."
836  */
837 void offer_start_page(StrBuf *Target, WCTemplputParams *TP)
838 {
839         wprintf("<a href=\"change_start_page?startpage=");
840         urlescputs(WC->this_page);
841         wprintf("\">");
842         wprintf(_("Make this my start page"));
843         wprintf("</a>");
844 #ifdef TECH_PREVIEW
845         wprintf("<br/><a href=\"rss?room=");
846         urlescputs(ChrPtr(WC->wc_roomname));
847         wprintf("\" title=\"RSS 2.0 feed for ");
848         escputs(ChrPtr(WC->wc_roomname));
849         wprintf("\"><img alt=\"RSS\" border=\"0\" src=\"static/xml_button.gif\"/></a>\n");
850 #endif
851 }
852
853
854 /*
855  * Change the user's start page
856  */
857 void change_start_page(void) 
858 {
859         if (!havebstr("startpage")) {
860                 set_preference_backend(HKEY("startpage"), 
861                                        0, 
862                                        NewStrBufPlain(HKEY("")),
863                                        PRF_STRING,
864                                        1, 
865                                        NULL);
866                 safestrncpy(WC->ImportantMessage,
867                             _("You no longer have a start page selected."),
868                             sizeof( WC->ImportantMessage));
869                 display_main_menu();
870                 return;
871         }
872
873         set_preference_backend(HKEY("startpage"), 
874                                0, 
875                                NewStrBufDup(sbstr("startpage")),
876                                PRF_STRING,
877                                1, 
878                                NULL);
879
880         output_headers(1, 1, 0, 0, 0, 0);
881         do_template("newstartpage", NULL);
882         wDumpContent(1);
883 }
884
885
886 void 
887 InitModule_PREFERENCES
888 (void)
889 {
890         WebcitAddUrlHandler(HKEY("set_preferences"), set_preferences, 0);
891         WebcitAddUrlHandler(HKEY("change_start_page"), change_start_page, 0);
892
893
894         RegisterNamespace("OFFERSTARTPAGE", 0, 0, offer_start_page, CTX_NONE);
895         RegisterNamespace("PREF:ROOM:VALUE", 1, 2, tmplput_CFG_RoomValue,  CTX_NONE);
896         RegisterNamespace("PREF:VALUE", 1, 2, tmplput_CFG_Value, CTX_NONE);
897         RegisterNamespace("PREF:DESCR", 1, 1, tmplput_CFG_Descr, CTX_NONE);
898         RegisterIterator("PREF:ZONE", 0, ZoneHash, NULL, CfgZoneTempl, NULL, CTX_PREF, CTX_NONE, IT_NOFLAG);
899
900         RegisterConditional(HKEY("COND:PREF"), 4, ConditionalPreference, CTX_NONE);
901         RegisterConditional(HKEY("COND:PREF:SET"), 4, ConditionalHasPreference, CTX_NONE);
902         RegisterConditional(HKEY("COND:ROOM:SET"), 4, ConditionalHasRoomPreference, CTX_NONE);
903         
904         RegisterIterator("PREF:VALID:EMAIL:ADDR", 0, NULL, 
905                          GetGVEAHash, NULL, DeleteGVEAHash, CTX_STRBUF, CTX_NONE, IT_NOFLAG);
906         RegisterIterator("PREF:VALID:EMAIL:NAME", 0, NULL, 
907                          GetGVSNHash, NULL, DeleteGVSNHash, CTX_STRBUF, CTX_NONE, IT_NOFLAG);
908
909 }
910 /*@}*/