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