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