4 * Server functions which handle file transfers and room directories.
23 #if TIME_WITH_SYS_TIME
24 # include <sys/time.h>
28 # include <sys/time.h>
37 #include "serv_extensions.h"
40 #include "sysdep_decls.h"
46 #include "citserver.h"
53 * network_talking_to() -- concurrency checker
55 int network_talking_to(char *nodename, int operation) {
57 static char *nttlist = NULL;
63 begin_critical_section(S_NTTLIST);
68 if (nttlist == NULL) nttlist = strdup("");
69 if (nttlist == NULL) break;
70 nttlist = (char *)realloc(nttlist,
71 (strlen(nttlist) + strlen(nodename) + 3) );
73 strcat(nttlist, nodename);
77 if (nttlist == NULL) break;
78 if (strlen(nttlist) == 0) break;
79 ptr = malloc(strlen(nttlist));
80 if (ptr == NULL) break;
82 for (i = 0; i < num_tokens(nttlist, '|'); ++i) {
83 extract_token(buf, nttlist, i, '|', sizeof buf);
84 if ( (strlen(buf) > 0)
85 && (strcasecmp(buf, nodename)) ) {
95 if (nttlist == NULL) break;
96 if (strlen(nttlist) == 0) break;
97 for (i = 0; i < num_tokens(nttlist, '|'); ++i) {
98 extract_token(buf, nttlist, i, '|', sizeof buf);
99 if (!strcasecmp(buf, nodename)) ++retval;
104 if (nttlist != NULL) lprintf(CTDL_DEBUG, "nttlist=<%s>\n", nttlist);
105 end_critical_section(S_NTTLIST);
113 * Server command to delete a file from a room's directory
115 void cmd_delf(char *filename)
120 if (CtdlAccessCheck(ac_room_aide))
123 if ((CC->room.QRflags & QR_DIRECTORY) == 0) {
124 cprintf("%d No directory in this room.\n",
129 if (strlen(filename) == 0) {
130 cprintf("%d You must specify a file name.\n",
131 ERROR + FILE_NOT_FOUND);
134 for (a = 0; a < strlen(filename); ++a) {
135 if (filename[a] == '/') {
139 snprintf(pathname, sizeof pathname,
140 #ifndef HAVE_DATA_DIR
141 "." /* FIXME: should here be CTDLDIR ? */
146 CC->room.QRdirname, filename);
147 a = unlink(pathname);
149 cprintf("%d File '%s' deleted.\n", CIT_OK, pathname);
152 cprintf("%d File '%s' not found.\n",
153 ERROR + FILE_NOT_FOUND, pathname);
161 * move a file from one room directory to another
163 void cmd_movf(char *cmdbuf)
165 char filename[PATH_MAX];
166 char pathname[PATH_MAX];
167 char newpath[PATH_MAX];
168 char newroom[ROOMNAMELEN];
171 struct ctdlroom qrbuf;
173 extract_token(filename, cmdbuf, 0, '|', sizeof filename);
174 extract_token(newroom, cmdbuf, 1, '|', sizeof newroom);
176 if (CtdlAccessCheck(ac_room_aide)) return;
178 if ((CC->room.QRflags & QR_DIRECTORY) == 0) {
179 cprintf("%d No directory in this room.\n",
184 if (strlen(filename) == 0) {
185 cprintf("%d You must specify a file name.\n",
186 ERROR + FILE_NOT_FOUND);
190 for (a = 0; a < strlen(filename); ++a) {
191 if (filename[a] == '/') {
195 snprintf(pathname, sizeof pathname, "./files/%s/%s",
196 CC->room.QRdirname, filename);
197 if (access(pathname, 0) != 0) {
198 cprintf("%d File '%s' not found.\n",
199 ERROR + FILE_NOT_FOUND, pathname);
203 if (getroom(&qrbuf, newroom) != 0) {
204 cprintf("%d '%s' does not exist.\n", ERROR + ROOM_NOT_FOUND, newroom);
207 if ((qrbuf.QRflags & QR_DIRECTORY) == 0) {
208 cprintf("%d '%s' is not a directory room.\n",
209 ERROR + NOT_HERE, qrbuf.QRname);
212 snprintf(newpath, sizeof newpath, "./files/%s/%s", qrbuf.QRdirname,
214 if (link(pathname, newpath) != 0) {
215 cprintf("%d Couldn't move file: %s\n", ERROR + INTERNAL_ERROR,
221 /* this is a crude method of copying the file description */
222 snprintf(buf, sizeof buf,
223 "cat ./files/%s/filedir |grep %s >>./files/%s/filedir",
224 CC->room.QRdirname, filename, qrbuf.QRdirname);
226 cprintf("%d File '%s' has been moved.\n", CIT_OK, filename);
231 * send a file over the net
233 void cmd_netf(char *cmdbuf)
235 char pathname[256], filename[256], destsys[256], buf[256];
242 extract_token(filename, cmdbuf, 0, '|', sizeof filename);
243 extract_token(destsys, cmdbuf, 1, '|', sizeof destsys);
245 if (CtdlAccessCheck(ac_room_aide)) return;
247 if ((CC->room.QRflags & QR_DIRECTORY) == 0) {
248 cprintf("%d No directory in this room.\n",
253 if (strlen(filename) == 0) {
254 cprintf("%d You must specify a file name.\n",
255 ERROR + FILE_NOT_FOUND);
259 for (a = 0; a < strlen(filename); ++a) {
260 if (filename[a] == '/') {
264 snprintf(pathname, sizeof pathname, "./files/%s/%s",
265 CC->room.QRdirname, filename);
266 if (access(pathname, 0) != 0) {
267 cprintf("%d File '%s' not found.\n",
268 ERROR + FILE_NOT_FOUND, pathname);
271 snprintf(buf, sizeof buf, "sysop@%s", destsys);
273 if (e != MES_IGNET) {
274 cprintf("%d No such system: '%s'\n",
275 ERROR + NO_SUCH_SYSTEM, destsys);
278 snprintf(outfile, sizeof outfile,
279 #ifndef HAVE_SPOOL_DIR
284 "/network/spoolin/nsf.%04lx.%04x",
285 (long)getpid(), ++seq);
286 ofp = fopen(outfile, "a");
288 cprintf("%d internal error\n", ERROR + INTERNAL_ERROR);
293 putc(MES_NORMAL, ofp);
295 fprintf(ofp, "Pcit%ld", CC->user.usernum);
298 fprintf(ofp, "T%ld", (long) now);
300 fprintf(ofp, "A%s", CC->user.fullname);
302 fprintf(ofp, "O%s", CC->room.QRname);
304 fprintf(ofp, "N%s", NODENAME);
306 fprintf(ofp, "D%s", destsys);
308 fprintf(ofp, "SFILE");
313 snprintf(buf, sizeof buf,
315 #ifndef HAVE_DATA_DIR
316 "." /* FIXME: should here be CTDLDIR ? */
320 /* FIXME: detect uuencode while installation? or inline */
321 "/files/%s; uuencode %s <%s 2>/dev/null >>%s",
322 CC->room.QRdirname, filename, filename, outfile);
325 ofp = fopen(outfile, "a");
329 cprintf("%d File '%s' has been sent to %s.\n", CIT_OK, filename,
331 /* FIXME start a network run here. */
336 * This code is common to all commands which open a file for downloading,
337 * regardless of whether it's a file from the directory, an image, a network
338 * spool file, a MIME attachment, etc.
339 * It examines the file and displays the OK result code and some information
340 * about the file. NOTE: this stuff is Unix dependent.
342 void OpenCmdResult(char *filename, char *mime_type)
348 fstat(fileno(CC->download_fp), &statbuf);
349 filesize = (long) statbuf.st_size;
350 modtime = (time_t) statbuf.st_mtime;
352 cprintf("%d %ld|%ld|%s|%s\n",
353 CIT_OK, filesize, (long)modtime, filename, mime_type);
358 * open a file for downloading
360 void cmd_open(char *cmdbuf)
363 char pathname[PATH_MAX];
366 extract_token(filename, cmdbuf, 0, '|', sizeof filename);
368 if (CtdlAccessCheck(ac_logged_in)) return;
370 if ((CC->room.QRflags & QR_DIRECTORY) == 0) {
371 cprintf("%d No directory in this room.\n",
376 if (strlen(filename) == 0) {
377 cprintf("%d You must specify a file name.\n",
378 ERROR + FILE_NOT_FOUND);
382 if (CC->download_fp != NULL) {
383 cprintf("%d You already have a download file open.\n",
384 ERROR + RESOURCE_BUSY);
388 for (a = 0; a < strlen(filename); ++a) {
389 if (filename[a] == '/') {
394 snprintf(pathname, sizeof pathname,
395 #ifndef HAVE_DATA_DIR
396 "." /* FIXME: should here be CTDLDIR ? */
400 "/files/%s/%s", CC->room.QRdirname, filename);
401 CC->download_fp = fopen(pathname, "r");
403 if (CC->download_fp == NULL) {
404 cprintf("%d cannot open %s: %s\n",
405 ERROR + INTERNAL_ERROR, pathname, strerror(errno));
409 OpenCmdResult(filename, "application/octet-stream");
415 void cmd_oimg(char *cmdbuf)
418 char pathname[PATH_MAX];
419 struct ctdluser usbuf;
420 char which_user[USERNAME_SIZE];
424 extract_token(filename, cmdbuf, 0, '|', sizeof filename);
426 if (strlen(filename) == 0) {
427 cprintf("%d You must specify a file name.\n",
428 ERROR + FILE_NOT_FOUND);
432 if (CC->download_fp != NULL) {
433 cprintf("%d You already have a download file open.\n",
434 ERROR + RESOURCE_BUSY);
438 if (!strcasecmp(filename, "_userpic_")) {
439 extract_token(which_user, cmdbuf, 1, '|', sizeof which_user);
440 if (getuser(&usbuf, which_user) != 0) {
441 cprintf("%d No such user.\n",
442 ERROR + NO_SUCH_USER);
445 snprintf(pathname, sizeof pathname,
446 #ifndef HAVE_DATA_DIR
447 "." /* FIXME: should here be CTDLDIR ? */
453 } else if (!strcasecmp(filename, "_floorpic_")) {
454 which_floor = extract_int(cmdbuf, 1);
455 snprintf(pathname, sizeof pathname,
456 #ifndef HAVE_DATA_DIR
457 "." /* FIXME: should here be CTDLDIR ? */
461 "/images/floor.%d.gif", which_floor);
462 } else if (!strcasecmp(filename, "_roompic_")) {
463 assoc_file_name(pathname, sizeof pathname, &CC->room, "images");
465 for (a = 0; a < strlen(filename); ++a) {
466 filename[a] = tolower(filename[a]);
467 if (filename[a] == '/') {
471 snprintf(pathname, sizeof pathname,
472 #ifndef HAVE_DATA_DIR
473 "." /* FIXME: should here be CTDLDIR ? */
481 CC->download_fp = fopen(pathname, "rb");
482 if (CC->download_fp == NULL) {
483 cprintf("%d Cannot open %s: %s\n",
484 ERROR + FILE_NOT_FOUND, pathname, strerror(errno));
488 OpenCmdResult(pathname, "image/gif");
492 * open a file for uploading
494 void cmd_uopn(char *cmdbuf)
498 extract_token(CC->upl_file, cmdbuf, 0, '|', sizeof CC->upl_file);
499 extract_token(CC->upl_comment, cmdbuf, 1, '|', sizeof CC->upl_comment);
501 if (CtdlAccessCheck(ac_logged_in)) return;
503 if ((CC->room.QRflags & QR_DIRECTORY) == 0) {
504 cprintf("%d No directory in this room.\n",
509 if (strlen(CC->upl_file) == 0) {
510 cprintf("%d You must specify a file name.\n",
511 ERROR + FILE_NOT_FOUND);
515 if (CC->upload_fp != NULL) {
516 cprintf("%d You already have a upload file open.\n",
517 ERROR + RESOURCE_BUSY);
521 for (a = 0; a < strlen(CC->upl_file); ++a) {
522 if (CC->upl_file[a] == '/') {
523 CC->upl_file[a] = '_';
526 snprintf(CC->upl_path, sizeof CC->upl_path,
527 #ifndef HAVE_DATA_DIR
528 "." /* FIXME: should here be CTDLDIR ? */
533 CC->room.QRdirname, CC->upl_file);
534 snprintf(CC->upl_filedir, sizeof CC->upl_filedir,
535 #ifndef HAVE_DATA_DIR
536 "." /* FIXME: should here be CTDLDIR ? */
540 "/files/%s/filedir", CC->room.QRdirname);
542 CC->upload_fp = fopen(CC->upl_path, "r");
543 if (CC->upload_fp != NULL) {
544 fclose(CC->upload_fp);
545 CC->upload_fp = NULL;
546 cprintf("%d '%s' already exists\n",
547 ERROR + ALREADY_EXISTS, CC->upl_path);
551 CC->upload_fp = fopen(CC->upl_path, "wb");
552 if (CC->upload_fp == NULL) {
553 cprintf("%d Cannot open %s: %s\n",
554 ERROR + INTERNAL_ERROR, CC->upl_path, strerror(errno));
557 cprintf("%d Ok\n", CIT_OK);
563 * open an image file for uploading
565 void cmd_uimg(char *cmdbuf)
567 int is_this_for_real;
572 if (num_parms(cmdbuf) < 2) {
573 cprintf("%d Usage error.\n", ERROR + ILLEGAL_VALUE);
577 is_this_for_real = extract_int(cmdbuf, 0);
578 extract_token(basenm, cmdbuf, 1, '|', sizeof basenm);
579 if (CC->upload_fp != NULL) {
580 cprintf("%d You already have an upload file open.\n",
581 ERROR + RESOURCE_BUSY);
585 strcpy(CC->upl_path, "");
587 for (a = 0; a < strlen(basenm); ++a) {
588 basenm[a] = tolower(basenm[a]);
589 if (basenm[a] == '/') {
594 if (CC->user.axlevel >= 6) {
595 snprintf(CC->upl_path, sizeof CC->upl_path,
596 #ifndef HAVE_DATA_DIR
597 "." /* FIXME: should here be CTDLDIR ? */
605 if (!strcasecmp(basenm, "_userpic_")) {
606 snprintf(CC->upl_path, sizeof CC->upl_path,
607 #ifndef HAVE_DATA_DIR
608 "." /* FIXME: should here be CTDLDIR ? */
612 "/userpics/%ld.gif", CC->user.usernum);
615 if ((!strcasecmp(basenm, "_floorpic_"))
616 && (CC->user.axlevel >= 6)) {
617 which_floor = extract_int(cmdbuf, 2);
618 snprintf(CC->upl_path, sizeof CC->upl_path,
619 #ifndef HAVE_DATA_DIR
620 "." /* FIXME: should here be CTDLDIR ? */
624 "/images/floor.%d.gif", which_floor);
627 if ((!strcasecmp(basenm, "_roompic_")) && (is_room_aide())) {
628 assoc_file_name(CC->upl_path, sizeof CC->upl_path, &CC->room, "images");
631 if (strlen(CC->upl_path) == 0) {
632 cprintf("%d Higher access required.\n",
633 ERROR + HIGHER_ACCESS_REQUIRED);
637 if (is_this_for_real == 0) {
638 cprintf("%d Ok to send image\n", CIT_OK);
642 CC->upload_fp = fopen(CC->upl_path, "wb");
643 if (CC->upload_fp == NULL) {
644 cprintf("%d Cannot open %s: %s\n",
645 ERROR + INTERNAL_ERROR, CC->upl_path, strerror(errno));
648 cprintf("%d Ok\n", CIT_OK);
649 CC->upload_type = UPL_IMAGE;
654 * close the download file
660 if (CC->download_fp == NULL) {
661 cprintf("%d You don't have a download file open.\n",
662 ERROR + RESOURCE_NOT_OPEN);
666 fclose(CC->download_fp);
667 CC->download_fp = NULL;
669 if (CC->dl_is_net == 1) {
671 snprintf(buf, sizeof buf,
672 #ifndef HAVE_SPOOL_DIR
677 "/network/spoolout/%s",
682 cprintf("%d Ok\n", CIT_OK);
689 void abort_upl(struct CitContext *who)
691 if (who->upload_fp != NULL) {
692 fclose(who->upload_fp);
693 who->upload_fp = NULL;
694 unlink(CC->upl_path);
701 * close the upload file
703 void cmd_ucls(char *cmd)
706 char upload_notice[512];
708 if (CC->upload_fp == NULL) {
709 cprintf("%d You don't have an upload file open.\n", ERROR + RESOURCE_NOT_OPEN);
713 fclose(CC->upload_fp);
714 CC->upload_fp = NULL;
716 if ((!strcasecmp(cmd, "1")) && (CC->upload_type != UPL_FILE)) {
717 CC->upload_type = UPL_FILE;
718 cprintf("%d Upload completed.\n", CIT_OK);
720 /* FIXME ... here we need to trigger a network run */
725 if (!strcasecmp(cmd, "1")) {
726 cprintf("%d File '%s' saved.\n", CIT_OK, CC->upl_path);
727 fp = fopen(CC->upl_filedir, "a");
729 fp = fopen(CC->upl_filedir, "w");
732 fprintf(fp, "%s %s\n", CC->upl_file,
737 /* put together an upload notice */
738 snprintf(upload_notice, sizeof upload_notice,
739 "NEW UPLOAD: '%s'\n %s\n",
740 CC->upl_file, CC->upl_comment);
741 quickie_message(CC->curr_user, NULL, CC->room.QRname,
742 upload_notice, 0, NULL);
745 cprintf("%d File '%s' aborted.\n", CIT_OK, CC->upl_path);
752 * read from the download file
754 void cmd_read(char *cmdbuf)
761 start_pos = extract_long(cmdbuf, 0);
762 bytes = extract_int(cmdbuf, 1);
764 if (CC->download_fp == NULL) {
765 cprintf("%d You don't have a download file open.\n",
766 ERROR + RESOURCE_NOT_OPEN);
770 if (bytes > 100000) bytes = 100000;
771 buf = malloc(bytes + 1);
773 fseek(CC->download_fp, start_pos, 0);
774 actual_bytes = fread(buf, 1, bytes, CC->download_fp);
775 cprintf("%d %d\n", BINARY_FOLLOWS, (int)actual_bytes);
776 client_write(buf, actual_bytes);
783 * write to the upload file
785 void cmd_writ(char *cmdbuf)
792 bytes = extract_int(cmdbuf, 0);
794 if (CC->upload_fp == NULL) {
795 cprintf("%d You don't have an upload file open.\n", ERROR + RESOURCE_NOT_OPEN);
799 if (bytes > 100000) {
800 cprintf("%d You may not write more than 100000 bytes.\n",
805 cprintf("%d %d\n", SEND_BINARY, bytes);
806 buf = malloc(bytes + 1);
807 client_read(buf, bytes);
808 fwrite(buf, bytes, 1, CC->upload_fp);
816 * cmd_ndop() - open a network spool file for downloading
818 void cmd_ndop(char *cmdbuf)
823 if (strlen(CC->net_node) == 0) {
824 cprintf("%d Not authenticated as a network node.\n",
825 ERROR + NOT_LOGGED_IN);
829 if (CC->download_fp != NULL) {
830 cprintf("%d You already have a download file open.\n",
831 ERROR + RESOURCE_BUSY);
835 snprintf(pathname, sizeof pathname,
836 #ifndef HAVE_SPOOL_DIR
841 "/network/spoolout/%s",
844 /* first open the file in append mode in order to create a
845 * zero-length file if it doesn't already exist
847 CC->download_fp = fopen(pathname, "a");
848 if (CC->download_fp != NULL)
849 fclose(CC->download_fp);
852 CC->download_fp = fopen(pathname, "r");
853 if (CC->download_fp == NULL) {
854 cprintf("%d cannot open %s: %s\n",
855 ERROR + INTERNAL_ERROR, pathname, strerror(errno));
860 /* set this flag so other routines know that the download file
861 * currently open is a network spool file
865 stat(pathname, &statbuf);
866 cprintf("%d %ld\n", CIT_OK, (long)statbuf.st_size);
870 * cmd_nuop() - open a network spool file for uploading
872 void cmd_nuop(char *cmdbuf)
876 if (strlen(CC->net_node) == 0) {
877 cprintf("%d Not authenticated as a network node.\n",
878 ERROR + NOT_LOGGED_IN);
882 if (CC->upload_fp != NULL) {
883 cprintf("%d You already have an upload file open.\n",
884 ERROR + RESOURCE_BUSY);
888 snprintf(CC->upl_path, sizeof CC->upl_path,
889 #ifndef HAVE_SPOOL_DIR
894 "/network/spoolin/%s.%04lx.%04x",
895 CC->net_node, (long)getpid(), ++seq);
897 CC->upload_fp = fopen(CC->upl_path, "r");
898 if (CC->upload_fp != NULL) {
899 fclose(CC->upload_fp);
900 CC->upload_fp = NULL;
901 cprintf("%d '%s' already exists\n",
902 ERROR + ALREADY_EXISTS, CC->upl_path);
906 CC->upload_fp = fopen(CC->upl_path, "w");
907 if (CC->upload_fp == NULL) {
908 cprintf("%d Cannot open %s: %s\n",
909 ERROR + INTERNAL_ERROR, CC->upl_path, strerror(errno));
913 CC->upload_type = UPL_NET;
914 cprintf("%d Ok\n", CIT_OK);