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