if StrBuf_ServGetln() is called in a loop, its return value has to be checked for...
[citadel.git] / webcit / preferences.c
1 /*
2  * Manage user preferences with a little help from the Citadel server.
3  */
4
5 #include "webcit.h"
6 #include "webserver.h"
7 #include "groupdav.h"
8
9 HashList *PreferenceHooks;
10 extern HashList *HandlerHash;
11
12 typedef struct _PrefDef {
13         ePrefType eType;
14         StrBuf *Setting;
15         const char *PrefStr;
16         PrefEvalFunc OnLoad;
17         StrBuf *OnLoadName;
18 } PrefDef;
19
20 typedef struct _Preference {
21         PrefDef *Type;
22         ePrefType eFlatPrefType;
23
24         StrBuf *Key;
25         StrBuf *Val;
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                          ePrefType 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->eType = 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                         Pref->eFlatPrefType = Pref->Type->eType;
110
111                         syslog(1, "Loading [%s]with type [%ld] [\"%s\"]\n",
112                                 ChrPtr(Pref->Key),
113                                 Pref->Type->eType,
114                                 ChrPtr(Pref->Val));
115
116                         switch (Pref->Type->eType)
117                         {
118                         case PRF_UNSET: /* WHUT? */
119                                 break;
120                         case PRF_STRING:
121                                 break;
122                         case PRF_INT:
123                                 Pref->lval = StrTol(Pref->Val);
124                                 Pref->decoded = 1;
125                                 break;
126                         case PRF_QP_STRING:
127                                 Pref->DeQPed = NewStrBufPlain(NULL, StrLength(Pref->Val));
128                                 StrBufEUid_unescapize(Pref->DeQPed, Pref->Val);
129                                 Pref->decoded = 1;
130                                 break;
131                         case PRF_YESNO:
132                                 Pref->lval = strcmp(ChrPtr(Pref->Val), "yes") == 0;
133                                 Pref->decoded = 1;
134                                 break;
135                         }
136
137                         if (PrefType->OnLoad != NULL){
138
139                                 syslog(1, "Loading with: -> %s(\"%s\", %ld)\n",
140                                         ChrPtr(PrefType->OnLoadName),
141                                         ChrPtr(Pref->Val),
142                                         Pref->lval);
143                                 PrefType->OnLoad(Pref->Val, Pref->lval);
144                         }
145                 }
146         }
147         DeleteHashPos(&It);
148 }
149
150 void ParsePref(HashList **List, StrBuf *ReadBuf)
151 {
152         int Done = 0;
153         Preference *Data = NULL;
154         Preference *LastData = NULL;
155                                 
156         while (!Done) {
157                 if (StrBuf_ServGetln(ReadBuf) < 0)
158                         break;
159                 if ( (StrLength(ReadBuf)==3) && 
160                      !strcmp(ChrPtr(ReadBuf), "000")) {
161                         Done = 1;
162                         break;
163                 }
164
165                 if ((ChrPtr(ReadBuf)[0] == ' ') &&
166                     (LastData != NULL)) {
167                         StrBufAppendBuf(LastData->Val, ReadBuf, 1);
168                 }
169                 else {
170                         LastData = Data = malloc(sizeof(Preference));
171                         memset(Data, 0, sizeof(Preference));
172                         Data->Key = NewStrBuf();
173                         Data->Val = NewStrBuf();
174                         StrBufExtract_token(Data->Key, ReadBuf, 0, '|');
175                         StrBufExtract_token(Data->Val, ReadBuf, 1, '|');
176                         if (!IsEmptyStr(ChrPtr(Data->Key)))
177                         {
178                                 Put(*List, 
179                                     SKEY(Data->Key),
180                                     Data, 
181                                     DestroyPreference);
182                         }
183                         else 
184                         {
185                                 StrBufTrim(ReadBuf);
186                                 syslog(1, "ignoring spurious preference line: [%s]\n", 
187                                         ChrPtr(ReadBuf));
188                                 DestroyPreference(Data);
189                                 LastData = NULL;
190                         }
191                         Data = NULL;
192                 }
193         }
194         GetPrefTypes(*List);
195 }
196
197
198 /*
199  * display preferences dialog
200  */
201 void load_preferences(void) 
202 {
203         folder Room;
204         wcsession *WCC = WC;
205         int Done = 0;
206         StrBuf *ReadBuf;
207         long msgnum = 0L;
208         
209         memset(&Room, 0, sizeof(folder));
210         ReadBuf = NewStrBufPlain(NULL, SIZ * 4);
211         if (goto_config_room(ReadBuf, &Room) != 0) {
212                 FreeStrBuf(&ReadBuf);
213                 FlushFolder(&Room);
214
215                 return; /* oh well. */
216         }
217
218         serv_puts("MSGS ALL|0|1");
219         StrBuf_ServGetln(ReadBuf);
220         if (GetServerStatus(ReadBuf, NULL) == 8) {
221                 serv_puts("subj|__ WebCit Preferences __");
222                 serv_puts("000");
223         }
224         while (!Done &&
225                (StrBuf_ServGetln(ReadBuf) >= 0)) {
226                 if ( (StrLength(ReadBuf)==3) && 
227                      !strcmp(ChrPtr(ReadBuf), "000")) {
228                         Done = 1;
229                         break;
230                 }
231                 msgnum = StrTol(ReadBuf);
232         }
233
234         if (msgnum > 0L) {
235                 serv_printf("MSG0 %ld", msgnum);
236                 StrBuf_ServGetln(ReadBuf);
237                 if (GetServerStatus(ReadBuf, NULL) == 1) {
238                         while ((StrBuf_ServGetln(ReadBuf) >= 0) && 
239                                (strcmp(ChrPtr(ReadBuf), "text") && 
240                                 strcmp(ChrPtr(ReadBuf), "000"))) {
241                         }
242                         if (!strcmp(ChrPtr(ReadBuf), "text")) {
243                                 ParsePref(&WCC->hash_prefs, ReadBuf);
244                         }
245                 }
246         }
247
248         /* Go back to the room we're supposed to be in */
249         if (StrLength(WCC->CurRoom.name) > 0) {
250                 serv_printf("GOTO %s", ChrPtr(WCC->CurRoom.name));
251                 StrBuf_ServGetln(ReadBuf);
252                 GetServerStatus(ReadBuf, NULL);
253         }
254         FreeStrBuf(&ReadBuf);
255         FlushFolder(&Room);
256 }
257
258 /*
259  * Goto the user's configuration room, creating it if necessary.
260  * returns 0 on success or nonzero upon failure.
261  */
262 int goto_config_room(StrBuf *Buf, folder *Room) 
263 {
264         serv_printf("GOTO %s", USERCONFIGROOM);
265         StrBuf_ServGetln(Buf);
266         if (GetServerStatus(Buf, NULL) != 2) {  /* try to create the config room if not there */
267                 serv_printf("CRE8 1|%s|4|0", USERCONFIGROOM);
268                 StrBuf_ServGetln(Buf);
269                 GetServerStatus(Buf, NULL);
270
271                 serv_printf("GOTO %s", USERCONFIGROOM);
272                 StrBuf_ServGetln(Buf);
273                 if (GetServerStatus(Buf, NULL) != 2) {
274                         return(1);
275                 }
276         }
277         ParseGoto(Room, Buf);
278         return(0);
279 }
280
281 void WritePrefsToServer(HashList *Hash)
282 {
283         wcsession *WCC = WC;
284         long len;
285         HashPos *HashPos;
286         void *vPref;
287         const char *Key;
288         Preference *Pref;
289         StrBuf *SubBuf = NULL;
290         
291         Hash = WCC->hash_prefs;
292 #ifdef DBG_PREFS_HASH
293         dbg_PrintHash(Hash, PrintPref, NULL);
294 #endif
295         HashPos = GetNewHashPos(Hash, 0);
296         while (GetNextHashPos(Hash, HashPos, &len, &Key, &vPref)!=0)
297         {
298                 size_t nchars;
299                 if (vPref == NULL)
300                         continue;
301                 Pref = (Preference*) vPref;
302                 nchars = StrLength(Pref->Val);
303                 if (nchars > 80){
304                         int n = 0;
305                         size_t offset, nchars;
306                         if (SubBuf == NULL)
307                                 SubBuf = NewStrBufPlain(NULL, SIZ);
308                         nchars = 1;
309                         offset = 0;
310                         while (nchars > 0) {
311                                 if (n == 0)
312                                         nchars = 70;
313                                 else 
314                                         nchars = 80;
315                                 
316                                 nchars = StrBufSub(SubBuf, Pref->Val, offset, nchars);
317                                 
318                                 if (n == 0)
319                                         serv_printf("%s|%s", ChrPtr(Pref->Key), ChrPtr(SubBuf));
320                                 else
321                                         serv_printf(" %s", ChrPtr(SubBuf));
322                                 
323                                 offset += nchars;
324                                 nchars = StrLength(Pref->Val) - offset;
325                                 n++;
326                         }
327                         
328                 }
329                 else
330                         serv_printf("%s|%s", ChrPtr(Pref->Key), ChrPtr(Pref->Val));
331                 
332         }
333         FreeStrBuf(&SubBuf);
334         DeleteHashPos(&HashPos);
335 }
336
337 /**
338  * \brief save the modifications
339  */
340 void save_preferences(void) 
341 {
342         folder Room;
343         wcsession *WCC = WC;
344         int Done = 0;
345         StrBuf *ReadBuf;
346         long msgnum = 0L;
347         
348         ReadBuf = NewStrBuf();
349         memset(&Room, 0, sizeof(folder));
350         if (goto_config_room(ReadBuf, &Room) != 0) {
351                 FreeStrBuf(&ReadBuf);
352                 FlushFolder(&Room);
353
354                 return; /* oh well. */
355         }
356
357         /* make shure the config room has the right type, else it might reject our config */
358         if (Room.view != VIEW_BBS) {
359                 serv_printf("VIEW %d", VIEW_BBS);
360                 StrBuf_ServGetln(ReadBuf);
361                 if (GetServerStatus(ReadBuf, NULL) != 2) {
362                         /* UPS? */
363                 }
364                 else if (goto_config_room(ReadBuf, &Room) != 0) {
365                         FreeStrBuf(&ReadBuf);
366                         FlushFolder(&Room);
367                         
368                         return; /* oh well. */
369                 }
370         }
371
372         serv_puts("MSGS ALL|0|1");
373         StrBuf_ServGetln(ReadBuf);
374         if (GetServerStatus(ReadBuf, NULL) == 8) {
375                 serv_puts("subj|__ WebCit Preferences __");
376                 serv_puts("000");
377         }
378         while (!Done &&
379                (StrBuf_ServGetln(ReadBuf) >= 0)) {
380                 if ( (StrLength(ReadBuf)==3) && 
381                      !strcmp(ChrPtr(ReadBuf), "000")) {
382                         Done = 1;
383                         break;
384                 }
385                 msgnum = StrTol(ReadBuf);
386         }
387
388         if (msgnum > 0L) {
389                 serv_printf("DELE %ld", msgnum);
390                 StrBuf_ServGetln(ReadBuf);
391                 GetServerStatus(ReadBuf, NULL);
392         }
393
394         serv_printf("ENT0 1||0|1|__ WebCit Preferences __|");
395         StrBuf_ServGetln(ReadBuf);
396         if (GetServerStatus(ReadBuf, NULL) == 4) {
397
398                 WritePrefsToServer(WCC->hash_prefs);
399                 serv_puts("");
400                 serv_puts("000");
401         }
402
403         /** Go back to the room we're supposed to be in */
404         if (StrLength(WCC->CurRoom.name) > 0) {
405                 serv_printf("GOTO %s", ChrPtr(WCC->CurRoom.name));
406                 StrBuf_ServGetln(ReadBuf);
407                 GetServerStatus(ReadBuf, NULL);
408         }
409         FreeStrBuf(&ReadBuf);
410         FlushFolder(&Room);
411 }
412
413 /**
414  * \brief query the actual setting of key in the citadel database
415  * \param key config key to query
416  * \param keylen length of the key string
417  * \param value StrBuf-value to the key to get
418  * \returns found?
419  */
420 int get_pref_backend(const char *key, size_t keylen, Preference **Pref)
421 {
422         void *hash_value = NULL;
423 #ifdef DBG_PREFS_HASH
424         dbg_PrintHash(WC->hash_prefs, PrintPref, NULL);
425 #endif
426         if (GetHash(WC->hash_prefs, key, keylen, &hash_value) == 0) {
427                 *Pref = NULL;
428                 return 0;
429         }
430         else {
431                 *Pref = (Preference*) hash_value;
432                 return 1;
433         }
434 }
435
436 int get_PREFERENCE(const char *key, size_t keylen, StrBuf **value)
437 {
438         Preference *Pref;
439         int Ret;
440
441         Ret = get_pref_backend(key, keylen, &Pref);
442         if (Ret != 0)
443                 *value = Pref->Val;
444         else
445                 *value = NULL;
446         return Ret;
447 }
448
449 /**
450  * \brief       Write a key into the webcit preferences database for this user
451  *
452  * \params      key             key whichs value is to be modified
453  * \param keylen length of the key string
454  * \param       value           value to set
455  * \param       save_to_server  1 = flush all data to the server, 0 = cache it for now
456  */
457 long compare_preference(const Preference *PrefA, 
458                         const Preference *PrefB)
459 {
460         ePrefType TypeA, TypeB;
461
462         if (PrefA->Type != NULL)
463                 TypeA = PrefA->Type->eType;
464         else 
465                 TypeA = PrefA->eFlatPrefType;
466
467         if (PrefB->Type != NULL)
468                 TypeB = PrefB->Type->eType;
469         else 
470                 TypeB = PrefB->eFlatPrefType;
471
472         if ((TypeA != PRF_UNSET) && 
473             (TypeB != PRF_UNSET) && 
474             (TypeA != TypeB))
475         {
476                 if (TypeA > TypeB)
477                         return 1;
478                 else /* (PrefA->Type < PrefB->Type) */
479                         return -1;
480         }
481
482         if (TypeB == PRF_UNSET)
483                 TypeA = PRF_UNSET;
484                     
485         switch (TypeA)
486         {
487         default:
488         case PRF_UNSET:
489         case PRF_STRING:
490                 return strcmp(ChrPtr(PrefA->Val), 
491                               ChrPtr(PrefB->Val));
492         case PRF_YESNO:
493         case PRF_INT:
494                 if (PrefA->lval == PrefB->lval)
495                         return 0;
496                 else if (PrefA->lval > PrefB->lval)
497                         return 1;
498                 else
499                         return -1;
500         case PRF_QP_STRING:
501                 return strcmp(ChrPtr(PrefA->DeQPed), 
502                               ChrPtr(PrefB->DeQPed));
503         }
504 }
505
506 /**
507  * \brief       Write a key into the webcit preferences database for this user
508  *
509  * \params      key             key whichs value is to be modified
510  * \param keylen length of the key string
511  * \param       value           value to set
512  * \param       save_to_server  1 = flush all data to the server, 0 = cache it for now
513  */
514 void set_preference_backend(const char *key, size_t keylen, 
515                             long lvalue, 
516                             StrBuf *value, 
517                             long lPrefType,
518                             int save_to_server, 
519                             PrefDef *PrefType) 
520 {
521         wcsession *WCC = WC;
522         void *vPrefDef;
523         void *vPrefB;
524         Preference *Pref;
525
526         Pref = (Preference*) malloc(sizeof(Preference));
527         memset(Pref, 0, sizeof(Preference));
528         Pref->Key = NewStrBufPlain(key, keylen);
529
530         if ((PrefType == NULL) &&
531             GetHash(PreferenceHooks, SKEY(Pref->Key), &vPrefDef) && 
532             (vPrefDef != NULL))
533                 PrefType = (PrefDef*) vPrefDef;
534
535         if (PrefType != NULL)
536         {
537                 Pref->Type = PrefType;
538                 Pref->eFlatPrefType = PrefType->eType;
539                 if (Pref->Type->eType != lPrefType)
540                         syslog(1, "warning: saving preference with wrong type [%s] %ld != %ld \n",
541                                 key, Pref->Type->eType, lPrefType);
542                 switch (Pref->Type->eType)
543                 {
544                 case PRF_UNSET: /* default to string... */
545                 case PRF_STRING:
546                         Pref->Val = value;
547                         Pref->decoded = 1;
548                         break;
549                 case PRF_INT:
550                         Pref->lval = lvalue;
551                         Pref->Val = value;
552                         if (Pref->Val == NULL)
553                                 Pref->Val = NewStrBufPlain(NULL, 64);
554                         StrBufPrintf(Pref->Val, "%ld", lvalue);
555                         Pref->decoded = 1;
556                         break;
557                 case PRF_QP_STRING:
558                         Pref->DeQPed = value;
559                         Pref->Val = NewStrBufPlain(NULL, StrLength(Pref->DeQPed) * 3);
560                         StrBufEUid_escapize(Pref->Val, Pref->DeQPed);
561                         Pref->decoded = 1;
562                         break;
563                 case PRF_YESNO:
564                         Pref->lval = lvalue;
565                         if (lvalue) 
566                                 Pref->Val = NewStrBufPlain(HKEY("yes"));
567                         else
568                                 Pref->Val = NewStrBufPlain(HKEY("no"));
569                         Pref->decoded = 1;
570                         break;
571                 }
572                 if (Pref->Type->OnLoad != NULL)
573                         Pref->Type->OnLoad(Pref->Val, Pref->lval);
574         }
575         else {
576                 Pref->eFlatPrefType = lPrefType;
577                 switch (lPrefType)
578                 {
579                 case PRF_STRING:
580                         Pref->Val = value;
581                         Pref->decoded = 1;
582                         break;
583                 case PRF_INT:
584                         Pref->lval = lvalue;
585                         Pref->Val = value;
586                         if (Pref->Val == NULL)
587                                 Pref->Val = NewStrBufPlain(NULL, 64);
588                         StrBufPrintf(Pref->Val, "%ld", lvalue);
589                         Pref->decoded = 1;
590                         break;
591                 case PRF_QP_STRING:
592                         Pref->DeQPed = value;
593                         Pref->Val = NewStrBufPlain(NULL, StrLength(Pref->DeQPed) * 3);
594                         StrBufEUid_escapize(Pref->Val, Pref->DeQPed);
595                         Pref->decoded = 1;
596                         break;
597                 case PRF_YESNO:
598                         Pref->lval = lvalue;
599                         if (lvalue) 
600                                 Pref->Val = NewStrBufPlain(HKEY("yes"));
601                         else
602                                 Pref->Val = NewStrBufPlain(HKEY("no"));
603                         Pref->decoded = 1;
604                         break;
605                 }
606         }
607
608         if ((save_to_server != 0) && 
609             GetHash(WCC->hash_prefs, key, keylen, &vPrefB) && 
610             (vPrefB != NULL) && 
611             (compare_preference (Pref, vPrefB) == 0))
612                 save_to_server = 0;
613
614         Put(WCC->hash_prefs, key, keylen, Pref, DestroyPreference);
615         
616         if (save_to_server) WCC->SavePrefsToServer = 1;
617 }
618
619 void set_PREFERENCE(const char *key, size_t keylen, StrBuf *value, int save_to_server) 
620 {
621         set_preference_backend(key, keylen, 0, value, PRF_STRING, save_to_server, NULL);
622 }
623
624 int get_PREF_LONG(const char *key, size_t keylen, long *value, long Default)
625 {
626         Preference *Pref;
627         int Ret;
628
629         Ret = get_pref_backend(key, keylen, &Pref);
630         if (Ret == 0) {
631                 *value = Default;
632                 return 0;
633         }
634
635         if (Pref->decoded)
636                 *value = Pref->lval;
637         else {
638                 *value = Pref->lval = atol(ChrPtr(Pref->Val));
639                 Pref->decoded = 1;
640         }
641         return Ret;
642 }
643
644
645 void set_PREF_LONG(const char *key, size_t keylen, long value, int save_to_server)
646 {
647         set_preference_backend(key, keylen, value, NULL, PRF_INT, save_to_server, NULL);
648 }
649
650 int get_PREF_YESNO(const char *key, size_t keylen, int *value, int Default)
651 {
652         Preference *Pref;
653         int Ret;
654
655         Ret = get_pref_backend(key, keylen, &Pref);
656         if (Ret == 0) {
657                 *value = Default;
658                 return 0;
659         }
660
661         if (Pref->decoded)
662                 *value = Pref->lval;
663         else {
664                 *value = Pref->lval = strcmp(ChrPtr(Pref->Val), "yes") == 0;
665                 Pref->decoded = 1;
666         }
667         return Ret;
668 }
669
670 void set_PREF_YESNO(const char *key, size_t keylen, long value, int save_to_server)
671 {
672         set_preference_backend(key, keylen, value, NULL, PRF_YESNO, save_to_server, NULL);
673 }
674
675 int get_room_prefs_backend(const char *key, size_t keylen, 
676                            Preference **Pref)
677 {
678         StrBuf *pref_name;
679         int Ret;
680
681         pref_name = NewStrBufPlain (HKEY("ROOM:"));
682         StrBufAppendBuf(pref_name, WC->CurRoom.name, 0);
683         StrBufAppendBufPlain(pref_name, HKEY(":"), 0);
684         StrBufAppendBufPlain(pref_name, key, keylen, 0);
685         Ret = get_pref_backend(SKEY(pref_name), Pref);
686         FreeStrBuf(&pref_name);
687
688         return Ret;
689 }
690
691 const StrBuf *get_X_PREFS(const char *key, size_t keylen, 
692                           const char *xkey, size_t xkeylen)
693 {
694         int ret;
695         StrBuf *pref_name;
696         Preference *Prf;
697         
698         pref_name = NewStrBufPlain (HKEY("XPREF:"));
699         StrBufAppendBufPlain(pref_name, xkey, xkeylen, 0);
700         StrBufAppendBufPlain(pref_name, HKEY(":"), 0);
701         StrBufAppendBufPlain(pref_name, key, keylen, 0);
702
703         ret = get_pref_backend(SKEY(pref_name), &Prf);
704         FreeStrBuf(&pref_name);
705
706         if (ret)
707                 return Prf->Val;
708         else return NULL;
709 }
710
711 void set_X_PREFS(const char *key, size_t keylen, const char *xkey, size_t xkeylen, StrBuf *value, int save_to_server)
712 {
713         StrBuf *pref_name;
714         
715         pref_name = NewStrBufPlain (HKEY("XPREF:"));
716         StrBufAppendBufPlain(pref_name, xkey, xkeylen, 0);
717         StrBufAppendBufPlain(pref_name, HKEY(":"), 0);
718         StrBufAppendBufPlain(pref_name, key, keylen, 0);
719
720         set_preference_backend(SKEY(pref_name), 0, value, PRF_STRING, save_to_server, NULL);
721         FreeStrBuf(&pref_name);
722 }
723
724
725 long get_ROOM_PREFS_LONG(const char *key, size_t keylen, long *value, long Default)
726 {
727         Preference *Pref;
728         int Ret;
729
730         Ret = get_room_prefs_backend(key, keylen, &Pref);
731
732         if (Ret == 0) {
733                 *value = Default;
734                 return 0;
735         }
736
737         if (Pref->decoded)
738                 *value = Pref->lval;
739         else {
740                 *value = Pref->lval = atol(ChrPtr(Pref->Val));
741                 Pref->decoded = 1;
742         }
743         return Ret;
744 }
745
746
747 StrBuf *get_ROOM_PREFS(const char *key, size_t keylen)
748 {
749         Preference *Pref;
750         int Ret;
751
752         Ret = get_room_prefs_backend(key, keylen, &Pref);
753
754         if (Ret == 0) {
755                 return NULL;
756         }
757         else 
758                 return Pref->Val;
759 }
760
761 void set_ROOM_PREFS(const char *key, size_t keylen, StrBuf *value, int save_to_server)
762 {
763         StrBuf *pref_name;
764         
765         pref_name = NewStrBufPlain (HKEY("ROOM:"));
766         StrBufAppendBuf(pref_name, WC->CurRoom.name, 0);
767         StrBufAppendBufPlain(pref_name, HKEY(":"), 0);
768         StrBufAppendBufPlain(pref_name, key, keylen, 0);
769         set_preference_backend(SKEY(pref_name), 0, value, PRF_STRING, save_to_server, NULL);
770         FreeStrBuf(&pref_name);
771 }
772
773
774 void GetPreferences(HashList *Setting)
775 {
776         wcsession *WCC = WC;
777         HashPos *It;
778         long len;
779         const char *Key;
780         void *vSetting;
781         PrefDef *PrefType;
782         StrBuf *Buf;
783         long lval;
784         HashList *Tmp;
785
786         Tmp = WCC->hash_prefs;
787         WCC->hash_prefs = Setting;
788
789         It = GetNewHashPos(PreferenceHooks, 0);
790         while (GetNextHashPos(PreferenceHooks, It, &len, &Key, &vSetting)) {
791                 PrefType = (PrefDef*) vSetting;
792
793                 if (!HaveBstr(SKEY(PrefType->Setting)))
794                         continue;
795                 switch (PrefType->eType) {
796                 case PRF_UNSET:
797                 case PRF_STRING:
798                         Buf = NewStrBufDup(SBstr(SKEY(PrefType->Setting)));
799                         set_preference_backend(SKEY(PrefType->Setting),
800                                                0, 
801                                                Buf, 
802                                                PRF_STRING,
803                                                1, 
804                                                PrefType);
805                         break;
806                 case PRF_INT:
807                         lval = LBstr(SKEY(PrefType->Setting));
808                         set_preference_backend(SKEY(PrefType->Setting),
809                                                lval, 
810                                                NULL, 
811                                                PRF_INT,
812                                                1, 
813                                                PrefType);
814                         break;
815                 case PRF_QP_STRING:
816                         Buf = NewStrBufDup(SBstr(SKEY(PrefType->Setting)));
817                         set_preference_backend(SKEY(PrefType->Setting),
818                                                0, 
819                                                Buf, 
820                                                PRF_QP_STRING,
821                                                1, 
822                                                PrefType);
823                         break;
824                 case PRF_YESNO:
825                         lval = YesBstr(SKEY(PrefType->Setting));
826                         set_preference_backend(SKEY(PrefType->Setting),
827                                                lval, 
828                                                NULL, 
829                                                PRF_YESNO,
830                                                1, 
831                                                PrefType);
832                         break;
833                 }
834         }
835         WCC->hash_prefs = Tmp;
836         DeleteHashPos(&It);
837 }
838
839
840 /**
841  * \brief Commit new preferences and settings
842  */
843 void set_preferences(void)
844 {       
845         if (!havebstr("change_button")) {
846                 safestrncpy(WC->ImportantMessage, 
847                             _("Cancelled.  No settings were changed."),
848                             sizeof WC->ImportantMessage);
849                 display_main_menu();
850                 return;
851         }
852         GetPreferences(WC->hash_prefs);
853         display_main_menu();
854 }
855
856
857 void tmplput_CFG_Value(StrBuf *Target, WCTemplputParams *TP)
858 {
859         Preference *Pref;
860         if (get_pref_backend(TKEY(0), &Pref))
861         {
862                 if (Pref->Type == NULL) {
863                         StrBufAppendTemplate(Target, TP, Pref->Val, 1);
864                 }
865                 switch (Pref->Type->eType)
866                 {
867                 case PRF_UNSET: /* default to string... */
868                 case PRF_STRING:
869                         StrBufAppendTemplate(Target, TP, Pref->Val, 1);
870                         break;
871                 case PRF_INT:
872                         if (Pref->decoded != 1) {
873                                 if (Pref->Val == NULL)
874                                         Pref->Val = NewStrBufPlain(NULL, 64);
875                                 StrBufPrintf(Pref->Val, "%ld", Pref->lval);
876                                 Pref->decoded = 1;
877                         }
878                         StrBufAppendTemplate(Target, TP, Pref->Val, 1);
879                         break;
880                 case PRF_QP_STRING:
881                         if (Pref->decoded != 1) {
882                                 if (Pref->DeQPed == NULL)
883                                         Pref->DeQPed = NewStrBufPlain(NULL, StrLength(Pref->Val));
884                                         
885                                 StrBufEUid_unescapize(Pref->DeQPed, Pref->Val);
886                                 Pref->decoded = 1;
887                         }
888                         StrBufAppendTemplate(Target, TP, Pref->DeQPed, 1);
889                         break;
890                 case PRF_YESNO:
891                         if (Pref->decoded != 1) {
892                                 Pref->lval = strcmp(ChrPtr(Pref->Val), "yes") == 0;
893                                 Pref->decoded = 1;
894                         }
895                         StrBufAppendTemplate(Target, TP, Pref->Val, 1);
896                         break;
897                 }
898         }
899 }
900
901 void tmplput_CFG_Descr(StrBuf *Target, WCTemplputParams *TP)
902 {
903         const char *SettingStr;
904         SettingStr = PrefGetLocalStr(TKEY(0));
905         if (SettingStr != NULL) 
906                 StrBufAppendBufPlain(Target, SettingStr, -1, 0);
907 }
908 void tmplput_CFG_RoomValueLong(StrBuf *Target, WCTemplputParams *TP)
909 {
910         long lvalue;
911         long defval = 0;
912
913         if (HAVE_PARAM(1))
914                 defval = GetTemplateTokenNumber(Target, TP, 1, 0);
915         get_ROOM_PREFS_LONG(TKEY(0), &lvalue, defval);
916         StrBufAppendPrintf(Target, "%ld", lvalue);
917 }
918 void tmplput_CFG_RoomValue(StrBuf *Target, WCTemplputParams *TP)
919 {
920         StrBuf *pref = get_ROOM_PREFS(TKEY(0));
921         if (pref != NULL) 
922                 StrBufAppendBuf(Target, pref, 0);
923 }
924 int ConditionalHasRoomPreference(StrBuf *Target, WCTemplputParams *TP) 
925 {
926         if (get_ROOM_PREFS(TP->Tokens->Params[0]->Start, 
927                            TP->Tokens->Params[0]->len) != NULL) 
928                 return 1;
929   
930         return 0;
931 }
932
933 int ConditionalPreference(StrBuf *Target, WCTemplputParams *TP)
934 {
935         StrBuf *Pref;
936
937         if (!get_PREFERENCE(TKEY(2), &Pref)) 
938                 return 0;
939         
940         if (!HAVE_PARAM(3)) {
941                 return 1;
942         }
943         else if (IS_NUMBER(TP->Tokens->Params[3]->Type))
944         {
945                 return StrTol(Pref) == GetTemplateTokenNumber (Target, TP, 3, 0);
946         }
947         else 
948         {
949                 const char *pch;
950                 long len;
951                 
952                 GetTemplateTokenString(Target, TP, 3, &pch, &len);
953                 
954                 return ((len == StrLength(Pref)) &&
955                         (strcmp(pch, ChrPtr(Pref)) == 0));
956         }
957 }
958
959 int ConditionalHasPreference(StrBuf *Target, WCTemplputParams *TP)
960 {
961         StrBuf *Pref;
962
963         if (!get_PREFERENCE(TKEY(2), &Pref) || 
964             (Pref == NULL)) 
965                 return 0;
966         else 
967                 return 1;
968 }
969
970
971 /********************************************************************************
972  *                 preferences stored discrete in citserver
973  ********************************************************************************/
974 HashList *GetGVEAHash(StrBuf *Target, WCTemplputParams *TP)
975 {
976         StrBuf *Rcp;
977         HashList *List = NULL;
978         int Done = 0;
979         int i, n = 1;
980         char N[64];
981
982         Rcp = NewStrBuf();
983         serv_puts("GVEA");
984         StrBuf_ServGetln(Rcp);
985         if (GetServerStatus(Rcp, NULL) == 1) {
986                 FlushStrBuf(Rcp);
987                 List = NewHash(1, NULL);
988                 while (!Done && (StrBuf_ServGetln(Rcp)>=0)) {
989                         if ( (StrLength(Rcp)==3) && 
990                              !strcmp(ChrPtr(Rcp), "000")) 
991                         {
992                                 Done = 1;
993                         }
994                         else {
995                                 i = snprintf(N, sizeof(N), "%d", n);
996                                 StrBufTrim(Rcp);
997                                 Put(List, N, i, Rcp, HFreeStrBuf);
998                                 Rcp = NewStrBuf();
999                         }
1000                         n++;
1001                 }
1002         }
1003         FreeStrBuf(&Rcp);
1004         return List;
1005 }
1006 void DeleteGVEAHash(HashList **KillMe)
1007 {
1008         DeleteHash(KillMe);
1009 }
1010
1011 HashList *GetGVSNHash(StrBuf *Target, WCTemplputParams *TP)
1012 {
1013         StrBuf *Rcp;
1014         HashList *List = NULL;
1015         int Done = 0;
1016         int i, n = 1;
1017         char N[64];
1018
1019         Rcp = NewStrBuf();
1020         serv_puts("GVSN");
1021         StrBuf_ServGetln(Rcp);
1022         if (GetServerStatus(Rcp, NULL) == 1) {
1023                 FlushStrBuf(Rcp);
1024                 List = NewHash(1, NULL);
1025                 while (!Done && (StrBuf_ServGetln(Rcp)>=0)) {
1026                         if ( (StrLength(Rcp)==3) && 
1027                              !strcmp(ChrPtr(Rcp), "000")) 
1028                         {
1029                                 Done = 1;
1030                         }
1031                         else {
1032                                 i = snprintf(N, sizeof(N), "%d", n);
1033                                 StrBufTrim(Rcp);
1034                                 Put(List, N, i, Rcp, HFreeStrBuf);
1035                                 Rcp = NewStrBuf();
1036                         }
1037                         n++;
1038                 }
1039         }
1040         FreeStrBuf(&Rcp);
1041         return List;
1042 }
1043 void DeleteGVSNHash(HashList **KillMe)
1044 {
1045         DeleteHash(KillMe);
1046 }
1047
1048
1049
1050
1051 /*
1052  * Offer to make any page the user's "start page" (only if logged in)
1053  */
1054 void offer_start_page(StrBuf *Target, WCTemplputParams *TP)
1055 {
1056         if (WC->logged_in) {
1057                 wc_printf("<a href=\"change_start_page?startpage=");
1058                 urlescputs(ChrPtr(WC->Hdr->this_page));
1059                 wc_printf("\">");
1060                 wc_printf(_("Make this my start page"));
1061                 wc_printf("</a>");
1062         };
1063 }
1064
1065
1066 /*
1067  * Change the user's start page
1068  */
1069 void change_start_page(void) 
1070 {
1071         wcsession *WCC = WC;
1072         const char *pch;
1073         void *vHandler;
1074         int ProhibitSave = 0;
1075         const StrBuf *pStartPage = sbstr("startpage");
1076
1077         if (pStartPage != NULL) {
1078                 pch = strchr(ChrPtr(pStartPage), '?');
1079
1080                 if ((pch != NULL) && (
1081                             GetHash(HandlerHash, ChrPtr(pStartPage), pch - ChrPtr(pStartPage), &vHandler), 
1082                             (vHandler != NULL) &&
1083                             ((((WebcitHandler*)vHandler)->Flags & PROHIBIT_STARTPAGE) != 0)))
1084                 { /* OK, This handler doesn't want to be set as start page, prune it. */
1085                         ProhibitSave = 1;
1086                 }
1087         }
1088
1089         if ((pStartPage == NULL) || 
1090             (ProhibitSave == 1))
1091         {
1092                 set_preference_backend(HKEY("startpage"), 
1093                                        0, 
1094                                        NewStrBufPlain(HKEY("")),
1095                                        PRF_STRING,
1096                                        1, 
1097                                        NULL);
1098                 if (ProhibitSave == 1)
1099                         StrBufAppendBufPlain(WCC->ImportantMsg,
1100                                              _("This isn't allowed to become the start page."),
1101                                              -1, 0);
1102                 else
1103                         StrBufAppendBufPlain(WCC->ImportantMsg,
1104                                              _("You no longer have a start page selected."),
1105                                              -1, 0);
1106                 display_main_menu();
1107                 return;
1108         }
1109
1110
1111
1112         set_preference_backend(HKEY("startpage"), 
1113                                0, 
1114                                NewStrBufDup(pStartPage),
1115                                PRF_STRING,
1116                                1, 
1117                                NULL);
1118
1119         output_headers(1, 1, 0, 0, 0, 0);
1120         do_template("newstartpage", NULL);
1121         wDumpContent(1);
1122 }
1123
1124
1125 void LoadStartpage(StrBuf *URL, long lvalue)
1126 {
1127         const char *pch;
1128         void *vHandler;
1129         pch = strchr(ChrPtr(URL), '?');
1130         if (pch == NULL) {
1131                 /* purge the sins of the past... */
1132                 pch = strchr(ChrPtr(URL), '&');
1133                 if (pch != NULL) {
1134                         StrBufPeek(URL, pch, -1, '?');
1135                         WC->SavePrefsToServer = 1;
1136                 }
1137         }
1138         else if (GetHash(HandlerHash, ChrPtr(URL), pch - ChrPtr(URL), &vHandler), 
1139                  (vHandler != NULL) &&
1140                  ((((WebcitHandler*)vHandler)->Flags & PROHIBIT_STARTPAGE) != 0))
1141         { /* OK, This handler doesn't want to be set as start page, prune it. */
1142                 FlushStrBuf(URL);
1143                 WC->SavePrefsToServer = 1;
1144         }
1145 }
1146
1147
1148 void 
1149 InitModule_PREFERENCES
1150 (void)
1151 {
1152         WebcitAddUrlHandler(HKEY("set_preferences"), "", 0, set_preferences, 0);
1153         WebcitAddUrlHandler(HKEY("change_start_page"), "", 0, change_start_page, 0);
1154
1155         RegisterPreference("startpage", _("Prefered startpage"), PRF_STRING, LoadStartpage);
1156
1157         RegisterNamespace("OFFERSTARTPAGE", 0, 0, offer_start_page, NULL, CTX_NONE);
1158         RegisterNamespace("PREF:ROOM:VALUE", 1, 2, tmplput_CFG_RoomValue,  NULL, CTX_NONE);
1159         RegisterNamespace("PREF:ROOM:VALUE:INT", 1, 2, tmplput_CFG_RoomValueLong,  NULL, CTX_NONE);
1160         RegisterNamespace("PREF:VALUE", 1, 2, tmplput_CFG_Value, NULL, CTX_NONE);
1161         
1162         RegisterNamespace("PREF:DESCR", 1, 1, tmplput_CFG_Descr, NULL, CTX_NONE);
1163
1164         RegisterConditional(HKEY("COND:PREF"), 4, ConditionalPreference, CTX_NONE);
1165         RegisterConditional(HKEY("COND:PREF:SET"), 4, ConditionalHasPreference, CTX_NONE);
1166         RegisterConditional(HKEY("COND:ROOM:SET"), 4, ConditionalHasRoomPreference, CTX_NONE);
1167         
1168         RegisterIterator("PREF:VALID:EMAIL:ADDR", 0, NULL, 
1169                          GetGVEAHash, NULL, DeleteGVEAHash, CTX_STRBUF, CTX_NONE, IT_NOFLAG);
1170         RegisterIterator("PREF:VALID:EMAIL:NAME", 0, NULL, 
1171                          GetGVSNHash, NULL, DeleteGVSNHash, CTX_STRBUF, CTX_NONE, IT_NOFLAG);
1172
1173 }
1174
1175
1176 void 
1177 ServerStartModule_PREFERENCES
1178 (void)
1179 {
1180         PreferenceHooks = NewHash(1, NULL);
1181 }
1182
1183
1184
1185 void 
1186 ServerShutdownModule_PREFERENCES
1187 (void)
1188 {
1189         DeleteHash(&PreferenceHooks);
1190 }
1191
1192 void
1193 SessionDetachModule__PREFERENCES
1194 (wcsession *sess)
1195 {
1196         if (sess->SavePrefsToServer) {
1197                 save_preferences();
1198                 sess->SavePrefsToServer = 0;
1199         }
1200 }
1201
1202 void
1203 SessionNewModule_PREFERENCES
1204 (wcsession *sess)
1205 {
1206         sess->hash_prefs = NewHash(1,NULL);
1207 }
1208
1209 void 
1210 SessionDestroyModule_PREFERENCES
1211 (wcsession *sess)
1212 {
1213         DeleteHash(&sess->hash_prefs);
1214 }
1215
1216
1217
1218 /*@}*/