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