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