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