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