2a2fc2847bc7bc837f69a05f072cc33eac730c8d
[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         filedir = opendir (DirName);
167         if (filedir == NULL) {
168                 return 0;
169         }
170
171         d = (struct dirent *)malloc(offsetof(struct dirent, d_name) + PATH_MAX + 1);
172         if (d == NULL) {
173                 closedir(filedir);
174                 return 0;
175         }
176
177         Dir = NewStrBufPlain(DirName, -1);
178         WebDir = NewStrBufPlain(RelDir, -1);
179         istoplevel = IsEmptyStr(RelDir);
180         OneWebName = NewStrBuf();
181
182         while ((readdir_r(filedir, d, &filedir_entry) == 0) &&
183                (filedir_entry != NULL))
184         {
185 #ifdef _DIRENT_HAVE_D_NAMLEN
186                 d_namelen = filedir_entry->d_namlen;
187
188 #else
189                 d_namelen = strlen(filedir_entry->d_name);
190 #endif
191
192 #ifdef _DIRENT_HAVE_D_TYPE
193                 d_type = filedir_entry->d_type;
194 #else
195
196 #ifndef DT_UNKNOWN
197 #define DT_UNKNOWN     0
198 #define DT_DIR         4
199 #define DT_REG         8
200 #define DT_LNK         10
201
202 #define IFTODT(mode)   (((mode) & 0170000) >> 12)
203 #define DTTOIF(dirtype)        ((dirtype) << 12)
204 #endif
205                 d_type = DT_UNKNOWN;
206 #endif
207                 if ((d_namelen > 1) && filedir_entry->d_name[d_namelen - 1] == '~')
208                         continue; /* Ignore backup files... */
209
210                 if ((d_namelen == 1) && 
211                     (filedir_entry->d_name[0] == '.'))
212                         continue;
213
214                 if ((d_namelen == 2) && 
215                     (filedir_entry->d_name[0] == '.') &&
216                     (filedir_entry->d_name[1] == '.'))
217                         continue;
218
219                 if (d_type == DT_UNKNOWN) {
220                         struct stat s;
221                         char path[PATH_MAX];
222                         snprintf(path, PATH_MAX, "%s/%s", 
223                                 DirName, filedir_entry->d_name);
224                         if (lstat(path, &s) == 0) {
225                                 d_type = IFTODT(s.st_mode);
226                         }
227                 }
228
229                 switch (d_type)
230                 {
231                 case DT_DIR:
232                         /* Skip directories we are not interested in... */
233                         if ((strcmp(filedir_entry->d_name, ".svn") == 0) ||
234                             (strcmp(filedir_entry->d_name, "t") == 0))
235                                 break;
236                         snprintf(dirname, PATH_MAX, "%s/%s/", 
237                                  DirName, filedir_entry->d_name);
238                         if (istoplevel)
239                                 snprintf(reldir, PATH_MAX, "%s/", 
240                                          filedir_entry->d_name);
241                         else
242                                 snprintf(reldir, PATH_MAX, "%s/%s/", 
243                                          RelDir, filedir_entry->d_name);
244                         StripSlashes(dirname, 1);
245                         StripSlashes(reldir, 1);
246                         LoadStaticDir(dirname, DirList, reldir);                                 
247                         break;
248                 case DT_LNK: /* TODO: check whether its a file or a directory */
249                 case DT_REG:
250                         FileName = NewStrBufDup(Dir);
251                         if (ChrPtr(FileName) [ StrLength(FileName) - 1] != '/')
252                                 StrBufAppendBufPlain(FileName, "/", 1, 0);
253                         StrBufAppendBufPlain(FileName, filedir_entry->d_name, d_namelen, 0);
254
255                         FlushStrBuf(OneWebName);
256                         StrBufAppendBuf(OneWebName, WebDir, 0);
257                         if ((StrLength(OneWebName) != 0) && 
258                             (ChrPtr(OneWebName) [ StrLength(OneWebName) - 1] != '/'))
259                                 StrBufAppendBufPlain(OneWebName, "/", 1, 0);
260                         StrBufAppendBufPlain(OneWebName, filedir_entry->d_name, d_namelen, 0);
261
262                         Put(DirList, SKEY(OneWebName), FileName, HFreeStrBuf);
263                         /* syslog(LOG_DEBUG, "[%s | %s]\n", ChrPtr(OneWebName), ChrPtr(FileName)); */
264                         break;
265                 default:
266                         break;
267                 }
268
269
270         }
271         free(d);
272         closedir(filedir);
273         FreeStrBuf(&Dir);
274         FreeStrBuf(&WebDir);
275         FreeStrBuf(&OneWebName);
276         return 1;
277 }
278
279
280 void output_flat_static(void)
281 {
282         wcsession *WCC = WC;
283         void *vFile;
284         StrBuf *File;
285
286         if (WCC->Hdr->HR.Handler == NULL)
287                 return;
288         if (GetHash(StaticFilemappings[0], SKEY(WCC->Hdr->HR.Handler->Name), &vFile) &&
289             (vFile != NULL))
290         {
291                 File = (StrBuf*) vFile;
292                 output_static(ChrPtr(File));
293         }
294 }
295
296 void output_static_safe(HashList *DirList)
297 {
298         wcsession *WCC = WC;
299         void *vFile;
300         StrBuf *File;
301         const char *MimeType;
302
303         if (GetHash(DirList, SKEY(WCC->Hdr->HR.ReqLine), &vFile) &&
304             (vFile != NULL))
305         {
306                 File = (StrBuf*) vFile;
307                 output_static(ChrPtr(File));
308         }
309         else {
310                 syslog(LOG_INFO, "output_static_safe() file %s not found. \n", 
311                         ChrPtr(WCC->Hdr->HR.ReqLine));
312                 MimeType =  GuessMimeByFilename(SKEY(WCC->Hdr->HR.ReqLine));
313                 if (strstr(MimeType, "image/") != NULL)
314                 {
315                         output_error_pic("the file you requested isn't known to our cache", "maybe reload webcit?");
316                 }
317                 else
318                 {                   
319                         do_404();
320                 }
321         }
322 }
323 void output_static_0(void)
324 {
325         output_static_safe(StaticFilemappings[0]);
326 }
327 void output_static_1(void)
328 {
329         output_static_safe(StaticFilemappings[1]);
330 }
331 void output_static_2(void)
332 {
333         output_static_safe(StaticFilemappings[2]);
334 }
335 void output_static_3(void)
336 {
337         output_static_safe(StaticFilemappings[4]);
338 }
339
340
341 /*
342  * robots.txt
343  */
344 void robots_txt(void) {
345         output_headers(0, 0, 0, 0, 0, 0);
346
347         hprintf("Content-type: text/plain\r\n"
348                 "Server: %s\r\n"
349                 "Connection: close\r\n",
350                 PACKAGE_STRING);
351         begin_burst();
352
353         wc_printf("User-agent: *\r\n"
354                 "Disallow: /printmsg\r\n"
355                 "Disallow: /msgheaders\r\n"
356                 "Disallow: /groupdav\r\n"
357                 "Disallow: /do_template\r\n"
358                 "Disallow: /static\r\n"
359                 "Disallow: /display_page\r\n"
360                 "Disallow: /readnew\r\n"
361                 "Disallow: /display_enter\r\n"
362                 "Disallow: /skip\r\n"
363                 "Disallow: /ungoto\r\n"
364                 "Sitemap: %s/sitemap.xml\r\n"
365                 "\r\n"
366                 ,
367                 ChrPtr(site_prefix)
368         );
369
370         wDumpContent(0);
371 }
372
373
374 void 
375 ServerStartModule_STATIC
376 (void)
377 {
378         StaticFilemappings[0] = NewHash(1, NULL);
379         StaticFilemappings[1] = NewHash(1, NULL);
380         StaticFilemappings[2] = NewHash(1, NULL);
381         StaticFilemappings[3] = NewHash(1, NULL);
382         StaticFilemappings[4] = NewHash(1, NULL);
383 }
384 void 
385 ServerShutdownModule_STATIC
386 (void)
387 {
388         DeleteHash(&StaticFilemappings[0]);
389         DeleteHash(&StaticFilemappings[1]);
390         DeleteHash(&StaticFilemappings[2]);
391         DeleteHash(&StaticFilemappings[3]);
392         DeleteHash(&StaticFilemappings[4]);
393 }
394
395
396 void 
397 InitModule_STATIC
398 (void)
399 {
400         LoadStaticDir(static_dirs[0], StaticFilemappings[0], "");
401         LoadStaticDir(static_dirs[1], StaticFilemappings[1], "");
402         LoadStaticDir(static_dirs[2], StaticFilemappings[2], "");
403         LoadStaticDir(static_dirs[3], StaticFilemappings[3], "");
404         LoadStaticDir(static_dirs[4], StaticFilemappings[4], "");
405
406         WebcitAddUrlHandler(HKEY("robots.txt"), "", 0, robots_txt, ANONYMOUS|COOKIEUNNEEDED|ISSTATIC|LOGCHATTY);
407         WebcitAddUrlHandler(HKEY("favicon.ico"), "", 0, output_flat_static, ANONYMOUS|COOKIEUNNEEDED|ISSTATIC|LOGCHATTY);
408         WebcitAddUrlHandler(HKEY("static"), "", 0, output_static_0, ANONYMOUS|COOKIEUNNEEDED|ISSTATIC|LOGCHATTY);
409         WebcitAddUrlHandler(HKEY("static.local"), "", 0, output_static_1, ANONYMOUS|COOKIEUNNEEDED|ISSTATIC|LOGCHATTY);
410         WebcitAddUrlHandler(HKEY("tinymce"), "", 0, output_static_2, ANONYMOUS|COOKIEUNNEEDED|ISSTATIC|LOGCHATTY);
411         WebcitAddUrlHandler(HKEY("tiny_mce"), "", 0, output_static_2, ANONYMOUS|COOKIEUNNEEDED|ISSTATIC|LOGCHATTY);
412         WebcitAddUrlHandler(HKEY("markdown"), "", 0, output_static_3, ANONYMOUS|COOKIEUNNEEDED|ISSTATIC|LOGCHATTY);
413         WebcitAddUrlHandler(HKEY("epiceditor"), "", 0, output_static_3, ANONYMOUS|COOKIEUNNEEDED|ISSTATIC|LOGCHATTY);
414 }