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