rework the networking locking facility using the hashlist.
[citadel.git] / citadel / file_ops.c
1 /* 
2  * Server functions which handle file transfers and room directories.
3  */
4
5 #include "sysdep.h"
6 #include <stdlib.h>
7 #include <unistd.h>
8 #include <stdio.h>
9 #include <fcntl.h>
10 #include <sys/stat.h>
11 #include <errno.h>
12 #include <ctype.h>
13 #include <string.h>
14 #include <sys/stat.h>
15 #include <sys/mman.h>
16
17 #if TIME_WITH_SYS_TIME
18 # include <sys/time.h>
19 # include <time.h>
20 #else
21 # if HAVE_SYS_TIME_H
22 #  include <sys/time.h>
23 # else
24 #  include <time.h>
25 # endif
26 #endif
27
28 #include <limits.h>
29 #include <libcitadel.h>
30 #include "citadel.h"
31 #include "server.h"
32 #include "config.h"
33 #include "file_ops.h"
34 #include "sysdep_decls.h"
35 #include "support.h"
36 #include "room_ops.h"
37 #include "msgbase.h"
38 #include "citserver.h"
39 #include "threads.h"
40
41 #ifndef HAVE_SNPRINTF
42 #include "snprintf.h"
43 #endif
44
45 #include "ctdl_module.h"
46 #include "user_ops.h"
47
48 /*
49  * network_talking_to()  --  concurrency checker
50  */
51 static HashList *nttlist = NULL;
52 int network_talking_to(const char *nodename, long len, int operation) {
53
54         int retval = 0;
55         HashPos *Pos = NULL;
56         void *vdata;
57
58         begin_critical_section(S_NTTLIST);
59
60         switch(operation) {
61
62                 case NTT_ADD:
63                         if (nttlist == NULL) 
64                                 nttlist = NewHash(1, NULL);
65                         Put(nttlist, nodename, len, NewStrBufPlain(nodename, len), HFreeStrBuf);
66                         syslog(LOG_DEBUG, "nttlist: added <%s>\n", nodename);
67                         break;
68                 case NTT_REMOVE:
69                         if ((nttlist == NULL) ||
70                             (GetCount(nttlist) == 0))
71                                 break;
72                         Pos = GetNewHashPos(nttlist, 1);
73                         GetHashPosFromKey (nttlist, nodename, len, Pos);
74
75                         DeleteEntryFromHash(nttlist, Pos);
76                         DeleteHashPos(&Pos);
77                         syslog(LOG_DEBUG, "nttlist: removed <%s>\n", nodename);
78
79                         break;
80
81                 case NTT_CHECK:
82                         if ((nttlist == NULL) ||
83                             (GetCount(nttlist) == 0))
84                                 break;
85                         if (!GetHash(nttlist, nodename, len, &vdata))
86                                 retval ++;
87                         syslog(LOG_DEBUG, "nttlist: have [%d] <%s>\n", retval, nodename);
88                         break;
89         }
90
91         end_critical_section(S_NTTLIST);
92         return(retval);
93 }
94
95 void cleanup_nttlist(void)
96 {
97         begin_critical_section(S_NTTLIST);
98         DeleteHash(&nttlist);
99         end_critical_section(S_NTTLIST);
100 }
101
102
103
104 /*
105  * Server command to delete a file from a room's directory
106  */
107 void cmd_delf(char *filename)
108 {
109         char pathname[64];
110         int a;
111
112         if (CtdlAccessCheck(ac_room_aide))
113                 return;
114
115         if ((CC->room.QRflags & QR_DIRECTORY) == 0) {
116                 cprintf("%d No directory in this room.\n",
117                         ERROR + NOT_HERE);
118                 return;
119         }
120
121         if (IsEmptyStr(filename)) {
122                 cprintf("%d You must specify a file name.\n",
123                         ERROR + FILE_NOT_FOUND);
124                 return;
125         }
126         for (a = 0; !IsEmptyStr(&filename[a]); ++a) {
127                 if ( (filename[a] == '/') || (filename[a] == '\\') ) {
128                         filename[a] = '_';
129                 }
130         }
131         snprintf(pathname, sizeof pathname,
132                          "%s/%s/%s",
133                          ctdl_file_dir,
134                          CC->room.QRdirname, filename);
135         a = unlink(pathname);
136         if (a == 0) {
137                 cprintf("%d File '%s' deleted.\n", CIT_OK, pathname);
138         }
139         else {
140                 cprintf("%d File '%s' not found.\n",
141                         ERROR + FILE_NOT_FOUND, pathname);
142         }
143 }
144
145
146
147
148 /*
149  * move a file from one room directory to another
150  */
151 void cmd_movf(char *cmdbuf)
152 {
153         char filename[PATH_MAX];
154         char pathname[PATH_MAX];
155         char newpath[PATH_MAX];
156         char newroom[ROOMNAMELEN];
157         char buf[PATH_MAX];
158         int a;
159         struct ctdlroom qrbuf;
160         int rv = 0;
161
162         extract_token(filename, cmdbuf, 0, '|', sizeof filename);
163         extract_token(newroom, cmdbuf, 1, '|', sizeof newroom);
164
165         if (CtdlAccessCheck(ac_room_aide)) return;
166
167         if ((CC->room.QRflags & QR_DIRECTORY) == 0) {
168                 cprintf("%d No directory in this room.\n",
169                         ERROR + NOT_HERE);
170                 return;
171         }
172
173         if (IsEmptyStr(filename)) {
174                 cprintf("%d You must specify a file name.\n",
175                         ERROR + FILE_NOT_FOUND);
176                 return;
177         }
178
179         for (a = 0; !IsEmptyStr(&filename[a]); ++a) {
180                 if ( (filename[a] == '/') || (filename[a] == '\\') ) {
181                         filename[a] = '_';
182                 }
183         }
184         snprintf(pathname, sizeof pathname, "./files/%s/%s",
185                  CC->room.QRdirname, filename);
186         if (access(pathname, 0) != 0) {
187                 cprintf("%d File '%s' not found.\n",
188                         ERROR + FILE_NOT_FOUND, pathname);
189                 return;
190         }
191
192         if (CtdlGetRoom(&qrbuf, newroom) != 0) {
193                 cprintf("%d '%s' does not exist.\n", ERROR + ROOM_NOT_FOUND, newroom);
194                 return;
195         }
196         if ((qrbuf.QRflags & QR_DIRECTORY) == 0) {
197                 cprintf("%d '%s' is not a directory room.\n",
198                         ERROR + NOT_HERE, qrbuf.QRname);
199                 return;
200         }
201         snprintf(newpath, sizeof newpath, "./files/%s/%s", qrbuf.QRdirname,
202                  filename);
203         if (link(pathname, newpath) != 0) {
204                 cprintf("%d Couldn't move file: %s\n", ERROR + INTERNAL_ERROR,
205                         strerror(errno));
206                 return;
207         }
208         unlink(pathname);
209
210         /* this is a crude method of copying the file description */
211         snprintf(buf, sizeof buf,
212                  "cat ./files/%s/filedir |grep \"%s\" >>./files/%s/filedir",
213                  CC->room.QRdirname, filename, qrbuf.QRdirname);
214         rv = system(buf);
215         cprintf("%d File '%s' has been moved.\n", CIT_OK, filename);
216 }
217
218
219 /*
220  * This code is common to all commands which open a file for downloading,
221  * regardless of whether it's a file from the directory, an image, a network
222  * spool file, a MIME attachment, etc.
223  * It examines the file and displays the OK result code and some information
224  * about the file.  NOTE: this stuff is Unix dependent.
225  */
226 void OpenCmdResult(char *filename, const char *mime_type)
227 {
228         struct stat statbuf;
229         time_t modtime;
230         long filesize;
231
232         fstat(fileno(CC->download_fp), &statbuf);
233         CC->download_fp_total = statbuf.st_size;
234         filesize = (long) statbuf.st_size;
235         modtime = (time_t) statbuf.st_mtime;
236
237         cprintf("%d %ld|%ld|%s|%s\n",
238                 CIT_OK, filesize, (long)modtime, filename, mime_type);
239 }
240
241
242 /*
243  * open a file for downloading
244  */
245 void cmd_open(char *cmdbuf)
246 {
247         char filename[256];
248         char pathname[PATH_MAX];
249         int a;
250
251         extract_token(filename, cmdbuf, 0, '|', sizeof filename);
252
253         if (CtdlAccessCheck(ac_logged_in)) return;
254
255         if ((CC->room.QRflags & QR_DIRECTORY) == 0) {
256                 cprintf("%d No directory in this room.\n",
257                         ERROR + NOT_HERE);
258                 return;
259         }
260
261         if (IsEmptyStr(filename)) {
262                 cprintf("%d You must specify a file name.\n",
263                         ERROR + FILE_NOT_FOUND);
264                 return;
265         }
266
267         if (CC->download_fp != NULL) {
268                 cprintf("%d You already have a download file open.\n",
269                         ERROR + RESOURCE_BUSY);
270                 return;
271         }
272
273         for (a = 0; !IsEmptyStr(&filename[a]); ++a) {
274                 if ( (filename[a] == '/') || (filename[a] == '\\') ) {
275                         filename[a] = '_';
276                 }
277         }
278
279         snprintf(pathname, sizeof pathname,
280                          "%s/%s/%s",
281                          ctdl_file_dir,
282                          CC->room.QRdirname, filename);
283         CC->download_fp = fopen(pathname, "r");
284
285         if (CC->download_fp == NULL) {
286                 cprintf("%d cannot open %s: %s\n",
287                         ERROR + INTERNAL_ERROR, pathname, strerror(errno));
288                 return;
289         }
290
291         OpenCmdResult(filename, "application/octet-stream");
292 }
293
294 /*
295  * open an image file
296  */
297 void cmd_oimg(char *cmdbuf)
298 {
299         char filename[256];
300         char pathname[PATH_MAX];
301         char MimeTestBuf[32];
302         struct ctdluser usbuf;
303         char which_user[USERNAME_SIZE];
304         int which_floor;
305         int a;
306         int rv;
307
308         extract_token(filename, cmdbuf, 0, '|', sizeof filename);
309
310         if (IsEmptyStr(filename)) {
311                 cprintf("%d You must specify a file name.\n",
312                         ERROR + FILE_NOT_FOUND);
313                 return;
314         }
315
316         if (CC->download_fp != NULL) {
317                 cprintf("%d You already have a download file open.\n",
318                         ERROR + RESOURCE_BUSY);
319                 return;
320         }
321
322         if (!strcasecmp(filename, "_userpic_")) {
323                 extract_token(which_user, cmdbuf, 1, '|', sizeof which_user);
324                 if (CtdlGetUser(&usbuf, which_user) != 0) {
325                         cprintf("%d No such user.\n",
326                                 ERROR + NO_SUCH_USER);
327                         return;
328                 }
329                 snprintf(pathname, sizeof pathname, 
330                                  "%s/%ld",
331                                  ctdl_usrpic_dir,
332                                  usbuf.usernum);
333         } else if (!strcasecmp(filename, "_floorpic_")) {
334                 which_floor = extract_int(cmdbuf, 1);
335                 snprintf(pathname, sizeof pathname,
336                                  "%s/floor.%d",
337                                  ctdl_image_dir, which_floor);
338         } else if (!strcasecmp(filename, "_roompic_")) {
339                 assoc_file_name(pathname, sizeof pathname, &CC->room, ctdl_image_dir);
340         } else {
341                 for (a = 0; !IsEmptyStr(&filename[a]); ++a) {
342                         filename[a] = tolower(filename[a]);
343                         if ( (filename[a] == '/') || (filename[a] == '\\') ) {
344                                 filename[a] = '_';
345                         }
346                 }
347                 snprintf(pathname, sizeof pathname,
348                                  "%s/%s",
349                                  ctdl_image_dir,
350                                  filename);
351         }
352
353         CC->download_fp = fopen(pathname, "rb");
354         if (CC->download_fp == NULL) {
355                 strcat(pathname, ".gif");
356                 CC->download_fp = fopen(pathname, "rb");
357         }
358         if (CC->download_fp == NULL) {
359                 cprintf("%d Cannot open %s: %s\n",
360                         ERROR + FILE_NOT_FOUND, pathname, strerror(errno));
361                 return;
362         }
363         rv = fread(&MimeTestBuf[0], 1, 32, CC->download_fp);
364         if (rv == -1) {
365                 cprintf("%d Cannot access %s: %s\n",
366                         ERROR + FILE_NOT_FOUND, pathname, strerror(errno));
367                 return;
368         }
369
370         rewind (CC->download_fp);
371         OpenCmdResult(pathname, GuessMimeType(&MimeTestBuf[0], 32));
372 }
373
374 /*
375  * open a file for uploading
376  */
377 void cmd_uopn(char *cmdbuf)
378 {
379         int a;
380
381         extract_token(CC->upl_file, cmdbuf, 0, '|', sizeof CC->upl_file);
382         extract_token(CC->upl_mimetype, cmdbuf, 1, '|', sizeof CC->upl_mimetype);
383         extract_token(CC->upl_comment, cmdbuf, 2, '|', sizeof CC->upl_comment);
384
385         if (CtdlAccessCheck(ac_logged_in)) return;
386
387         if ((CC->room.QRflags & QR_DIRECTORY) == 0) {
388                 cprintf("%d No directory in this room.\n",
389                         ERROR + NOT_HERE);
390                 return;
391         }
392
393         if (IsEmptyStr(CC->upl_file)) {
394                 cprintf("%d You must specify a file name.\n",
395                         ERROR + FILE_NOT_FOUND);
396                 return;
397         }
398
399         if (CC->upload_fp != NULL) {
400                 cprintf("%d You already have a upload file open.\n",
401                         ERROR + RESOURCE_BUSY);
402                 return;
403         }
404
405         for (a = 0; !IsEmptyStr(&CC->upl_file[a]); ++a) {
406                 if ( (CC->upl_file[a] == '/') || (CC->upl_file[a] == '\\') ) {
407                         CC->upl_file[a] = '_';
408                 }
409         }
410         snprintf(CC->upl_path, sizeof CC->upl_path, 
411                          "%s/%s/%s",
412                          ctdl_file_dir,
413                          CC->room.QRdirname, CC->upl_file);
414         snprintf(CC->upl_filedir, sizeof CC->upl_filedir,
415                          "%s/%s/filedir", 
416                          ctdl_file_dir,
417                          CC->room.QRdirname);
418
419         CC->upload_fp = fopen(CC->upl_path, "r");
420         if (CC->upload_fp != NULL) {
421                 fclose(CC->upload_fp);
422                 CC->upload_fp = NULL;
423                 cprintf("%d '%s' already exists\n",
424                         ERROR + ALREADY_EXISTS, CC->upl_path);
425                 return;
426         }
427
428         CC->upload_fp = fopen(CC->upl_path, "wb");
429         if (CC->upload_fp == NULL) {
430                 cprintf("%d Cannot open %s: %s\n",
431                         ERROR + INTERNAL_ERROR, CC->upl_path, strerror(errno));
432                 return;
433         }
434         cprintf("%d Ok\n", CIT_OK);
435 }
436
437
438
439 /*
440  * open an image file for uploading
441  */
442 void cmd_uimg(char *cmdbuf)
443 {
444         int is_this_for_real;
445         char basenm[256];
446         int which_floor;
447         int a;
448
449         if (num_parms(cmdbuf) < 2) {
450                 cprintf("%d Usage error.\n", ERROR + ILLEGAL_VALUE);
451                 return;
452         }
453
454         is_this_for_real = extract_int(cmdbuf, 0);
455         extract_token(CC->upl_mimetype, cmdbuf, 1, '|', sizeof CC->upl_mimetype);
456         extract_token(basenm, cmdbuf, 2, '|', sizeof basenm);
457         if (CC->upload_fp != NULL) {
458                 cprintf("%d You already have an upload file open.\n",
459                         ERROR + RESOURCE_BUSY);
460                 return;
461         }
462
463         strcpy(CC->upl_path, "");
464
465         for (a = 0; !IsEmptyStr(&basenm[a]); ++a) {
466                 basenm[a] = tolower(basenm[a]);
467                 if ( (basenm[a] == '/') || (basenm[a] == '\\') ) {
468                         basenm[a] = '_';
469                 }
470         }
471
472         if (CC->user.axlevel >= AxAideU) {
473                 snprintf(CC->upl_path, sizeof CC->upl_path, 
474                                  "%s/%s",
475                                  ctdl_image_dir,
476                                  basenm);
477         }
478
479         if (!strcasecmp(basenm, "_userpic_")) {
480                 snprintf(CC->upl_path, sizeof CC->upl_path,
481                                  "%s/%ld.gif",
482                                  ctdl_usrpic_dir,
483                                  CC->user.usernum);
484         }
485
486         if ((!strcasecmp(basenm, "_floorpic_"))
487             && (CC->user.axlevel >= AxAideU)) {
488                 which_floor = extract_int(cmdbuf, 2);
489                 snprintf(CC->upl_path, sizeof CC->upl_path,
490                                  "%s/floor.%d.gif",
491                                  ctdl_image_dir,
492                                  which_floor);
493         }
494
495         if ((!strcasecmp(basenm, "_roompic_")) && (is_room_aide())) {
496                 assoc_file_name(CC->upl_path, sizeof CC->upl_path, &CC->room, ctdl_image_dir);
497         }
498
499         if (IsEmptyStr(CC->upl_path)) {
500                 cprintf("%d Higher access required.\n",
501                         ERROR + HIGHER_ACCESS_REQUIRED);
502                 return;
503         }
504
505         if (is_this_for_real == 0) {
506                 cprintf("%d Ok to send image\n", CIT_OK);
507                 return;
508         }
509
510         CC->upload_fp = fopen(CC->upl_path, "wb");
511         if (CC->upload_fp == NULL) {
512                 cprintf("%d Cannot open %s: %s\n",
513                         ERROR + INTERNAL_ERROR, CC->upl_path, strerror(errno));
514                 return;
515         }
516         cprintf("%d Ok\n", CIT_OK);
517         CC->upload_type = UPL_IMAGE;
518 }
519
520
521 /*
522  * close the download file
523  */
524 void cmd_clos(char *cmdbuf)
525 {
526         char buf[256];
527
528         if (CC->download_fp == NULL) {
529                 cprintf("%d You don't have a download file open.\n",
530                         ERROR + RESOURCE_NOT_OPEN);
531                 return;
532         }
533
534         fclose(CC->download_fp);
535         CC->download_fp = NULL;
536
537         if (CC->dl_is_net == 1) {
538                 CC->dl_is_net = 0;
539                 snprintf(buf, sizeof buf, 
540                                  "%s/%s",
541                                  ctdl_netout_dir,
542                                  CC->net_node);
543                 unlink(buf);
544         }
545
546         cprintf("%d Ok\n", CIT_OK);
547 }
548
549
550 /*
551  * abort an upload
552  */
553 void abort_upl(CitContext *who)
554 {
555         if (who->upload_fp != NULL) {
556                 fclose(who->upload_fp);
557                 who->upload_fp = NULL;
558                 unlink(CC->upl_path);
559         }
560 }
561
562
563
564 /*
565  * close the upload file
566  */
567 void cmd_ucls(char *cmd)
568 {
569         FILE *fp;
570         char upload_notice[512];
571         static int seq = 0;
572
573         if (CC->upload_fp == NULL) {
574                 cprintf("%d You don't have an upload file open.\n", ERROR + RESOURCE_NOT_OPEN);
575                 return;
576         }
577
578         fclose(CC->upload_fp);
579         CC->upload_fp = NULL;
580
581         if ((!strcasecmp(cmd, "1")) && (CC->upload_type != UPL_FILE)) {
582                 cprintf("%d Upload completed.\n", CIT_OK);
583
584                 if (CC->upload_type == UPL_NET) {
585                         char final_filename[PATH_MAX];
586                         snprintf(final_filename, sizeof final_filename,
587                                 "%s/%s.%04lx.%04x",
588                                 ctdl_netin_dir,
589                                 CC->net_node,
590                                 (long)getpid(),
591                                 ++seq
592                         );
593
594                         if (link(CC->upl_path, final_filename) == 0) {
595                                 unlink(CC->upl_path);
596                         }
597                         else {
598                                 syslog(LOG_ALERT, "Cannot link %s to %s: %s\n",
599                                         CC->upl_path, final_filename, strerror(errno)
600                                 );
601                         }
602
603                         /* FIXME ... here we need to trigger a network run */
604                 }
605
606                 CC->upload_type = UPL_FILE;
607                 return;
608         }
609
610         if (!strcasecmp(cmd, "1")) {
611                 cprintf("%d File '%s' saved.\n", CIT_OK, CC->upl_path);
612                 fp = fopen(CC->upl_filedir, "a");
613                 if (fp == NULL) {
614                         fp = fopen(CC->upl_filedir, "w");
615                 }
616                 if (fp != NULL) {
617                         fprintf(fp, "%s %s %s\n", CC->upl_file,
618                                 CC->upl_mimetype,
619                                 CC->upl_comment);
620                         fclose(fp);
621                 }
622
623                 /* put together an upload notice */
624                 snprintf(upload_notice, sizeof upload_notice,
625                         "NEW UPLOAD: '%s'\n %s\n%s\n",
626                          CC->upl_file, 
627                          CC->upl_comment, 
628                          CC->upl_mimetype);
629                 quickie_message(CC->curr_user, NULL, NULL, CC->room.QRname,
630                                 upload_notice, 0, NULL);
631         } else {
632                 abort_upl(CC);
633                 cprintf("%d File '%s' aborted.\n", CIT_OK, CC->upl_path);
634         }
635 }
636
637
638 /*
639  * read from the download file
640  */
641 void cmd_read(char *cmdbuf)
642 {
643         long start_pos;
644         size_t bytes;
645         char buf[SIZ];
646
647         /* The client will transmit its requested offset and byte count */
648         start_pos = extract_long(cmdbuf, 0);
649         bytes = extract_int(cmdbuf, 1);
650
651         if (CC->download_fp == NULL) {
652                 cprintf("%d You don't have a download file open.\n",
653                         ERROR + RESOURCE_NOT_OPEN);
654                 return;
655         }
656
657         /* If necessary, reduce the byte count to the size of our buffer */
658         if (bytes > sizeof(buf)) {
659                 bytes = sizeof(buf);
660         }
661
662         fseek(CC->download_fp, start_pos, 0);
663         bytes = fread(buf, 1, bytes, CC->download_fp);
664         if (bytes > 0) {
665                 /* Tell the client the actual byte count and transmit it */
666                 cprintf("%d %d\n", BINARY_FOLLOWS, (int)bytes);
667                 client_write(buf, bytes);
668         }
669         else {
670                 cprintf("%d %s\n", ERROR, strerror(errno));
671         }
672 }
673
674
675 /*
676  * write to the upload file
677  */
678 void cmd_writ(char *cmdbuf)
679 {
680         int bytes;
681         char *buf;
682         int rv;
683
684         unbuffer_output();
685
686         bytes = extract_int(cmdbuf, 0);
687
688         if (CC->upload_fp == NULL) {
689                 cprintf("%d You don't have an upload file open.\n", ERROR + RESOURCE_NOT_OPEN);
690                 return;
691         }
692
693         if (bytes > 100000) {
694                 cprintf("%d You may not write more than 100000 bytes.\n",
695                         ERROR + TOO_BIG);
696                 return;
697         }
698
699         cprintf("%d %d\n", SEND_BINARY, bytes);
700         buf = malloc(bytes + 1);
701         client_read(buf, bytes);
702         rv = fwrite(buf, bytes, 1, CC->upload_fp);
703         if (rv == -1) {
704                 syslog(LOG_EMERG, "Couldn't write: %s\n",
705                        strerror(errno));
706         }
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                 CtdlRegisterCleanupHook(cleanup_nttlist);
831         }
832         /* return our Subversion id for the Log */
833         return "file_ops";
834 }