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