Removed the _floorpic_ infrastructure entirely -- we don't use those anymore.
[citadel.git] / citadel / modules / ctdlproto / serv_file.c
1 /* 
2  * Server functions which handle file transfers and room directories.
3  *
4  * Copyright (c) 1987-2016 by the citadel.org team
5  *
6  * This program is open source software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License version 3.
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  */
14
15 #include <stdio.h>
16 #include <libcitadel.h>
17 #include <dirent.h>
18
19 #include "ctdl_module.h"
20 #include "citserver.h"
21 #include "support.h"
22 #include "config.h"
23 #include "user_ops.h"
24
25
26 /*
27  * Server command to delete a file from a room's directory
28  */
29 void cmd_delf(char *filename)
30 {
31         char pathname[64];
32         int a;
33
34         if (CtdlAccessCheck(ac_room_aide))
35                 return;
36
37         if ((CC->room.QRflags & QR_DIRECTORY) == 0) {
38                 cprintf("%d No directory in this room.\n",
39                         ERROR + NOT_HERE);
40                 return;
41         }
42
43         if (IsEmptyStr(filename)) {
44                 cprintf("%d You must specify a file name.\n",
45                         ERROR + FILE_NOT_FOUND);
46                 return;
47         }
48         for (a = 0; !IsEmptyStr(&filename[a]); ++a) {
49                 if ( (filename[a] == '/') || (filename[a] == '\\') ) {
50                         filename[a] = '_';
51                 }
52         }
53         snprintf(pathname, sizeof pathname,
54                          "%s/%s/%s",
55                          ctdl_file_dir,
56                          CC->room.QRdirname, filename);
57         a = unlink(pathname);
58         if (a == 0) {
59                 cprintf("%d File '%s' deleted.\n", CIT_OK, pathname);
60         }
61         else {
62                 cprintf("%d File '%s' not found.\n",
63                         ERROR + FILE_NOT_FOUND, pathname);
64         }
65 }
66
67
68
69
70 /*
71  * move a file from one room directory to another
72  */
73 void cmd_movf(char *cmdbuf)
74 {
75         char filename[PATH_MAX];
76         char pathname[PATH_MAX];
77         char newpath[PATH_MAX];
78         char newroom[ROOMNAMELEN];
79         char buf[PATH_MAX];
80         int a;
81         struct ctdlroom qrbuf;
82
83         extract_token(filename, cmdbuf, 0, '|', sizeof filename);
84         extract_token(newroom, cmdbuf, 1, '|', sizeof newroom);
85
86         if (CtdlAccessCheck(ac_room_aide)) return;
87
88         if ((CC->room.QRflags & QR_DIRECTORY) == 0) {
89                 cprintf("%d No directory in this room.\n",
90                         ERROR + NOT_HERE);
91                 return;
92         }
93
94         if (IsEmptyStr(filename)) {
95                 cprintf("%d You must specify a file name.\n",
96                         ERROR + FILE_NOT_FOUND);
97                 return;
98         }
99
100         for (a = 0; !IsEmptyStr(&filename[a]); ++a) {
101                 if ( (filename[a] == '/') || (filename[a] == '\\') ) {
102                         filename[a] = '_';
103                 }
104         }
105         snprintf(pathname, sizeof pathname, "./files/%s/%s",
106                  CC->room.QRdirname, filename);
107         if (access(pathname, 0) != 0) {
108                 cprintf("%d File '%s' not found.\n",
109                         ERROR + FILE_NOT_FOUND, pathname);
110                 return;
111         }
112
113         if (CtdlGetRoom(&qrbuf, newroom) != 0) {
114                 cprintf("%d '%s' does not exist.\n", ERROR + ROOM_NOT_FOUND, newroom);
115                 return;
116         }
117         if ((qrbuf.QRflags & QR_DIRECTORY) == 0) {
118                 cprintf("%d '%s' is not a directory room.\n",
119                         ERROR + NOT_HERE, qrbuf.QRname);
120                 return;
121         }
122         snprintf(newpath, sizeof newpath, "./files/%s/%s", qrbuf.QRdirname,
123                  filename);
124         if (link(pathname, newpath) != 0) {
125                 cprintf("%d Couldn't move file: %s\n", ERROR + INTERNAL_ERROR,
126                         strerror(errno));
127                 return;
128         }
129         unlink(pathname);
130
131         /* this is a crude method of copying the file description */
132         snprintf(buf, sizeof buf,
133                  "cat ./files/%s/filedir |grep \"%s\" >>./files/%s/filedir",
134                  CC->room.QRdirname, filename, qrbuf.QRdirname);
135         system(buf);
136         cprintf("%d File '%s' has been moved.\n", CIT_OK, filename);
137 }
138
139
140 /*
141  * This code is common to all commands which open a file for downloading,
142  * regardless of whether it's a file from the directory, an image, a network
143  * spool file, a MIME attachment, etc.
144  * It examines the file and displays the OK result code and some information
145  * about the file.  NOTE: this stuff is Unix dependent.
146  */
147 void OpenCmdResult(char *filename, const char *mime_type)
148 {
149         struct stat statbuf;
150         time_t modtime;
151         long filesize;
152
153         fstat(fileno(CC->download_fp), &statbuf);
154         CC->download_fp_total = statbuf.st_size;
155         filesize = (long) statbuf.st_size;
156         modtime = (time_t) statbuf.st_mtime;
157
158         cprintf("%d %ld|%ld|%s|%s\n",
159                 CIT_OK, filesize, (long)modtime, filename, mime_type);
160 }
161
162
163 /*
164  * open a file for downloading
165  */
166 void cmd_open(char *cmdbuf)
167 {
168         char filename[256];
169         char pathname[PATH_MAX];
170         int a;
171
172         extract_token(filename, cmdbuf, 0, '|', sizeof filename);
173
174         if (CtdlAccessCheck(ac_logged_in)) return;
175
176         if ((CC->room.QRflags & QR_DIRECTORY) == 0) {
177                 cprintf("%d No directory in this room.\n",
178                         ERROR + NOT_HERE);
179                 return;
180         }
181
182         if (IsEmptyStr(filename)) {
183                 cprintf("%d You must specify a file name.\n",
184                         ERROR + FILE_NOT_FOUND);
185                 return;
186         }
187         if (strstr(filename, "../") != NULL)
188         {
189                 cprintf("%d syntax error.\n",
190                         ERROR + ILLEGAL_VALUE);
191                 return;
192         }
193
194         if (CC->download_fp != NULL) {
195                 cprintf("%d You already have a download file open.\n",
196                         ERROR + RESOURCE_BUSY);
197                 return;
198         }
199
200         for (a = 0; !IsEmptyStr(&filename[a]); ++a) {
201                 if ( (filename[a] == '/') || (filename[a] == '\\') ) {
202                         filename[a] = '_';
203                 }
204         }
205
206         snprintf(pathname, sizeof pathname,
207                          "%s/%s/%s",
208                          ctdl_file_dir,
209                          CC->room.QRdirname, filename);
210         CC->download_fp = fopen(pathname, "r");
211
212         if (CC->download_fp == NULL) {
213                 cprintf("%d cannot open %s: %s\n",
214                         ERROR + INTERNAL_ERROR, pathname, strerror(errno));
215                 return;
216         }
217
218         OpenCmdResult(filename, "application/octet-stream");
219 }
220
221 /*
222  * open an image file
223  */
224 void cmd_oimg(char *cmdbuf)
225 {
226         char filename[PATH_MAX];
227         char pathname[PATH_MAX];
228         char MimeTestBuf[32];
229         int a;
230         int rv;
231
232         extract_token(filename, cmdbuf, 0, '|', sizeof filename);
233
234         if (IsEmptyStr(filename)) {
235                 cprintf("%d You must specify a file name.\n",
236                         ERROR + FILE_NOT_FOUND);
237                 return;
238         }
239
240         if (CC->download_fp != NULL) {
241                 cprintf("%d You already have a download file open.\n",
242                         ERROR + RESOURCE_BUSY);
243                 return;
244         }
245
246         if (!strcasecmp(filename, "_roompic_")) {
247                 assoc_file_name(pathname, sizeof pathname, &CC->room, ctdl_image_dir);
248         }
249         else {
250                 for (a = 0; !IsEmptyStr(&filename[a]); ++a) {
251                         filename[a] = tolower(filename[a]);
252                         if ( (filename[a] == '/') || (filename[a] == '\\') ) {
253                                 filename[a] = '_';
254                         }
255                 }
256                 if (strstr(filename, "../") != NULL)
257                 {
258                         cprintf("%d syntax error.\n",
259                                 ERROR + ILLEGAL_VALUE);
260                         return;
261                 }
262
263                 snprintf(pathname, sizeof pathname,
264                                  "%s/%s",
265                                  ctdl_image_dir,
266                                  filename);
267         }
268
269         CC->download_fp = fopen(pathname, "rb");
270         if (CC->download_fp == NULL) {
271                 strcat(pathname, ".gif");
272                 CC->download_fp = fopen(pathname, "rb");
273         }
274         if (CC->download_fp == NULL) {
275                 cprintf("%d Cannot open %s: %s\n",
276                         ERROR + FILE_NOT_FOUND, pathname, strerror(errno));
277                 return;
278         }
279         rv = fread(&MimeTestBuf[0], 1, 32, CC->download_fp);
280         if (rv == -1) {
281                 cprintf("%d Cannot access %s: %s\n",
282                         ERROR + FILE_NOT_FOUND, pathname, strerror(errno));
283                 return;
284         }
285
286         rewind (CC->download_fp);
287         OpenCmdResult(pathname, GuessMimeType(&MimeTestBuf[0], 32));
288 }
289
290
291 /*
292  * open a file for uploading
293  */
294 void cmd_uopn(char *cmdbuf)
295 {
296         int a;
297
298         extract_token(CC->upl_file, cmdbuf, 0, '|', sizeof CC->upl_file);
299         extract_token(CC->upl_mimetype, cmdbuf, 1, '|', sizeof CC->upl_mimetype);
300         extract_token(CC->upl_comment, cmdbuf, 2, '|', sizeof CC->upl_comment);
301
302         if (CtdlAccessCheck(ac_logged_in)) return;
303
304         if ((CC->room.QRflags & QR_DIRECTORY) == 0) {
305                 cprintf("%d No directory in this room.\n",
306                         ERROR + NOT_HERE);
307                 return;
308         }
309
310         if (IsEmptyStr(CC->upl_file)) {
311                 cprintf("%d You must specify a file name.\n",
312                         ERROR + FILE_NOT_FOUND);
313                 return;
314         }
315
316         if (CC->upload_fp != NULL) {
317                 cprintf("%d You already have a upload file open.\n",
318                         ERROR + RESOURCE_BUSY);
319                 return;
320         }
321
322         for (a = 0; !IsEmptyStr(&CC->upl_file[a]); ++a) {
323                 if ( (CC->upl_file[a] == '/') || (CC->upl_file[a] == '\\') ) {
324                         CC->upl_file[a] = '_';
325                 }
326         }
327         snprintf(CC->upl_path, sizeof CC->upl_path, 
328                          "%s/%s/%s",
329                          ctdl_file_dir,
330                          CC->room.QRdirname, CC->upl_file);
331         snprintf(CC->upl_filedir, sizeof CC->upl_filedir,
332                          "%s/%s/filedir", 
333                          ctdl_file_dir,
334                          CC->room.QRdirname);
335
336         CC->upload_fp = fopen(CC->upl_path, "r");
337         if (CC->upload_fp != NULL) {
338                 fclose(CC->upload_fp);
339                 CC->upload_fp = NULL;
340                 cprintf("%d '%s' already exists\n",
341                         ERROR + ALREADY_EXISTS, CC->upl_path);
342                 return;
343         }
344
345         CC->upload_fp = fopen(CC->upl_path, "wb");
346         if (CC->upload_fp == NULL) {
347                 cprintf("%d Cannot open %s: %s\n",
348                         ERROR + INTERNAL_ERROR, CC->upl_path, strerror(errno));
349                 return;
350         }
351         cprintf("%d Ok\n", CIT_OK);
352 }
353
354
355
356 /*
357  * open an image file for uploading
358  */
359 void cmd_uimg(char *cmdbuf)
360 {
361         int is_this_for_real;
362         char basenm[256];
363         int a;
364
365         if (num_parms(cmdbuf) < 2) {
366                 cprintf("%d Usage error.\n", ERROR + ILLEGAL_VALUE);
367                 return;
368         }
369
370         is_this_for_real = extract_int(cmdbuf, 0);
371         extract_token(CC->upl_mimetype, cmdbuf, 1, '|', sizeof CC->upl_mimetype);
372         extract_token(basenm, cmdbuf, 2, '|', sizeof basenm);
373         if (CC->upload_fp != NULL) {
374                 cprintf("%d You already have an upload file open.\n",
375                         ERROR + RESOURCE_BUSY);
376                 return;
377         }
378
379         strcpy(CC->upl_path, "");
380
381         for (a = 0; !IsEmptyStr(&basenm[a]); ++a) {
382                 basenm[a] = tolower(basenm[a]);
383                 if ( (basenm[a] == '/') || (basenm[a] == '\\') ) {
384                         basenm[a] = '_';
385                 }
386         }
387
388         if (CC->user.axlevel >= AxAideU) {
389                 snprintf(CC->upl_path, sizeof CC->upl_path, 
390                                  "%s/%s",
391                                  ctdl_image_dir,
392                                  basenm);
393         }
394
395         if ((!strcasecmp(basenm, "_roompic_")) && (is_room_aide())) {
396                 assoc_file_name(CC->upl_path, sizeof CC->upl_path, &CC->room, ctdl_image_dir);
397         }
398
399         if (IsEmptyStr(CC->upl_path)) {
400                 cprintf("%d Higher access required.\n",
401                         ERROR + HIGHER_ACCESS_REQUIRED);
402                 return;
403         }
404
405         if (is_this_for_real == 0) {
406                 cprintf("%d Ok to send image\n", CIT_OK);
407                 return;
408         }
409
410         CC->upload_fp = fopen(CC->upl_path, "wb");
411         if (CC->upload_fp == NULL) {
412                 cprintf("%d Cannot open %s: %s\n",
413                         ERROR + INTERNAL_ERROR, CC->upl_path, strerror(errno));
414                 return;
415         }
416         cprintf("%d Ok\n", CIT_OK);
417         CC->upload_type = UPL_IMAGE;
418 }
419
420
421 /*
422  * close the download file
423  */
424 void cmd_clos(char *cmdbuf)
425 {
426         char buf[256];
427
428         if (CC->download_fp == NULL) {
429                 cprintf("%d You don't have a download file open.\n",
430                         ERROR + RESOURCE_NOT_OPEN);
431                 return;
432         }
433
434         fclose(CC->download_fp);
435         CC->download_fp = NULL;
436
437         if (CC->dl_is_net == 1) {
438                 CC->dl_is_net = 0;
439                 snprintf(buf, sizeof buf, 
440                                  "%s/%s",
441                                  ctdl_netout_dir,
442                                  CC->net_node);
443                 unlink(buf);
444         }
445
446         cprintf("%d Ok\n", CIT_OK);
447 }
448
449
450 /*
451  * abort an upload
452  */
453 void abort_upl(CitContext *who)
454 {
455         if (who->upload_fp != NULL) {
456                 fclose(who->upload_fp);
457                 who->upload_fp = NULL;
458                 unlink(CC->upl_path);
459         }
460 }
461
462
463
464 /*
465  * close the upload file
466  */
467 void cmd_ucls(char *cmd)
468 {
469         struct CitContext *CCC = CC;
470         FILE *fp;
471         char upload_notice[512];
472         static int seq = 0;
473
474         if (CCC->upload_fp == NULL) {
475                 cprintf("%d You don't have an upload file open.\n", ERROR + RESOURCE_NOT_OPEN);
476                 return;
477         }
478
479         fclose(CC->upload_fp);
480         CCC->upload_fp = NULL;
481
482         if ((!strcasecmp(cmd, "1")) && (CCC->upload_type != UPL_FILE)) {
483                 cprintf("%d Upload completed.\n", CIT_OK);
484
485                 if (CCC->upload_type == UPL_NET) {
486                         char final_filename[PATH_MAX];
487                         snprintf(final_filename, sizeof final_filename,
488                                 "%s/%s.%04lx.%04x",
489                                 ctdl_netin_dir,
490                                 CCC->net_node,
491                                 (long)getpid(),
492                                 ++seq
493                         );
494
495                         if (link(CCC->upl_path, final_filename) == 0) {
496                                 CTDL_syslog(LOG_INFO, "UCLS: updoaded %s",
497                                        final_filename);
498                                 unlink(CCC->upl_path);
499                         }
500                         else {
501                                 CTDL_syslog(LOG_INFO, "Cannot link %s to %s: %s",
502                                             CCC->upl_path, final_filename, strerror(errno)
503                                 );
504                         }
505                         
506
507                         /* FIXME ... here we need to trigger a network run */
508                 }
509
510                 CCC->upload_type = UPL_FILE;
511                 return;
512         }
513
514         if (!strcasecmp(cmd, "1")) {
515                 cprintf("%d File '%s' saved.\n", CIT_OK, CCC->upl_path);
516                 fp = fopen(CCC->upl_filedir, "a");
517                 if (fp == NULL) {
518                         fp = fopen(CCC->upl_filedir, "w");
519                 }
520                 if (fp != NULL) {
521                         fprintf(fp, "%s %s %s\n", CCC->upl_file,
522                                 CCC->upl_mimetype,
523                                 CCC->upl_comment);
524                         fclose(fp);
525                 }
526
527                 if ((CCC->room.QRflags2 & QR2_NOUPLMSG) == 0) {
528                         /* put together an upload notice */
529                         snprintf(upload_notice, sizeof upload_notice,
530                                  "NEW UPLOAD: '%s'\n %s\n%s\n",
531                                  CCC->upl_file, 
532                                  CCC->upl_comment, 
533                                  CCC->upl_mimetype);
534                         quickie_message(CCC->curr_user, NULL, NULL, CCC->room.QRname,
535                                         upload_notice, 0, NULL);
536                 }
537         } else {
538                 abort_upl(CCC);
539                 cprintf("%d File '%s' aborted.\n", CIT_OK, CCC->upl_path);
540         }
541 }
542
543
544 /*
545  * read from the download file
546  */
547 void cmd_read(char *cmdbuf)
548 {
549         long start_pos;
550         size_t bytes;
551         char buf[SIZ];
552         int rc;
553
554         /* The client will transmit its requested offset and byte count */
555         start_pos = extract_long(cmdbuf, 0);
556         bytes = extract_int(cmdbuf, 1);
557         if ((start_pos < 0) || (bytes <= 0)) {
558                 cprintf("%d you have to specify a value > 0.\n", ERROR + ILLEGAL_VALUE);
559                 return;
560         }
561
562         if (CC->download_fp == NULL) {
563                 cprintf("%d You don't have a download file open.\n",
564                         ERROR + RESOURCE_NOT_OPEN);
565                 return;
566         }
567
568         /* If necessary, reduce the byte count to the size of our buffer */
569         if (bytes > sizeof(buf)) {
570                 bytes = sizeof(buf);
571         }
572
573         rc = fseek(CC->download_fp, start_pos, 0);
574         if (rc < 0) {
575                 struct CitContext *CCC = CC;
576                 cprintf("%d your file is smaller then %ld.\n", ERROR + ILLEGAL_VALUE, start_pos);
577                 CTDL_syslog(LOG_ERR, "your file %s is smaller then %ld. [%s]\n", 
578                             CC->upl_path, 
579                             start_pos,
580                             strerror(errno));
581
582                 return;
583         }
584         bytes = fread(buf, 1, bytes, CC->download_fp);
585         if (bytes > 0) {
586                 /* Tell the client the actual byte count and transmit it */
587                 cprintf("%d %d\n", BINARY_FOLLOWS, (int)bytes);
588                 client_write(buf, bytes);
589         }
590         else {
591                 cprintf("%d %s\n", ERROR, strerror(errno));
592         }
593 }
594
595
596 /*
597  * write to the upload file
598  */
599 void cmd_writ(char *cmdbuf)
600 {
601         struct CitContext *CCC = CC;
602         int bytes;
603         char *buf;
604         int rv;
605
606         unbuffer_output();
607
608         bytes = extract_int(cmdbuf, 0);
609
610         if (CCC->upload_fp == NULL) {
611                 cprintf("%d You don't have an upload file open.\n", ERROR + RESOURCE_NOT_OPEN);
612                 return;
613         }
614         if (bytes <= 0) {
615                 cprintf("%d you have to specify a value > 0.\n", ERROR + ILLEGAL_VALUE);
616                 return;
617         }
618
619         if (bytes > 100000) {
620                 bytes = 100000;
621         }
622
623         cprintf("%d %d\n", SEND_BINARY, bytes);
624         buf = malloc(bytes + 1);
625         client_read(buf, bytes);
626         rv = fwrite(buf, bytes, 1, CCC->upload_fp);
627         if (rv == -1) {
628                 CTDL_syslog(LOG_EMERG, "Couldn't write: %s\n",
629                             strerror(errno));
630         }
631         free(buf);
632 }
633
634
635
636
637 /*
638  * cmd_ndop() - open a network spool file for downloading
639  */
640 void cmd_ndop(char *cmdbuf)
641 {
642         struct CitContext *CCC = CC;
643         char pathname[256];
644         struct stat statbuf;
645
646         if (IsEmptyStr(CCC->net_node)) {
647                 cprintf("%d Not authenticated as a network node.\n",
648                         ERROR + NOT_LOGGED_IN);
649                 return;
650         }
651
652         if (CCC->download_fp != NULL) {
653                 cprintf("%d You already have a download file open.\n",
654                         ERROR + RESOURCE_BUSY);
655                 return;
656         }
657
658         snprintf(pathname, sizeof pathname, 
659                          "%s/%s",
660                          ctdl_netout_dir,
661                          CCC->net_node);
662
663         /* first open the file in append mode in order to create a
664          * zero-length file if it doesn't already exist 
665          */
666         CCC->download_fp = fopen(pathname, "a");
667         if (CCC->download_fp != NULL)
668                 fclose(CCC->download_fp);
669
670         /* now open it */
671         CCC->download_fp = fopen(pathname, "r");
672         if (CCC->download_fp == NULL) {
673                 cprintf("%d cannot open %s: %s\n",
674                         ERROR + INTERNAL_ERROR, pathname, strerror(errno));
675                 return;
676         }
677
678
679         /* set this flag so other routines know that the download file
680          * currently open is a network spool file 
681          */
682         CCC->dl_is_net = 1;
683
684         stat(pathname, &statbuf);
685         CCC->download_fp_total = statbuf.st_size;
686         cprintf("%d %ld\n", CIT_OK, (long)statbuf.st_size);
687 }
688
689 /*
690  * cmd_nuop() - open a network spool file for uploading
691  */
692 void cmd_nuop(char *cmdbuf)
693 {
694         static int seq = 1;
695
696         if (IsEmptyStr(CC->net_node)) {
697                 cprintf("%d Not authenticated as a network node.\n",
698                         ERROR + NOT_LOGGED_IN);
699                 return;
700         }
701
702         if (CC->upload_fp != NULL) {
703                 cprintf("%d You already have an upload file open.\n",
704                         ERROR + RESOURCE_BUSY);
705                 return;
706         }
707
708         snprintf(CC->upl_path, sizeof CC->upl_path,
709                          "%s/%s.%04lx.%04x",
710                          ctdl_nettmp_dir,
711                          CC->net_node, 
712                          (long)getpid(), 
713                          ++seq);
714
715         CC->upload_fp = fopen(CC->upl_path, "r");
716         if (CC->upload_fp != NULL) {
717                 fclose(CC->upload_fp);
718                 CC->upload_fp = NULL;
719                 cprintf("%d '%s' already exists\n",
720                         ERROR + ALREADY_EXISTS, CC->upl_path);
721                 return;
722         }
723
724         CC->upload_fp = fopen(CC->upl_path, "w");
725         if (CC->upload_fp == NULL) {
726                 cprintf("%d Cannot open %s: %s\n",
727                         ERROR + INTERNAL_ERROR, CC->upl_path, strerror(errno));
728                 return;
729         }
730
731         CC->upload_type = UPL_NET;
732         cprintf("%d Ok\n", CIT_OK);
733 }
734 void files_logout_hook(void)
735 {
736         CitContext *CCC = MyContext();
737
738         /*
739          * If there is a download in progress, abort it.
740          */
741         if (CCC->download_fp != NULL) {
742                 fclose(CCC->download_fp);
743                 CCC->download_fp = NULL;
744         }
745
746         /*
747          * If there is an upload in progress, abort it.
748          */
749         if (CCC->upload_fp != NULL) {
750                 abort_upl(CCC);
751         }
752
753 }
754
755 /* 
756  * help_subst()  -  support routine for help file viewer
757  */
758 void help_subst(char *strbuf, char *source, char *dest)
759 {
760         char workbuf[SIZ];
761         int p;
762
763         while (p = pattern2(strbuf, source), (p >= 0)) {
764                 strcpy(workbuf, &strbuf[p + strlen(source)]);
765                 strcpy(&strbuf[p], dest);
766                 strcat(strbuf, workbuf);
767         }
768 }
769
770 void do_help_subst(char *buffer)
771 {
772         char buf2[16];
773
774         help_subst(buffer, "^nodename", CtdlGetConfigStr("c_nodename"));
775         help_subst(buffer, "^humannode", CtdlGetConfigStr("c_humannode"));
776         help_subst(buffer, "^fqdn", CtdlGetConfigStr("c_fqdn"));
777         help_subst(buffer, "^username", CC->user.fullname);
778         snprintf(buf2, sizeof buf2, "%ld", CC->user.usernum);
779         help_subst(buffer, "^usernum", buf2);
780         help_subst(buffer, "^sysadm", CtdlGetConfigStr("c_sysadm"));
781         help_subst(buffer, "^variantname", CITADEL);
782         help_subst(buffer, "^maxsessions", CtdlGetConfigStr("c_maxsessions"));          // yes it's numeric but str is ok here
783         help_subst(buffer, "^bbsdir", ctdl_message_dir);
784 }
785
786
787 typedef const char *ccharp;
788 /*
789  * display system messages or help
790  */
791 void cmd_mesg(char *mname)
792 {
793         FILE *mfp;
794         char targ[256];
795         char buf[256];
796         char buf2[256];
797         char *dirs[2];
798         DIR *dp;
799         struct dirent *d;
800
801         extract_token(buf, mname, 0, '|', sizeof buf);
802
803         dirs[0] = strdup(ctdl_message_dir);
804         dirs[1] = strdup(ctdl_hlp_dir);
805
806         snprintf(buf2, sizeof buf2, "%s.%d.%d",
807                 buf, CC->cs_clientdev, CC->cs_clienttyp);
808
809         /* If the client requested "?" then produce a listing */
810         if (!strcmp(buf, "?")) {
811                 cprintf("%d %s\n", LISTING_FOLLOWS, buf);
812                 dp = opendir(dirs[1]);
813                 if (dp != NULL) {
814                         while (d = readdir(dp), d != NULL) {
815                                 if (d->d_name[0] != '.') {
816                                         cprintf(" %s\n", d->d_name);
817                                 }
818                         }
819                         closedir(dp);
820                 }
821                 cprintf("000\n");
822                 free(dirs[0]);
823                 free(dirs[1]);
824                 return;
825         }
826
827         /* Otherwise, look for the requested file by name. */
828         else {
829                 mesg_locate(targ, sizeof targ, buf2, 2, (const ccharp*)dirs);
830                 if (IsEmptyStr(targ)) {
831                         snprintf(buf2, sizeof buf2, "%s.%d",
832                                                         buf, CC->cs_clientdev);
833                         mesg_locate(targ, sizeof targ, buf2, 2,
834                                     (const ccharp*)dirs);
835                         if (IsEmptyStr(targ)) {
836                                 mesg_locate(targ, sizeof targ, buf, 2,
837                                             (const ccharp*)dirs);
838                         }       
839                 }
840         }
841
842         free(dirs[0]);
843         free(dirs[1]);
844
845         if (IsEmptyStr(targ)) {
846                 cprintf("%d '%s' not found.  (Searching in %s and %s)\n",
847                         ERROR + FILE_NOT_FOUND,
848                         mname,
849                         ctdl_message_dir,
850                         ctdl_hlp_dir
851                 );
852                 return;
853         }
854
855         mfp = fopen(targ, "r");
856         if (mfp==NULL) {
857                 cprintf("%d Cannot open '%s': %s\n",
858                         ERROR + INTERNAL_ERROR, targ, strerror(errno));
859                 return;
860         }
861         cprintf("%d %s\n", LISTING_FOLLOWS,buf);
862
863         while (fgets(buf, (sizeof buf - 1), mfp) != NULL) {
864                 buf[strlen(buf)-1] = 0;
865                 do_help_subst(buf);
866                 cprintf("%s\n",buf);
867         }
868
869         fclose(mfp);
870         cprintf("000\n");
871 }
872
873
874 /*
875  * enter system messages or help
876  */
877 void cmd_emsg(char *mname)
878 {
879         FILE *mfp;
880         char targ[256];
881         char buf[256];
882         char *dirs[2];
883         int a;
884
885         unbuffer_output();
886
887         if (CtdlAccessCheck(ac_aide)) return;
888
889         extract_token(buf, mname, 0, '|', sizeof buf);
890         for (a=0; !IsEmptyStr(&buf[a]); ++a) {          /* security measure */
891                 if (buf[a] == '/') buf[a] = '.';
892         }
893
894         dirs[0] = strdup(ctdl_message_dir);
895         dirs[1] = strdup(ctdl_hlp_dir);
896
897         mesg_locate(targ, sizeof targ, buf, 2, (const ccharp*)dirs);
898         free(dirs[0]);
899         free(dirs[1]);
900
901         if (IsEmptyStr(targ)) {
902                 snprintf(targ, sizeof targ, 
903                                  "%s/%s",
904                                  ctdl_hlp_dir, buf);
905         }
906
907         mfp = fopen(targ,"w");
908         if (mfp==NULL) {
909                 cprintf("%d Cannot open '%s': %s\n",
910                         ERROR + INTERNAL_ERROR, targ, strerror(errno));
911                 return;
912         }
913         cprintf("%d %s\n", SEND_LISTING, targ);
914
915         while (client_getln(buf, sizeof buf) >=0 && strcmp(buf, "000")) {
916                 fprintf(mfp, "%s\n", buf);
917         }
918
919         fclose(mfp);
920 }
921
922 /*****************************************************************************/
923 /*                      MODULE INITIALIZATION STUFF                          */
924 /*****************************************************************************/
925
926 CTDL_MODULE_INIT(file_ops)
927 {
928         if (!threading) {
929                 CtdlRegisterSessionHook(files_logout_hook, EVT_LOGOUT, PRIO_LOGOUT + 8);
930
931                 CtdlRegisterProtoHook(cmd_delf, "DELF", "Delete a file");
932                 CtdlRegisterProtoHook(cmd_movf, "MOVF", "Move a file");
933                 CtdlRegisterProtoHook(cmd_open, "OPEN", "Open a download file transfer");
934                 CtdlRegisterProtoHook(cmd_clos, "CLOS", "Close a download file transfer");
935                 CtdlRegisterProtoHook(cmd_uopn, "UOPN", "Open an upload file transfer");
936                 CtdlRegisterProtoHook(cmd_ucls, "UCLS", "Close an upload file transfer");
937                 CtdlRegisterProtoHook(cmd_read, "READ", "File transfer read operation");
938                 CtdlRegisterProtoHook(cmd_writ, "WRIT", "File transfer write operation");
939                 CtdlRegisterProtoHook(cmd_ndop, "NDOP", "Open a network spool file for download");
940                 CtdlRegisterProtoHook(cmd_nuop, "NUOP", "Open a network spool file for upload");
941                 CtdlRegisterProtoHook(cmd_oimg, "OIMG", "Open an image file for download");
942                 CtdlRegisterProtoHook(cmd_uimg, "UIMG", "Upload an image file");
943
944                 CtdlRegisterProtoHook(cmd_mesg, "MESG", "fetch system banners");
945                 CtdlRegisterProtoHook(cmd_emsg, "EMSG", "submit system banners");
946         }
947         /* return our Subversion id for the Log */
948         return "file_ops";
949 }