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