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