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