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