* Inbound network authentication working. Fixed a bug in the split-horizon
[citadel.git] / citadel / file_ops.c
1 /* 
2  * $Id$
3  *
4  * Server functions which handle file transfers and room directories.
5  *
6  */
7
8 #ifdef DLL_EXPORT
9 #define IN_LIBCIT
10 #endif
11
12 #include "sysdep.h"
13 #include <stdlib.h>
14 #include <unistd.h>
15 #include <stdio.h>
16 #include <fcntl.h>
17 #include <sys/stat.h>
18 #include <errno.h>
19 #include <string.h>
20 #include <sys/stat.h>
21
22 #if TIME_WITH_SYS_TIME
23 # include <sys/time.h>
24 # include <time.h>
25 #else
26 # if HAVE_SYS_TIME_H
27 #  include <sys/time.h>
28 # else
29 #  include <time.h>
30 # endif
31 #endif
32
33 #include <limits.h>
34 #include "citadel.h"
35 #include "server.h"
36 #include "dynloader.h"
37 #include "config.h"
38 #include "file_ops.h"
39 #include "sysdep_decls.h"
40 #include "user_ops.h"
41 #include "support.h"
42 #include "room_ops.h"
43 #include "msgbase.h"
44 #include "tools.h"
45 #include "citserver.h"
46
47
48 /*
49  * Server command to delete a file from a room's directory
50  */
51 void cmd_delf(char *filename)
52 {
53         char pathname[64];
54         int a;
55
56         if (CtdlAccessCheck(ac_room_aide))
57                 return;
58
59         if ((CC->quickroom.QRflags & QR_DIRECTORY) == 0) {
60                 cprintf("%d No directory in this room.\n",
61                         ERROR + NOT_HERE);
62                 return;
63         }
64
65         if (strlen(filename) == 0) {
66                 cprintf("%d You must specify a file name.\n",
67                         ERROR + FILE_NOT_FOUND);
68                 return;
69         }
70         for (a = 0; a < strlen(filename); ++a) {
71                 if (filename[a] == '/') {
72                         filename[a] = '_';
73                 }
74         }
75         snprintf(pathname, sizeof pathname, "./files/%s/%s",
76                  CC->quickroom.QRdirname, filename);
77         a = unlink(pathname);
78         if (a == 0) {
79                 cprintf("%d File '%s' deleted.\n", OK, pathname);
80         }
81         else {
82                 cprintf("%d File '%s' not found.\n",
83                         ERROR + FILE_NOT_FOUND, pathname);
84         }
85 }
86
87
88
89
90 /*
91  * move a file from one room directory to another
92  */
93 void cmd_movf(char *cmdbuf)
94 {
95         char filename[SIZ];
96         char pathname[SIZ];
97         char newpath[SIZ];
98         char newroom[SIZ];
99         char buf[SIZ];
100         int a;
101         struct quickroom qrbuf;
102
103         extract(filename, cmdbuf, 0);
104         extract(newroom, cmdbuf, 1);
105
106         if (CtdlAccessCheck(ac_room_aide)) return;
107
108         if ((CC->quickroom.QRflags & QR_DIRECTORY) == 0) {
109                 cprintf("%d No directory in this room.\n",
110                         ERROR + NOT_HERE);
111                 return;
112         }
113
114         if (strlen(filename) == 0) {
115                 cprintf("%d You must specify a file name.\n",
116                         ERROR + FILE_NOT_FOUND);
117                 return;
118         }
119
120         for (a = 0; a < strlen(filename); ++a) {
121                 if (filename[a] == '/') {
122                         filename[a] = '_';
123                 }
124         }
125         snprintf(pathname, sizeof pathname, "./files/%s/%s",
126                  CC->quickroom.QRdirname, filename);
127         if (access(pathname, 0) != 0) {
128                 cprintf("%d File '%s' not found.\n",
129                         ERROR + FILE_NOT_FOUND, pathname);
130                 return;
131         }
132
133         if (getroom(&qrbuf, newroom) != 0) {
134                 cprintf("%d '%s' does not exist.\n", ERROR, newroom);
135                 return;
136         }
137         if ((qrbuf.QRflags & QR_DIRECTORY) == 0) {
138                 cprintf("%d '%s' is not a directory room.\n",
139                         ERROR + NOT_HERE, qrbuf.QRname);
140                 return;
141         }
142         snprintf(newpath, sizeof newpath, "./files/%s/%s", qrbuf.QRdirname,
143                  filename);
144         if (link(pathname, newpath) != 0) {
145                 cprintf("%d Couldn't move file: %s\n", ERROR,
146                         strerror(errno));
147                 return;
148         }
149         unlink(pathname);
150
151         /* this is a crude method of copying the file description */
152         snprintf(buf, sizeof buf,
153                  "cat ./files/%s/filedir |grep %s >>./files/%s/filedir",
154                  CC->quickroom.QRdirname, filename, qrbuf.QRdirname);
155         system(buf);
156         cprintf("%d File '%s' has been moved.\n", OK, filename);
157 }
158
159
160 /*
161  * send a file over the net
162  */
163 void cmd_netf(char *cmdbuf)
164 {
165         char pathname[SIZ], filename[SIZ], destsys[SIZ], buf[SIZ];
166         char outfile[SIZ];
167         int a, e;
168         time_t now;
169         FILE *ofp;
170         static int seq = 1;
171
172         extract(filename, cmdbuf, 0);
173         extract(destsys, cmdbuf, 1);
174
175         if (CtdlAccessCheck(ac_room_aide)) return;
176
177         if ((CC->quickroom.QRflags & QR_DIRECTORY) == 0) {
178                 cprintf("%d No directory in this room.\n",
179                         ERROR + NOT_HERE);
180                 return;
181         }
182
183         if (strlen(filename) == 0) {
184                 cprintf("%d You must specify a file name.\n",
185                         ERROR + FILE_NOT_FOUND);
186                 return;
187         }
188
189         for (a = 0; a < strlen(filename); ++a) {
190                 if (filename[a] == '/') {
191                         filename[a] = '_';
192                 }
193         }
194         snprintf(pathname, sizeof pathname, "./files/%s/%s",
195                  CC->quickroom.QRdirname, filename);
196         if (access(pathname, 0) != 0) {
197                 cprintf("%d File '%s' not found.\n",
198                         ERROR + FILE_NOT_FOUND, pathname);
199                 return;
200         }
201         snprintf(buf, sizeof buf, "sysop@%s", destsys);
202         e = alias(buf);
203         if (e != MES_BINARY) {
204                 cprintf("%d No such system: '%s'\n",
205                         ERROR + NO_SUCH_SYSTEM, destsys);
206                 return;
207         }
208         snprintf(outfile, sizeof outfile,
209                  "%s/network/spoolin/nsf.%04x.%04x",
210                  BBSDIR, getpid(), ++seq);
211         ofp = fopen(outfile, "a");
212         if (ofp == NULL) {
213                 cprintf("%d internal error\n", ERROR);
214                 return;
215         }
216
217         putc(255, ofp);
218         putc(MES_NORMAL, ofp);
219         putc(0, ofp);
220         fprintf(ofp, "Pcit%ld", CC->usersupp.usernum);
221         putc(0, ofp);
222         time(&now);
223         fprintf(ofp, "T%ld", (long) now);
224         putc(0, ofp);
225         fprintf(ofp, "A%s", CC->usersupp.fullname);
226         putc(0, ofp);
227         fprintf(ofp, "O%s", CC->quickroom.QRname);
228         putc(0, ofp);
229         fprintf(ofp, "N%s", NODENAME);
230         putc(0, ofp);
231         fprintf(ofp, "D%s", destsys);
232         putc(0, ofp);
233         fprintf(ofp, "SFILE");
234         putc(0, ofp);
235         putc('M', ofp);
236         fclose(ofp);
237
238         snprintf(buf, sizeof buf,
239                  "cd ./files/%s; uuencode %s <%s 2>/dev/null >>%s",
240                  CC->quickroom.QRdirname, filename, filename, outfile);
241         system(buf);
242
243         ofp = fopen(outfile, "a");
244         putc(0, ofp);
245         fclose(ofp);
246
247         cprintf("%d File '%s' has been sent to %s.\n", OK, filename,
248                 destsys);
249         system("nohup ./netproc -i >/dev/null 2>&1 &");
250         return;
251 }
252
253 /*
254  * This code is common to all commands which open a file for downloading,
255  * regardless of whether it's a file from the directory, an image, a network
256  * spool file, a MIME attachment, etc.
257  * It examines the file and displays the OK result code and some information
258  * about the file.  NOTE: this stuff is Unix dependent.
259  */
260 void OpenCmdResult(char *filename, char *mime_type)
261 {
262         struct stat statbuf;
263         time_t modtime;
264         long filesize;
265
266         fstat(fileno(CC->download_fp), &statbuf);
267         filesize = (long) statbuf.st_size;
268         modtime = (time_t) statbuf.st_mtime;
269
270         cprintf("%d %ld|%ld|%s|%s\n",
271                 OK, filesize, modtime, filename, mime_type);
272 }
273
274
275 /*
276  * open a file for downloading
277  */
278 void cmd_open(char *cmdbuf)
279 {
280         char filename[SIZ];
281         char pathname[SIZ];
282         int a;
283
284         extract(filename, cmdbuf, 0);
285
286         if (CtdlAccessCheck(ac_logged_in)) return;
287
288         if ((CC->quickroom.QRflags & QR_DIRECTORY) == 0) {
289                 cprintf("%d No directory in this room.\n",
290                         ERROR + NOT_HERE);
291                 return;
292         }
293
294         if (strlen(filename) == 0) {
295                 cprintf("%d You must specify a file name.\n",
296                         ERROR + FILE_NOT_FOUND);
297                 return;
298         }
299
300         if (CC->download_fp != NULL) {
301                 cprintf("%d You already have a download file open.\n",
302                         ERROR);
303                 return;
304         }
305
306         for (a = 0; a < strlen(filename); ++a) {
307                 if (filename[a] == '/') {
308                         filename[a] = '_';
309                 }
310         }
311
312         snprintf(pathname, sizeof pathname,
313                  "./files/%s/%s", CC->quickroom.QRdirname, filename);
314         CC->download_fp = fopen(pathname, "r");
315
316         if (CC->download_fp == NULL) {
317                 cprintf("%d cannot open %s: %s\n",
318                         ERROR, pathname, strerror(errno));
319                 return;
320         }
321
322         OpenCmdResult(filename, "application/octet-stream");
323 }
324
325 /*
326  * open an image file
327  */
328 void cmd_oimg(char *cmdbuf)
329 {
330         char filename[SIZ];
331         char pathname[SIZ];
332         struct usersupp usbuf;
333         char which_user[32];
334         int which_floor;
335         int a;
336
337         extract(filename, cmdbuf, 0);
338
339         if (strlen(filename) == 0) {
340                 cprintf("%d You must specify a file name.\n",
341                         ERROR + FILE_NOT_FOUND);
342                 return;
343         }
344
345         if (CC->download_fp != NULL) {
346                 cprintf("%d You already have a download file open.\n",
347                         ERROR);
348                 return;
349         }
350
351         if (!strcasecmp(filename, "_userpic_")) {
352                 extract(which_user, cmdbuf, 1);
353                 if (getuser(&usbuf, which_user) != 0) {
354                         cprintf("%d No such user.\n",
355                                 ERROR + NO_SUCH_USER);
356                         return;
357                 }
358                 snprintf(pathname, sizeof pathname, "./userpics/%ld.gif",
359                          usbuf.usernum);
360         } else if (!strcasecmp(filename, "_floorpic_")) {
361                 which_floor = extract_int(cmdbuf, 1);
362                 snprintf(pathname, sizeof pathname,
363                          "./images/floor.%d.gif", which_floor);
364         } else if (!strcasecmp(filename, "_roompic_")) {
365                 assoc_file_name(pathname, &CC->quickroom, "images");
366         } else {
367                 for (a = 0; a < strlen(filename); ++a) {
368                         filename[a] = tolower(filename[a]);
369                         if (filename[a] == '/') {
370                                 filename[a] = '_';
371                         }
372                 }
373                 snprintf(pathname, sizeof pathname, "./images/%s.gif",
374                          filename);
375         }
376
377         CC->download_fp = fopen(pathname, "rb");
378         if (CC->download_fp == NULL) {
379                 cprintf("%d Cannot open %s: %s\n",
380                         ERROR + FILE_NOT_FOUND, pathname, strerror(errno));
381                 return;
382         }
383
384         OpenCmdResult(pathname, "image/gif");
385 }
386
387 /*
388  * open a file for uploading
389  */
390 void cmd_uopn(char *cmdbuf)
391 {
392         int a;
393
394         extract(CC->upl_file, cmdbuf, 0);
395         extract(CC->upl_comment, cmdbuf, 1);
396
397         if (CtdlAccessCheck(ac_logged_in)) return;
398
399         if ((CC->quickroom.QRflags & QR_DIRECTORY) == 0) {
400                 cprintf("%d No directory in this room.\n",
401                         ERROR + NOT_HERE);
402                 return;
403         }
404
405         if (strlen(CC->upl_file) == 0) {
406                 cprintf("%d You must specify a file name.\n",
407                         ERROR + FILE_NOT_FOUND);
408                 return;
409         }
410
411         if (CC->upload_fp != NULL) {
412                 cprintf("%d You already have a upload file open.\n",
413                         ERROR);
414                 return;
415         }
416
417         for (a = 0; a < strlen(CC->upl_file); ++a) {
418                 if (CC->upl_file[a] == '/') {
419                         CC->upl_file[a] = '_';
420                 }
421         }
422         snprintf(CC->upl_path, sizeof CC->upl_path, "./files/%s/%s",
423                  CC->quickroom.QRdirname, CC->upl_file);
424         snprintf(CC->upl_filedir, sizeof CC->upl_filedir,
425                  "./files/%s/filedir", CC->quickroom.QRdirname);
426
427         CC->upload_fp = fopen(CC->upl_path, "r");
428         if (CC->upload_fp != NULL) {
429                 fclose(CC->upload_fp);
430                 CC->upload_fp = NULL;
431                 cprintf("%d '%s' already exists\n",
432                         ERROR + ALREADY_EXISTS, CC->upl_path);
433                 return;
434         }
435
436         CC->upload_fp = fopen(CC->upl_path, "wb");
437         if (CC->upload_fp == NULL) {
438                 cprintf("%d Cannot open %s: %s\n",
439                         ERROR, CC->upl_path, strerror(errno));
440                 return;
441         }
442         cprintf("%d Ok\n", OK);
443 }
444
445
446
447 /*
448  * open an image file for uploading
449  */
450 void cmd_uimg(char *cmdbuf)
451 {
452         int is_this_for_real;
453         char basenm[SIZ];
454         int which_floor;
455         int a;
456
457         if (num_parms(cmdbuf) < 2) {
458                 cprintf("%d Usage error.\n", ERROR);
459                 return;
460         }
461
462         is_this_for_real = extract_int(cmdbuf, 0);
463         extract(basenm, cmdbuf, 1);
464         if (CC->upload_fp != NULL) {
465                 cprintf("%d You already have an upload file open.\n",
466                         ERROR);
467                 return;
468         }
469
470         strcpy(CC->upl_path, "");
471
472         for (a = 0; a < strlen(basenm); ++a) {
473                 basenm[a] = tolower(basenm[a]);
474                 if (basenm[a] == '/') {
475                         basenm[a] = '_';
476                 }
477         }
478
479         if (CC->usersupp.axlevel >= 6) {
480                 snprintf(CC->upl_path, sizeof CC->upl_path, "./images/%s",
481                          basenm);
482         }
483
484         if (!strcasecmp(basenm, "_userpic_")) {
485                 snprintf(CC->upl_path, sizeof CC->upl_path,
486                          "./userpics/%ld.gif", CC->usersupp.usernum);
487         }
488
489         if ((!strcasecmp(basenm, "_floorpic_"))
490             && (CC->usersupp.axlevel >= 6)) {
491                 which_floor = extract_int(cmdbuf, 2);
492                 snprintf(CC->upl_path, sizeof CC->upl_path,
493                          "./images/floor.%d.gif", which_floor);
494         }
495
496         if ((!strcasecmp(basenm, "_roompic_")) && (is_room_aide())) {
497                 assoc_file_name(CC->upl_path, &CC->quickroom, "images");
498         }
499
500         if (strlen(CC->upl_path) == 0) {
501                 cprintf("%d Higher access required.\n",
502                         ERROR + HIGHER_ACCESS_REQUIRED);
503                 return;
504         }
505
506         if (is_this_for_real == 0) {
507                 cprintf("%d Ok to send image\n", OK);
508                 return;
509         }
510
511         CC->upload_fp = fopen(CC->upl_path, "wb");
512         if (CC->upload_fp == NULL) {
513                 cprintf("%d Cannot open %s: %s\n",
514                         ERROR, CC->upl_path, strerror(errno));
515                 return;
516         }
517         cprintf("%d Ok\n", OK);
518         CC->upload_type = UPL_IMAGE;
519 }
520
521
522 /*
523  * close the download file
524  */
525 void cmd_clos(void)
526 {
527         char buf[SIZ];
528
529         if (CC->download_fp == NULL) {
530                 cprintf("%d You don't have a download file open.\n",
531                         ERROR);
532                 return;
533         }
534
535         fclose(CC->download_fp);
536         CC->download_fp = NULL;
537
538         if (CC->dl_is_net == 1) {
539                 CC->dl_is_net = 0;
540                 snprintf(buf, sizeof buf, "%s/network/spoolout/%s", BBSDIR,
541                          CC->net_node);
542                 unlink(buf);
543         }
544
545         cprintf("%d Ok\n", OK);
546 }
547
548
549 /*
550  * abort an upload
551  */
552 void abort_upl(struct CitContext *who)
553 {
554         if (who->upload_fp != NULL) {
555                 fclose(who->upload_fp);
556                 who->upload_fp = NULL;
557                 unlink(CC->upl_path);
558         }
559 }
560
561
562
563 /*
564  * close the upload file
565  */
566 void cmd_ucls(char *cmd)
567 {
568         FILE *fp;
569         char upload_notice[512];
570
571         if (CC->upload_fp == NULL) {
572                 cprintf("%d You don't have an upload file open.\n", ERROR);
573                 return;
574         }
575
576         fclose(CC->upload_fp);
577         CC->upload_fp = NULL;
578
579         if ((!strcasecmp(cmd, "1")) && (CC->upload_type != UPL_FILE)) {
580                 CC->upload_type = UPL_FILE;
581                 cprintf("%d Upload completed.\n", OK);
582
583                 /* FIXME ... here we need to trigger a network run */
584
585                 return;
586         }
587
588         if (!strcasecmp(cmd, "1")) {
589                 cprintf("%d File '%s' saved.\n", OK, CC->upl_path);
590                 fp = fopen(CC->upl_filedir, "a");
591                 if (fp == NULL) {
592                         fp = fopen(CC->upl_filedir, "w");
593                 }
594                 if (fp != NULL) {
595                         fprintf(fp, "%s %s\n", CC->upl_file,
596                                 CC->upl_comment);
597                         fclose(fp);
598                 }
599
600                 /* put together an upload notice */
601                 sprintf(upload_notice,
602                         "NEW UPLOAD: '%s'\n %s\n",
603                         CC->upl_file, CC->upl_comment);
604                 quickie_message(CC->curr_user, NULL, CC->quickroom.QRname,
605                                 upload_notice);
606         } else {
607                 abort_upl(CC);
608                 cprintf("%d File '%s' aborted.\n", OK, CC->upl_path);
609         }
610 }
611
612
613
614 /*
615  * read from the download file
616  */
617 void cmd_read(char *cmdbuf)
618 {
619         long start_pos;
620         int bytes;
621         char buf[4096];
622
623         start_pos = extract_long(cmdbuf, 0);
624         bytes = extract_int(cmdbuf, 1);
625
626         if (CC->download_fp == NULL) {
627                 cprintf("%d You don't have a download file open.\n",
628                         ERROR);
629                 return;
630         }
631
632         if (bytes > 4096) {
633                 cprintf("%d You may not read more than 4096 bytes.\n",
634                         ERROR);
635                 return;
636         }
637
638         fseek(CC->download_fp, start_pos, 0);
639         fread(buf, bytes, 1, CC->download_fp);
640         cprintf("%d %d\n", BINARY_FOLLOWS, bytes);
641         client_write(buf, bytes);
642 }
643
644
645
646 /*
647  * write to the upload file
648  */
649 void cmd_writ(char *cmdbuf)
650 {
651         int bytes;
652         char buf[4096];
653
654         bytes = extract_int(cmdbuf, 0);
655
656         if (CC->upload_fp == NULL) {
657                 cprintf("%d You don't have an upload file open.\n", ERROR);
658                 return;
659         }
660
661         if (bytes > 4096) {
662                 cprintf("%d You may not write more than 4096 bytes.\n",
663                         ERROR);
664                 return;
665         }
666
667         cprintf("%d %d\n", SEND_BINARY, bytes);
668         client_read(buf, bytes);
669         fwrite(buf, bytes, 1, CC->upload_fp);
670 }
671
672
673
674
675 /*
676  * cmd_ndop() - open a network spool file for downloading
677  */
678 void cmd_ndop(char *cmdbuf)
679 {
680         char pathname[SIZ];
681         struct stat statbuf;
682
683         if (strlen(CC->net_node) == 0) {
684                 cprintf("%d Not authenticated as a network node.\n",
685                         ERROR + NOT_LOGGED_IN);
686                 return;
687         }
688
689         if (CC->download_fp != NULL) {
690                 cprintf("%d You already have a download file open.\n",
691                         ERROR);
692                 return;
693         }
694
695         snprintf(pathname, sizeof pathname, "%s/network/spoolout/%s",
696                  BBSDIR, CC->net_node);
697
698         /* first open the file in append mode in order to create a
699          * zero-length file if it doesn't already exist 
700          */
701         CC->download_fp = fopen(pathname, "a");
702         if (CC->download_fp != NULL)
703                 fclose(CC->download_fp);
704
705         /* now open it */
706         CC->download_fp = fopen(pathname, "r");
707         if (CC->download_fp == NULL) {
708                 cprintf("%d cannot open %s: %s\n",
709                         ERROR, pathname, strerror(errno));
710                 return;
711         }
712
713
714         /* set this flag so other routines know that the download file
715          * currently open is a network spool file 
716          */
717         CC->dl_is_net = 1;
718
719         stat(pathname, &statbuf);
720         cprintf("%d %ld\n", OK, statbuf.st_size);
721 }
722
723 /*
724  * cmd_nuop() - open a network spool file for uploading
725  */
726 void cmd_nuop(char *cmdbuf)
727 {
728         static int seq = 1;
729
730         if (strlen(CC->net_node) == 0) {
731                 cprintf("%d Not authenticated as a network node.\n",
732                         ERROR + NOT_LOGGED_IN);
733                 return;
734         }
735
736         if (CC->upload_fp != NULL) {
737                 cprintf("%d You already have an upload file open.\n",
738                         ERROR);
739                 return;
740         }
741
742         snprintf(CC->upl_path, sizeof CC->upl_path,
743                  "%s/network/spoolin/%s.%04x.%04x",
744                  BBSDIR, CC->net_node, getpid(), ++seq);
745
746         CC->upload_fp = fopen(CC->upl_path, "r");
747         if (CC->upload_fp != NULL) {
748                 fclose(CC->upload_fp);
749                 CC->upload_fp = NULL;
750                 cprintf("%d '%s' already exists\n",
751                         ERROR + ALREADY_EXISTS, CC->upl_path);
752                 return;
753         }
754
755         CC->upload_fp = fopen(CC->upl_path, "w");
756         if (CC->upload_fp == NULL) {
757                 cprintf("%d Cannot open %s: %s\n",
758                         ERROR, CC->upl_path, strerror(errno));
759                 return;
760         }
761
762         CC->upload_type = UPL_NET;
763         cprintf("%d Ok\n", OK);
764 }