4 * Server functions which handle file transfers and room directories.
19 #if TIME_WITH_SYS_TIME
20 # include <sys/time.h>
24 # include <sys/time.h>
31 #include <libcitadel.h>
36 #include "sysdep_decls.h"
41 #include "citserver.h"
48 #include "ctdl_module.h"
51 * network_talking_to() -- concurrency checker
53 int network_talking_to(char *nodename, int operation) {
55 static char *nttlist = NULL;
61 begin_critical_section(S_NTTLIST);
66 if (nttlist == NULL) nttlist = strdup("");
67 if (nttlist == NULL) break;
68 nttlist = (char *)realloc(nttlist,
69 (strlen(nttlist) + strlen(nodename) + 3) );
71 strcat(nttlist, nodename);
75 if (nttlist == NULL) break;
76 if (IsEmptyStr(nttlist)) break;
77 ptr = malloc(strlen(nttlist));
78 if (ptr == NULL) break;
80 for (i = 0; i < num_tokens(nttlist, '|'); ++i) {
81 extract_token(buf, nttlist, i, '|', sizeof buf);
82 if ( (!IsEmptyStr(buf))
83 && (strcasecmp(buf, nodename)) ) {
93 if (nttlist == NULL) break;
94 if (IsEmptyStr(nttlist)) break;
95 for (i = 0; i < num_tokens(nttlist, '|'); ++i) {
96 extract_token(buf, nttlist, i, '|', sizeof buf);
97 if (!strcasecmp(buf, nodename)) ++retval;
102 if (nttlist != NULL) CtdlLogPrintf(CTDL_DEBUG, "nttlist=<%s>\n", nttlist);
103 end_critical_section(S_NTTLIST);
111 * Server command to delete a file from a room's directory
113 void cmd_delf(char *filename)
118 if (CtdlAccessCheck(ac_room_aide))
121 if ((CC->room.QRflags & QR_DIRECTORY) == 0) {
122 cprintf("%d No directory in this room.\n",
127 if (IsEmptyStr(filename)) {
128 cprintf("%d You must specify a file name.\n",
129 ERROR + FILE_NOT_FOUND);
132 for (a = 0; !IsEmptyStr(&filename[a]); ++a) {
133 if ( (filename[a] == '/') || (filename[a] == '\\') ) {
137 snprintf(pathname, sizeof pathname,
140 CC->room.QRdirname, filename);
141 a = unlink(pathname);
143 cprintf("%d File '%s' deleted.\n", CIT_OK, pathname);
146 cprintf("%d File '%s' not found.\n",
147 ERROR + FILE_NOT_FOUND, pathname);
155 * move a file from one room directory to another
157 void cmd_movf(char *cmdbuf)
159 char filename[PATH_MAX];
160 char pathname[PATH_MAX];
161 char newpath[PATH_MAX];
162 char newroom[ROOMNAMELEN];
165 struct ctdlroom qrbuf;
167 extract_token(filename, cmdbuf, 0, '|', sizeof filename);
168 extract_token(newroom, cmdbuf, 1, '|', sizeof newroom);
170 if (CtdlAccessCheck(ac_room_aide)) return;
172 if ((CC->room.QRflags & QR_DIRECTORY) == 0) {
173 cprintf("%d No directory in this room.\n",
178 if (IsEmptyStr(filename)) {
179 cprintf("%d You must specify a file name.\n",
180 ERROR + FILE_NOT_FOUND);
184 for (a = 0; !IsEmptyStr(&filename[a]); ++a) {
185 if ( (filename[a] == '/') || (filename[a] == '\\') ) {
189 snprintf(pathname, sizeof pathname, "./files/%s/%s",
190 CC->room.QRdirname, filename);
191 if (access(pathname, 0) != 0) {
192 cprintf("%d File '%s' not found.\n",
193 ERROR + FILE_NOT_FOUND, pathname);
197 if (getroom(&qrbuf, newroom) != 0) {
198 cprintf("%d '%s' does not exist.\n", ERROR + ROOM_NOT_FOUND, newroom);
201 if ((qrbuf.QRflags & QR_DIRECTORY) == 0) {
202 cprintf("%d '%s' is not a directory room.\n",
203 ERROR + NOT_HERE, qrbuf.QRname);
206 snprintf(newpath, sizeof newpath, "./files/%s/%s", qrbuf.QRdirname,
208 if (link(pathname, newpath) != 0) {
209 cprintf("%d Couldn't move file: %s\n", ERROR + INTERNAL_ERROR,
215 /* this is a crude method of copying the file description */
216 snprintf(buf, sizeof buf,
217 "cat ./files/%s/filedir |grep \"%s\" >>./files/%s/filedir",
218 CC->room.QRdirname, filename, qrbuf.QRdirname);
220 cprintf("%d File '%s' has been moved.\n", CIT_OK, filename);
225 * This code is common to all commands which open a file for downloading,
226 * regardless of whether it's a file from the directory, an image, a network
227 * spool file, a MIME attachment, etc.
228 * It examines the file and displays the OK result code and some information
229 * about the file. NOTE: this stuff is Unix dependent.
231 void OpenCmdResult(char *filename, const char *mime_type)
237 fstat(fileno(CC->download_fp), &statbuf);
238 filesize = (long) statbuf.st_size;
239 modtime = (time_t) statbuf.st_mtime;
241 cprintf("%d %ld|%ld|%s|%s\n",
242 CIT_OK, filesize, (long)modtime, filename, mime_type);
247 * open a file for downloading
249 void cmd_open(char *cmdbuf)
252 char pathname[PATH_MAX];
255 extract_token(filename, cmdbuf, 0, '|', sizeof filename);
257 if (CtdlAccessCheck(ac_logged_in)) return;
259 if ((CC->room.QRflags & QR_DIRECTORY) == 0) {
260 cprintf("%d No directory in this room.\n",
265 if (IsEmptyStr(filename)) {
266 cprintf("%d You must specify a file name.\n",
267 ERROR + FILE_NOT_FOUND);
271 if (CC->download_fp != NULL) {
272 cprintf("%d You already have a download file open.\n",
273 ERROR + RESOURCE_BUSY);
277 for (a = 0; !IsEmptyStr(&filename[a]); ++a) {
278 if ( (filename[a] == '/') || (filename[a] == '\\') ) {
283 snprintf(pathname, sizeof pathname,
286 CC->room.QRdirname, filename);
287 CC->download_fp = fopen(pathname, "r");
289 if (CC->download_fp == NULL) {
290 cprintf("%d cannot open %s: %s\n",
291 ERROR + INTERNAL_ERROR, pathname, strerror(errno));
295 OpenCmdResult(filename, "application/octet-stream");
301 void cmd_oimg(char *cmdbuf)
304 char pathname[PATH_MAX];
305 char MimeTestBuf[32];
306 struct ctdluser usbuf;
307 char which_user[USERNAME_SIZE];
311 extract_token(filename, cmdbuf, 0, '|', sizeof filename);
313 if (IsEmptyStr(filename)) {
314 cprintf("%d You must specify a file name.\n",
315 ERROR + FILE_NOT_FOUND);
319 if (CC->download_fp != NULL) {
320 cprintf("%d You already have a download file open.\n",
321 ERROR + RESOURCE_BUSY);
325 if (!strcasecmp(filename, "_userpic_")) {
326 extract_token(which_user, cmdbuf, 1, '|', sizeof which_user);
327 if (getuser(&usbuf, which_user) != 0) {
328 cprintf("%d No such user.\n",
329 ERROR + NO_SUCH_USER);
332 snprintf(pathname, sizeof pathname,
336 } else if (!strcasecmp(filename, "_floorpic_")) {
337 which_floor = extract_int(cmdbuf, 1);
338 snprintf(pathname, sizeof pathname,
340 ctdl_image_dir, which_floor);
341 } else if (!strcasecmp(filename, "_roompic_")) {
342 assoc_file_name(pathname, sizeof pathname, &CC->room, ctdl_image_dir);
344 for (a = 0; !IsEmptyStr(&filename[a]); ++a) {
345 filename[a] = tolower(filename[a]);
346 if ( (filename[a] == '/') || (filename[a] == '\\') ) {
350 snprintf(pathname, sizeof pathname,
356 CC->download_fp = fopen(pathname, "rb");
357 if (CC->download_fp == NULL) {
358 strcat(pathname, ".gif");
359 CC->download_fp = fopen(pathname, "rb");
361 if (CC->download_fp == NULL) {
362 cprintf("%d Cannot open %s: %s\n",
363 ERROR + FILE_NOT_FOUND, pathname, strerror(errno));
366 fread(&MimeTestBuf[0], 1, 32, CC->download_fp);
367 rewind (CC->download_fp);
368 OpenCmdResult(pathname, GuessMimeType(&MimeTestBuf[0], 32));
372 * open a file for uploading
374 void cmd_uopn(char *cmdbuf)
378 extract_token(CC->upl_file, cmdbuf, 0, '|', sizeof CC->upl_file);
379 extract_token(CC->upl_mimetype, cmdbuf, 1, '|', sizeof CC->upl_mimetype);
380 extract_token(CC->upl_comment, cmdbuf, 2, '|', sizeof CC->upl_comment);
382 if (CtdlAccessCheck(ac_logged_in)) return;
384 if ((CC->room.QRflags & QR_DIRECTORY) == 0) {
385 cprintf("%d No directory in this room.\n",
390 if (IsEmptyStr(CC->upl_file)) {
391 cprintf("%d You must specify a file name.\n",
392 ERROR + FILE_NOT_FOUND);
396 if (CC->upload_fp != NULL) {
397 cprintf("%d You already have a upload file open.\n",
398 ERROR + RESOURCE_BUSY);
402 for (a = 0; !IsEmptyStr(&CC->upl_file[a]); ++a) {
403 if ( (CC->upl_file[a] == '/') || (CC->upl_file[a] == '\\') ) {
404 CC->upl_file[a] = '_';
407 snprintf(CC->upl_path, sizeof CC->upl_path,
410 CC->room.QRdirname, CC->upl_file);
411 snprintf(CC->upl_filedir, sizeof CC->upl_filedir,
416 CC->upload_fp = fopen(CC->upl_path, "r");
417 if (CC->upload_fp != NULL) {
418 fclose(CC->upload_fp);
419 CC->upload_fp = NULL;
420 cprintf("%d '%s' already exists\n",
421 ERROR + ALREADY_EXISTS, CC->upl_path);
425 CC->upload_fp = fopen(CC->upl_path, "wb");
426 if (CC->upload_fp == NULL) {
427 cprintf("%d Cannot open %s: %s\n",
428 ERROR + INTERNAL_ERROR, CC->upl_path, strerror(errno));
431 cprintf("%d Ok\n", CIT_OK);
437 * open an image file for uploading
439 void cmd_uimg(char *cmdbuf)
441 int is_this_for_real;
446 if (num_parms(cmdbuf) < 2) {
447 cprintf("%d Usage error.\n", ERROR + ILLEGAL_VALUE);
451 is_this_for_real = extract_int(cmdbuf, 0);
452 extract_token(CC->upl_mimetype, cmdbuf, 1, '|', sizeof CC->upl_mimetype);
453 extract_token(basenm, cmdbuf, 2, '|', sizeof basenm);
454 if (CC->upload_fp != NULL) {
455 cprintf("%d You already have an upload file open.\n",
456 ERROR + RESOURCE_BUSY);
460 strcpy(CC->upl_path, "");
462 for (a = 0; !IsEmptyStr(&basenm[a]); ++a) {
463 basenm[a] = tolower(basenm[a]);
464 if ( (basenm[a] == '/') || (basenm[a] == '\\') ) {
469 if (CC->user.axlevel >= 6) {
470 snprintf(CC->upl_path, sizeof CC->upl_path,
476 if (!strcasecmp(basenm, "_userpic_")) {
477 snprintf(CC->upl_path, sizeof CC->upl_path,
483 if ((!strcasecmp(basenm, "_floorpic_"))
484 && (CC->user.axlevel >= 6)) {
485 which_floor = extract_int(cmdbuf, 2);
486 snprintf(CC->upl_path, sizeof CC->upl_path,
492 if ((!strcasecmp(basenm, "_roompic_")) && (is_room_aide())) {
493 assoc_file_name(CC->upl_path, sizeof CC->upl_path, &CC->room, ctdl_image_dir);
496 if (IsEmptyStr(CC->upl_path)) {
497 cprintf("%d Higher access required.\n",
498 ERROR + HIGHER_ACCESS_REQUIRED);
502 if (is_this_for_real == 0) {
503 cprintf("%d Ok to send image\n", CIT_OK);
507 CC->upload_fp = fopen(CC->upl_path, "wb");
508 if (CC->upload_fp == NULL) {
509 cprintf("%d Cannot open %s: %s\n",
510 ERROR + INTERNAL_ERROR, CC->upl_path, strerror(errno));
513 cprintf("%d Ok\n", CIT_OK);
514 CC->upload_type = UPL_IMAGE;
519 * close the download file
521 void cmd_clos(char *cmdbuf)
525 if (CC->download_fp == NULL) {
526 cprintf("%d You don't have a download file open.\n",
527 ERROR + RESOURCE_NOT_OPEN);
531 fclose(CC->download_fp);
532 CC->download_fp = NULL;
534 if (CC->dl_is_net == 1) {
536 snprintf(buf, sizeof buf,
543 cprintf("%d Ok\n", CIT_OK);
550 void abort_upl(struct CitContext *who)
552 if (who->upload_fp != NULL) {
553 fclose(who->upload_fp);
554 who->upload_fp = NULL;
555 unlink(CC->upl_path);
562 * close the upload file
564 void cmd_ucls(char *cmd)
567 char upload_notice[512];
569 if (CC->upload_fp == NULL) {
570 cprintf("%d You don't have an upload file open.\n", ERROR + RESOURCE_NOT_OPEN);
574 fclose(CC->upload_fp);
575 CC->upload_fp = NULL;
577 if ((!strcasecmp(cmd, "1")) && (CC->upload_type != UPL_FILE)) {
578 CC->upload_type = UPL_FILE;
579 cprintf("%d Upload completed.\n", CIT_OK);
581 /* FIXME ... here we need to trigger a network run */
586 if (!strcasecmp(cmd, "1")) {
587 cprintf("%d File '%s' saved.\n", CIT_OK, CC->upl_path);
588 fp = fopen(CC->upl_filedir, "a");
590 fp = fopen(CC->upl_filedir, "w");
593 fprintf(fp, "%s %s %s\n", CC->upl_file,
599 /* put together an upload notice */
600 snprintf(upload_notice, sizeof upload_notice,
601 "NEW UPLOAD: '%s'\n %s\n%s\n",
605 quickie_message(CC->curr_user, NULL, NULL, CC->room.QRname,
606 upload_notice, 0, NULL);
609 cprintf("%d File '%s' aborted.\n", CIT_OK, CC->upl_path);
616 * read from the download file
618 void cmd_read(char *cmdbuf)
625 start_pos = extract_long(cmdbuf, 0);
626 bytes = extract_int(cmdbuf, 1);
628 if (CC->download_fp == NULL) {
629 cprintf("%d You don't have a download file open.\n",
630 ERROR + RESOURCE_NOT_OPEN);
634 if (bytes > 100000) bytes = 100000;
635 buf = malloc(bytes + 1);
637 fseek(CC->download_fp, start_pos, 0);
638 actual_bytes = fread(buf, 1, bytes, CC->download_fp);
639 cprintf("%d %d\n", BINARY_FOLLOWS, (int)actual_bytes);
640 client_write(buf, actual_bytes);
647 * write to the upload file
649 void cmd_writ(char *cmdbuf)
656 bytes = extract_int(cmdbuf, 0);
658 if (CC->upload_fp == NULL) {
659 cprintf("%d You don't have an upload file open.\n", ERROR + RESOURCE_NOT_OPEN);
663 if (bytes > 100000) {
664 cprintf("%d You may not write more than 100000 bytes.\n",
669 cprintf("%d %d\n", SEND_BINARY, bytes);
670 buf = malloc(bytes + 1);
671 client_read(buf, bytes);
672 fwrite(buf, bytes, 1, CC->upload_fp);
680 * cmd_ndop() - open a network spool file for downloading
682 void cmd_ndop(char *cmdbuf)
687 if (IsEmptyStr(CC->net_node)) {
688 cprintf("%d Not authenticated as a network node.\n",
689 ERROR + NOT_LOGGED_IN);
693 if (CC->download_fp != NULL) {
694 cprintf("%d You already have a download file open.\n",
695 ERROR + RESOURCE_BUSY);
699 snprintf(pathname, sizeof pathname,
704 /* first open the file in append mode in order to create a
705 * zero-length file if it doesn't already exist
707 CC->download_fp = fopen(pathname, "a");
708 if (CC->download_fp != NULL)
709 fclose(CC->download_fp);
712 CC->download_fp = fopen(pathname, "r");
713 if (CC->download_fp == NULL) {
714 cprintf("%d cannot open %s: %s\n",
715 ERROR + INTERNAL_ERROR, pathname, strerror(errno));
720 /* set this flag so other routines know that the download file
721 * currently open is a network spool file
725 stat(pathname, &statbuf);
726 cprintf("%d %ld\n", CIT_OK, (long)statbuf.st_size);
730 * cmd_nuop() - open a network spool file for uploading
732 void cmd_nuop(char *cmdbuf)
736 if (IsEmptyStr(CC->net_node)) {
737 cprintf("%d Not authenticated as a network node.\n",
738 ERROR + NOT_LOGGED_IN);
742 if (CC->upload_fp != NULL) {
743 cprintf("%d You already have an upload file open.\n",
744 ERROR + RESOURCE_BUSY);
748 snprintf(CC->upl_path, sizeof CC->upl_path,
755 CC->upload_fp = fopen(CC->upl_path, "r");
756 if (CC->upload_fp != NULL) {
757 fclose(CC->upload_fp);
758 CC->upload_fp = NULL;
759 cprintf("%d '%s' already exists\n",
760 ERROR + ALREADY_EXISTS, CC->upl_path);
764 CC->upload_fp = fopen(CC->upl_path, "w");
765 if (CC->upload_fp == NULL) {
766 cprintf("%d Cannot open %s: %s\n",
767 ERROR + INTERNAL_ERROR, CC->upl_path, strerror(errno));
771 CC->upload_type = UPL_NET;
772 cprintf("%d Ok\n", CIT_OK);
776 /*****************************************************************************/
777 /* MODULE INITIALIZATION STUFF */
778 /*****************************************************************************/
780 CTDL_MODULE_INIT(file_ops)
782 CtdlRegisterProtoHook(cmd_delf, "DELF", "Autoconverted. TODO: document me.");
783 CtdlRegisterProtoHook(cmd_movf, "MOVF", "Autoconverted. TODO: document me.");
784 CtdlRegisterProtoHook(cmd_open, "OPEN", "Autoconverted. TODO: document me.");
785 CtdlRegisterProtoHook(cmd_clos, "CLOS", "Autoconverted. TODO: document me.");
786 CtdlRegisterProtoHook(cmd_uopn, "UOPN", "Autoconverted. TODO: document me.");
787 CtdlRegisterProtoHook(cmd_ucls, "UCLS", "Autoconverted. TODO: document me.");
788 CtdlRegisterProtoHook(cmd_read, "READ", "Autoconverted. TODO: document me.");
789 CtdlRegisterProtoHook(cmd_writ, "WRIT", "Autoconverted. TODO: document me.");
790 CtdlRegisterProtoHook(cmd_oimg, "OIMG", "Autoconverted. TODO: document me.");
791 CtdlRegisterProtoHook(cmd_ndop, "NDOP", "Autoconverted. TODO: document me.");
792 CtdlRegisterProtoHook(cmd_nuop, "NUOP", "Autoconverted. TODO: document me.");
793 CtdlRegisterProtoHook(cmd_uimg, "UIMG", "Autoconverted. TODO: document me.");
794 /* return our Subversion id for the Log */