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