* migrated cmd_ent3() to CtdlSaveMessage()
[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         char upload_notice[512];
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                 sprintf(upload_notice,
553                         "NEW UPLOAD: '%s'\n %s\n",
554                         CC->upl_file,CC->upl_comment);
555                 quickie_message(CC->curr_user, NULL, CC->quickroom.QRname,
556                                 upload_notice);
557                 }
558         else {
559                 abort_upl(CC);
560                 cprintf("%d File '%s' aborted.\n",OK,CC->upl_path);
561                 }
562         }
563
564 /*
565  * read from the download file
566  */
567 void cmd_read(char *cmdbuf)
568 {
569         long start_pos;
570         int bytes;
571         char buf[4096];
572
573         start_pos = extract_long(cmdbuf,0);
574         bytes = extract_int(cmdbuf,1);
575         
576         if (CC->download_fp == NULL) {
577                 cprintf("%d You don't have a download file open.\n",ERROR);
578                 return;
579                 }
580
581         if (bytes > 4096) {
582                 cprintf("%d You may not read more than 4096 bytes.\n",ERROR);
583                 return;
584                 }
585
586         fseek(CC->download_fp,start_pos,0);
587         fread(buf,bytes,1,CC->download_fp);
588         cprintf("%d %d\n",BINARY_FOLLOWS,bytes);
589         client_write(buf, bytes);
590         }
591
592
593
594 /*
595  * write to the upload file
596  */
597 void cmd_writ(char *cmdbuf)
598 {
599         int bytes;
600         char buf[4096];
601
602         bytes = extract_int(cmdbuf,0);
603         
604         if (CC->upload_fp == NULL) {
605                 cprintf("%d You don't have an upload file open.\n",ERROR);
606                 return;
607                 }
608
609         if (bytes > 4096) {
610                 cprintf("%d You may not write more than 4096 bytes.\n",ERROR);
611                 return;
612                 }
613
614         cprintf("%d %d\n",SEND_BINARY,bytes);
615         client_read(buf, bytes);
616         fwrite(buf,bytes,1,CC->upload_fp);
617         }
618
619
620
621 /*
622  * cmd_netp() - identify as network poll session
623  */
624 void cmd_netp(char *cmdbuf)
625 {
626         char buf[256];
627         
628         extract(buf,cmdbuf,1);
629         if (strcasecmp(buf,config.c_net_password)) {
630                 cprintf("%d authentication failed\n",ERROR);
631                 return;
632                 }
633         extract(CC->net_node,cmdbuf,0);
634         cprintf("%d authenticated as network node '%s'\n",OK,CC->net_node);
635         }
636
637 /*
638  * cmd_ndop() - open a network spool file for downloading
639  */
640 void cmd_ndop(char *cmdbuf)
641 {
642         char pathname[256];
643         struct stat statbuf;
644
645         if (strlen(CC->net_node)==0) {
646                 cprintf("%d Not authenticated as a network node.\n",
647                         ERROR+NOT_LOGGED_IN);
648                 return;
649                 }
650
651         if (CC->download_fp != NULL) {
652                 cprintf("%d You already have a download file open.\n",ERROR);
653                 return;
654                 }
655
656         snprintf(pathname,sizeof pathname,"%s/network/spoolout/%s",BBSDIR,
657                  CC->net_node);
658
659         /* first open the file in append mode in order to create a
660          * zero-length file if it doesn't already exist 
661          */
662         CC->download_fp = fopen(pathname,"a");
663         if (CC->download_fp != NULL) fclose(CC->download_fp);
664
665         /* now open it */
666         CC->download_fp = fopen(pathname,"r");
667         if (CC->download_fp==NULL) {
668                 cprintf("%d cannot open %s: %s\n",
669                         ERROR,pathname,strerror(errno));
670                 return;
671                 }
672
673
674         /* set this flag so other routines know that the download file
675          * currently open is a network spool file 
676          */
677         CC->dl_is_net = 1;
678
679         stat(pathname,&statbuf);
680         cprintf("%d %ld\n",OK,statbuf.st_size);
681         }
682
683 /*
684  * cmd_nuop() - open a network spool file for uploading
685  */
686 void cmd_nuop(char *cmdbuf)
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,"%s/network/spoolin/%s.%d",
700                 BBSDIR,CC->net_node,getpid());
701
702         CC->upload_fp = fopen(CC->upl_path,"r");
703         if (CC->upload_fp != NULL) {
704                 fclose(CC->upload_fp);
705                 CC->upload_fp = NULL;
706                 cprintf("%d '%s' already exists\n",
707                         ERROR+ALREADY_EXISTS,CC->upl_path);
708                 return;
709                 }
710
711         CC->upload_fp = fopen(CC->upl_path,"w");
712         if (CC->upload_fp == NULL) {
713                 cprintf("%d Cannot open %s: %s\n",
714                         ERROR,CC->upl_path,strerror(errno));
715                 return;
716                 }
717
718         CC->upload_type = UPL_NET;
719         cprintf("%d Ok\n",OK);
720         }