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