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