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>
33 #include "serv_extensions.h"
36 #include "sysdep_decls.h"
42 #include "citserver.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 (strlen(nttlist) == 0) 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 ( (strlen(buf) > 0)
81 && (strcasecmp(buf, nodename)) ) {
91 if (nttlist == NULL) break;
92 if (strlen(nttlist) == 0) 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) lprintf(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 (strlen(filename) == 0) {
126 cprintf("%d You must specify a file name.\n",
127 ERROR + FILE_NOT_FOUND);
130 for (a = 0; a < strlen(filename); ++a) {
131 if (filename[a] == '/') {
135 snprintf(pathname, sizeof pathname,
136 #ifndef HAVE_DATA_DIR
142 CC->room.QRdirname, filename);
143 a = unlink(pathname);
145 cprintf("%d File '%s' deleted.\n", CIT_OK, pathname);
148 cprintf("%d File '%s' not found.\n",
149 ERROR + FILE_NOT_FOUND, pathname);
157 * move a file from one room directory to another
159 void cmd_movf(char *cmdbuf)
161 char filename[PATH_MAX];
162 char pathname[PATH_MAX];
163 char newpath[PATH_MAX];
164 char newroom[ROOMNAMELEN];
167 struct ctdlroom qrbuf;
169 extract_token(filename, cmdbuf, 0, '|', sizeof filename);
170 extract_token(newroom, cmdbuf, 1, '|', sizeof newroom);
172 if (CtdlAccessCheck(ac_room_aide)) return;
174 if ((CC->room.QRflags & QR_DIRECTORY) == 0) {
175 cprintf("%d No directory in this room.\n",
180 if (strlen(filename) == 0) {
181 cprintf("%d You must specify a file name.\n",
182 ERROR + FILE_NOT_FOUND);
186 for (a = 0; a < strlen(filename); ++a) {
187 if (filename[a] == '/') {
191 snprintf(pathname, sizeof pathname, "./files/%s/%s",
192 CC->room.QRdirname, filename);
193 if (access(pathname, 0) != 0) {
194 cprintf("%d File '%s' not found.\n",
195 ERROR + FILE_NOT_FOUND, pathname);
199 if (getroom(&qrbuf, newroom) != 0) {
200 cprintf("%d '%s' does not exist.\n", ERROR + ROOM_NOT_FOUND, newroom);
203 if ((qrbuf.QRflags & QR_DIRECTORY) == 0) {
204 cprintf("%d '%s' is not a directory room.\n",
205 ERROR + NOT_HERE, qrbuf.QRname);
208 snprintf(newpath, sizeof newpath, "./files/%s/%s", qrbuf.QRdirname,
210 if (link(pathname, newpath) != 0) {
211 cprintf("%d Couldn't move file: %s\n", ERROR + INTERNAL_ERROR,
217 /* this is a crude method of copying the file description */
218 snprintf(buf, sizeof buf,
219 "cat ./files/%s/filedir |grep %s >>./files/%s/filedir",
220 CC->room.QRdirname, filename, qrbuf.QRdirname);
222 cprintf("%d File '%s' has been moved.\n", CIT_OK, filename);
227 * send a file over the net
229 void cmd_netf(char *cmdbuf)
231 char pathname[256], filename[256], destsys[256], buf[256];
238 extract_token(filename, cmdbuf, 0, '|', sizeof filename);
239 extract_token(destsys, cmdbuf, 1, '|', sizeof destsys);
241 if (CtdlAccessCheck(ac_room_aide)) return;
243 if ((CC->room.QRflags & QR_DIRECTORY) == 0) {
244 cprintf("%d No directory in this room.\n",
249 if (strlen(filename) == 0) {
250 cprintf("%d You must specify a file name.\n",
251 ERROR + FILE_NOT_FOUND);
255 for (a = 0; a < strlen(filename); ++a) {
256 if (filename[a] == '/') {
260 snprintf(pathname, sizeof pathname, "./files/%s/%s",
261 CC->room.QRdirname, filename);
262 if (access(pathname, 0) != 0) {
263 cprintf("%d File '%s' not found.\n",
264 ERROR + FILE_NOT_FOUND, pathname);
267 snprintf(buf, sizeof buf, "sysop@%s", destsys);
269 if (e != MES_IGNET) {
270 cprintf("%d No such system: '%s'\n",
271 ERROR + NO_SUCH_SYSTEM, destsys);
274 snprintf(outfile, sizeof outfile,
275 #ifndef HAVE_SPOOL_DIR
280 "/network/spoolin/nsf.%04lx.%04x",
281 (long)getpid(), ++seq);
282 ofp = fopen(outfile, "a");
284 cprintf("%d internal error\n", ERROR + INTERNAL_ERROR);
289 putc(MES_NORMAL, ofp);
291 fprintf(ofp, "Pcit%ld", CC->user.usernum);
294 fprintf(ofp, "T%ld", (long) now);
296 fprintf(ofp, "A%s", CC->user.fullname);
298 fprintf(ofp, "O%s", CC->room.QRname);
300 fprintf(ofp, "N%s", NODENAME);
302 fprintf(ofp, "D%s", destsys);
304 fprintf(ofp, "SFILE");
309 snprintf(buf, sizeof buf,
311 #ifndef HAVE_DATA_DIR
316 /* FIXME: detect uuencode while installation? or inline */
317 "/files/%s; uuencode %s <%s 2>/dev/null >>%s",
318 CC->room.QRdirname, filename, filename, outfile);
321 ofp = fopen(outfile, "a");
325 cprintf("%d File '%s' has been sent to %s.\n", CIT_OK, filename,
327 /* FIXME start a network run here. */
332 * This code is common to all commands which open a file for downloading,
333 * regardless of whether it's a file from the directory, an image, a network
334 * spool file, a MIME attachment, etc.
335 * It examines the file and displays the OK result code and some information
336 * about the file. NOTE: this stuff is Unix dependent.
338 void OpenCmdResult(char *filename, char *mime_type)
344 fstat(fileno(CC->download_fp), &statbuf);
345 filesize = (long) statbuf.st_size;
346 modtime = (time_t) statbuf.st_mtime;
348 cprintf("%d %ld|%ld|%s|%s\n",
349 CIT_OK, filesize, (long)modtime, filename, mime_type);
354 * open a file for downloading
356 void cmd_open(char *cmdbuf)
359 char pathname[PATH_MAX];
362 extract_token(filename, cmdbuf, 0, '|', sizeof filename);
364 if (CtdlAccessCheck(ac_logged_in)) return;
366 if ((CC->room.QRflags & QR_DIRECTORY) == 0) {
367 cprintf("%d No directory in this room.\n",
372 if (strlen(filename) == 0) {
373 cprintf("%d You must specify a file name.\n",
374 ERROR + FILE_NOT_FOUND);
378 if (CC->download_fp != NULL) {
379 cprintf("%d You already have a download file open.\n",
380 ERROR + RESOURCE_BUSY);
384 for (a = 0; a < strlen(filename); ++a) {
385 if (filename[a] == '/') {
390 snprintf(pathname, sizeof pathname,
391 #ifndef HAVE_DATA_DIR
396 "/files/%s/%s", CC->room.QRdirname, filename);
397 CC->download_fp = fopen(pathname, "r");
399 if (CC->download_fp == NULL) {
400 cprintf("%d cannot open %s: %s\n",
401 ERROR + INTERNAL_ERROR, pathname, strerror(errno));
405 OpenCmdResult(filename, "application/octet-stream");
411 void cmd_oimg(char *cmdbuf)
414 char pathname[PATH_MAX];
415 struct ctdluser usbuf;
416 char which_user[USERNAME_SIZE];
420 extract_token(filename, cmdbuf, 0, '|', sizeof filename);
422 if (strlen(filename) == 0) {
423 cprintf("%d You must specify a file name.\n",
424 ERROR + FILE_NOT_FOUND);
428 if (CC->download_fp != NULL) {
429 cprintf("%d You already have a download file open.\n",
430 ERROR + RESOURCE_BUSY);
434 if (!strcasecmp(filename, "_userpic_")) {
435 extract_token(which_user, cmdbuf, 1, '|', sizeof which_user);
436 if (getuser(&usbuf, which_user) != 0) {
437 cprintf("%d No such user.\n",
438 ERROR + NO_SUCH_USER);
441 snprintf(pathname, sizeof pathname,
442 #ifndef HAVE_DATA_DIR
449 } else if (!strcasecmp(filename, "_floorpic_")) {
450 which_floor = extract_int(cmdbuf, 1);
451 snprintf(pathname, sizeof pathname,
452 #ifndef HAVE_DATA_DIR
457 "/images/floor.%d.gif", which_floor);
458 } else if (!strcasecmp(filename, "_roompic_")) {
459 assoc_file_name(pathname, sizeof pathname, &CC->room, "images");
461 for (a = 0; a < strlen(filename); ++a) {
462 filename[a] = tolower(filename[a]);
463 if (filename[a] == '/') {
467 snprintf(pathname, sizeof pathname,
468 #ifndef HAVE_DATA_DIR
477 CC->download_fp = fopen(pathname, "rb");
478 if (CC->download_fp == NULL) {
479 cprintf("%d Cannot open %s: %s\n",
480 ERROR + FILE_NOT_FOUND, pathname, strerror(errno));
484 OpenCmdResult(pathname, "image/gif");
488 * open a file for uploading
490 void cmd_uopn(char *cmdbuf)
494 extract_token(CC->upl_file, cmdbuf, 0, '|', sizeof CC->upl_file);
495 extract_token(CC->upl_comment, cmdbuf, 1, '|', sizeof CC->upl_comment);
497 if (CtdlAccessCheck(ac_logged_in)) return;
499 if ((CC->room.QRflags & QR_DIRECTORY) == 0) {
500 cprintf("%d No directory in this room.\n",
505 if (strlen(CC->upl_file) == 0) {
506 cprintf("%d You must specify a file name.\n",
507 ERROR + FILE_NOT_FOUND);
511 if (CC->upload_fp != NULL) {
512 cprintf("%d You already have a upload file open.\n",
513 ERROR + RESOURCE_BUSY);
517 for (a = 0; a < strlen(CC->upl_file); ++a) {
518 if (CC->upl_file[a] == '/') {
519 CC->upl_file[a] = '_';
522 snprintf(CC->upl_path, sizeof CC->upl_path,
523 #ifndef HAVE_DATA_DIR
529 CC->room.QRdirname, CC->upl_file);
530 snprintf(CC->upl_filedir, sizeof CC->upl_filedir,
531 #ifndef HAVE_DATA_DIR
536 "/files/%s/filedir", CC->room.QRdirname);
538 CC->upload_fp = fopen(CC->upl_path, "r");
539 if (CC->upload_fp != NULL) {
540 fclose(CC->upload_fp);
541 CC->upload_fp = NULL;
542 cprintf("%d '%s' already exists\n",
543 ERROR + ALREADY_EXISTS, CC->upl_path);
547 CC->upload_fp = fopen(CC->upl_path, "wb");
548 if (CC->upload_fp == NULL) {
549 cprintf("%d Cannot open %s: %s\n",
550 ERROR + INTERNAL_ERROR, CC->upl_path, strerror(errno));
553 cprintf("%d Ok\n", CIT_OK);
559 * open an image file for uploading
561 void cmd_uimg(char *cmdbuf)
563 int is_this_for_real;
568 if (num_parms(cmdbuf) < 2) {
569 cprintf("%d Usage error.\n", ERROR + ILLEGAL_VALUE);
573 is_this_for_real = extract_int(cmdbuf, 0);
574 extract_token(basenm, cmdbuf, 1, '|', sizeof basenm);
575 if (CC->upload_fp != NULL) {
576 cprintf("%d You already have an upload file open.\n",
577 ERROR + RESOURCE_BUSY);
581 strcpy(CC->upl_path, "");
583 for (a = 0; a < strlen(basenm); ++a) {
584 basenm[a] = tolower(basenm[a]);
585 if (basenm[a] == '/') {
590 if (CC->user.axlevel >= 6) {
591 snprintf(CC->upl_path, sizeof CC->upl_path,
592 #ifndef HAVE_DATA_DIR
601 if (!strcasecmp(basenm, "_userpic_")) {
602 snprintf(CC->upl_path, sizeof CC->upl_path,
603 #ifndef HAVE_DATA_DIR
608 "/userpics/%ld.gif", CC->user.usernum);
611 if ((!strcasecmp(basenm, "_floorpic_"))
612 && (CC->user.axlevel >= 6)) {
613 which_floor = extract_int(cmdbuf, 2);
614 snprintf(CC->upl_path, sizeof CC->upl_path,
615 #ifndef HAVE_DATA_DIR
620 "/images/floor.%d.gif", which_floor);
623 if ((!strcasecmp(basenm, "_roompic_")) && (is_room_aide())) {
624 assoc_file_name(CC->upl_path, sizeof CC->upl_path, &CC->room, "images");
627 if (strlen(CC->upl_path) == 0) {
628 cprintf("%d Higher access required.\n",
629 ERROR + HIGHER_ACCESS_REQUIRED);
633 if (is_this_for_real == 0) {
634 cprintf("%d Ok to send image\n", CIT_OK);
638 CC->upload_fp = fopen(CC->upl_path, "wb");
639 if (CC->upload_fp == NULL) {
640 cprintf("%d Cannot open %s: %s\n",
641 ERROR + INTERNAL_ERROR, CC->upl_path, strerror(errno));
644 cprintf("%d Ok\n", CIT_OK);
645 CC->upload_type = UPL_IMAGE;
650 * close the download file
656 if (CC->download_fp == NULL) {
657 cprintf("%d You don't have a download file open.\n",
658 ERROR + RESOURCE_NOT_OPEN);
662 fclose(CC->download_fp);
663 CC->download_fp = NULL;
665 if (CC->dl_is_net == 1) {
667 snprintf(buf, sizeof buf,
668 #ifndef HAVE_SPOOL_DIR
673 "/network/spoolout/%s",
678 cprintf("%d Ok\n", CIT_OK);
685 void abort_upl(struct CitContext *who)
687 if (who->upload_fp != NULL) {
688 fclose(who->upload_fp);
689 who->upload_fp = NULL;
690 unlink(CC->upl_path);
697 * close the upload file
699 void cmd_ucls(char *cmd)
702 char upload_notice[512];
704 if (CC->upload_fp == NULL) {
705 cprintf("%d You don't have an upload file open.\n", ERROR + RESOURCE_NOT_OPEN);
709 fclose(CC->upload_fp);
710 CC->upload_fp = NULL;
712 if ((!strcasecmp(cmd, "1")) && (CC->upload_type != UPL_FILE)) {
713 CC->upload_type = UPL_FILE;
714 cprintf("%d Upload completed.\n", CIT_OK);
716 /* FIXME ... here we need to trigger a network run */
721 if (!strcasecmp(cmd, "1")) {
722 cprintf("%d File '%s' saved.\n", CIT_OK, CC->upl_path);
723 fp = fopen(CC->upl_filedir, "a");
725 fp = fopen(CC->upl_filedir, "w");
728 fprintf(fp, "%s %s\n", CC->upl_file,
733 /* put together an upload notice */
734 snprintf(upload_notice, sizeof upload_notice,
735 "NEW UPLOAD: '%s'\n %s\n",
736 CC->upl_file, CC->upl_comment);
737 quickie_message(CC->curr_user, NULL, CC->room.QRname,
738 upload_notice, 0, NULL);
741 cprintf("%d File '%s' aborted.\n", CIT_OK, CC->upl_path);
748 * read from the download file
750 void cmd_read(char *cmdbuf)
757 start_pos = extract_long(cmdbuf, 0);
758 bytes = extract_int(cmdbuf, 1);
760 if (CC->download_fp == NULL) {
761 cprintf("%d You don't have a download file open.\n",
762 ERROR + RESOURCE_NOT_OPEN);
766 if (bytes > 100000) bytes = 100000;
767 buf = malloc(bytes + 1);
769 fseek(CC->download_fp, start_pos, 0);
770 actual_bytes = fread(buf, 1, bytes, CC->download_fp);
771 cprintf("%d %d\n", BINARY_FOLLOWS, (int)actual_bytes);
772 client_write(buf, actual_bytes);
779 * write to the upload file
781 void cmd_writ(char *cmdbuf)
788 bytes = extract_int(cmdbuf, 0);
790 if (CC->upload_fp == NULL) {
791 cprintf("%d You don't have an upload file open.\n", ERROR + RESOURCE_NOT_OPEN);
795 if (bytes > 100000) {
796 cprintf("%d You may not write more than 100000 bytes.\n",
801 cprintf("%d %d\n", SEND_BINARY, bytes);
802 buf = malloc(bytes + 1);
803 client_read(buf, bytes);
804 fwrite(buf, bytes, 1, CC->upload_fp);
812 * cmd_ndop() - open a network spool file for downloading
814 void cmd_ndop(char *cmdbuf)
819 if (strlen(CC->net_node) == 0) {
820 cprintf("%d Not authenticated as a network node.\n",
821 ERROR + NOT_LOGGED_IN);
825 if (CC->download_fp != NULL) {
826 cprintf("%d You already have a download file open.\n",
827 ERROR + RESOURCE_BUSY);
831 snprintf(pathname, sizeof pathname,
832 #ifndef HAVE_SPOOL_DIR
837 "/network/spoolout/%s",
840 /* first open the file in append mode in order to create a
841 * zero-length file if it doesn't already exist
843 CC->download_fp = fopen(pathname, "a");
844 if (CC->download_fp != NULL)
845 fclose(CC->download_fp);
848 CC->download_fp = fopen(pathname, "r");
849 if (CC->download_fp == NULL) {
850 cprintf("%d cannot open %s: %s\n",
851 ERROR + INTERNAL_ERROR, pathname, strerror(errno));
856 /* set this flag so other routines know that the download file
857 * currently open is a network spool file
861 stat(pathname, &statbuf);
862 cprintf("%d %ld\n", CIT_OK, (long)statbuf.st_size);
866 * cmd_nuop() - open a network spool file for uploading
868 void cmd_nuop(char *cmdbuf)
872 if (strlen(CC->net_node) == 0) {
873 cprintf("%d Not authenticated as a network node.\n",
874 ERROR + NOT_LOGGED_IN);
878 if (CC->upload_fp != NULL) {
879 cprintf("%d You already have an upload file open.\n",
880 ERROR + RESOURCE_BUSY);
884 snprintf(CC->upl_path, sizeof CC->upl_path,
885 #ifndef HAVE_SPOOL_DIR
890 "/network/spoolin/%s.%04lx.%04x",
891 CC->net_node, (long)getpid(), ++seq);
893 CC->upload_fp = fopen(CC->upl_path, "r");
894 if (CC->upload_fp != NULL) {
895 fclose(CC->upload_fp);
896 CC->upload_fp = NULL;
897 cprintf("%d '%s' already exists\n",
898 ERROR + ALREADY_EXISTS, CC->upl_path);
902 CC->upload_fp = fopen(CC->upl_path, "w");
903 if (CC->upload_fp == NULL) {
904 cprintf("%d Cannot open %s: %s\n",
905 ERROR + INTERNAL_ERROR, CC->upl_path, strerror(errno));
909 CC->upload_type = UPL_NET;
910 cprintf("%d Ok\n", CIT_OK);