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