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