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