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