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