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;
168 extract_token(filename, cmdbuf, 0, '|', sizeof filename);
169 extract_token(newroom, cmdbuf, 1, '|', sizeof newroom);
171 if (CtdlAccessCheck(ac_room_aide)) return;
173 if ((CC->room.QRflags & QR_DIRECTORY) == 0) {
174 cprintf("%d No directory in this room.\n",
179 if (IsEmptyStr(filename)) {
180 cprintf("%d You must specify a file name.\n",
181 ERROR + FILE_NOT_FOUND);
185 for (a = 0; !IsEmptyStr(&filename[a]); ++a) {
186 if ( (filename[a] == '/') || (filename[a] == '\\') ) {
190 snprintf(pathname, sizeof pathname, "./files/%s/%s",
191 CC->room.QRdirname, filename);
192 if (access(pathname, 0) != 0) {
193 cprintf("%d File '%s' not found.\n",
194 ERROR + FILE_NOT_FOUND, pathname);
198 if (CtdlGetRoom(&qrbuf, newroom) != 0) {
199 cprintf("%d '%s' does not exist.\n", ERROR + ROOM_NOT_FOUND, newroom);
202 if ((qrbuf.QRflags & QR_DIRECTORY) == 0) {
203 cprintf("%d '%s' is not a directory room.\n",
204 ERROR + NOT_HERE, qrbuf.QRname);
207 snprintf(newpath, sizeof newpath, "./files/%s/%s", qrbuf.QRdirname,
209 if (link(pathname, newpath) != 0) {
210 cprintf("%d Couldn't move file: %s\n", ERROR + INTERNAL_ERROR,
216 /* this is a crude method of copying the file description */
217 snprintf(buf, sizeof buf,
218 "cat ./files/%s/filedir |grep \"%s\" >>./files/%s/filedir",
219 CC->room.QRdirname, filename, qrbuf.QRdirname);
221 cprintf("%d File '%s' has been moved.\n", CIT_OK, filename);
226 * This code is common to all commands which open a file for downloading,
227 * regardless of whether it's a file from the directory, an image, a network
228 * spool file, a MIME attachment, etc.
229 * It examines the file and displays the OK result code and some information
230 * about the file. NOTE: this stuff is Unix dependent.
232 void OpenCmdResult(char *filename, const char *mime_type)
238 fstat(fileno(CC->download_fp), &statbuf);
239 filesize = (long) statbuf.st_size;
240 modtime = (time_t) statbuf.st_mtime;
242 cprintf("%d %ld|%ld|%s|%s\n",
243 CIT_OK, filesize, (long)modtime, filename, mime_type);
248 * open a file for downloading
250 void cmd_open(char *cmdbuf)
253 char pathname[PATH_MAX];
256 extract_token(filename, cmdbuf, 0, '|', sizeof filename);
258 if (CtdlAccessCheck(ac_logged_in)) return;
260 if ((CC->room.QRflags & QR_DIRECTORY) == 0) {
261 cprintf("%d No directory in this room.\n",
266 if (IsEmptyStr(filename)) {
267 cprintf("%d You must specify a file name.\n",
268 ERROR + FILE_NOT_FOUND);
272 if (CC->download_fp != NULL) {
273 cprintf("%d You already have a download file open.\n",
274 ERROR + RESOURCE_BUSY);
278 for (a = 0; !IsEmptyStr(&filename[a]); ++a) {
279 if ( (filename[a] == '/') || (filename[a] == '\\') ) {
284 snprintf(pathname, sizeof pathname,
287 CC->room.QRdirname, filename);
288 CC->download_fp = fopen(pathname, "r");
290 if (CC->download_fp == NULL) {
291 cprintf("%d cannot open %s: %s\n",
292 ERROR + INTERNAL_ERROR, pathname, strerror(errno));
296 OpenCmdResult(filename, "application/octet-stream");
302 void cmd_oimg(char *cmdbuf)
305 char pathname[PATH_MAX];
306 char MimeTestBuf[32];
307 struct ctdluser usbuf;
308 char which_user[USERNAME_SIZE];
313 extract_token(filename, cmdbuf, 0, '|', sizeof filename);
315 if (IsEmptyStr(filename)) {
316 cprintf("%d You must specify a file name.\n",
317 ERROR + FILE_NOT_FOUND);
321 if (CC->download_fp != NULL) {
322 cprintf("%d You already have a download file open.\n",
323 ERROR + RESOURCE_BUSY);
327 if (!strcasecmp(filename, "_userpic_")) {
328 extract_token(which_user, cmdbuf, 1, '|', sizeof which_user);
329 if (CtdlGetUser(&usbuf, which_user) != 0) {
330 cprintf("%d No such user.\n",
331 ERROR + NO_SUCH_USER);
334 snprintf(pathname, sizeof pathname,
338 } else if (!strcasecmp(filename, "_floorpic_")) {
339 which_floor = extract_int(cmdbuf, 1);
340 snprintf(pathname, sizeof pathname,
342 ctdl_image_dir, which_floor);
343 } else if (!strcasecmp(filename, "_roompic_")) {
344 assoc_file_name(pathname, sizeof pathname, &CC->room, ctdl_image_dir);
346 for (a = 0; !IsEmptyStr(&filename[a]); ++a) {
347 filename[a] = tolower(filename[a]);
348 if ( (filename[a] == '/') || (filename[a] == '\\') ) {
352 snprintf(pathname, sizeof pathname,
358 CC->download_fp = fopen(pathname, "rb");
359 if (CC->download_fp == NULL) {
360 strcat(pathname, ".gif");
361 CC->download_fp = fopen(pathname, "rb");
363 if (CC->download_fp == NULL) {
364 cprintf("%d Cannot open %s: %s\n",
365 ERROR + FILE_NOT_FOUND, pathname, strerror(errno));
368 rv = fread(&MimeTestBuf[0], 1, 32, CC->download_fp);
369 rewind (CC->download_fp);
370 OpenCmdResult(pathname, GuessMimeType(&MimeTestBuf[0], 32));
374 * open a file for uploading
376 void cmd_uopn(char *cmdbuf)
380 extract_token(CC->upl_file, cmdbuf, 0, '|', sizeof CC->upl_file);
381 extract_token(CC->upl_mimetype, cmdbuf, 1, '|', sizeof CC->upl_mimetype);
382 extract_token(CC->upl_comment, cmdbuf, 2, '|', sizeof CC->upl_comment);
384 if (CtdlAccessCheck(ac_logged_in)) return;
386 if ((CC->room.QRflags & QR_DIRECTORY) == 0) {
387 cprintf("%d No directory in this room.\n",
392 if (IsEmptyStr(CC->upl_file)) {
393 cprintf("%d You must specify a file name.\n",
394 ERROR + FILE_NOT_FOUND);
398 if (CC->upload_fp != NULL) {
399 cprintf("%d You already have a upload file open.\n",
400 ERROR + RESOURCE_BUSY);
404 for (a = 0; !IsEmptyStr(&CC->upl_file[a]); ++a) {
405 if ( (CC->upl_file[a] == '/') || (CC->upl_file[a] == '\\') ) {
406 CC->upl_file[a] = '_';
409 snprintf(CC->upl_path, sizeof CC->upl_path,
412 CC->room.QRdirname, CC->upl_file);
413 snprintf(CC->upl_filedir, sizeof CC->upl_filedir,
418 CC->upload_fp = fopen(CC->upl_path, "r");
419 if (CC->upload_fp != NULL) {
420 fclose(CC->upload_fp);
421 CC->upload_fp = NULL;
422 cprintf("%d '%s' already exists\n",
423 ERROR + ALREADY_EXISTS, CC->upl_path);
427 CC->upload_fp = fopen(CC->upl_path, "wb");
428 if (CC->upload_fp == NULL) {
429 cprintf("%d Cannot open %s: %s\n",
430 ERROR + INTERNAL_ERROR, CC->upl_path, strerror(errno));
433 cprintf("%d Ok\n", CIT_OK);
439 * open an image file for uploading
441 void cmd_uimg(char *cmdbuf)
443 int is_this_for_real;
448 if (num_parms(cmdbuf) < 2) {
449 cprintf("%d Usage error.\n", ERROR + ILLEGAL_VALUE);
453 is_this_for_real = extract_int(cmdbuf, 0);
454 extract_token(CC->upl_mimetype, cmdbuf, 1, '|', sizeof CC->upl_mimetype);
455 extract_token(basenm, cmdbuf, 2, '|', sizeof basenm);
456 if (CC->upload_fp != NULL) {
457 cprintf("%d You already have an upload file open.\n",
458 ERROR + RESOURCE_BUSY);
462 strcpy(CC->upl_path, "");
464 for (a = 0; !IsEmptyStr(&basenm[a]); ++a) {
465 basenm[a] = tolower(basenm[a]);
466 if ( (basenm[a] == '/') || (basenm[a] == '\\') ) {
471 if (CC->user.axlevel >= 6) {
472 snprintf(CC->upl_path, sizeof CC->upl_path,
478 if (!strcasecmp(basenm, "_userpic_")) {
479 snprintf(CC->upl_path, sizeof CC->upl_path,
485 if ((!strcasecmp(basenm, "_floorpic_"))
486 && (CC->user.axlevel >= 6)) {
487 which_floor = extract_int(cmdbuf, 2);
488 snprintf(CC->upl_path, sizeof CC->upl_path,
494 if ((!strcasecmp(basenm, "_roompic_")) && (is_room_aide())) {
495 assoc_file_name(CC->upl_path, sizeof CC->upl_path, &CC->room, ctdl_image_dir);
498 if (IsEmptyStr(CC->upl_path)) {
499 cprintf("%d Higher access required.\n",
500 ERROR + HIGHER_ACCESS_REQUIRED);
504 if (is_this_for_real == 0) {
505 cprintf("%d Ok to send image\n", CIT_OK);
509 CC->upload_fp = fopen(CC->upl_path, "wb");
510 if (CC->upload_fp == NULL) {
511 cprintf("%d Cannot open %s: %s\n",
512 ERROR + INTERNAL_ERROR, CC->upl_path, strerror(errno));
515 cprintf("%d Ok\n", CIT_OK);
516 CC->upload_type = UPL_IMAGE;
521 * close the download file
523 void cmd_clos(char *cmdbuf)
527 if (CC->download_fp == NULL) {
528 cprintf("%d You don't have a download file open.\n",
529 ERROR + RESOURCE_NOT_OPEN);
533 fclose(CC->download_fp);
534 CC->download_fp = NULL;
536 if (CC->dl_is_net == 1) {
538 snprintf(buf, sizeof buf,
545 cprintf("%d Ok\n", CIT_OK);
552 void abort_upl(CitContext *who)
554 if (who->upload_fp != NULL) {
555 fclose(who->upload_fp);
556 who->upload_fp = NULL;
557 unlink(CC->upl_path);
564 * close the upload file
566 void cmd_ucls(char *cmd)
569 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 CC->upload_type = UPL_FILE;
581 cprintf("%d Upload completed.\n", CIT_OK);
583 /* FIXME ... here we need to trigger a network run */
588 if (!strcasecmp(cmd, "1")) {
589 cprintf("%d File '%s' saved.\n", CIT_OK, CC->upl_path);
590 fp = fopen(CC->upl_filedir, "a");
592 fp = fopen(CC->upl_filedir, "w");
595 fprintf(fp, "%s %s %s\n", CC->upl_file,
601 /* put together an upload notice */
602 snprintf(upload_notice, sizeof upload_notice,
603 "NEW UPLOAD: '%s'\n %s\n%s\n",
607 quickie_message(CC->curr_user, NULL, NULL, CC->room.QRname,
608 upload_notice, 0, NULL);
611 cprintf("%d File '%s' aborted.\n", CIT_OK, CC->upl_path);
618 * read from the download file
620 void cmd_read(char *cmdbuf)
627 start_pos = extract_long(cmdbuf, 0);
628 bytes = extract_int(cmdbuf, 1);
630 if (CC->download_fp == NULL) {
631 cprintf("%d You don't have a download file open.\n",
632 ERROR + RESOURCE_NOT_OPEN);
636 if (bytes > 100000) bytes = 100000;
637 buf = malloc(bytes + 1);
639 fseek(CC->download_fp, start_pos, 0);
640 actual_bytes = fread(buf, 1, bytes, CC->download_fp);
641 cprintf("%d %d\n", BINARY_FOLLOWS, (int)actual_bytes);
642 client_write(buf, actual_bytes);
649 * write to the upload file
651 void cmd_writ(char *cmdbuf)
659 bytes = extract_int(cmdbuf, 0);
661 if (CC->upload_fp == NULL) {
662 cprintf("%d You don't have an upload file open.\n", ERROR + RESOURCE_NOT_OPEN);
666 if (bytes > 100000) {
667 cprintf("%d You may not write more than 100000 bytes.\n",
672 cprintf("%d %d\n", SEND_BINARY, bytes);
673 buf = malloc(bytes + 1);
674 client_read(buf, bytes);
675 rv = fwrite(buf, bytes, 1, CC->upload_fp);
683 * cmd_ndop() - open a network spool file for downloading
685 void cmd_ndop(char *cmdbuf)
690 if (IsEmptyStr(CC->net_node)) {
691 cprintf("%d Not authenticated as a network node.\n",
692 ERROR + NOT_LOGGED_IN);
696 if (CC->download_fp != NULL) {
697 cprintf("%d You already have a download file open.\n",
698 ERROR + RESOURCE_BUSY);
702 snprintf(pathname, sizeof pathname,
707 /* first open the file in append mode in order to create a
708 * zero-length file if it doesn't already exist
710 CC->download_fp = fopen(pathname, "a");
711 if (CC->download_fp != NULL)
712 fclose(CC->download_fp);
715 CC->download_fp = fopen(pathname, "r");
716 if (CC->download_fp == NULL) {
717 cprintf("%d cannot open %s: %s\n",
718 ERROR + INTERNAL_ERROR, pathname, strerror(errno));
723 /* set this flag so other routines know that the download file
724 * currently open is a network spool file
728 stat(pathname, &statbuf);
729 cprintf("%d %ld\n", CIT_OK, (long)statbuf.st_size);
733 * cmd_nuop() - open a network spool file for uploading
735 void cmd_nuop(char *cmdbuf)
739 if (IsEmptyStr(CC->net_node)) {
740 cprintf("%d Not authenticated as a network node.\n",
741 ERROR + NOT_LOGGED_IN);
745 if (CC->upload_fp != NULL) {
746 cprintf("%d You already have an upload file open.\n",
747 ERROR + RESOURCE_BUSY);
751 snprintf(CC->upl_path, sizeof CC->upl_path,
758 CC->upload_fp = fopen(CC->upl_path, "r");
759 if (CC->upload_fp != NULL) {
760 fclose(CC->upload_fp);
761 CC->upload_fp = NULL;
762 cprintf("%d '%s' already exists\n",
763 ERROR + ALREADY_EXISTS, CC->upl_path);
767 CC->upload_fp = fopen(CC->upl_path, "w");
768 if (CC->upload_fp == NULL) {
769 cprintf("%d Cannot open %s: %s\n",
770 ERROR + INTERNAL_ERROR, CC->upl_path, strerror(errno));
774 CC->upload_type = UPL_NET;
775 cprintf("%d Ok\n", CIT_OK);
779 /*****************************************************************************/
780 /* MODULE INITIALIZATION STUFF */
781 /*****************************************************************************/
783 CTDL_MODULE_INIT(file_ops)
786 CtdlRegisterProtoHook(cmd_delf, "DELF", "Autoconverted. TODO: document me.");
787 CtdlRegisterProtoHook(cmd_movf, "MOVF", "Autoconverted. TODO: document me.");
788 CtdlRegisterProtoHook(cmd_open, "OPEN", "Autoconverted. TODO: document me.");
789 CtdlRegisterProtoHook(cmd_clos, "CLOS", "Autoconverted. TODO: document me.");
790 CtdlRegisterProtoHook(cmd_uopn, "UOPN", "Autoconverted. TODO: document me.");
791 CtdlRegisterProtoHook(cmd_ucls, "UCLS", "Autoconverted. TODO: document me.");
792 CtdlRegisterProtoHook(cmd_read, "READ", "Autoconverted. TODO: document me.");
793 CtdlRegisterProtoHook(cmd_writ, "WRIT", "Autoconverted. TODO: document me.");
794 CtdlRegisterProtoHook(cmd_oimg, "OIMG", "Autoconverted. TODO: document me.");
795 CtdlRegisterProtoHook(cmd_ndop, "NDOP", "Autoconverted. TODO: document me.");
796 CtdlRegisterProtoHook(cmd_nuop, "NUOP", "Autoconverted. TODO: document me.");
797 CtdlRegisterProtoHook(cmd_uimg, "UIMG", "Autoconverted. TODO: document me.");
799 /* return our Subversion id for the Log */