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