VACATION and REJECT messages now appear to come from
[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                          "%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 (strlen(filename) == 0) {
177                 cprintf("%d You must specify a file name.\n",
178                         ERROR + FILE_NOT_FOUND);
179                 return;
180         }
181
182         for (a = 0; a < strlen(filename); ++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 (strlen(filename) == 0) {
246                 cprintf("%d You must specify a file name.\n",
247                         ERROR + FILE_NOT_FOUND);
248                 return;
249         }
250
251         for (a = 0; a < strlen(filename); ++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, "Pcit%ld", CC->user.usernum);
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, 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 (strlen(filename) == 0) {
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; a < strlen(filename); ++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         struct ctdluser usbuf;
400         char which_user[USERNAME_SIZE];
401         int which_floor;
402         int a;
403
404         extract_token(filename, cmdbuf, 0, '|', sizeof filename);
405
406         if (strlen(filename) == 0) {
407                 cprintf("%d You must specify a file name.\n",
408                         ERROR + FILE_NOT_FOUND);
409                 return;
410         }
411
412         if (CC->download_fp != NULL) {
413                 cprintf("%d You already have a download file open.\n",
414                         ERROR + RESOURCE_BUSY);
415                 return;
416         }
417
418         if (!strcasecmp(filename, "_userpic_")) {
419                 extract_token(which_user, cmdbuf, 1, '|', sizeof which_user);
420                 if (getuser(&usbuf, which_user) != 0) {
421                         cprintf("%d No such user.\n",
422                                 ERROR + NO_SUCH_USER);
423                         return;
424                 }
425                 snprintf(pathname, sizeof pathname, 
426                                  "%s/%ld.gif",
427                                  ctdl_usrpic_dir,
428                                  usbuf.usernum);
429         } else if (!strcasecmp(filename, "_floorpic_")) {
430                 which_floor = extract_int(cmdbuf, 1);
431                 snprintf(pathname, sizeof pathname,
432                                  "%s/floor.%d.gif",
433                                  ctdl_image_dir, which_floor);
434         } else if (!strcasecmp(filename, "_roompic_")) {
435                 assoc_file_name(pathname, sizeof pathname, &CC->room, ctdl_image_dir);
436         } else {
437                 for (a = 0; a < strlen(filename); ++a) {
438                         filename[a] = tolower(filename[a]);
439                         if (filename[a] == '/') {
440                                 filename[a] = '_';
441                         }
442                 }
443                 snprintf(pathname, sizeof pathname,
444                                  "%s/%s.gif",
445                                  ctdl_image_dir,
446                                  filename);
447         }
448
449         CC->download_fp = fopen(pathname, "rb");
450         if (CC->download_fp == NULL) {
451                 cprintf("%d Cannot open %s: %s\n",
452                         ERROR + FILE_NOT_FOUND, pathname, strerror(errno));
453                 return;
454         }
455
456         OpenCmdResult(pathname, "image/gif");
457 }
458
459 /*
460  * open a file for uploading
461  */
462 void cmd_uopn(char *cmdbuf)
463 {
464         int a;
465
466         extract_token(CC->upl_file, cmdbuf, 0, '|', sizeof CC->upl_file);
467         extract_token(CC->upl_comment, cmdbuf, 1, '|', sizeof CC->upl_comment);
468
469         if (CtdlAccessCheck(ac_logged_in)) return;
470
471         if ((CC->room.QRflags & QR_DIRECTORY) == 0) {
472                 cprintf("%d No directory in this room.\n",
473                         ERROR + NOT_HERE);
474                 return;
475         }
476
477         if (strlen(CC->upl_file) == 0) {
478                 cprintf("%d You must specify a file name.\n",
479                         ERROR + FILE_NOT_FOUND);
480                 return;
481         }
482
483         if (CC->upload_fp != NULL) {
484                 cprintf("%d You already have a upload file open.\n",
485                         ERROR + RESOURCE_BUSY);
486                 return;
487         }
488
489         for (a = 0; a < strlen(CC->upl_file); ++a) {
490                 if (CC->upl_file[a] == '/') {
491                         CC->upl_file[a] = '_';
492                 }
493         }
494         snprintf(CC->upl_path, sizeof CC->upl_path, 
495                          "%s/%s/%s",
496                          ctdl_file_dir,
497                          CC->room.QRdirname, CC->upl_file);
498         snprintf(CC->upl_filedir, sizeof CC->upl_filedir,
499                          "%s/%s/filedir", 
500                          ctdl_file_dir,
501                          CC->room.QRdirname);
502
503         CC->upload_fp = fopen(CC->upl_path, "r");
504         if (CC->upload_fp != NULL) {
505                 fclose(CC->upload_fp);
506                 CC->upload_fp = NULL;
507                 cprintf("%d '%s' already exists\n",
508                         ERROR + ALREADY_EXISTS, CC->upl_path);
509                 return;
510         }
511
512         CC->upload_fp = fopen(CC->upl_path, "wb");
513         if (CC->upload_fp == NULL) {
514                 cprintf("%d Cannot open %s: %s\n",
515                         ERROR + INTERNAL_ERROR, CC->upl_path, strerror(errno));
516                 return;
517         }
518         cprintf("%d Ok\n", CIT_OK);
519 }
520
521
522
523 /*
524  * open an image file for uploading
525  */
526 void cmd_uimg(char *cmdbuf)
527 {
528         int is_this_for_real;
529         char basenm[256];
530         int which_floor;
531         int a;
532
533         if (num_parms(cmdbuf) < 2) {
534                 cprintf("%d Usage error.\n", ERROR + ILLEGAL_VALUE);
535                 return;
536         }
537
538         is_this_for_real = extract_int(cmdbuf, 0);
539         extract_token(basenm, cmdbuf, 1, '|', sizeof basenm);
540         if (CC->upload_fp != NULL) {
541                 cprintf("%d You already have an upload file open.\n",
542                         ERROR + RESOURCE_BUSY);
543                 return;
544         }
545
546         strcpy(CC->upl_path, "");
547
548         for (a = 0; a < strlen(basenm); ++a) {
549                 basenm[a] = tolower(basenm[a]);
550                 if (basenm[a] == '/') {
551                         basenm[a] = '_';
552                 }
553         }
554
555         if (CC->user.axlevel >= 6) {
556                 snprintf(CC->upl_path, sizeof CC->upl_path, 
557                                  "%s/%s",
558                                  ctdl_image_dir,
559                                  basenm);
560         }
561
562         if (!strcasecmp(basenm, "_userpic_")) {
563                 snprintf(CC->upl_path, sizeof CC->upl_path,
564                                  "%s/%ld.gif",
565                                  ctdl_usrpic_dir,
566                                  CC->user.usernum);
567         }
568
569         if ((!strcasecmp(basenm, "_floorpic_"))
570             && (CC->user.axlevel >= 6)) {
571                 which_floor = extract_int(cmdbuf, 2);
572                 snprintf(CC->upl_path, sizeof CC->upl_path,
573                                  "%s/floor.%d.gif",
574                                  ctdl_image_dir,
575                                  which_floor);
576         }
577
578         if ((!strcasecmp(basenm, "_roompic_")) && (is_room_aide())) {
579                 assoc_file_name(CC->upl_path, sizeof CC->upl_path, &CC->room, ctdl_image_dir);
580         }
581
582         if (strlen(CC->upl_path) == 0) {
583                 cprintf("%d Higher access required.\n",
584                         ERROR + HIGHER_ACCESS_REQUIRED);
585                 return;
586         }
587
588         if (is_this_for_real == 0) {
589                 cprintf("%d Ok to send image\n", CIT_OK);
590                 return;
591         }
592
593         CC->upload_fp = fopen(CC->upl_path, "wb");
594         if (CC->upload_fp == NULL) {
595                 cprintf("%d Cannot open %s: %s\n",
596                         ERROR + INTERNAL_ERROR, CC->upl_path, strerror(errno));
597                 return;
598         }
599         cprintf("%d Ok\n", CIT_OK);
600         CC->upload_type = UPL_IMAGE;
601 }
602
603
604 /*
605  * close the download file
606  */
607 void cmd_clos(void)
608 {
609         char buf[256];
610
611         if (CC->download_fp == NULL) {
612                 cprintf("%d You don't have a download file open.\n",
613                         ERROR + RESOURCE_NOT_OPEN);
614                 return;
615         }
616
617         fclose(CC->download_fp);
618         CC->download_fp = NULL;
619
620         if (CC->dl_is_net == 1) {
621                 CC->dl_is_net = 0;
622                 snprintf(buf, sizeof buf, 
623                                  "%s/%s",
624                                  ctdl_netout_dir,
625                                  CC->net_node);
626                 unlink(buf);
627         }
628
629         cprintf("%d Ok\n", CIT_OK);
630 }
631
632
633 /*
634  * abort an upload
635  */
636 void abort_upl(struct CitContext *who)
637 {
638         if (who->upload_fp != NULL) {
639                 fclose(who->upload_fp);
640                 who->upload_fp = NULL;
641                 unlink(CC->upl_path);
642         }
643 }
644
645
646
647 /*
648  * close the upload file
649  */
650 void cmd_ucls(char *cmd)
651 {
652         FILE *fp;
653         char upload_notice[512];
654
655         if (CC->upload_fp == NULL) {
656                 cprintf("%d You don't have an upload file open.\n", ERROR + RESOURCE_NOT_OPEN);
657                 return;
658         }
659
660         fclose(CC->upload_fp);
661         CC->upload_fp = NULL;
662
663         if ((!strcasecmp(cmd, "1")) && (CC->upload_type != UPL_FILE)) {
664                 CC->upload_type = UPL_FILE;
665                 cprintf("%d Upload completed.\n", CIT_OK);
666
667                 /* FIXME ... here we need to trigger a network run */
668
669                 return;
670         }
671
672         if (!strcasecmp(cmd, "1")) {
673                 cprintf("%d File '%s' saved.\n", CIT_OK, CC->upl_path);
674                 fp = fopen(CC->upl_filedir, "a");
675                 if (fp == NULL) {
676                         fp = fopen(CC->upl_filedir, "w");
677                 }
678                 if (fp != NULL) {
679                         fprintf(fp, "%s %s\n", CC->upl_file,
680                                 CC->upl_comment);
681                         fclose(fp);
682                 }
683
684                 /* put together an upload notice */
685                 snprintf(upload_notice, sizeof upload_notice,
686                         "NEW UPLOAD: '%s'\n %s\n",
687                         CC->upl_file, CC->upl_comment);
688                 quickie_message(CC->curr_user, NULL, NULL, CC->room.QRname,
689                                 upload_notice, 0, NULL);
690         } else {
691                 abort_upl(CC);
692                 cprintf("%d File '%s' aborted.\n", CIT_OK, CC->upl_path);
693         }
694 }
695
696
697
698 /*
699  * read from the download file
700  */
701 void cmd_read(char *cmdbuf)
702 {
703         long start_pos;
704         size_t bytes;
705         size_t actual_bytes;
706         char *buf = NULL;
707
708         start_pos = extract_long(cmdbuf, 0);
709         bytes = extract_int(cmdbuf, 1);
710
711         if (CC->download_fp == NULL) {
712                 cprintf("%d You don't have a download file open.\n",
713                         ERROR + RESOURCE_NOT_OPEN);
714                 return;
715         }
716
717         if (bytes > 100000) bytes = 100000;
718         buf = malloc(bytes + 1);
719
720         fseek(CC->download_fp, start_pos, 0);
721         actual_bytes = fread(buf, 1, bytes, CC->download_fp);
722         cprintf("%d %d\n", BINARY_FOLLOWS, (int)actual_bytes);
723         client_write(buf, actual_bytes);
724         free(buf);
725 }
726
727
728
729 /*
730  * write to the upload file
731  */
732 void cmd_writ(char *cmdbuf)
733 {
734         int bytes;
735         char *buf;
736
737         unbuffer_output();
738
739         bytes = extract_int(cmdbuf, 0);
740
741         if (CC->upload_fp == NULL) {
742                 cprintf("%d You don't have an upload file open.\n", ERROR + RESOURCE_NOT_OPEN);
743                 return;
744         }
745
746         if (bytes > 100000) {
747                 cprintf("%d You may not write more than 100000 bytes.\n",
748                         ERROR + TOO_BIG);
749                 return;
750         }
751
752         cprintf("%d %d\n", SEND_BINARY, bytes);
753         buf = malloc(bytes + 1);
754         client_read(buf, bytes);
755         fwrite(buf, bytes, 1, CC->upload_fp);
756         free(buf);
757 }
758
759
760
761
762 /*
763  * cmd_ndop() - open a network spool file for downloading
764  */
765 void cmd_ndop(char *cmdbuf)
766 {
767         char pathname[256];
768         struct stat statbuf;
769
770         if (strlen(CC->net_node) == 0) {
771                 cprintf("%d Not authenticated as a network node.\n",
772                         ERROR + NOT_LOGGED_IN);
773                 return;
774         }
775
776         if (CC->download_fp != NULL) {
777                 cprintf("%d You already have a download file open.\n",
778                         ERROR + RESOURCE_BUSY);
779                 return;
780         }
781
782         snprintf(pathname, sizeof pathname, 
783                          "%s/%s",
784                          ctdl_netout_dir,
785                          CC->net_node);
786
787         /* first open the file in append mode in order to create a
788          * zero-length file if it doesn't already exist 
789          */
790         CC->download_fp = fopen(pathname, "a");
791         if (CC->download_fp != NULL)
792                 fclose(CC->download_fp);
793
794         /* now open it */
795         CC->download_fp = fopen(pathname, "r");
796         if (CC->download_fp == NULL) {
797                 cprintf("%d cannot open %s: %s\n",
798                         ERROR + INTERNAL_ERROR, pathname, strerror(errno));
799                 return;
800         }
801
802
803         /* set this flag so other routines know that the download file
804          * currently open is a network spool file 
805          */
806         CC->dl_is_net = 1;
807
808         stat(pathname, &statbuf);
809         cprintf("%d %ld\n", CIT_OK, (long)statbuf.st_size);
810 }
811
812 /*
813  * cmd_nuop() - open a network spool file for uploading
814  */
815 void cmd_nuop(char *cmdbuf)
816 {
817         static int seq = 1;
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->upload_fp != NULL) {
826                 cprintf("%d You already have an upload file open.\n",
827                         ERROR + RESOURCE_BUSY);
828                 return;
829         }
830
831         snprintf(CC->upl_path, sizeof CC->upl_path,
832                          "%s/%s.%04lx.%04x",
833                          ctdl_netin_dir,
834                          CC->net_node, 
835                          (long)getpid(), 
836                          ++seq);
837
838         CC->upload_fp = fopen(CC->upl_path, "r");
839         if (CC->upload_fp != NULL) {
840                 fclose(CC->upload_fp);
841                 CC->upload_fp = NULL;
842                 cprintf("%d '%s' already exists\n",
843                         ERROR + ALREADY_EXISTS, CC->upl_path);
844                 return;
845         }
846
847         CC->upload_fp = fopen(CC->upl_path, "w");
848         if (CC->upload_fp == NULL) {
849                 cprintf("%d Cannot open %s: %s\n",
850                         ERROR + INTERNAL_ERROR, CC->upl_path, strerror(errno));
851                 return;
852         }
853
854         CC->upload_type = UPL_NET;
855         cprintf("%d Ok\n", CIT_OK);
856 }