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