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