2 * Server functions which handle file transfers and room directories.
17 #if TIME_WITH_SYS_TIME
18 # include <sys/time.h>
22 # include <sys/time.h>
29 #include <libcitadel.h>
34 #include "sysdep_decls.h"
38 #include "citserver.h"
45 #include "ctdl_module.h"
49 * network_talking_to() -- concurrency checker
51 int network_talking_to(char *nodename, int operation) {
53 static char *nttlist = NULL;
59 begin_critical_section(S_NTTLIST);
64 if (nttlist == NULL) nttlist = strdup("");
65 if (nttlist == NULL) break;
66 nttlist = (char *)realloc(nttlist,
67 (strlen(nttlist) + strlen(nodename) + 3) );
69 strcat(nttlist, nodename);
73 if (nttlist == NULL) break;
74 if (IsEmptyStr(nttlist)) break;
75 ptr = malloc(strlen(nttlist));
76 if (ptr == NULL) break;
78 for (i = 0; i < num_tokens(nttlist, '|'); ++i) {
79 extract_token(buf, nttlist, i, '|', sizeof buf);
80 if ( (!IsEmptyStr(buf))
81 && (strcasecmp(buf, nodename)) ) {
91 if (nttlist == NULL) break;
92 if (IsEmptyStr(nttlist)) break;
93 for (i = 0; i < num_tokens(nttlist, '|'); ++i) {
94 extract_token(buf, nttlist, i, '|', sizeof buf);
95 if (!strcasecmp(buf, nodename)) ++retval;
100 if (nttlist != NULL) CtdlLogPrintf(CTDL_DEBUG, "nttlist=<%s>\n", nttlist);
101 end_critical_section(S_NTTLIST);
109 * Server command to delete a file from a room's directory
111 void cmd_delf(char *filename)
116 if (CtdlAccessCheck(ac_room_aide))
119 if ((CC->room.QRflags & QR_DIRECTORY) == 0) {
120 cprintf("%d No directory in this room.\n",
125 if (IsEmptyStr(filename)) {
126 cprintf("%d You must specify a file name.\n",
127 ERROR + FILE_NOT_FOUND);
130 for (a = 0; !IsEmptyStr(&filename[a]); ++a) {
131 if ( (filename[a] == '/') || (filename[a] == '\\') ) {
135 snprintf(pathname, sizeof pathname,
138 CC->room.QRdirname, filename);
139 a = unlink(pathname);
141 cprintf("%d File '%s' deleted.\n", CIT_OK, pathname);
144 cprintf("%d File '%s' not found.\n",
145 ERROR + FILE_NOT_FOUND, pathname);
153 * move a file from one room directory to another
155 void cmd_movf(char *cmdbuf)
157 char filename[PATH_MAX];
158 char pathname[PATH_MAX];
159 char newpath[PATH_MAX];
160 char newroom[ROOMNAMELEN];
163 struct ctdlroom qrbuf;
166 extract_token(filename, cmdbuf, 0, '|', sizeof filename);
167 extract_token(newroom, cmdbuf, 1, '|', sizeof newroom);
169 if (CtdlAccessCheck(ac_room_aide)) return;
171 if ((CC->room.QRflags & QR_DIRECTORY) == 0) {
172 cprintf("%d No directory in this room.\n",
177 if (IsEmptyStr(filename)) {
178 cprintf("%d You must specify a file name.\n",
179 ERROR + FILE_NOT_FOUND);
183 for (a = 0; !IsEmptyStr(&filename[a]); ++a) {
184 if ( (filename[a] == '/') || (filename[a] == '\\') ) {
188 snprintf(pathname, sizeof pathname, "./files/%s/%s",
189 CC->room.QRdirname, filename);
190 if (access(pathname, 0) != 0) {
191 cprintf("%d File '%s' not found.\n",
192 ERROR + FILE_NOT_FOUND, pathname);
196 if (CtdlGetRoom(&qrbuf, newroom) != 0) {
197 cprintf("%d '%s' does not exist.\n", ERROR + ROOM_NOT_FOUND, newroom);
200 if ((qrbuf.QRflags & QR_DIRECTORY) == 0) {
201 cprintf("%d '%s' is not a directory room.\n",
202 ERROR + NOT_HERE, qrbuf.QRname);
205 snprintf(newpath, sizeof newpath, "./files/%s/%s", qrbuf.QRdirname,
207 if (link(pathname, newpath) != 0) {
208 cprintf("%d Couldn't move file: %s\n", ERROR + INTERNAL_ERROR,
214 /* this is a crude method of copying the file description */
215 snprintf(buf, sizeof buf,
216 "cat ./files/%s/filedir |grep \"%s\" >>./files/%s/filedir",
217 CC->room.QRdirname, filename, qrbuf.QRdirname);
219 cprintf("%d File '%s' has been moved.\n", CIT_OK, filename);
224 * This code is common to all commands which open a file for downloading,
225 * regardless of whether it's a file from the directory, an image, a network
226 * spool file, a MIME attachment, etc.
227 * It examines the file and displays the OK result code and some information
228 * about the file. NOTE: this stuff is Unix dependent.
230 void OpenCmdResult(char *filename, const char *mime_type)
236 fstat(fileno(CC->download_fp), &statbuf);
237 CC->download_fp_total = statbuf.st_size;
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];
312 extract_token(filename, cmdbuf, 0, '|', sizeof filename);
314 if (IsEmptyStr(filename)) {
315 cprintf("%d You must specify a file name.\n",
316 ERROR + FILE_NOT_FOUND);
320 if (CC->download_fp != NULL) {
321 cprintf("%d You already have a download file open.\n",
322 ERROR + RESOURCE_BUSY);
326 if (!strcasecmp(filename, "_userpic_")) {
327 extract_token(which_user, cmdbuf, 1, '|', sizeof which_user);
328 if (CtdlGetUser(&usbuf, which_user) != 0) {
329 cprintf("%d No such user.\n",
330 ERROR + NO_SUCH_USER);
333 snprintf(pathname, sizeof pathname,
337 } else if (!strcasecmp(filename, "_floorpic_")) {
338 which_floor = extract_int(cmdbuf, 1);
339 snprintf(pathname, sizeof pathname,
341 ctdl_image_dir, which_floor);
342 } else if (!strcasecmp(filename, "_roompic_")) {
343 assoc_file_name(pathname, sizeof pathname, &CC->room, ctdl_image_dir);
345 for (a = 0; !IsEmptyStr(&filename[a]); ++a) {
346 filename[a] = tolower(filename[a]);
347 if ( (filename[a] == '/') || (filename[a] == '\\') ) {
351 snprintf(pathname, sizeof pathname,
357 CC->download_fp = fopen(pathname, "rb");
358 if (CC->download_fp == NULL) {
359 strcat(pathname, ".gif");
360 CC->download_fp = fopen(pathname, "rb");
362 if (CC->download_fp == NULL) {
363 cprintf("%d Cannot open %s: %s\n",
364 ERROR + FILE_NOT_FOUND, pathname, strerror(errno));
367 rv = fread(&MimeTestBuf[0], 1, 32, CC->download_fp);
368 rewind (CC->download_fp);
369 OpenCmdResult(pathname, GuessMimeType(&MimeTestBuf[0], 32));
373 * open a file for uploading
375 void cmd_uopn(char *cmdbuf)
379 extract_token(CC->upl_file, cmdbuf, 0, '|', sizeof CC->upl_file);
380 extract_token(CC->upl_mimetype, cmdbuf, 1, '|', sizeof CC->upl_mimetype);
381 extract_token(CC->upl_comment, cmdbuf, 2, '|', sizeof CC->upl_comment);
383 if (CtdlAccessCheck(ac_logged_in)) return;
385 if ((CC->room.QRflags & QR_DIRECTORY) == 0) {
386 cprintf("%d No directory in this room.\n",
391 if (IsEmptyStr(CC->upl_file)) {
392 cprintf("%d You must specify a file name.\n",
393 ERROR + FILE_NOT_FOUND);
397 if (CC->upload_fp != NULL) {
398 cprintf("%d You already have a upload file open.\n",
399 ERROR + RESOURCE_BUSY);
403 for (a = 0; !IsEmptyStr(&CC->upl_file[a]); ++a) {
404 if ( (CC->upl_file[a] == '/') || (CC->upl_file[a] == '\\') ) {
405 CC->upl_file[a] = '_';
408 snprintf(CC->upl_path, sizeof CC->upl_path,
411 CC->room.QRdirname, CC->upl_file);
412 snprintf(CC->upl_filedir, sizeof CC->upl_filedir,
417 CC->upload_fp = fopen(CC->upl_path, "r");
418 if (CC->upload_fp != NULL) {
419 fclose(CC->upload_fp);
420 CC->upload_fp = NULL;
421 cprintf("%d '%s' already exists\n",
422 ERROR + ALREADY_EXISTS, CC->upl_path);
426 CC->upload_fp = fopen(CC->upl_path, "wb");
427 if (CC->upload_fp == NULL) {
428 cprintf("%d Cannot open %s: %s\n",
429 ERROR + INTERNAL_ERROR, CC->upl_path, strerror(errno));
432 cprintf("%d Ok\n", CIT_OK);
438 * open an image file for uploading
440 void cmd_uimg(char *cmdbuf)
442 int is_this_for_real;
447 if (num_parms(cmdbuf) < 2) {
448 cprintf("%d Usage error.\n", ERROR + ILLEGAL_VALUE);
452 is_this_for_real = extract_int(cmdbuf, 0);
453 extract_token(CC->upl_mimetype, cmdbuf, 1, '|', sizeof CC->upl_mimetype);
454 extract_token(basenm, cmdbuf, 2, '|', sizeof basenm);
455 if (CC->upload_fp != NULL) {
456 cprintf("%d You already have an upload file open.\n",
457 ERROR + RESOURCE_BUSY);
461 strcpy(CC->upl_path, "");
463 for (a = 0; !IsEmptyStr(&basenm[a]); ++a) {
464 basenm[a] = tolower(basenm[a]);
465 if ( (basenm[a] == '/') || (basenm[a] == '\\') ) {
470 if (CC->user.axlevel >= AxAideU) {
471 snprintf(CC->upl_path, sizeof CC->upl_path,
477 if (!strcasecmp(basenm, "_userpic_")) {
478 snprintf(CC->upl_path, sizeof CC->upl_path,
484 if ((!strcasecmp(basenm, "_floorpic_"))
485 && (CC->user.axlevel >= AxAideU)) {
486 which_floor = extract_int(cmdbuf, 2);
487 snprintf(CC->upl_path, sizeof CC->upl_path,
493 if ((!strcasecmp(basenm, "_roompic_")) && (is_room_aide())) {
494 assoc_file_name(CC->upl_path, sizeof CC->upl_path, &CC->room, ctdl_image_dir);
497 if (IsEmptyStr(CC->upl_path)) {
498 cprintf("%d Higher access required.\n",
499 ERROR + HIGHER_ACCESS_REQUIRED);
503 if (is_this_for_real == 0) {
504 cprintf("%d Ok to send image\n", CIT_OK);
508 CC->upload_fp = fopen(CC->upl_path, "wb");
509 if (CC->upload_fp == NULL) {
510 cprintf("%d Cannot open %s: %s\n",
511 ERROR + INTERNAL_ERROR, CC->upl_path, strerror(errno));
514 cprintf("%d Ok\n", CIT_OK);
515 CC->upload_type = UPL_IMAGE;
520 * close the download file
522 void cmd_clos(char *cmdbuf)
526 if (CC->download_fp == NULL) {
527 cprintf("%d You don't have a download file open.\n",
528 ERROR + RESOURCE_NOT_OPEN);
532 fclose(CC->download_fp);
533 CC->download_fp = NULL;
535 if (CC->dl_is_net == 1) {
537 snprintf(buf, sizeof buf,
544 cprintf("%d Ok\n", CIT_OK);
551 void abort_upl(CitContext *who)
553 if (who->upload_fp != NULL) {
554 fclose(who->upload_fp);
555 who->upload_fp = NULL;
556 unlink(CC->upl_path);
563 * close the upload file
565 void cmd_ucls(char *cmd)
568 char upload_notice[512];
571 if (CC->upload_fp == NULL) {
572 cprintf("%d You don't have an upload file open.\n", ERROR + RESOURCE_NOT_OPEN);
576 fclose(CC->upload_fp);
577 CC->upload_fp = NULL;
579 if ((!strcasecmp(cmd, "1")) && (CC->upload_type != UPL_FILE)) {
580 cprintf("%d Upload completed.\n", CIT_OK);
582 if (CC->upload_type == UPL_NET) {
583 char final_filename[PATH_MAX];
584 snprintf(final_filename, sizeof final_filename,
592 if (link(CC->upl_path, final_filename) == 0) {
593 unlink(CC->upl_path);
596 CtdlLogPrintf(CTDL_ALERT, "Cannot link %d to %d: %s\n",
597 CC->upl_path, final_filename, strerror(errno)
601 /* FIXME ... here we need to trigger a network run */
604 CC->upload_type = UPL_FILE;
608 if (!strcasecmp(cmd, "1")) {
609 cprintf("%d File '%s' saved.\n", CIT_OK, CC->upl_path);
610 fp = fopen(CC->upl_filedir, "a");
612 fp = fopen(CC->upl_filedir, "w");
615 fprintf(fp, "%s %s %s\n", CC->upl_file,
621 /* put together an upload notice */
622 snprintf(upload_notice, sizeof upload_notice,
623 "NEW UPLOAD: '%s'\n %s\n%s\n",
627 quickie_message(CC->curr_user, NULL, NULL, CC->room.QRname,
628 upload_notice, 0, NULL);
631 cprintf("%d File '%s' aborted.\n", CIT_OK, CC->upl_path);
638 * read from the download file
640 void cmd_read(char *cmdbuf)
647 start_pos = extract_long(cmdbuf, 0);
648 bytes = extract_int(cmdbuf, 1);
650 if (CC->download_fp == NULL) {
651 cprintf("%d You don't have a download file open.\n",
652 ERROR + RESOURCE_NOT_OPEN);
657 CC->download_fp_total,
660 fileno(CC->download_fp),
663 actual_bytes = CC->download_fp_total - start_pos;
664 if ((actual_bytes > 0) && (buf != NULL)) {
665 cprintf("%d %d\n", BINARY_FOLLOWS, (int)actual_bytes);
666 client_write(buf + start_pos, actual_bytes);
669 cprintf("%d %s\n", ERROR, strerror(errno));
671 munmap(buf, CC->download_fp_total);
677 * write to the upload file
679 void cmd_writ(char *cmdbuf)
687 bytes = extract_int(cmdbuf, 0);
689 if (CC->upload_fp == NULL) {
690 cprintf("%d You don't have an upload file open.\n", ERROR + RESOURCE_NOT_OPEN);
694 if (bytes > 100000) {
695 cprintf("%d You may not write more than 100000 bytes.\n",
700 cprintf("%d %d\n", SEND_BINARY, bytes);
701 buf = malloc(bytes + 1);
702 client_read(buf, bytes);
703 rv = fwrite(buf, bytes, 1, CC->upload_fp);
711 * cmd_ndop() - open a network spool file for downloading
713 void cmd_ndop(char *cmdbuf)
718 if (IsEmptyStr(CC->net_node)) {
719 cprintf("%d Not authenticated as a network node.\n",
720 ERROR + NOT_LOGGED_IN);
724 if (CC->download_fp != NULL) {
725 cprintf("%d You already have a download file open.\n",
726 ERROR + RESOURCE_BUSY);
730 snprintf(pathname, sizeof pathname,
735 /* first open the file in append mode in order to create a
736 * zero-length file if it doesn't already exist
738 CC->download_fp = fopen(pathname, "a");
739 if (CC->download_fp != NULL)
740 fclose(CC->download_fp);
743 CC->download_fp = fopen(pathname, "r");
744 if (CC->download_fp == NULL) {
745 cprintf("%d cannot open %s: %s\n",
746 ERROR + INTERNAL_ERROR, pathname, strerror(errno));
751 /* set this flag so other routines know that the download file
752 * currently open is a network spool file
756 stat(pathname, &statbuf);
757 CC->download_fp_total = statbuf.st_size;
758 cprintf("%d %ld\n", CIT_OK, (long)statbuf.st_size);
762 * cmd_nuop() - open a network spool file for uploading
764 void cmd_nuop(char *cmdbuf)
768 if (IsEmptyStr(CC->net_node)) {
769 cprintf("%d Not authenticated as a network node.\n",
770 ERROR + NOT_LOGGED_IN);
774 if (CC->upload_fp != NULL) {
775 cprintf("%d You already have an upload file open.\n",
776 ERROR + RESOURCE_BUSY);
780 snprintf(CC->upl_path, sizeof CC->upl_path,
787 CC->upload_fp = fopen(CC->upl_path, "r");
788 if (CC->upload_fp != NULL) {
789 fclose(CC->upload_fp);
790 CC->upload_fp = NULL;
791 cprintf("%d '%s' already exists\n",
792 ERROR + ALREADY_EXISTS, CC->upl_path);
796 CC->upload_fp = fopen(CC->upl_path, "w");
797 if (CC->upload_fp == NULL) {
798 cprintf("%d Cannot open %s: %s\n",
799 ERROR + INTERNAL_ERROR, CC->upl_path, strerror(errno));
803 CC->upload_type = UPL_NET;
804 cprintf("%d Ok\n", CIT_OK);
808 /*****************************************************************************/
809 /* MODULE INITIALIZATION STUFF */
810 /*****************************************************************************/
812 CTDL_MODULE_INIT(file_ops)
815 CtdlRegisterProtoHook(cmd_delf, "DELF", "Delete a file");
816 CtdlRegisterProtoHook(cmd_movf, "MOVF", "Move a file");
817 CtdlRegisterProtoHook(cmd_open, "OPEN", "Open a download file transfer");
818 CtdlRegisterProtoHook(cmd_clos, "CLOS", "Close a download file transfer");
819 CtdlRegisterProtoHook(cmd_uopn, "UOPN", "Open an upload file transfer");
820 CtdlRegisterProtoHook(cmd_ucls, "UCLS", "Close an upload file transfer");
821 CtdlRegisterProtoHook(cmd_read, "READ", "File transfer read operation");
822 CtdlRegisterProtoHook(cmd_writ, "WRIT", "File transfer write operation");
823 CtdlRegisterProtoHook(cmd_ndop, "NDOP", "Open a network spool file for download");
824 CtdlRegisterProtoHook(cmd_nuop, "NUOP", "Open a network spool file for upload");
825 CtdlRegisterProtoHook(cmd_oimg, "OIMG", "Open an image file for download");
826 CtdlRegisterProtoHook(cmd_uimg, "UIMG", "Upload an image file");
828 /* return our Subversion id for the Log */