* move templates to static/t/ ; made make install target respect this
[citadel.git] / webcit / subst.c
1 /*
2  * $Id$
3  */
4 /**
5  * \defgroup Subst Variable substitution type stuff
6  * \ingroup CitadelConfig
7  */
8
9 /*@{*/
10
11 #include "sysdep.h"
12 #include <sys/types.h>
13 #include <sys/stat.h>
14 #include <unistd.h>
15 #include <dirent.h>
16 #include <errno.h>
17
18 #include "webcit.h"
19 #include "webserver.h"
20
21 HashList *WirelessTemplateCache;
22 HashList *WirelessLocalTemplateCache;
23 HashList *TemplateCache;
24 HashList *LocalTemplateCache;
25
26 typedef struct _TemplateToken {
27         const char *pTokenStart;
28         size_t TokenStart;
29         size_t TokenEnd;
30         const char *pTokenEnd;
31
32         const char *pName;
33         size_t NameEnd;
34
35         int HaveParameters;
36         int nParameters;
37         size_t ParamStart [10];
38         size_t ParamEnd [10];
39 } WCTemplateToken;
40
41 typedef struct _WCTemplate {
42         StrBuf *Data;
43         int nTokensUsed;
44         int TokenSpace;
45         WCTemplateToken **Tokens;
46 } WCTemplate;
47
48 HashList *GlobalNS;
49
50 typedef struct _HashHandler {
51         int foo;
52 }HashHandler;
53
54 typedef int (*HandlerFunc)(int nArgs, va_list vaarg);
55 void RegisterNS(const char *NSName, int nMinArgs, int nMaxArgs, HandlerFunc Handler)
56 {
57
58 }
59
60
61 /**
62  * \brief debugging function to print array to log
63  */
64 void VarPrintTransition(void *vVar1, void *vVar2, int odd){}
65 /**
66  * \brief debugging function to print array to log
67  */
68 void VarPrintEntry(const char *Key, void *vSubst, int odd)
69 {
70         wcsubst *ptr;
71         lprintf(1,"Subst[%s] : ", Key);
72         ptr = (wcsubst*) vSubst;
73
74         switch(ptr->wcs_type) {
75         case WCS_STRING:
76                 lprintf(1, "  -> %s\n", ptr->wcs_value);
77                 break;
78         case WCS_SERVCMD:
79                 lprintf(1, "  -> Server [%s]\n", ptr->wcs_value);
80                 break;
81         case WCS_FUNCTION:
82                 lprintf(1, "  -> function at [%0xd]\n", ptr->wcs_function);
83                 break;
84         default:
85                 lprintf(1,"  WARNING: invalid type: [%ld]!\n", ptr->wcs_type);
86         }
87 }
88
89
90
91 /**
92  * \brief Clear out the list of substitution variables local to this session
93  */
94 void clear_substs(struct wcsession *wc) {
95
96         if (wc->vars != NULL) {
97         
98                 DeleteHash(&wc->vars);
99         }
100 }
101
102 /**
103  * \brief Clear out the list of substitution variables local to this session
104  */
105 void clear_local_substs(void) {
106         clear_substs (WC);
107 }
108
109 /**
110  * \brief destructor; kill one entry.
111  */
112 void deletevar(void *data)
113 {
114         wcsubst *ptr = (wcsubst*)data;
115 //              if ((wc->vars->wcs_type == WCS_STRING)
116 //                 || (wc->vars->wcs_type == WCS_SERVCMD)) {
117         if (ptr->wcs_type != WCS_FUNCTION)
118                 free(ptr->wcs_value);
119         free(ptr);      
120 }
121
122 /**
123  * \brief Add a substitution variable (local to this session) (strlen version...)
124  * \param keyname the replacementstring to substitute
125  * \param keytype the kind of the key
126  * \param format the format string ala printf
127  * \param ... the arguments to substitute in the formatstring
128  */
129 void SVPRINTF(char *keyname, int keytype, const char *format,...)
130 {
131         va_list arg_ptr;
132         char wbuf[SIZ];
133         void *vPtr;
134         wcsubst *ptr = NULL;
135         size_t keylen;
136         struct wcsession *WCC = WC;
137         
138         keylen = strlen(keyname);
139         /**
140          * First look if we're doing a replacement of
141          * an existing key
142          */
143         /*PrintHash(WCC->vars, VarPrintTransition, VarPrintEntry);*/
144         if (GetHash(WCC->vars, keyname, keylen, &vPtr)) {
145                 ptr = (wcsubst*)vPtr;
146                 if (ptr->wcs_value != NULL)
147                         free(ptr->wcs_value);
148         }
149         else    /** Otherwise allocate a new one */
150         {
151                 ptr = (wcsubst *) malloc(sizeof(wcsubst));
152                 safestrncpy(ptr->wcs_key, keyname, sizeof ptr->wcs_key);
153                 Put(WCC->vars, keyname, keylen, ptr,  deletevar);
154         }
155
156         /** Format the string and save it */
157
158         va_start(arg_ptr, format);
159         vsnprintf(wbuf, sizeof wbuf, format, arg_ptr);
160         va_end(arg_ptr);
161
162         ptr->wcs_function = NULL;
163         ptr->wcs_type = keytype;
164         ptr->wcs_value = strdup(wbuf);
165 }
166
167 /**
168  * \brief Add a substitution variable (local to this session)
169  * \param keyname the replacementstring to substitute
170  * \param keytype the kind of the key
171  * \param format the format string ala printf
172  * \param ... the arguments to substitute in the formatstring
173  */
174 void svprintf(char *keyname, size_t keylen, int keytype, const char *format,...)
175 {
176         va_list arg_ptr;
177         char wbuf[SIZ];
178         void *vPtr;
179         wcsubst *ptr = NULL;
180         struct wcsession *WCC = WC;
181         size_t len;
182         
183         /**
184          * First look if we're doing a replacement of
185          * an existing key
186          */
187         /*PrintHash(WCC->vars, VarPrintTransition, VarPrintEntry);*/
188         if (GetHash(WCC->vars, keyname, keylen, &vPtr)) {
189                 ptr = (wcsubst*)vPtr;
190                 if (ptr->wcs_value != NULL)
191                         free(ptr->wcs_value);
192         }
193         else    /** Otherwise allocate a new one */
194         {
195                 ptr = (wcsubst *) malloc(sizeof(wcsubst));
196                 safestrncpy(ptr->wcs_key, keyname, sizeof ptr->wcs_key);
197                 Put(WCC->vars, keyname, keylen, ptr,  deletevar);
198         }
199
200         /** Format the string and save it */
201
202         va_start(arg_ptr, format);
203         len = vsnprintf(wbuf, sizeof wbuf, format, arg_ptr);
204         va_end(arg_ptr);
205
206         ptr->wcs_value = (char*) malloc(len + 1);
207         memcpy(ptr->wcs_value, wbuf, len + 1);
208         ptr->wcs_function = NULL;
209         ptr->wcs_type = keytype;
210 }
211
212 /**
213  * \brief Add a substitution variable (local to this session)
214  * \param keyname the replacementstring to substitute
215  * \param keytype the kind of the key
216  * \param format the format string ala printf
217  * \param ... the arguments to substitute in the formatstring
218  */
219 void SVPut(char *keyname, size_t keylen, int keytype, char *Data)
220 {
221         void *vPtr;
222         wcsubst *ptr = NULL;
223         struct wcsession *WCC = WC;
224
225         
226         /**
227          * First look if we're doing a replacement of
228          * an existing key
229          */
230         /*PrintHash(WCC->vars, VarPrintTransition, VarPrintEntry);*/
231         if (GetHash(WCC->vars, keyname, keylen, &vPtr)) {
232                 ptr = (wcsubst*)vPtr;
233                 if (ptr->wcs_value != NULL)
234                         free(ptr->wcs_value);
235         }
236         else    /** Otherwise allocate a new one */
237         {
238                 ptr = (wcsubst *) malloc(sizeof(wcsubst));
239                 safestrncpy(ptr->wcs_key, keyname, sizeof ptr->wcs_key);
240                 Put(WCC->vars, keyname, keylen, ptr,  deletevar);
241         }
242
243         ptr->wcs_function = NULL;
244         ptr->wcs_type = keytype;
245         ptr->wcs_value = strdup(Data);
246 }
247
248 /**
249  * \brief Add a substitution variable (local to this session) that does a callback
250  * \param keyname the keystring to substitute
251  * \param fcn_ptr the function callback to give the substitution string
252  */
253 void SVCallback(char *keyname, size_t keylen, var_callback_fptr fcn_ptr)
254 {
255         wcsubst *ptr;
256         void *vPtr;
257         struct wcsession *WCC = WC;
258
259         /**
260          * First look if we're doing a replacement of
261          * an existing key
262          */
263         /*PrintHash(WCC->vars, VarPrintTransition, VarPrintEntry);*/
264         if (GetHash(WCC->vars, keyname, keylen, &vPtr)) {
265                 ptr = (wcsubst*)vPtr;
266                 if (ptr->wcs_value != NULL)
267                         free(ptr->wcs_value);
268         }
269         else    /** Otherwise allocate a new one */
270         {
271                 ptr = (wcsubst *) malloc(sizeof(wcsubst));
272                 safestrncpy(ptr->wcs_key, keyname, sizeof ptr->wcs_key);
273                 Put(WCC->vars, keyname, keylen, ptr,  deletevar);
274         }
275
276         ptr->wcs_value = NULL;
277         ptr->wcs_type = WCS_FUNCTION;
278         ptr->wcs_function = fcn_ptr;
279 }
280 inline void SVCALLBACK(char *keyname, var_callback_fptr fcn_ptr)
281 {
282         SVCallback(keyname, strlen(keyname), fcn_ptr);
283 }
284
285
286
287 /**
288  * \brief back end for print_value_of() ... does a server command
289  * \param servcmd server command to execute on the citadel server
290  */
291 void pvo_do_cmd(char *servcmd) {
292         char buf[SIZ];
293
294         serv_puts(servcmd);
295         serv_getln(buf, sizeof buf);
296
297         switch(buf[0]) {
298                 case '2':
299                 case '3':
300                 case '5':
301                         wprintf("%s\n", &buf[4]);
302                         break;
303                 case '1':
304                         fmout("CENTER");
305                         break;
306                 case '4':
307                         wprintf("%s\n", &buf[4]);
308                         serv_puts("000");
309                         break;
310         }
311 }
312
313 /**
314  * \brief Print the value of a variable
315  * \param keyname get a key to print
316  */
317 void print_value_of(const char *keyname, size_t keylen) {
318         struct wcsession *WCC = WC;
319         wcsubst *ptr;
320         void *fcn();
321         void *vVar;
322
323         /*if (WCC->vars != NULL) PrintHash(WCC->vars, VarPrintTransition, VarPrintEntry);*/
324         if (keyname[0] == '=') {
325                 DoTemplate(keyname+1, keylen - 1);
326         }
327         /** Page-local variables */
328         if ((WCC->vars!= NULL) && GetHash(WCC->vars, keyname, keylen, &vVar)) {
329                 ptr = (wcsubst*) vVar;
330                 switch(ptr->wcs_type) {
331                 case WCS_STRING:
332                         wprintf("%s", (const char*)ptr->wcs_value);
333                         break;
334                 case WCS_SERVCMD:
335                         pvo_do_cmd(ptr->wcs_value);
336                         break;
337                 case WCS_FUNCTION:
338                         (*ptr->wcs_function) ();
339                         break;
340                 default:
341                         lprintf(1,"WARNING: invalid value in SV-Hash at %s!", keyname);
342                 }
343         }
344         else {
345                 char ch;
346                 ch = keyname[keylen];
347                 ((char*) keyname)[keylen] = '\0'; ////TODO
348                 if (!strcasecmp(keyname, "SERV_PID")) {
349                         wprintf("%d", WCC->ctdl_pid);
350                 }
351
352                 else if (!strcasecmp(keyname, "SERV_NODENAME")) {
353                         escputs(serv_info.serv_nodename);
354                 }
355
356                 else if (!strcasecmp(keyname, "SERV_HUMANNODE")) {
357                         escputs(serv_info.serv_humannode);
358                 }
359
360                 else if (!strcasecmp(keyname, "SERV_FQDN")) {
361                         escputs(serv_info.serv_fqdn);
362                 }
363
364                 else if (!strcasecmp(keyname, "SERV_SOFTWARE")) {
365                         escputs(serv_info.serv_software);
366                 }
367
368                 else if (!strcasecmp(keyname, "SERV_REV_LEVEL")) {
369                         wprintf("%d.%02d",
370                                 serv_info.serv_rev_level / 100,
371                                 serv_info.serv_rev_level % 100
372                                 );
373                 }
374
375                 else if (!strcasecmp(keyname, "SERV_BBS_CITY")) {
376                         escputs(serv_info.serv_bbs_city);
377                 }
378
379                 else if (!strcasecmp(keyname, "CURRENT_USER")) {
380                         escputs(WCC->wc_fullname);
381                 }
382
383                 else if (!strcasecmp(keyname, "CURRENT_ROOM")) {
384                         escputs(WCC->wc_roomname);
385                 }
386                 ((char*) keyname)[keylen] = ch;//// TODO
387                 
388         }
389
390 }
391
392 extern char *static_dirs[PATH_MAX];  /**< Disk representation */
393
394
395 void PutNewToken(WCTemplate *Template, WCTemplateToken *NewToken)
396 {
397         if (Template->nTokensUsed + 1 >= Template->TokenSpace) {
398                 if (Template->TokenSpace <= 0) {
399                         Template->Tokens = (WCTemplateToken**)malloc(
400                                 sizeof(WCTemplateToken*) * 10);
401                         Template->TokenSpace = 10;
402                 }
403                 else {
404                         WCTemplateToken **NewTokens;
405                         NewTokens= (WCTemplateToken**)malloc(
406                                 sizeof(WCTemplateToken*) * 
407                                 Template->TokenSpace * 2);
408                         memcpy(NewTokens, Template->Tokens, 
409                                sizeof(WCTemplateToken) * Template->nTokensUsed);
410                         free(Template->Tokens);
411                         Template->TokenSpace *= 2;
412                         Template->Tokens = NewTokens;
413                 }
414                 
415
416         }
417         Template->Tokens[(Template->nTokensUsed)++] = NewToken;
418 }
419
420 WCTemplateToken *NewTemlpateSubstitute(const char *pStart, const char *pTmplStart, const char *pTmplEnd)
421 {
422         WCTemplateToken *NewToken = (WCTemplateToken*)malloc(sizeof(WCTemplateToken));
423
424         NewToken->pTokenStart = pTmplStart;
425         NewToken->TokenStart = pTmplStart - pStart;
426         NewToken->TokenEnd =  (pTmplEnd - pStart) - NewToken->TokenStart;
427         NewToken->pTokenEnd = pTmplEnd;
428         
429         NewToken->pName = pTmplStart + 2;
430         NewToken->NameEnd = NewToken->TokenEnd - 2;
431         NewToken->HaveParameters = 0;;
432         NewToken->nParameters = 0;
433         NewToken->ParamStart[0] = 0;
434         NewToken->ParamEnd[0] = 0;
435         return NewToken;
436 }
437
438 void FreeWCTemplate(void *vFreeMe)
439 {
440         int i;
441         WCTemplate *FreeMe = (WCTemplate*)vFreeMe;
442
443         if (FreeMe->TokenSpace > 0) {
444                 for (i = 0; i < FreeMe->nTokensUsed; i ++) {
445                         free(FreeMe->Tokens[i]);
446                 }
447                 free(FreeMe->Tokens);
448         }
449         free(FreeMe);
450 }
451
452 void EvaluateToken(StrBuf *Target, WCTemplateToken *Token)
453 {
454
455
456         
457         print_value_of(Token->pName, Token->NameEnd);
458 }
459
460
461 void ProcessTemplate(WCTemplate *Tmpl, StrBuf *Target)
462 {
463         int done = 0;
464         int i;
465         const char *pData, *pS;
466         long len;
467
468         pS = pData = ChrPtr(Tmpl->Data);
469         len = StrLength(Tmpl->Data);
470         i = 0;
471         while (!done) {
472                 if (i >= Tmpl->nTokensUsed) {
473                         StrBufAppendBufPlain(Target, pData, len, 0);
474                         done = 1;
475                 }
476                 else {
477                         StrBufAppendBufPlain(
478                                 Target, pData, 
479                                 Tmpl->Tokens[i]->pTokenStart - pData, 0);
480                         EvaluateToken(Target, Tmpl->Tokens[i]);
481                         pData = Tmpl->Tokens[i++]->pTokenEnd + 1;
482                 }
483         }
484
485 }
486
487
488
489 /**
490  * \brief Display a variable-substituted template
491  * \param templatename template file to load
492  */
493 void *load_template(StrBuf *filename, StrBuf *Key, HashList *PutThere)
494 {
495         int fd;
496         struct stat statbuf;
497         const char *pS, *pE, *pch, *Err;
498         int pos;
499         WCTemplate *NewTemplate;
500
501         fd = open(ChrPtr(filename), O_RDONLY);
502         if (fd <= 0) {
503                 wprintf(_("ERROR: could not open template "));
504                 wprintf("'%s' - %s<br />\n",
505                         ChrPtr(filename), strerror(errno));
506                 return NULL;
507         }
508
509         if (fstat(fd, &statbuf) == -1) {
510                 wprintf(_("ERROR: could not stat template "));
511                 wprintf("'%s' - %s<br />\n",
512                         ChrPtr(filename), strerror(errno));
513                 return NULL;
514         }
515
516         NewTemplate = (WCTemplate *) malloc(sizeof(WCTemplate));
517         NewTemplate->Data = NewStrBufPlain(NULL, statbuf.st_size);
518         NewTemplate->nTokensUsed = 0;
519         NewTemplate->TokenSpace = 0;
520         NewTemplate->Tokens = NULL;
521         if (StrBufReadBLOB(NewTemplate->Data, &fd, 1, statbuf.st_size, &Err) < 0) {
522                 close(fd);
523                 FreeWCTemplate(NewTemplate);
524                 wprintf(_("ERROR: reading template "));
525                 wprintf("'%s' - %s<br />\n",
526                         ChrPtr(filename), strerror(errno));
527                 return NULL;
528         }
529         close(fd);
530
531         pS = pch = ChrPtr(NewTemplate->Data);
532         pE = pS + StrLength(NewTemplate->Data);
533         while (pch < pE) {
534                 const char *pts, *pte;
535                 int InQuotes = 0;
536                 int InDoubleQuotes = 0;
537                 pos = (-1);
538                 for (; pch < pE; pch ++) {
539                         if ((*pch=='<')&&(*(pch + 1)=='?'))
540                                 break;
541                 }
542                 if (pch >= pE)
543                         continue;
544                 pts = pch;
545                 for (; pch < pE - 1; pch ++) {
546                         if (*pch == '"')
547                                 InDoubleQuotes = ! InDoubleQuotes;
548                         else if (*pch == '\'')
549                                 InQuotes = ! InQuotes;
550                         else if ((!InQuotes  && !InDoubleQuotes) &&
551                                  ((*pch!='\\')&&(*(pch + 1)=='>'))) {
552                                 pch ++;
553                                 break;
554                         }
555                 }
556                 if (pch + 1 >= pE)
557                         continue;
558                 pte = pch;
559                 PutNewToken(NewTemplate, NewTemlpateSubstitute(pS, pts, pte));
560                 pch ++;
561         }
562         Put(PutThere, ChrPtr(Key), StrLength(Key), NewTemplate, FreeWCTemplate);
563         return NewTemplate;
564 }
565
566 /**
567  * \brief Display a variable-substituted template
568  * \param templatename template file to load
569  */
570 void DoTemplate(const char *templatename, long len) 
571 {
572         HashList *Static;
573         HashList *StaticLocal;
574         void *vTmpl;
575
576         if (WC->is_mobile) {
577                 Static = WirelessTemplateCache;
578                 StaticLocal = WirelessLocalTemplateCache;
579         }
580         else {
581                 Static = TemplateCache;
582                 StaticLocal = LocalTemplateCache;
583         }
584
585         if (!GetHash(StaticLocal, templatename, len, &vTmpl) &&
586             !GetHash(Static, templatename, len, &vTmpl)) {
587                 printf ("didn't find %s\n", templatename);
588                 return;
589         }
590         if (vTmpl == NULL) 
591                 return;
592         ProcessTemplate(vTmpl, WC->WBuf);
593         
594 }
595
596 int LoadTemplateDir(const char *DirName, HashList *wireless, HashList *big)
597 {
598         StrBuf *FileName;
599         StrBuf *Tag;
600         StrBuf *Dir;
601         DIR *filedir = NULL;
602         struct dirent *filedir_entry;
603         int d_namelen;
604         int d_without_ext;
605         int IsMobile;
606         
607         Dir = NewStrBuf();
608         StrBufPrintf(Dir, "%s/t", DirName);
609         filedir = opendir (ChrPtr(Dir));
610         if (filedir == NULL) {
611                 return 0;
612         }
613
614         FileName = NewStrBuf();
615         Tag = NewStrBuf();
616         while ((filedir_entry = readdir(filedir)))
617         {
618                 char *MinorPtr;
619                 char *PStart;
620 #ifdef _DIRENT_HAVE_D_NAMELEN
621                 d_namelen = filedir_entry->d_namelen;
622 #else
623                 d_namelen = strlen(filedir_entry->d_name);
624 #endif
625                 d_without_ext = d_namelen;
626                 while ((d_without_ext > 0) && (filedir_entry->d_name[d_without_ext] != '.'))
627                         d_without_ext --;
628                 if ((d_without_ext == 0) || (d_namelen < 3))
629                         continue;
630
631                 IsMobile = (strstr(filedir_entry->d_name, ".m.html")!= NULL);
632                 PStart = filedir_entry->d_name;
633                 StrBufPrintf(FileName, "%s/t/%s", ChrPtr(Dir),  filedir_entry->d_name);
634                 MinorPtr = strchr(filedir_entry->d_name, '.');
635                 if (MinorPtr != NULL)
636                         *MinorPtr = '\0';
637                 StrBufPlain(Tag, filedir_entry->d_name, MinorPtr - filedir_entry->d_name);
638
639
640                 printf("%s %d %s\n",ChrPtr(FileName), IsMobile, ChrPtr(Tag));
641                 load_template(FileName, Tag, (IsMobile)?wireless:big);          
642         }
643         closedir(filedir);
644         FreeStrBuf(&FileName);
645         FreeStrBuf(&Tag);
646         FreeStrBuf(&Dir);
647         return 1;
648 }
649
650 void InitTemplateCache(void)
651 {
652         
653         LoadTemplateDir(static_dirs[0],
654                         WirelessTemplateCache,
655                         TemplateCache);
656         LoadTemplateDir(static_dirs[1],
657                         WirelessLocalTemplateCache,
658                         LocalTemplateCache);
659
660
661 }
662
663 void 
664 InitModule_SUBST
665 (void)
666 {
667
668 }
669
670 /*@}*/