Remove APPDIR support in database_cleanup.sh
[citadel] / 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(LOG_DEBUG, "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(LOG_DEBUG, "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(LOG_INFO, "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(LOG_WARNING, "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 CtxType CTX_VEA = CTX_NONE;
1007 typedef struct __ValidEmailAddress {
1008         StrBuf *Address;
1009         int IsDefault;
1010 }ValidEmailAddress;
1011
1012 void DeleteValidEmailAddress(void *v)
1013 {
1014         ValidEmailAddress *VEA = (ValidEmailAddress*)v;
1015         FreeStrBuf(&VEA->Address);
1016         free(VEA);
1017 }
1018 void tmplput_VEA(StrBuf *Target, WCTemplputParams *TP)
1019 {
1020         ValidEmailAddress* VEA = (ValidEmailAddress*) CTX((CTX_VEA));
1021         StrBufAppendTemplate(Target, TP, VEA->Address, 0);
1022 }
1023 int ConditionalPreferenceIsDefaulVEA(StrBuf *Target, WCTemplputParams *TP)
1024 {
1025         ValidEmailAddress* VEA = (ValidEmailAddress*) CTX((CTX_VEA));
1026         return VEA->IsDefault;
1027 }
1028 HashList *GetGVEAHash(StrBuf *Target, WCTemplputParams *TP)
1029 {
1030         StrBuf *Rcp;
1031         HashList *List = NULL;
1032         int Done = 0;
1033         int i, n = 1;
1034         char N[64];
1035         StrBuf *DefaultFrom = NULL;
1036         const StrBuf *EnvelopeTo;
1037         ValidEmailAddress *VEA;
1038
1039         get_preference("defaultfrom", &DefaultFrom);
1040         EnvelopeTo = sbstr("nvto");
1041         Rcp = NewStrBuf();
1042         serv_puts("GVEA");
1043         StrBuf_ServGetln(Rcp);
1044         if (GetServerStatus(Rcp, NULL) == 1) {
1045                 FlushStrBuf(Rcp);
1046                 List = NewHash(1, NULL);
1047                 while (!Done && (StrBuf_ServGetln(Rcp)>=0)) {
1048                         if ( (StrLength(Rcp)==3) && 
1049                              !strcmp(ChrPtr(Rcp), "000")) 
1050                         {
1051                                 Done = 1;
1052                         }
1053                         else {
1054                                 VEA = (ValidEmailAddress*) malloc(sizeof(ValidEmailAddress));
1055                                 i = snprintf(N, sizeof(N), "%d", n);
1056                                 StrBufTrim(Rcp);
1057                                 VEA->Address = Rcp;
1058                                 if (StrLength(EnvelopeTo) > 0)
1059                                         VEA->IsDefault = strstr(ChrPtr(EnvelopeTo), ChrPtr(Rcp)) != NULL;
1060                                 else if (StrLength(DefaultFrom) > 0)
1061                                         VEA->IsDefault = !strcmp(ChrPtr(Rcp), ChrPtr(DefaultFrom));
1062                                 else
1063                                         VEA->IsDefault = 0;
1064
1065                                 Put(List, N, i, VEA, DeleteValidEmailAddress);
1066                                 Rcp = NewStrBuf();
1067                         }
1068                         n++;
1069                 }
1070         }
1071         FreeStrBuf(&Rcp);
1072         return List;
1073 }
1074 void DeleteGVEAHash(HashList **KillMe)
1075 {
1076         DeleteHash(KillMe);
1077 }
1078
1079 HashList *GetGVSNHash(StrBuf *Target, WCTemplputParams *TP)
1080 {
1081         StrBuf *Rcp;
1082         HashList *List = NULL;
1083         int Done = 0;
1084         int i, n = 1;
1085         char N[64];
1086
1087         Rcp = NewStrBuf();
1088         serv_puts("GVSN");
1089         StrBuf_ServGetln(Rcp);
1090         if (GetServerStatus(Rcp, NULL) == 1) {
1091                 FlushStrBuf(Rcp);
1092                 List = NewHash(1, NULL);
1093                 while (!Done && (StrBuf_ServGetln(Rcp)>=0)) {
1094                         if ( (StrLength(Rcp)==3) && 
1095                              !strcmp(ChrPtr(Rcp), "000")) 
1096                         {
1097                                 Done = 1;
1098                         }
1099                         else {
1100                                 i = snprintf(N, sizeof(N), "%d", n);
1101                                 StrBufTrim(Rcp);
1102                                 Put(List, N, i, Rcp, HFreeStrBuf);
1103                                 Rcp = NewStrBuf();
1104                         }
1105                         n++;
1106                 }
1107         }
1108         FreeStrBuf(&Rcp);
1109         return List;
1110 }
1111 void DeleteGVSNHash(HashList **KillMe)
1112 {
1113         DeleteHash(KillMe);
1114 }
1115
1116
1117
1118
1119 /*
1120  * Offer to make any page the user's "start page" (only if logged in)
1121  */
1122 void offer_start_page(StrBuf *Target, WCTemplputParams *TP)
1123 {
1124         if (WC->logged_in) {
1125                 wc_printf("<a href=\"change_start_page?startpage=");
1126                 urlescputs(ChrPtr(WC->Hdr->this_page));
1127                 wc_printf("\">");
1128                 wc_printf(_("Make this my start page"));
1129                 wc_printf("</a>");
1130         };
1131 }
1132
1133
1134 /*
1135  * Change the user's start page
1136  */
1137 void change_start_page(void) 
1138 {
1139         const char *pch;
1140         void *vHandler;
1141         int ProhibitSave = 0;
1142         const StrBuf *pStartPage = sbstr("startpage");
1143
1144         if (pStartPage != NULL) {
1145                 pch = strchr(ChrPtr(pStartPage), '?');
1146
1147                 if ((pch != NULL) && (
1148                             GetHash(HandlerHash, ChrPtr(pStartPage), pch - ChrPtr(pStartPage), &vHandler), 
1149                             (vHandler != NULL) &&
1150                             ((((WebcitHandler*)vHandler)->Flags & PROHIBIT_STARTPAGE) != 0)))
1151                 { /* OK, This handler doesn't want to be set as start page, prune it. */
1152                         ProhibitSave = 1;
1153                 }
1154         }
1155
1156         if ((pStartPage == NULL) || 
1157             (ProhibitSave == 1))
1158         {
1159                 set_preference_backend(HKEY("startpage"), 
1160                                        0, 
1161                                        NewStrBufPlain(HKEY("")),
1162                                        PRF_STRING,
1163                                        1, 
1164                                        NULL);
1165                 if (ProhibitSave == 1)
1166                         AppendImportantMessage(_("This isn't allowed to become the start page."), -1);
1167                 else
1168                         AppendImportantMessage(_("You no longer have a start page selected."), -1);
1169                 display_main_menu();
1170                 return;
1171         }
1172
1173
1174
1175         set_preference_backend(HKEY("startpage"), 
1176                                0, 
1177                                NewStrBufDup(pStartPage),
1178                                PRF_STRING,
1179                                1, 
1180                                NULL);
1181
1182         output_headers(1, 1, 0, 0, 0, 0);
1183         do_template("newstartpage");
1184         wDumpContent(1);
1185 }
1186
1187
1188 void LoadStartpage(StrBuf *URL, long lvalue)
1189 {
1190         const char *pch;
1191         void *vHandler;
1192         pch = strchr(ChrPtr(URL), '?');
1193         if (pch == NULL) {
1194                 /* purge the sins of the past... */
1195                 pch = strchr(ChrPtr(URL), '&');
1196                 if (pch != NULL) {
1197                         StrBufPeek(URL, pch, -1, '?');
1198                         WC->SavePrefsToServer = 1;
1199                 }
1200         }
1201         else if (GetHash(HandlerHash, ChrPtr(URL), pch - ChrPtr(URL), &vHandler), 
1202                  (vHandler != NULL) &&
1203                  ((((WebcitHandler*)vHandler)->Flags & PROHIBIT_STARTPAGE) != 0))
1204         { /* OK, This handler doesn't want to be set as start page, prune it. */
1205                 FlushStrBuf(URL);
1206                 WC->SavePrefsToServer = 1;
1207         }
1208 }
1209
1210
1211 void 
1212 InitModule_PREFERENCES
1213 (void)
1214 {
1215         RegisterCTX(CTX_VEA);
1216
1217         WebcitAddUrlHandler(HKEY("set_preferences"), "", 0, set_preferences, 0);
1218         WebcitAddUrlHandler(HKEY("change_start_page"), "", 0, change_start_page, 0);
1219
1220         RegisterPreference("startpage", _("Prefered startpage"), PRF_STRING, LoadStartpage);
1221
1222         RegisterNamespace("OFFERSTARTPAGE", 0, 0, offer_start_page, NULL, CTX_NONE);
1223         RegisterNamespace("PREF:ROOM:VALUE", 1, 2, tmplput_CFG_RoomValue,  NULL, CTX_NONE);
1224         RegisterNamespace("PREF:ROOM:VALUE:INT", 1, 2, tmplput_CFG_RoomValueLong,  NULL, CTX_NONE);
1225         RegisterNamespace("PREF:VALUE", 1, 2, tmplput_CFG_Value, NULL, CTX_NONE);
1226         
1227         RegisterNamespace("PREF:DESCR", 1, 1, tmplput_CFG_Descr, NULL, CTX_NONE);
1228
1229         RegisterConditional("COND:PREF", 4, ConditionalPreference, CTX_NONE);
1230         RegisterConditional("COND:PREF:SET", 4, ConditionalHasPreference, CTX_NONE);
1231         RegisterConditional("COND:ROOM:SET", 4, ConditionalHasRoomPreference, CTX_NONE);
1232         
1233         RegisterIterator("PREF:VALID:EMAIL:ADDR", 0, NULL, 
1234                          GetGVEAHash, NULL, DeleteGVEAHash, CTX_VEA, CTX_NONE, IT_NOFLAG);
1235         RegisterNamespace("PREF:VALID:EMAIL:ADDR:STR", 1, 1, tmplput_VEA, NULL, CTX_VEA);
1236         RegisterConditional("COND:PREF:VALID:EMAIL:ADDR:STR", 4, ConditionalPreferenceIsDefaulVEA, CTX_VEA);
1237
1238         RegisterIterator("PREF:VALID:EMAIL:NAME", 0, NULL, 
1239                          GetGVSNHash, NULL, DeleteGVSNHash, CTX_STRBUF, CTX_NONE, IT_NOFLAG);
1240
1241 }
1242
1243
1244 void 
1245 ServerStartModule_PREFERENCES
1246 (void)
1247 {
1248         PreferenceHooks = NewHash(1, NULL);
1249 }
1250
1251
1252
1253 void 
1254 ServerShutdownModule_PREFERENCES
1255 (void)
1256 {
1257         DeleteHash(&PreferenceHooks);
1258 }
1259
1260 void
1261 SessionDetachModule__PREFERENCES
1262 (wcsession *sess)
1263 {
1264         if (sess->SavePrefsToServer) {
1265                 save_preferences();
1266                 sess->SavePrefsToServer = 0;
1267         }
1268 }
1269
1270 void
1271 SessionNewModule_PREFERENCES
1272 (wcsession *sess)
1273 {
1274         sess->hash_prefs = NewHash(1,NULL);
1275 }
1276
1277 void 
1278 SessionDestroyModule_PREFERENCES
1279 (wcsession *sess)
1280 {
1281         DeleteHash(&sess->hash_prefs);
1282 }