6f0669093bc08d705aad4ad141101f18b341ad85
[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 "user_ops.h"
39 #include "support.h"
40 #include "room_ops.h"
41 #include "msgbase.h"
42 #include "citserver.h"
43 #include "threads.h"
44
45 #ifndef HAVE_SNPRINTF
46 #include "snprintf.h"
47 #endif
48
49 #include "ctdl_module.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 >= 6) {
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 >= 6)) {
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
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                 CC->upload_type = UPL_FILE;
583                 cprintf("%d Upload completed.\n", CIT_OK);
584
585                 /* FIXME ... here we need to trigger a network run */
586
587                 return;
588         }
589
590         if (!strcasecmp(cmd, "1")) {
591                 cprintf("%d File '%s' saved.\n", CIT_OK, CC->upl_path);
592                 fp = fopen(CC->upl_filedir, "a");
593                 if (fp == NULL) {
594                         fp = fopen(CC->upl_filedir, "w");
595                 }
596                 if (fp != NULL) {
597                         fprintf(fp, "%s %s %s\n", CC->upl_file,
598                                 CC->upl_mimetype,
599                                 CC->upl_comment);
600                         fclose(fp);
601                 }
602
603                 /* put together an upload notice */
604                 snprintf(upload_notice, sizeof upload_notice,
605                         "NEW UPLOAD: '%s'\n %s\n%s\n",
606                          CC->upl_file, 
607                          CC->upl_comment, 
608                          CC->upl_mimetype);
609                 quickie_message(CC->curr_user, NULL, NULL, CC->room.QRname,
610                                 upload_notice, 0, NULL);
611         } else {
612                 abort_upl(CC);
613                 cprintf("%d File '%s' aborted.\n", CIT_OK, CC->upl_path);
614         }
615 }
616
617
618
619 /*
620  * read from the download file
621  */
622 void cmd_read(char *cmdbuf)
623 {
624         long start_pos;
625         size_t bytes;
626         size_t actual_bytes;
627         char *buf = NULL;
628
629         start_pos = extract_long(cmdbuf, 0);
630         bytes = extract_int(cmdbuf, 1);
631
632         if (CC->download_fp == NULL) {
633                 cprintf("%d You don't have a download file open.\n",
634                         ERROR + RESOURCE_NOT_OPEN);
635                 return;
636         }
637
638         buf = mmap(NULL, 
639                    CC->download_fp_total, 
640                    PROT_READ, 
641                    MAP_PRIVATE,
642                    fileno(CC->download_fp), 
643                    0);
644         
645         actual_bytes = CC->download_fp_total - start_pos;
646         if ((actual_bytes > 0) && (buf != NULL)) {
647                 cprintf("%d %d\n", BINARY_FOLLOWS, (int)actual_bytes);
648                 client_write(buf + start_pos, actual_bytes);
649         }
650         else {
651                 cprintf("%d %s\n", ERROR, strerror(errno));
652         }
653         munmap(buf, CC->download_fp_total);
654 }
655
656
657
658 /*
659  * write to the upload file
660  */
661 void cmd_writ(char *cmdbuf)
662 {
663         int bytes;
664         char *buf;
665         int rv;
666
667         unbuffer_output();
668
669         bytes = extract_int(cmdbuf, 0);
670
671         if (CC->upload_fp == NULL) {
672                 cprintf("%d You don't have an upload file open.\n", ERROR + RESOURCE_NOT_OPEN);
673                 return;
674         }
675
676         if (bytes > 100000) {
677                 cprintf("%d You may not write more than 100000 bytes.\n",
678                         ERROR + TOO_BIG);
679                 return;
680         }
681
682         cprintf("%d %d\n", SEND_BINARY, bytes);
683         buf = malloc(bytes + 1);
684         client_read(buf, bytes);
685         rv = fwrite(buf, bytes, 1, CC->upload_fp);
686         free(buf);
687 }
688
689
690
691
692 /*
693  * cmd_ndop() - open a network spool file for downloading
694  */
695 void cmd_ndop(char *cmdbuf)
696 {
697         char pathname[256];
698         struct stat statbuf;
699
700         if (IsEmptyStr(CC->net_node)) {
701                 cprintf("%d Not authenticated as a network node.\n",
702                         ERROR + NOT_LOGGED_IN);
703                 return;
704         }
705
706         if (CC->download_fp != NULL) {
707                 cprintf("%d You already have a download file open.\n",
708                         ERROR + RESOURCE_BUSY);
709                 return;
710         }
711
712         snprintf(pathname, sizeof pathname, 
713                          "%s/%s",
714                          ctdl_netout_dir,
715                          CC->net_node);
716
717         /* first open the file in append mode in order to create a
718          * zero-length file if it doesn't already exist 
719          */
720         CC->download_fp = fopen(pathname, "a");
721         if (CC->download_fp != NULL)
722                 fclose(CC->download_fp);
723
724         /* now open it */
725         CC->download_fp = fopen(pathname, "r");
726         if (CC->download_fp == NULL) {
727                 cprintf("%d cannot open %s: %s\n",
728                         ERROR + INTERNAL_ERROR, pathname, strerror(errno));
729                 return;
730         }
731
732
733         /* set this flag so other routines know that the download file
734          * currently open is a network spool file 
735          */
736         CC->dl_is_net = 1;
737
738         stat(pathname, &statbuf);
739         CC->download_fp_total = statbuf.st_size;
740         cprintf("%d %ld\n", CIT_OK, (long)statbuf.st_size);
741 }
742
743 /*
744  * cmd_nuop() - open a network spool file for uploading
745  */
746 void cmd_nuop(char *cmdbuf)
747 {
748         static int seq = 1;
749
750         if (IsEmptyStr(CC->net_node)) {
751                 cprintf("%d Not authenticated as a network node.\n",
752                         ERROR + NOT_LOGGED_IN);
753                 return;
754         }
755
756         if (CC->upload_fp != NULL) {
757                 cprintf("%d You already have an upload file open.\n",
758                         ERROR + RESOURCE_BUSY);
759                 return;
760         }
761
762         snprintf(CC->upl_path, sizeof CC->upl_path,
763                          "%s/%s.%04lx.%04x",
764                          ctdl_netin_dir,
765                          CC->net_node, 
766                          (long)getpid(), 
767                          ++seq);
768
769         CC->upload_fp = fopen(CC->upl_path, "r");
770         if (CC->upload_fp != NULL) {
771                 fclose(CC->upload_fp);
772                 CC->upload_fp = NULL;
773                 cprintf("%d '%s' already exists\n",
774                         ERROR + ALREADY_EXISTS, CC->upl_path);
775                 return;
776         }
777
778         CC->upload_fp = fopen(CC->upl_path, "w");
779         if (CC->upload_fp == NULL) {
780                 cprintf("%d Cannot open %s: %s\n",
781                         ERROR + INTERNAL_ERROR, CC->upl_path, strerror(errno));
782                 return;
783         }
784
785         CC->upload_type = UPL_NET;
786         cprintf("%d Ok\n", CIT_OK);
787 }
788
789
790 /*****************************************************************************/
791 /*                      MODULE INITIALIZATION STUFF                          */
792 /*****************************************************************************/
793
794 CTDL_MODULE_INIT(file_ops)
795 {
796         if (!threading) {
797                 CtdlRegisterProtoHook(cmd_delf, "DELF", "Autoconverted. TODO: document me.");
798                 CtdlRegisterProtoHook(cmd_movf, "MOVF", "Autoconverted. TODO: document me.");
799                 CtdlRegisterProtoHook(cmd_open, "OPEN", "Autoconverted. TODO: document me.");
800                 CtdlRegisterProtoHook(cmd_clos, "CLOS", "Autoconverted. TODO: document me.");
801                 CtdlRegisterProtoHook(cmd_uopn, "UOPN", "Autoconverted. TODO: document me.");
802                 CtdlRegisterProtoHook(cmd_ucls, "UCLS", "Autoconverted. TODO: document me.");
803                 CtdlRegisterProtoHook(cmd_read, "READ", "Autoconverted. TODO: document me.");
804                 CtdlRegisterProtoHook(cmd_writ, "WRIT", "Autoconverted. TODO: document me.");
805                 CtdlRegisterProtoHook(cmd_oimg, "OIMG", "Autoconverted. TODO: document me.");
806                 CtdlRegisterProtoHook(cmd_ndop, "NDOP", "Autoconverted. TODO: document me.");
807                 CtdlRegisterProtoHook(cmd_nuop, "NUOP", "Autoconverted. TODO: document me.");
808                 CtdlRegisterProtoHook(cmd_uimg, "UIMG", "Autoconverted. TODO: document me.");
809         }
810         /* return our Subversion id for the Log */
811         return "$Id$";
812 }