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