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