Renderers cleanup part 1
[citadel.git] / webcit / static.c
1 /*
2  * This is the main transaction loop of the web service.  It maintains a
3  * persistent session to the Citadel server, handling HTTP WebCit requests as
4  * they arrive and presenting a user interface.
5  */
6 #include <sys/types.h>
7 #include <sys/stat.h>
8 #include <dirent.h>
9 #include <errno.h>
10
11 #include <unistd.h>
12 #include <stdio.h>
13 #include <stdarg.h>
14 #include <stddef.h>
15
16
17 #include "webcit.h"
18 #include "webserver.h"
19
20 unsigned char OnePixelGif[37] = {
21                 0x47,
22                 0x49,
23                 0x46,
24                 0x38,
25                 0x37,
26                 0x61,
27                 0x01,
28                 0x00,
29                 0x01,
30                 0x00,
31                 0x80,
32                 0x00,
33                 0x00,
34                 0xff,
35                 0xff,
36                 0xff,
37                 0xff,
38                 0xff,
39                 0xff,
40                 0x2c,
41                 0x00,
42                 0x00,
43                 0x00,
44                 0x00,
45                 0x01,
46                 0x00,
47                 0x01,
48                 0x00,
49                 0x00,
50                 0x02,
51                 0x02,
52                 0x44,
53                 0x01,
54                 0x00,
55                 0x3b 
56 };
57
58
59 HashList *StaticFilemappings[5] = {NULL, NULL, NULL, NULL, NULL};
60 /*
61   {
62   syslog(LOG_DEBUG, "Suspicious request. Ignoring.");
63   hprintf("HTTP/1.1 404 Security check failed\r\n");
64   hprintf("Content-Type: text/plain\r\n\r\n");
65   wc_printf("You have sent a malformed or invalid request.\r\n");
66   end_burst();
67   }
68 */
69
70
71 void output_error_pic(const char *ErrMsg1, const char *ErrMsg2)
72 {
73         hprintf("HTTP/1.1 200 %s\r\n", ErrMsg1);
74         hprintf("Content-Type: image/gif\r\n");
75         hprintf("x-webcit-errormessage: %s\r\n", ErrMsg2);
76         begin_burst();
77         StrBufPlain(WC->WBuf, (const char *)OnePixelGif, sizeof(OnePixelGif));
78         end_burst();
79 }
80
81 /*
82  * dump out static pages from disk
83  */
84 void output_static(const char *what)
85 {
86         int fd;
87         struct stat statbuf;
88         off_t bytes;
89         const char *content_type;
90         int len;
91         const char *Err;
92
93         len = strlen (what);
94         content_type = GuessMimeByFilename(what, len);
95         fd = open(what, O_RDONLY);
96         if (fd <= 0) {
97                 syslog(LOG_INFO, "output_static('%s') [%s]  -- NOT FOUND --\n", what, ChrPtr(WC->Hdr->this_page));
98                 if (strstr(content_type, "image/") != NULL)
99                 {
100                         output_error_pic("the file you requsted is gone.", strerror(errno));
101                 }
102                 else
103                 {
104                         hprintf("HTTP/1.1 404 %s\r\n", strerror(errno));
105                         hprintf("Content-Type: text/plain\r\n");
106                         begin_burst();
107                         wc_printf("Cannot open %s: %s\r\n", what, strerror(errno));
108                         end_burst();
109                 }
110         } else {
111                 if (fstat(fd, &statbuf) == -1) {
112                         syslog(LOG_INFO, "output_static('%s')  -- FSTAT FAILED --\n", what);
113                         if (strstr(content_type, "image/") != NULL)
114                         {
115                                 output_error_pic("Stat failed!", strerror(errno));
116                         }
117                         else
118                         {
119                                 hprintf("HTTP/1.1 404 %s\r\n", strerror(errno));
120                                 hprintf("Content-Type: text/plain\r\n");
121                                 begin_burst();
122                                 wc_printf("Cannot fstat %s: %s\n", what, strerror(errno));
123                                 end_burst();
124                         }
125                         if (fd > 0) close(fd);
126                         return;
127                 }
128
129                 bytes = statbuf.st_size;
130
131                 if (StrBufReadBLOB(WC->WBuf, &fd, 1, bytes, &Err) < 0)
132                 {
133                         if (fd > 0) close(fd);
134                         syslog(LOG_INFO, "output_static('%s')  -- FREAD FAILED (%s) --\n", what, strerror(errno));
135                                 hprintf("HTTP/1.1 500 internal server error \r\n");
136                                 hprintf("Content-Type: text/plain\r\n");
137                                 end_burst();
138                                 return;
139                 }
140
141
142                 close(fd);
143                 http_transmit_thing(content_type, 2);
144         }
145         if (yesbstr("force_close_session")) {
146                 end_webcit_session();
147         }
148 }
149
150
151 int LoadStaticDir(const char *DirName, HashList *DirList, const char *RelDir)
152 {
153         char dirname[PATH_MAX];
154         char reldir[PATH_MAX];
155         StrBuf *FileName = NULL;
156         StrBuf *Dir = NULL;
157         StrBuf *WebDir = NULL;
158         StrBuf *OneWebName = NULL;
159         DIR *filedir = NULL;
160         struct dirent *d;
161         struct dirent *filedir_entry;
162         int d_type = 0;
163         int d_namelen;
164         int istoplevel;
165                 
166         if (IsEmptyStr(DirName))
167         {
168                 return 0;
169         }
170
171         filedir = opendir (DirName);
172         if (filedir == NULL)
173         {
174                 return 0;
175         }
176
177         d = (struct dirent *)malloc(offsetof(struct dirent, d_name) + PATH_MAX + 1);
178         if (d == NULL)
179         {
180                 closedir(filedir);
181                 return 0;
182         }
183
184         Dir = NewStrBufPlain(DirName, -1);
185         WebDir = NewStrBufPlain(RelDir, -1);
186         istoplevel = IsEmptyStr(RelDir);
187         OneWebName = NewStrBuf();
188
189         while ((readdir_r(filedir, d, &filedir_entry) == 0) &&
190                (filedir_entry != NULL))
191         {
192 #ifdef _DIRENT_HAVE_D_NAMLEN
193                 d_namelen = filedir_entry->d_namlen;
194
195 #else
196                 d_namelen = strlen(filedir_entry->d_name);
197 #endif
198
199 #ifdef _DIRENT_HAVE_D_TYPE
200                 d_type = filedir_entry->d_type;
201 #else
202
203 #ifndef DT_UNKNOWN
204 #define DT_UNKNOWN     0
205 #define DT_DIR         4
206 #define DT_REG         8
207 #define DT_LNK         10
208
209 #define IFTODT(mode)   (((mode) & 0170000) >> 12)
210 #define DTTOIF(dirtype)        ((dirtype) << 12)
211 #endif
212                 d_type = DT_UNKNOWN;
213 #endif
214                 if ((d_namelen > 1) && filedir_entry->d_name[d_namelen - 1] == '~')
215                         continue; /* Ignore backup files... */
216
217                 if ((d_namelen == 1) && 
218                     (filedir_entry->d_name[0] == '.'))
219                         continue;
220
221                 if ((d_namelen == 2) && 
222                     (filedir_entry->d_name[0] == '.') &&
223                     (filedir_entry->d_name[1] == '.'))
224                         continue;
225
226                 if (d_type == DT_UNKNOWN) {
227                         struct stat s;
228                         char path[PATH_MAX];
229                         snprintf(path, PATH_MAX, "%s/%s", 
230                                 DirName, filedir_entry->d_name);
231                         if (lstat(path, &s) == 0) {
232                                 d_type = IFTODT(s.st_mode);
233                         }
234                 }
235
236                 switch (d_type)
237                 {
238                 case DT_DIR:
239                         /* Skip directories we are not interested in... */
240                         if ((strcmp(filedir_entry->d_name, ".svn") == 0) ||
241                             (strcmp(filedir_entry->d_name, "t") == 0))
242                                 break;
243                         snprintf(dirname, PATH_MAX, "%s/%s/", 
244                                  DirName, filedir_entry->d_name);
245                         if (istoplevel)
246                                 snprintf(reldir, PATH_MAX, "%s/", 
247                                          filedir_entry->d_name);
248                         else
249                                 snprintf(reldir, PATH_MAX, "%s/%s/", 
250                                          RelDir, filedir_entry->d_name);
251                         StripSlashes(dirname, 1);
252                         StripSlashes(reldir, 1);
253                         LoadStaticDir(dirname, DirList, reldir);                                 
254                         break;
255                 case DT_LNK: /* TODO: check whether its a file or a directory */
256                 case DT_REG:
257                         FileName = NewStrBufDup(Dir);
258                         if (ChrPtr(FileName) [ StrLength(FileName) - 1] != '/')
259                                 StrBufAppendBufPlain(FileName, "/", 1, 0);
260                         StrBufAppendBufPlain(FileName, filedir_entry->d_name, d_namelen, 0);
261
262                         FlushStrBuf(OneWebName);
263                         StrBufAppendBuf(OneWebName, WebDir, 0);
264                         if ((StrLength(OneWebName) != 0) && 
265                             (ChrPtr(OneWebName) [ StrLength(OneWebName) - 1] != '/'))
266                                 StrBufAppendBufPlain(OneWebName, "/", 1, 0);
267                         StrBufAppendBufPlain(OneWebName, filedir_entry->d_name, d_namelen, 0);
268
269                         Put(DirList, SKEY(OneWebName), FileName, HFreeStrBuf);
270                         /* syslog(LOG_DEBUG, "[%s | %s]\n", ChrPtr(OneWebName), ChrPtr(FileName)); */
271                         break;
272                 default:
273                         break;
274                 }
275
276
277         }
278         free(d);
279         closedir(filedir);
280         FreeStrBuf(&Dir);
281         FreeStrBuf(&WebDir);
282         FreeStrBuf(&OneWebName);
283         return 1;
284 }
285
286
287 void output_flat_static(void)
288 {
289         wcsession *WCC = WC;
290         void *vFile;
291         StrBuf *File;
292
293         if (WCC->Hdr->HR.Handler == NULL)
294                 return;
295         if (GetHash(StaticFilemappings[0], SKEY(WCC->Hdr->HR.Handler->Name), &vFile) &&
296             (vFile != NULL))
297         {
298                 File = (StrBuf*) vFile;
299                 output_static(ChrPtr(File));
300         }
301 }
302
303 void output_static_safe(HashList *DirList)
304 {
305         wcsession *WCC = WC;
306         void *vFile;
307         StrBuf *File;
308         const char *MimeType;
309
310         if (GetHash(DirList, SKEY(WCC->Hdr->HR.ReqLine), &vFile) &&
311             (vFile != NULL))
312         {
313                 File = (StrBuf*) vFile;
314                 output_static(ChrPtr(File));
315         }
316         else {
317                 syslog(LOG_INFO, "output_static_safe() file %s not found. \n", 
318                         ChrPtr(WCC->Hdr->HR.ReqLine));
319                 MimeType =  GuessMimeByFilename(SKEY(WCC->Hdr->HR.ReqLine));
320                 if (strstr(MimeType, "image/") != NULL)
321                 {
322                         output_error_pic("the file you requested isn't known to our cache", "maybe reload webcit?");
323                 }
324                 else
325                 {                   
326                         do_404();
327                 }
328         }
329 }
330 void output_static_0(void)
331 {
332         output_static_safe(StaticFilemappings[0]);
333 }
334 void output_static_1(void)
335 {
336         output_static_safe(StaticFilemappings[1]);
337 }
338 void output_static_2(void)
339 {
340         output_static_safe(StaticFilemappings[2]);
341 }
342 void output_static_3(void)
343 {
344         output_static_safe(StaticFilemappings[4]);
345 }
346
347
348 /*
349  * robots.txt
350  */
351 void robots_txt(void) {
352         output_headers(0, 0, 0, 0, 0, 0);
353
354         hprintf("Content-type: text/plain\r\n"
355                 "Server: %s\r\n"
356                 "Connection: close\r\n",
357                 PACKAGE_STRING);
358         begin_burst();
359
360         wc_printf("User-agent: *\r\n"
361                 "Disallow: /printmsg\r\n"
362                 "Disallow: /msgheaders\r\n"
363                 "Disallow: /groupdav\r\n"
364                 "Disallow: /do_template\r\n"
365                 "Disallow: /static\r\n"
366                 "Disallow: /display_page\r\n"
367                 "Disallow: /readnew\r\n"
368                 "Disallow: /display_enter\r\n"
369                 "Disallow: /skip\r\n"
370                 "Disallow: /ungoto\r\n"
371                 "Sitemap: %s/sitemap.xml\r\n"
372                 "\r\n"
373                 ,
374                 ChrPtr(site_prefix)
375         );
376
377         wDumpContent(0);
378 }
379
380
381 void 
382 ServerStartModule_STATIC
383 (void)
384 {
385         StaticFilemappings[0] = NewHash(1, NULL);
386         StaticFilemappings[1] = NewHash(1, NULL);
387         StaticFilemappings[2] = NewHash(1, NULL);
388         StaticFilemappings[3] = NewHash(1, NULL);
389         StaticFilemappings[4] = NewHash(1, NULL);
390 }
391 void 
392 ServerShutdownModule_STATIC
393 (void)
394 {
395         DeleteHash(&StaticFilemappings[0]);
396         DeleteHash(&StaticFilemappings[1]);
397         DeleteHash(&StaticFilemappings[2]);
398         DeleteHash(&StaticFilemappings[3]);
399         DeleteHash(&StaticFilemappings[4]);
400 }
401
402
403 void 
404 InitModule_STATIC
405 (void)
406 {
407         LoadStaticDir(static_dirs[0], StaticFilemappings[0], "");
408         LoadStaticDir(static_dirs[1], StaticFilemappings[1], "");
409         LoadStaticDir(static_dirs[2], StaticFilemappings[2], "");
410         LoadStaticDir(static_dirs[3], StaticFilemappings[3], "");
411         LoadStaticDir(static_dirs[4], StaticFilemappings[4], "");
412
413         WebcitAddUrlHandler(HKEY("robots.txt"), "", 0, robots_txt, ANONYMOUS|COOKIEUNNEEDED|ISSTATIC|LOGCHATTY);
414         WebcitAddUrlHandler(HKEY("favicon.ico"), "", 0, output_flat_static, ANONYMOUS|COOKIEUNNEEDED|ISSTATIC|LOGCHATTY);
415         WebcitAddUrlHandler(HKEY("static"), "", 0, output_static_0, ANONYMOUS|COOKIEUNNEEDED|ISSTATIC|LOGCHATTY);
416         WebcitAddUrlHandler(HKEY("static.local"), "", 0, output_static_1, ANONYMOUS|COOKIEUNNEEDED|ISSTATIC|LOGCHATTY);
417         WebcitAddUrlHandler(HKEY("tinymce"), "", 0, output_static_2, ANONYMOUS|COOKIEUNNEEDED|ISSTATIC|LOGCHATTY);
418         WebcitAddUrlHandler(HKEY("tiny_mce"), "", 0, output_static_2, ANONYMOUS|COOKIEUNNEEDED|ISSTATIC|LOGCHATTY);
419         WebcitAddUrlHandler(HKEY("epiceditor"), "", 0, output_static_3, ANONYMOUS|COOKIEUNNEEDED|ISSTATIC|LOGCHATTY);
420 }