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