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