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