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