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