d7b915c3c2d092842d6128ee11a0b8a31ecb748c
[citadel.git] / webcit / downloads.c
1 /*
2  * $Id$
3  */
4 #include "webcit.h"
5 #include "webserver.h"
6
7 extern char* static_dirs[];
8
9 typedef struct _FileListStruct {
10         StrBuf *Filename;
11         long FileSize;
12         StrBuf *MimeType;
13         StrBuf *Comment;
14         int IsPic;
15         int Sequence;
16 } FileListStruct;
17
18 void FreeFiles(void *vFile)
19 {
20         FileListStruct *F = (FileListStruct*) vFile;
21         FreeStrBuf(&F->Filename);
22         FreeStrBuf(&F->MimeType);
23         FreeStrBuf(&F->Comment);
24         free(F);
25 }
26
27 /* -------------------------------------------------------------------------------- */
28 void tmplput_FILE_NAME(StrBuf *Target, WCTemplputParams *TP)
29 {
30         FileListStruct *F = (FileListStruct*) CTX;
31         StrBufAppendTemplate(Target, TP, F->Filename, 0);
32 }
33 void tmplput_FILE_SIZE(StrBuf *Target, WCTemplputParams *TP)
34 {
35         FileListStruct *F = (FileListStruct*) CTX;
36         StrBufAppendPrintf(Target, "%ld", F->FileSize);
37 }
38 void tmplput_FILEMIMETYPE(StrBuf *Target, WCTemplputParams *TP)
39 {
40         FileListStruct *F = (FileListStruct*) CTX;
41         StrBufAppendTemplate(Target, TP, F->MimeType, 0);
42 }
43 void tmplput_FILE_COMMENT(StrBuf *Target, WCTemplputParams *TP)
44 {
45         FileListStruct *F = (FileListStruct*) CTX;
46         StrBufAppendTemplate(Target, TP, F->Comment, 0);
47 }
48
49 /* -------------------------------------------------------------------------------- */
50
51 int Conditional_FILE_ISPIC(StrBuf *Target, WCTemplputParams *TP)
52 {
53         FileListStruct *F = (FileListStruct*) CTX;
54         return F->IsPic;
55 }
56
57 /* -------------------------------------------------------------------------------- */
58 int CompareFilelistByMime(const void *vFile1, const void *vFile2)
59 {
60         FileListStruct *File1 = (FileListStruct*) GetSearchPayload(vFile1);
61         FileListStruct *File2 = (FileListStruct*) GetSearchPayload(vFile2);
62
63         if (File1->IsPic != File2->IsPic)
64                 return File1->IsPic > File2->IsPic;
65         return strcasecmp(ChrPtr(File1->MimeType), ChrPtr(File2->MimeType));
66 }
67 int CompareFilelistByMimeRev(const void *vFile1, const void *vFile2)
68 {
69         FileListStruct *File1 = (FileListStruct*) GetSearchPayload(vFile1);
70         FileListStruct *File2 = (FileListStruct*) GetSearchPayload(vFile2);
71         if (File1->IsPic != File2->IsPic)
72                 return File1->IsPic < File2->IsPic;
73         return strcasecmp(ChrPtr(File2->MimeType), ChrPtr(File1->MimeType));
74 }
75 int GroupchangeFilelistByMime(const void *vFile1, const void *vFile2)
76 {
77         FileListStruct *File1 = (FileListStruct*) vFile1;
78         FileListStruct *File2 = (FileListStruct*) vFile2;
79
80         if (File1->IsPic != File2->IsPic)
81                 return File1->IsPic > File2->IsPic;
82         return strcasecmp(ChrPtr(File1->MimeType), ChrPtr(File2->MimeType)) != 0;
83 }
84
85
86 int CompareFilelistByName(const void *vFile1, const void *vFile2)
87 {
88         FileListStruct *File1 = (FileListStruct*) GetSearchPayload(vFile1);
89         FileListStruct *File2 = (FileListStruct*) GetSearchPayload(vFile2);
90
91         if (File1->IsPic != File2->IsPic)
92                 return File1->IsPic > File2->IsPic;
93         return strcasecmp(ChrPtr(File1->Filename), ChrPtr(File2->Filename));
94 }
95 int CompareFilelistByNameRev(const void *vFile1, const void *vFile2)
96 {
97         FileListStruct *File1 = (FileListStruct*) GetSearchPayload(vFile1);
98         FileListStruct *File2 = (FileListStruct*) GetSearchPayload(vFile2);
99         if (File1->IsPic != File2->IsPic)
100                 return File1->IsPic < File2->IsPic;
101         return strcasecmp(ChrPtr(File2->Filename), ChrPtr(File1->Filename));
102 }
103 int GroupchangeFilelistByName(const void *vFile1, const void *vFile2)
104 {
105         FileListStruct *File1 = (FileListStruct*) vFile1;
106         FileListStruct *File2 = (FileListStruct*) vFile2;
107
108         return ChrPtr(File1->Filename)[0] != ChrPtr(File2->Filename)[0];
109 }
110
111
112 int CompareFilelistBySize(const void *vFile1, const void *vFile2)
113 {
114         FileListStruct *File1 = (FileListStruct*) GetSearchPayload(vFile1);
115         FileListStruct *File2 = (FileListStruct*) GetSearchPayload(vFile2);
116         if (File1->FileSize == File2->FileSize)
117                 return 0;
118         return (File1->FileSize > File2->FileSize);
119 }
120 int CompareFilelistBySizeRev(const void *vFile1, const void *vFile2)
121 {
122         FileListStruct *File1 = (FileListStruct*) GetSearchPayload(vFile1);
123         FileListStruct *File2 = (FileListStruct*) GetSearchPayload(vFile2);
124         if (File1->FileSize == File2->FileSize)
125                 return 0;
126         return (File1->FileSize < File2->FileSize);
127 }
128 int GroupchangeFilelistBySize(const void *vFile1, const void *vFile2)
129 {
130         return 0;
131 }
132
133
134 int CompareFilelistByComment(const void *vFile1, const void *vFile2)
135 {
136         FileListStruct *File1 = (FileListStruct*) GetSearchPayload(vFile1);
137         FileListStruct *File2 = (FileListStruct*) GetSearchPayload(vFile2);
138         return strcasecmp(ChrPtr(File1->Comment), ChrPtr(File2->Comment));
139 }
140 int CompareFilelistByCommentRev(const void *vFile1, const void *vFile2)
141 {
142         FileListStruct *File1 = (FileListStruct*) GetSearchPayload(vFile1);
143         FileListStruct *File2 = (FileListStruct*) GetSearchPayload(vFile2);
144         return strcasecmp(ChrPtr(File2->Comment), ChrPtr(File1->Comment));
145 }
146 int GroupchangeFilelistByComment(const void *vFile1, const void *vFile2)
147 {
148         FileListStruct *File1 = (FileListStruct*) vFile1;
149         FileListStruct *File2 = (FileListStruct*) vFile2;
150         return ChrPtr(File1->Comment)[9] != ChrPtr(File2->Comment)[0];
151 }
152
153
154 int CompareFilelistBySequence(const void *vFile1, const void *vFile2)
155 {
156         FileListStruct *File1 = (FileListStruct*) GetSearchPayload(vFile1);
157         FileListStruct *File2 = (FileListStruct*) GetSearchPayload(vFile2);
158         return (File2->Sequence >  File1->Sequence);
159 }
160 int GroupchangeFilelistBySequence(const void *vFile1, const void *vFile2)
161 {
162         return 0;
163 }
164
165 /* -------------------------------------------------------------------------------- */
166 HashList* LoadFileList(StrBuf *Target, WCTemplputParams *TP)
167 {
168         FileListStruct *Entry;
169         StrBuf *Buf;
170         HashList *Files;
171         int Done = 0;
172         int sequence = 0;
173         char buf[1024];
174         CompareFunc SortIt;
175         int HavePic;
176         WCTemplputParams SubTP;
177
178         memset(&TP, 0, sizeof(WCTemplputParams));
179         serv_puts("RDIR");
180         serv_getln(buf, sizeof buf);
181         if (buf[0] != '1') return NULL;
182
183         Buf = NewStrBuf();             
184         Files = NewHash(1, NULL);
185         while (!Done && (StrBuf_ServGetln(Buf)>=0)) {
186                 if ( (StrLength(Buf)==3) && 
187                     !strcmp(ChrPtr(Buf), "000")) 
188                 {
189                         Done = 1;
190                         continue;
191                 }
192
193                 Entry = (FileListStruct*) malloc(sizeof (FileListStruct));
194                 Entry->Filename = NewStrBufPlain(NULL, StrLength(Buf));
195                 Entry->MimeType = NewStrBufPlain(NULL, StrLength(Buf));
196                 Entry->Comment = NewStrBufPlain(NULL, StrLength(Buf));
197
198                 Entry->Sequence = sequence++;
199
200                 StrBufExtract_token(Entry->Filename, Buf, 0, '|');
201                 Entry->FileSize = StrBufExtract_long(Buf, 1, '|');
202                 StrBufExtract_token(Entry->MimeType, Buf, 2, '|');
203                 StrBufExtract_token(Entry->Comment, Buf, 3, '|');
204
205
206
207                 Entry->IsPic = (strstr(ChrPtr(Entry->MimeType), "image") != NULL);
208                 if (Entry->IsPic) {
209                         HavePic = 1;
210                 }
211                 Put(Files, SKEY(Entry->Filename), Entry, FreeFiles);
212         }
213         SubTP.Filter.ContextType = CTX_FILELIST;
214         SortIt = RetrieveSort(&SubTP, NULL, 0, HKEY("fileunsorted"), 0);
215         if (SortIt != NULL)
216                 SortByPayload(Files, SortIt);
217         else 
218                 SortByPayload(Files, CompareFilelistBySequence);
219         FreeStrBuf(&Buf);
220         svputlong("FILE:HAVEPICS", HavePic);
221         return Files;
222 }
223
224 void display_mime_icon(void)
225 {
226         char FileBuf[SIZ];
227         const char *FileName;
228         char *MimeType;
229         size_t tlen;
230
231         MimeType = xbstr("type", &tlen);
232         FileName = GetIconFilename(MimeType, tlen);
233
234         if (FileName == NULL)
235                 snprintf (FileBuf, SIZ, "%s%s", static_dirs[0], "/diskette_24x.gif");
236         else
237                 snprintf (FileBuf, SIZ, "%s%s", static_dirs[3], FileName);
238         output_static(FileBuf);
239 }
240
241 void download_file(void)
242 {
243         wcsession *WCC = WC;
244         StrBuf *Buf;
245         off_t bytes;
246         StrBuf *ContentType = NewStrBufPlain(HKEY("application/octet-stream"));
247
248         /* Setting to nonzero forces a MIME type of application/octet-stream */
249         int force_download = 1;
250         
251         Buf = NewStrBuf();
252         StrBufExtract_token(Buf, WCC->Hdr->HR.ReqLine, 0, '/');
253         StrBufUnescape(Buf, 1);
254         serv_printf("OPEN %s", ChrPtr(Buf));
255         StrBuf_ServGetln(Buf);
256         if (GetServerStatus(Buf, NULL) == 2) {
257                 StrBufCutLeft(Buf, 4);
258                 bytes = StrBufExtract_long(Buf, 0, '|');
259                 if (!force_download) {
260                         StrBufExtract_token(ContentType, Buf, 3, '|');
261                 }
262                 read_server_binary(WCC->WBuf, bytes, Buf);
263                 serv_puts("CLOS");
264                 StrBuf_ServGetln(Buf);
265                 http_transmit_thing(ChrPtr(ContentType), 0);
266         } else {
267                 StrBufCutLeft(Buf, 4);
268                 hprintf("HTTP/1.1 404 %s\n", ChrPtr(Buf));
269                 output_headers(0, 0, 0, 0, 0, 0);
270                 hprintf("Content-Type: text/plain\r\n");
271                 wprintf(_("An error occurred while retrieving this file: %s\n"), 
272                         ChrPtr(Buf));
273                 end_burst();
274         }
275         FreeStrBuf(&ContentType);
276         FreeStrBuf(&Buf);
277 }
278
279
280
281 void delete_file(void)
282 {
283         const StrBuf *MimeType;
284         StrBuf *Buf;
285         char buf[256];
286         
287         safestrncpy(buf, bstr("file"), sizeof buf);
288         unescape_input(buf);
289         serv_printf("DELF %s", buf);
290         Buf = NewStrBuf();
291         StrBuf_ServGetln(Buf);
292         GetServerStatus(Buf, NULL);
293         StrBufCutLeft(Buf, 4);
294         strcpy(WC->ImportantMessage, ChrPtr(Buf));
295         MimeType = DoTemplate(HKEY("files"), NULL, &NoCtx);
296         http_transmit_thing(ChrPtr(MimeType), 0);
297         FreeStrBuf(&Buf);
298 }
299
300
301
302 void upload_file(void)
303 {
304         const StrBuf *RetMimeType;
305         const char *MimeType;
306         char buf[1024];
307         long bytes_transmitted = 0;
308         long blocksize;
309         wcsession *WCC = WC;     /* stack this for faster access (WC is a function) */
310
311         MimeType = GuessMimeType(ChrPtr(WCC->upload), WCC->upload_length); 
312         serv_printf("UOPN %s|%s|%s", WCC->upload_filename, MimeType, bstr("description"));
313         serv_getln(buf, sizeof buf);
314         if (buf[0] != '2')
315         {
316                 strcpy(WCC->ImportantMessage, &buf[4]);
317                 RetMimeType = DoTemplate(HKEY("files"), NULL, &NoCtx);
318                 http_transmit_thing(ChrPtr(RetMimeType), 0);
319                 return;
320         }
321
322         while (bytes_transmitted < WCC->upload_length)
323         {
324                 blocksize = 4096;
325                 if (blocksize > (WCC->upload_length - bytes_transmitted))
326                 {
327                         blocksize = (WCC->upload_length - bytes_transmitted);
328                 }
329                 serv_printf("WRIT %ld", blocksize);
330                 serv_getln(buf, sizeof buf);
331                 if (buf[0] == '7')
332                 {
333                         blocksize = atoi(&buf[4]);
334                         serv_write(&ChrPtr(WCC->upload)[bytes_transmitted], blocksize);
335                         bytes_transmitted += blocksize;
336                 }
337         }
338
339         serv_puts("UCLS 1");
340         serv_getln(buf, sizeof buf);
341         strcpy(WCC->ImportantMessage, &buf[4]);
342         RetMimeType = DoTemplate(HKEY("files"), NULL, &NoCtx);
343         http_transmit_thing(ChrPtr(RetMimeType), 0);
344 }
345
346
347
348 /*
349  * When the browser requests an image file from the Citadel server,
350  * this function is called to transmit it.
351  */
352 void output_image(void)
353 {
354         StrBuf *Buf;
355         wcsession *WCC = WC;
356         off_t bytes;
357         const char *MimeType;
358         
359         Buf = NewStrBuf();
360         serv_printf("OIMG %s|%s", bstr("name"), bstr("parm"));
361         StrBuf_ServGetln(Buf);
362         if (GetServerStatus(Buf, NULL) == 2) {
363                 StrBufCutLeft(Buf, 4);
364                 bytes = StrBufExtract_long(Buf, 0, '|');
365
366                 /** Read it from the server */
367                 
368                 if (read_server_binary(WCC->WBuf, bytes, Buf) > 0) {
369                         serv_puts("CLOS");
370                         StrBuf_ServGetln(Buf);
371                 
372                         MimeType = GuessMimeType (ChrPtr(WCC->WBuf), StrLength(WCC->WBuf));
373                         /** Write it to the browser */
374                         if (!IsEmptyStr(MimeType))
375                         {
376                                 http_transmit_thing(MimeType, 0);
377                                 FreeStrBuf(&Buf);
378                                 return;
379                         }
380                 }
381                 /* hm... unknown mimetype? fallback to blank gif */
382         } 
383
384         
385         /*
386          * Instead of an ugly 404, send a 1x1 transparent GIF
387          * when there's no such image on the server.
388          */
389         StrBufPrintf (Buf, "%s%s", static_dirs[0], "/blank.gif");
390         output_static(ChrPtr(Buf));
391         FreeStrBuf(&Buf);
392 }
393
394 void 
395 InitModule_DOWNLOAD
396 (void)
397 {
398
399         RegisterIterator("ROOM:FILES", 0, NULL, LoadFileList,
400                          NULL, DeleteHash, CTX_FILELIST, CTX_NONE, 
401                          IT_FLAG_DETECT_GROUPCHANGE);
402
403         RegisterSortFunc(HKEY("filemime"),
404                          NULL, 0,
405                          CompareFilelistByMime,
406                          CompareFilelistByMimeRev,
407                          GroupchangeFilelistByMime,
408                          CTX_FILELIST);
409         RegisterSortFunc(HKEY("filename"),
410                          NULL, 0,
411                          CompareFilelistByName,
412                          CompareFilelistByNameRev,
413                          GroupchangeFilelistByName,
414                          CTX_FILELIST);
415         RegisterSortFunc(HKEY("filesize"),
416                          NULL, 0,
417                          CompareFilelistBySize,
418                          CompareFilelistBySizeRev,
419                          GroupchangeFilelistBySize,
420                          CTX_FILELIST);
421         RegisterSortFunc(HKEY("filesubject"),
422                          NULL, 0,
423                          CompareFilelistByComment,
424                          CompareFilelistByCommentRev,
425                          GroupchangeFilelistByComment,
426                          CTX_FILELIST);
427         RegisterSortFunc(HKEY("fileunsorted"),
428                          NULL, 0,
429                          CompareFilelistBySequence,
430                          CompareFilelistBySequence,
431                          GroupchangeFilelistBySequence,
432                          CTX_FILELIST);
433
434         RegisterNamespace("FILE:NAME", 0, 2, tmplput_FILE_NAME, NULL, CTX_FILELIST);
435         RegisterNamespace("FILE:SIZE", 0, 1, tmplput_FILE_SIZE, NULL, CTX_FILELIST);
436         RegisterNamespace("FILE:MIMETYPE", 0, 2, tmplput_FILEMIMETYPE, NULL, CTX_FILELIST);
437         RegisterNamespace("FILE:COMMENT", 0, 2, tmplput_FILE_COMMENT, NULL, CTX_FILELIST);
438
439         RegisterConditional(HKEY("COND:FILE:ISPIC"), 0, Conditional_FILE_ISPIC, CTX_FILELIST);
440
441         WebcitAddUrlHandler(HKEY("image"), output_image, ANONYMOUS);
442         WebcitAddUrlHandler(HKEY("display_mime_icon"), display_mime_icon , ANONYMOUS);
443         WebcitAddUrlHandler(HKEY("download_file"), download_file, NEED_URL);
444         WebcitAddUrlHandler(HKEY("delete_file"), delete_file, NEED_URL);
445         WebcitAddUrlHandler(HKEY("upload_file"), upload_file, 0);
446 }