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