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