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