3 * Copyright (c) 1996-2012 by the citadel.org team
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.
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.
15 #include "webserver.h"
17 CtxType CTX_FILELIST = CTX_NONE;
19 extern void output_static(const char *What);
21 extern char *static_dirs[];
23 typedef struct _FileListStruct {
32 void FreeFiles(void *vFile) {
33 FileListStruct *F = (FileListStruct *) vFile;
34 FreeStrBuf(&F->Filename);
35 FreeStrBuf(&F->MimeType);
36 FreeStrBuf(&F->Comment);
40 /* -------------------------------------------------------------------------------- */
41 void tmplput_FILE_NAME(StrBuf * Target, WCTemplputParams * TP) {
42 FileListStruct *F = (FileListStruct *) CTX(CTX_FILELIST);
43 StrBufAppendTemplate(Target, TP, F->Filename, 0);
45 void tmplput_FILE_SIZE(StrBuf * Target, WCTemplputParams * TP) {
46 FileListStruct *F = (FileListStruct *) CTX(CTX_FILELIST);
47 StrBufAppendPrintf(Target, "%ld", F->FileSize);
49 void tmplput_FILEMIMETYPE(StrBuf * Target, WCTemplputParams * TP) {
50 FileListStruct *F = (FileListStruct *) CTX(CTX_FILELIST);
51 StrBufAppendTemplate(Target, TP, F->MimeType, 0);
53 void tmplput_FILE_COMMENT(StrBuf * Target, WCTemplputParams * TP) {
54 FileListStruct *F = (FileListStruct *) CTX(CTX_FILELIST);
55 StrBufAppendTemplate(Target, TP, F->Comment, 0);
58 /* -------------------------------------------------------------------------------- */
60 int Conditional_FILE_ISPIC(StrBuf * Target, WCTemplputParams * TP) {
61 FileListStruct *F = (FileListStruct *) CTX(CTX_FILELIST);
65 /* -------------------------------------------------------------------------------- */
66 int CompareFilelistByMime(const void *vFile1, const void *vFile2) {
67 FileListStruct *File1 = (FileListStruct *) GetSearchPayload(vFile1);
68 FileListStruct *File2 = (FileListStruct *) GetSearchPayload(vFile2);
70 if (File1->IsPic != File2->IsPic)
71 return File1->IsPic > File2->IsPic;
72 return strcasecmp(ChrPtr(File1->MimeType), ChrPtr(File2->MimeType));
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));
81 int GroupchangeFilelistByMime(const void *vFile1, const void *vFile2) {
82 FileListStruct *File1 = (FileListStruct *) vFile1;
83 FileListStruct *File2 = (FileListStruct *) vFile2;
85 if (File1->IsPic != File2->IsPic)
86 return File1->IsPic > File2->IsPic;
87 return strcasecmp(ChrPtr(File1->MimeType), ChrPtr(File2->MimeType)) != 0;
91 int CompareFilelistByName(const void *vFile1, const void *vFile2) {
92 FileListStruct *File1 = (FileListStruct *) GetSearchPayload(vFile1);
93 FileListStruct *File2 = (FileListStruct *) GetSearchPayload(vFile2);
95 if (File1->IsPic != File2->IsPic)
96 return File1->IsPic > File2->IsPic;
97 return strcasecmp(ChrPtr(File1->Filename), ChrPtr(File2->Filename));
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));
106 int GroupchangeFilelistByName(const void *vFile1, const void *vFile2) {
107 FileListStruct *File1 = (FileListStruct *) vFile1;
108 FileListStruct *File2 = (FileListStruct *) vFile2;
110 return ChrPtr(File1->Filename)[0] != ChrPtr(File2->Filename)[0];
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)
119 return (File1->FileSize > File2->FileSize);
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)
126 return (File1->FileSize < File2->FileSize);
128 int GroupchangeFilelistBySize(const void *vFile1, const void *vFile2) {
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));
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));
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];
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);
155 int GroupchangeFilelistBySequence(const void *vFile1, const void *vFile2) {
159 /* -------------------------------------------------------------------------------- */
160 HashList *LoadFileList(StrBuf * Target, WCTemplputParams * TP) {
161 FileListStruct *Entry;
169 WCTemplputParams SubTP;
171 memset(&SubTP, 0, sizeof(WCTemplputParams));
173 serv_getln(buf, sizeof buf);
178 Files = NewHash(1, NULL);
179 while (!Done && (StrBuf_ServGetln(Buf) >= 0)) {
180 if ((StrLength(Buf) == 3) && !strcmp(ChrPtr(Buf), "000")) {
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));
190 Entry->Sequence = sequence++;
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, '|');
199 Entry->IsPic = (strstr(ChrPtr(Entry->MimeType), "image") != NULL);
203 Put(Files, SKEY(Entry->Filename), Entry, FreeFiles);
206 putbstr("__HAVE_PIC", NewStrBufPlain(HKEY("1")));
207 SubTP.Filter.ContextType = CTX_FILELIST;
208 SortIt = RetrieveSort(&SubTP, NULL, 0, HKEY("fileunsorted"), 0);
210 SortByPayload(Files, SortIt);
212 SortByPayload(Files, CompareFilelistBySequence);
217 void display_mime_icon(void) {
219 const char *FileName;
223 MimeType = xbstr("type", &tlen);
224 FileName = GetIconFilename(MimeType, tlen);
226 if (FileName == NULL)
227 snprintf(FileBuf, SIZ, "%s%s", static_dirs[0], "/webcit_icons/essen/16x16/file.png");
229 snprintf(FileBuf, SIZ, "%s%s", static_dirs[3], FileName);
230 output_static(FileBuf);
233 void download_file(void) {
237 StrBuf *ContentType = NewStrBufPlain(HKEY("application/octet-stream"));
239 /* Setting to nonzero forces a MIME type of application/octet-stream */
240 int force_download = 1;
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, '|');
252 CheckGZipCompressionAllowed(SKEY(ContentType));
254 FlushStrBuf(ContentType);
256 serv_read_binary_to_http(ContentType, bytes, 0, 0);
258 StrBuf_ServGetln(Buf);
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));
268 FreeStrBuf(&ContentType);
274 void delete_file(void) {
275 const StrBuf *MimeType;
279 safestrncpy(buf, bstr("file"), sizeof buf);
281 serv_printf("DELF %s", buf);
283 StrBuf_ServGetln(Line);
284 GetServerStatusMsg(Line, NULL, 1, 0);
286 MimeType = DoTemplate(HKEY("files"), NULL, &NoCtx);
287 http_transmit_thing(ChrPtr(MimeType), 0);
293 void upload_file(void) {
294 const StrBuf *RetMimeType;
295 const char *MimeType;
297 long bytes_transmitted = 0;
300 wcsession *WCC = WC; /* stack this for faster access (WC is a function) */
302 MimeType = GuessMimeType(ChrPtr(WCC->upload), WCC->upload_length);
304 Desc = sbstr("description");
306 serv_printf("UOPN %s|%s|%s", ChrPtr(WCC->upload_filename), MimeType, ChrPtr(Desc));
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);
316 while (bytes_transmitted < WCC->upload_length) {
318 if (blocksize > (WCC->upload_length - bytes_transmitted)) {
319 blocksize = (WCC->upload_length - bytes_transmitted);
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;
333 StrBuf_ServGetln(Line);
334 GetServerStatusMsg(Line, NULL, 1, 0);
335 RetMimeType = DoTemplate(HKEY("files"), NULL, &NoCtx);
336 http_transmit_thing(ChrPtr(RetMimeType), 0);
343 * When the browser requests an image file from the Citadel server,
344 * this function is called to transmit it.
346 void output_image(void) {
350 const char *MimeType;
353 serv_printf("OIMG %s|%s", bstr("name"), bstr("parm"));
354 StrBuf_ServGetln(Buf);
355 if (GetServerStatus(Buf, NULL) == 2) {
357 StrBufCutLeft(Buf, 4);
358 bytes = StrBufExtract_long(Buf, 0, '|');
360 /** Read it from the server */
362 rc = serv_read_binary(WCC->WBuf, bytes, Buf);
364 StrBuf_ServGetln(Buf);
367 MimeType = GuessMimeType(ChrPtr(WCC->WBuf), StrLength(WCC->WBuf));
369 /** Write it to the browser */
370 if (!IsEmptyStr(MimeType)) {
371 CheckGZipCompressionAllowed(MimeType, strlen(MimeType));
372 http_transmit_thing(MimeType, 0);
377 /* hm... unknown mimetype? fallback to blank gif */
380 syslog(LOG_DEBUG, "OIMG failed: %s", ChrPtr(Buf));
385 * Instead of an ugly 404, send a 1x1 transparent GIF
386 * when there's no such image on the server.
388 StrBufPrintf(Buf, "%s%s", static_dirs[0], "/webcit_icons/blank.gif");
389 output_static(ChrPtr(Buf));
393 void InitModule_DOWNLOAD(void) {
394 RegisterCTX(CTX_FILELIST);
396 RegisterIterator("ROOM:FILES", 0, NULL, LoadFileList, NULL, DeleteHash, CTX_FILELIST, CTX_NONE, IT_FLAG_DETECT_GROUPCHANGE);
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"),
406 CompareFilelistByComment, CompareFilelistByCommentRev, GroupchangeFilelistByComment, CTX_FILELIST);
407 RegisterSortFunc(HKEY("fileunsorted"),
409 CompareFilelistBySequence, CompareFilelistBySequence, GroupchangeFilelistBySequence, CTX_FILELIST);
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);
416 RegisterConditional("COND:FILE:ISPIC", 0, Conditional_FILE_ISPIC, CTX_FILELIST);
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);