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