ada1a78141a11a6e7ee583e5c12885091872531b
[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 #include <sys/mman.h>
19
20 #if TIME_WITH_SYS_TIME
21 # include <sys/time.h>
22 # include <time.h>
23 #else
24 # if HAVE_SYS_TIME_H
25 #  include <sys/time.h>
26 # else
27 #  include <time.h>
28 # endif
29 #endif
30
31 #include <limits.h>
32 #include <libcitadel.h>
33 #include "citadel.h"
34 #include "server.h"
35 #include "config.h"
36 #include "file_ops.h"
37 #include "sysdep_decls.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 #include "ctdl_module.h"
49 #include "user_ops.h"
50
51 /*
52  * network_talking_to()  --  concurrency checker
53  */
54 int network_talking_to(char *nodename, int operation) {
55
56         static char *nttlist = NULL;
57         char *ptr = NULL;
58         int i;
59         char buf[SIZ];
60         int retval = 0;
61
62         begin_critical_section(S_NTTLIST);
63
64         switch(operation) {
65
66                 case NTT_ADD:
67                         if (nttlist == NULL) nttlist = strdup("");
68                         if (nttlist == NULL) break;
69                         nttlist = (char *)realloc(nttlist,
70                                 (strlen(nttlist) + strlen(nodename) + 3) );
71                         strcat(nttlist, "|");
72                         strcat(nttlist, nodename);
73                         break;
74
75                 case NTT_REMOVE:
76                         if (nttlist == NULL) break;
77                         if (IsEmptyStr(nttlist)) break;
78                         ptr = malloc(strlen(nttlist));
79                         if (ptr == NULL) break;
80                         strcpy(ptr, "");
81                         for (i = 0; i < num_tokens(nttlist, '|'); ++i) {
82                                 extract_token(buf, nttlist, i, '|', sizeof buf);
83                                 if ( (!IsEmptyStr(buf))
84                                      && (strcasecmp(buf, nodename)) ) {
85                                                 strcat(ptr, buf);
86                                                 strcat(ptr, "|");
87                                 }
88                         }
89                         free(nttlist);
90                         nttlist = ptr;
91                         break;
92
93                 case NTT_CHECK:
94                         if (nttlist == NULL) break;
95                         if (IsEmptyStr(nttlist)) break;
96                         for (i = 0; i < num_tokens(nttlist, '|'); ++i) {
97                                 extract_token(buf, nttlist, i, '|', sizeof buf);
98                                 if (!strcasecmp(buf, nodename)) ++retval;
99                         }
100                         break;
101         }
102
103         if (nttlist != NULL) CtdlLogPrintf(CTDL_DEBUG, "nttlist=<%s>\n", nttlist);
104         end_critical_section(S_NTTLIST);
105         return(retval);
106 }
107
108
109
110
111 /*
112  * Server command to delete a file from a room's directory
113  */
114 void cmd_delf(char *filename)
115 {
116         char pathname[64];
117         int a;
118
119         if (CtdlAccessCheck(ac_room_aide))
120                 return;
121
122         if ((CC->room.QRflags & QR_DIRECTORY) == 0) {
123                 cprintf("%d No directory in this room.\n",
124                         ERROR + NOT_HERE);
125                 return;
126         }
127
128         if (IsEmptyStr(filename)) {
129                 cprintf("%d You must specify a file name.\n",
130                         ERROR + FILE_NOT_FOUND);
131                 return;
132         }
133         for (a = 0; !IsEmptyStr(&filename[a]); ++a) {
134                 if ( (filename[a] == '/') || (filename[a] == '\\') ) {
135                         filename[a] = '_';
136                 }
137         }
138         snprintf(pathname, sizeof pathname,
139                          "%s/%s/%s",
140                          ctdl_file_dir,
141                          CC->room.QRdirname, filename);
142         a = unlink(pathname);
143         if (a == 0) {
144                 cprintf("%d File '%s' deleted.\n", CIT_OK, pathname);
145         }
146         else {
147                 cprintf("%d File '%s' not found.\n",
148                         ERROR + FILE_NOT_FOUND, pathname);
149         }
150 }
151
152
153
154
155 /*
156  * move a file from one room directory to another
157  */
158 void cmd_movf(char *cmdbuf)
159 {
160         char filename[PATH_MAX];
161         char pathname[PATH_MAX];
162         char newpath[PATH_MAX];
163         char newroom[ROOMNAMELEN];
164         char buf[PATH_MAX];
165         int a;
166         struct ctdlroom qrbuf;
167         int rv = 0;
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 (IsEmptyStr(filename)) {
181                 cprintf("%d You must specify a file name.\n",
182                         ERROR + FILE_NOT_FOUND);
183                 return;
184         }
185
186         for (a = 0; !IsEmptyStr(&filename[a]); ++a) {
187                 if ( (filename[a] == '/') || (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 (CtdlGetRoom(&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         rv = system(buf);
222         cprintf("%d File '%s' has been moved.\n", CIT_OK, filename);
223 }
224
225
226 /*
227  * This code is common to all commands which open a file for downloading,
228  * regardless of whether it's a file from the directory, an image, a network
229  * spool file, a MIME attachment, etc.
230  * It examines the file and displays the OK result code and some information
231  * about the file.  NOTE: this stuff is Unix dependent.
232  */
233 void OpenCmdResult(char *filename, const char *mime_type)
234 {
235         struct stat statbuf;
236         time_t modtime;
237         long filesize;
238
239         fstat(fileno(CC->download_fp), &statbuf);
240         CC->download_fp_total = statbuf.st_size;
241         filesize = (long) statbuf.st_size;
242         modtime = (time_t) statbuf.st_mtime;
243
244         cprintf("%d %ld|%ld|%s|%s\n",
245                 CIT_OK, filesize, (long)modtime, filename, mime_type);
246 }
247
248
249 /*
250  * open a file for downloading
251  */
252 void cmd_open(char *cmdbuf)
253 {
254         char filename[256];
255         char pathname[PATH_MAX];
256         int a;
257
258         extract_token(filename, cmdbuf, 0, '|', sizeof filename);
259
260         if (CtdlAccessCheck(ac_logged_in)) return;
261
262         if ((CC->room.QRflags & QR_DIRECTORY) == 0) {
263                 cprintf("%d No directory in this room.\n",
264                         ERROR + NOT_HERE);
265                 return;
266         }
267
268         if (IsEmptyStr(filename)) {
269                 cprintf("%d You must specify a file name.\n",
270                         ERROR + FILE_NOT_FOUND);
271                 return;
272         }
273
274         if (CC->download_fp != NULL) {
275                 cprintf("%d You already have a download file open.\n",
276                         ERROR + RESOURCE_BUSY);
277                 return;
278         }
279
280         for (a = 0; !IsEmptyStr(&filename[a]); ++a) {
281                 if ( (filename[a] == '/') || (filename[a] == '\\') ) {
282                         filename[a] = '_';
283                 }
284         }
285
286         snprintf(pathname, sizeof pathname,
287                          "%s/%s/%s",
288                          ctdl_file_dir,
289                          CC->room.QRdirname, filename);
290         CC->download_fp = fopen(pathname, "r");
291
292         if (CC->download_fp == NULL) {
293                 cprintf("%d cannot open %s: %s\n",
294                         ERROR + INTERNAL_ERROR, pathname, strerror(errno));
295                 return;
296         }
297
298         OpenCmdResult(filename, "application/octet-stream");
299 }
300
301 /*
302  * open an image file
303  */
304 void cmd_oimg(char *cmdbuf)
305 {
306         char filename[256];
307         char pathname[PATH_MAX];
308         char MimeTestBuf[32];
309         struct ctdluser usbuf;
310         char which_user[USERNAME_SIZE];
311         int which_floor;
312         int a;
313         int rv;
314
315         extract_token(filename, cmdbuf, 0, '|', sizeof filename);
316
317         if (IsEmptyStr(filename)) {
318                 cprintf("%d You must specify a file name.\n",
319                         ERROR + FILE_NOT_FOUND);
320                 return;
321         }
322
323         if (CC->download_fp != NULL) {
324                 cprintf("%d You already have a download file open.\n",
325                         ERROR + RESOURCE_BUSY);
326                 return;
327         }
328
329         if (!strcasecmp(filename, "_userpic_")) {
330                 extract_token(which_user, cmdbuf, 1, '|', sizeof which_user);
331                 if (CtdlGetUser(&usbuf, which_user) != 0) {
332                         cprintf("%d No such user.\n",
333                                 ERROR + NO_SUCH_USER);
334                         return;
335                 }
336                 snprintf(pathname, sizeof pathname, 
337                                  "%s/%ld",
338                                  ctdl_usrpic_dir,
339                                  usbuf.usernum);
340         } else if (!strcasecmp(filename, "_floorpic_")) {
341                 which_floor = extract_int(cmdbuf, 1);
342                 snprintf(pathname, sizeof pathname,
343                                  "%s/floor.%d",
344                                  ctdl_image_dir, which_floor);
345         } else if (!strcasecmp(filename, "_roompic_")) {
346                 assoc_file_name(pathname, sizeof pathname, &CC->room, ctdl_image_dir);
347         } else {
348                 for (a = 0; !IsEmptyStr(&filename[a]); ++a) {
349                         filename[a] = tolower(filename[a]);
350                         if ( (filename[a] == '/') || (filename[a] == '\\') ) {
351                                 filename[a] = '_';
352                         }
353                 }
354                 snprintf(pathname, sizeof pathname,
355                                  "%s/%s",
356                                  ctdl_image_dir,
357                                  filename);
358         }
359
360         CC->download_fp = fopen(pathname, "rb");
361         if (CC->download_fp == NULL) {
362                 strcat(pathname, ".gif");
363                 CC->download_fp = fopen(pathname, "rb");
364         }
365         if (CC->download_fp == NULL) {
366                 cprintf("%d Cannot open %s: %s\n",
367                         ERROR + FILE_NOT_FOUND, pathname, strerror(errno));
368                 return;
369         }
370         rv = fread(&MimeTestBuf[0], 1, 32, CC->download_fp);
371         rewind (CC->download_fp);
372         OpenCmdResult(pathname, GuessMimeType(&MimeTestBuf[0], 32));
373 }
374
375 /*
376  * open a file for uploading
377  */
378 void cmd_uopn(char *cmdbuf)
379 {
380         int a;
381
382         extract_token(CC->upl_file, cmdbuf, 0, '|', sizeof CC->upl_file);
383         extract_token(CC->upl_mimetype, cmdbuf, 1, '|', sizeof CC->upl_mimetype);
384         extract_token(CC->upl_comment, cmdbuf, 2, '|', sizeof CC->upl_comment);
385
386         if (CtdlAccessCheck(ac_logged_in)) return;
387
388         if ((CC->room.QRflags & QR_DIRECTORY) == 0) {
389                 cprintf("%d No directory in this room.\n",
390                         ERROR + NOT_HERE);
391                 return;
392         }
393
394         if (IsEmptyStr(CC->upl_file)) {
395                 cprintf("%d You must specify a file name.\n",
396                         ERROR + FILE_NOT_FOUND);
397                 return;
398         }
399
400         if (CC->upload_fp != NULL) {
401                 cprintf("%d You already have a upload file open.\n",
402                         ERROR + RESOURCE_BUSY);
403                 return;
404         }
405
406         for (a = 0; !IsEmptyStr(&CC->upl_file[a]); ++a) {
407                 if ( (CC->upl_file[a] == '/') || (CC->upl_file[a] == '\\') ) {
408                         CC->upl_file[a] = '_';
409                 }
410         }
411         snprintf(CC->upl_path, sizeof CC->upl_path, 
412                          "%s/%s/%s",
413                          ctdl_file_dir,
414                          CC->room.QRdirname, CC->upl_file);
415         snprintf(CC->upl_filedir, sizeof CC->upl_filedir,
416                          "%s/%s/filedir", 
417                          ctdl_file_dir,
418                          CC->room.QRdirname);
419
420         CC->upload_fp = fopen(CC->upl_path, "r");
421         if (CC->upload_fp != NULL) {
422                 fclose(CC->upload_fp);
423                 CC->upload_fp = NULL;
424                 cprintf("%d '%s' already exists\n",
425                         ERROR + ALREADY_EXISTS, CC->upl_path);
426                 return;
427         }
428
429         CC->upload_fp = fopen(CC->upl_path, "wb");
430         if (CC->upload_fp == NULL) {
431                 cprintf("%d Cannot open %s: %s\n",
432                         ERROR + INTERNAL_ERROR, CC->upl_path, strerror(errno));
433                 return;
434         }
435         cprintf("%d Ok\n", CIT_OK);
436 }
437
438
439
440 /*
441  * open an image file for uploading
442  */
443 void cmd_uimg(char *cmdbuf)
444 {
445         int is_this_for_real;
446         char basenm[256];
447         int which_floor;
448         int a;
449
450         if (num_parms(cmdbuf) < 2) {
451                 cprintf("%d Usage error.\n", ERROR + ILLEGAL_VALUE);
452                 return;
453         }
454
455         is_this_for_real = extract_int(cmdbuf, 0);
456         extract_token(CC->upl_mimetype, cmdbuf, 1, '|', sizeof CC->upl_mimetype);
457         extract_token(basenm, cmdbuf, 2, '|', sizeof basenm);
458         if (CC->upload_fp != NULL) {
459                 cprintf("%d You already have an upload file open.\n",
460                         ERROR + RESOURCE_BUSY);
461                 return;
462         }
463
464         strcpy(CC->upl_path, "");
465
466         for (a = 0; !IsEmptyStr(&basenm[a]); ++a) {
467                 basenm[a] = tolower(basenm[a]);
468                 if ( (basenm[a] == '/') || (basenm[a] == '\\') ) {
469                         basenm[a] = '_';
470                 }
471         }
472
473         if (CC->user.axlevel >= AxAideU) {
474                 snprintf(CC->upl_path, sizeof CC->upl_path, 
475                                  "%s/%s",
476                                  ctdl_image_dir,
477                                  basenm);
478         }
479
480         if (!strcasecmp(basenm, "_userpic_")) {
481                 snprintf(CC->upl_path, sizeof CC->upl_path,
482                                  "%s/%ld.gif",
483                                  ctdl_usrpic_dir,
484                                  CC->user.usernum);
485         }
486
487         if ((!strcasecmp(basenm, "_floorpic_"))
488             && (CC->user.axlevel >= AxAideU)) {
489                 which_floor = extract_int(cmdbuf, 2);
490                 snprintf(CC->upl_path, sizeof CC->upl_path,
491                                  "%s/floor.%d.gif",
492                                  ctdl_image_dir,
493                                  which_floor);
494         }
495
496         if ((!strcasecmp(basenm, "_roompic_")) && (is_room_aide())) {
497                 assoc_file_name(CC->upl_path, sizeof CC->upl_path, &CC->room, ctdl_image_dir);
498         }
499
500         if (IsEmptyStr(CC->upl_path)) {
501                 cprintf("%d Higher access required.\n",
502                         ERROR + HIGHER_ACCESS_REQUIRED);
503                 return;
504         }
505
506         if (is_this_for_real == 0) {
507                 cprintf("%d Ok to send image\n", CIT_OK);
508                 return;
509         }
510
511         CC->upload_fp = fopen(CC->upl_path, "wb");
512         if (CC->upload_fp == NULL) {
513                 cprintf("%d Cannot open %s: %s\n",
514                         ERROR + INTERNAL_ERROR, CC->upl_path, strerror(errno));
515                 return;
516         }
517         cprintf("%d Ok\n", CIT_OK);
518         CC->upload_type = UPL_IMAGE;
519 }
520
521
522 /*
523  * close the download file
524  */
525 void cmd_clos(char *cmdbuf)
526 {
527         char buf[256];
528
529         if (CC->download_fp == NULL) {
530                 cprintf("%d You don't have a download file open.\n",
531                         ERROR + RESOURCE_NOT_OPEN);
532                 return;
533         }
534
535         fclose(CC->download_fp);
536         CC->download_fp = NULL;
537
538         if (CC->dl_is_net == 1) {
539                 CC->dl_is_net = 0;
540                 snprintf(buf, sizeof buf, 
541                                  "%s/%s",
542                                  ctdl_netout_dir,
543                                  CC->net_node);
544                 unlink(buf);
545         }
546
547         cprintf("%d Ok\n", CIT_OK);
548 }
549
550
551 /*
552  * abort an upload
553  */
554 void abort_upl(CitContext *who)
555 {
556         if (who->upload_fp != NULL) {
557                 fclose(who->upload_fp);
558                 who->upload_fp = NULL;
559                 unlink(CC->upl_path);
560         }
561 }
562
563
564
565 /*
566  * close the upload file
567  */
568 void cmd_ucls(char *cmd)
569 {
570         FILE *fp;
571         char upload_notice[512];
572         static int seq = 0;
573
574         if (CC->upload_fp == NULL) {
575                 cprintf("%d You don't have an upload file open.\n", ERROR + RESOURCE_NOT_OPEN);
576                 return;
577         }
578
579         fclose(CC->upload_fp);
580         CC->upload_fp = NULL;
581
582         if ((!strcasecmp(cmd, "1")) && (CC->upload_type != UPL_FILE)) {
583                 cprintf("%d Upload completed.\n", CIT_OK);
584
585                 if (CC->upload_type == UPL_NET) {
586                         char final_filename[PATH_MAX];
587                         snprintf(final_filename, sizeof final_filename,
588                                 "%s/%s.%04lx.%04x",
589                                 ctdl_netin_dir,
590                                 CC->net_node,
591                                 (long)getpid(),
592                                 ++seq
593                         );
594
595                         if (link(CC->upl_path, final_filename) == 0) {
596                                 unlink(CC->upl_path);
597                         }
598                         else {
599                                 CtdlLogPrintf(CTDL_ALERT, "Cannot link %d to %d: %s\n",
600                                         CC->upl_path, final_filename, strerror(errno)
601                                 );
602                         }
603
604                         /* FIXME ... here we need to trigger a network run */
605                 }
606
607                 CC->upload_type = UPL_FILE;
608                 return;
609         }
610
611         if (!strcasecmp(cmd, "1")) {
612                 cprintf("%d File '%s' saved.\n", CIT_OK, CC->upl_path);
613                 fp = fopen(CC->upl_filedir, "a");
614                 if (fp == NULL) {
615                         fp = fopen(CC->upl_filedir, "w");
616                 }
617                 if (fp != NULL) {
618                         fprintf(fp, "%s %s %s\n", CC->upl_file,
619                                 CC->upl_mimetype,
620                                 CC->upl_comment);
621                         fclose(fp);
622                 }
623
624                 /* put together an upload notice */
625                 snprintf(upload_notice, sizeof upload_notice,
626                         "NEW UPLOAD: '%s'\n %s\n%s\n",
627                          CC->upl_file, 
628                          CC->upl_comment, 
629                          CC->upl_mimetype);
630                 quickie_message(CC->curr_user, NULL, NULL, CC->room.QRname,
631                                 upload_notice, 0, NULL);
632         } else {
633                 abort_upl(CC);
634                 cprintf("%d File '%s' aborted.\n", CIT_OK, CC->upl_path);
635         }
636 }
637
638
639
640 /*
641  * read from the download file
642  */
643 void cmd_read(char *cmdbuf)
644 {
645         long start_pos;
646         size_t bytes;
647         size_t actual_bytes;
648         char *buf = NULL;
649
650         start_pos = extract_long(cmdbuf, 0);
651         bytes = extract_int(cmdbuf, 1);
652
653         if (CC->download_fp == NULL) {
654                 cprintf("%d You don't have a download file open.\n",
655                         ERROR + RESOURCE_NOT_OPEN);
656                 return;
657         }
658
659         buf = mmap(NULL, 
660                    CC->download_fp_total, 
661                    PROT_READ, 
662                    MAP_PRIVATE,
663                    fileno(CC->download_fp), 
664                    0);
665         
666         actual_bytes = CC->download_fp_total - start_pos;
667         if ((actual_bytes > 0) && (buf != NULL)) {
668                 cprintf("%d %d\n", BINARY_FOLLOWS, (int)actual_bytes);
669                 client_write(buf + start_pos, actual_bytes);
670         }
671         else {
672                 cprintf("%d %s\n", ERROR, strerror(errno));
673         }
674         munmap(buf, CC->download_fp_total);
675 }
676
677
678
679 /*
680  * write to the upload file
681  */
682 void cmd_writ(char *cmdbuf)
683 {
684         int bytes;
685         char *buf;
686         int rv;
687
688         unbuffer_output();
689
690         bytes = extract_int(cmdbuf, 0);
691
692         if (CC->upload_fp == NULL) {
693                 cprintf("%d You don't have an upload file open.\n", ERROR + RESOURCE_NOT_OPEN);
694                 return;
695         }
696
697         if (bytes > 100000) {
698                 cprintf("%d You may not write more than 100000 bytes.\n",
699                         ERROR + TOO_BIG);
700                 return;
701         }
702
703         cprintf("%d %d\n", SEND_BINARY, bytes);
704         buf = malloc(bytes + 1);
705         client_read(buf, bytes);
706         rv = fwrite(buf, bytes, 1, CC->upload_fp);
707         free(buf);
708 }
709
710
711
712
713 /*
714  * cmd_ndop() - open a network spool file for downloading
715  */
716 void cmd_ndop(char *cmdbuf)
717 {
718         char pathname[256];
719         struct stat statbuf;
720
721         if (IsEmptyStr(CC->net_node)) {
722                 cprintf("%d Not authenticated as a network node.\n",
723                         ERROR + NOT_LOGGED_IN);
724                 return;
725         }
726
727         if (CC->download_fp != NULL) {
728                 cprintf("%d You already have a download file open.\n",
729                         ERROR + RESOURCE_BUSY);
730                 return;
731         }
732
733         snprintf(pathname, sizeof pathname, 
734                          "%s/%s",
735                          ctdl_netout_dir,
736                          CC->net_node);
737
738         /* first open the file in append mode in order to create a
739          * zero-length file if it doesn't already exist 
740          */
741         CC->download_fp = fopen(pathname, "a");
742         if (CC->download_fp != NULL)
743                 fclose(CC->download_fp);
744
745         /* now open it */
746         CC->download_fp = fopen(pathname, "r");
747         if (CC->download_fp == NULL) {
748                 cprintf("%d cannot open %s: %s\n",
749                         ERROR + INTERNAL_ERROR, pathname, strerror(errno));
750                 return;
751         }
752
753
754         /* set this flag so other routines know that the download file
755          * currently open is a network spool file 
756          */
757         CC->dl_is_net = 1;
758
759         stat(pathname, &statbuf);
760         CC->download_fp_total = statbuf.st_size;
761         cprintf("%d %ld\n", CIT_OK, (long)statbuf.st_size);
762 }
763
764 /*
765  * cmd_nuop() - open a network spool file for uploading
766  */
767 void cmd_nuop(char *cmdbuf)
768 {
769         static int seq = 1;
770
771         if (IsEmptyStr(CC->net_node)) {
772                 cprintf("%d Not authenticated as a network node.\n",
773                         ERROR + NOT_LOGGED_IN);
774                 return;
775         }
776
777         if (CC->upload_fp != NULL) {
778                 cprintf("%d You already have an upload file open.\n",
779                         ERROR + RESOURCE_BUSY);
780                 return;
781         }
782
783         snprintf(CC->upl_path, sizeof CC->upl_path,
784                          "%s/%s.%04lx.%04x",
785                          ctdl_nettmp_dir,
786                          CC->net_node, 
787                          (long)getpid(), 
788                          ++seq);
789
790         CC->upload_fp = fopen(CC->upl_path, "r");
791         if (CC->upload_fp != NULL) {
792                 fclose(CC->upload_fp);
793                 CC->upload_fp = NULL;
794                 cprintf("%d '%s' already exists\n",
795                         ERROR + ALREADY_EXISTS, CC->upl_path);
796                 return;
797         }
798
799         CC->upload_fp = fopen(CC->upl_path, "w");
800         if (CC->upload_fp == NULL) {
801                 cprintf("%d Cannot open %s: %s\n",
802                         ERROR + INTERNAL_ERROR, CC->upl_path, strerror(errno));
803                 return;
804         }
805
806         CC->upload_type = UPL_NET;
807         cprintf("%d Ok\n", CIT_OK);
808 }
809
810
811 /*****************************************************************************/
812 /*                      MODULE INITIALIZATION STUFF                          */
813 /*****************************************************************************/
814
815 CTDL_MODULE_INIT(file_ops)
816 {
817         if (!threading) {
818                 CtdlRegisterProtoHook(cmd_delf, "DELF", "Delete a file");
819                 CtdlRegisterProtoHook(cmd_movf, "MOVF", "Move a file");
820                 CtdlRegisterProtoHook(cmd_open, "OPEN", "Open a download file transfer");
821                 CtdlRegisterProtoHook(cmd_clos, "CLOS", "Close a download file transfer");
822                 CtdlRegisterProtoHook(cmd_uopn, "UOPN", "Open an upload file transfer");
823                 CtdlRegisterProtoHook(cmd_ucls, "UCLS", "Close an upload file transfer");
824                 CtdlRegisterProtoHook(cmd_read, "READ", "File transfer read operation");
825                 CtdlRegisterProtoHook(cmd_writ, "WRIT", "File transfer write operation");
826                 CtdlRegisterProtoHook(cmd_ndop, "NDOP", "Open a network spool file for download");
827                 CtdlRegisterProtoHook(cmd_nuop, "NUOP", "Open a network spool file for upload");
828                 CtdlRegisterProtoHook(cmd_oimg, "OIMG", "Open an image file for download");
829                 CtdlRegisterProtoHook(cmd_uimg, "UIMG", "Upload an image file");
830         }
831         /* return our Subversion id for the Log */
832         return "$Id$";
833 }