original netbsd's tr(1)
[minix.git] / commands / ftpd200 / file.c
blob381cfdbeac55910475001040d7c894e6fafb0249
1 /* file.c Copyright 1992-2000 by Michael Temari All Rights Reserved
3 * This file is part of ftpd.
5 * This file handles:
7 * ALLO APPE CDUP CWD DELE LIST MDTM MODE MKD NLST PWD REST RETR
8 * RMD RNFR RNTO SITE SIZE STAT STOR STOU STRU SYST TYPE
10 * 01/25/96 Initial Release Michael Temari
11 * 03/09/00 Michael Temari, <Michael@TemWare.Com>
14 #include <sys/types.h>
15 #include <sys/stat.h>
16 #include <sys/wait.h>
17 #include <stdio.h>
18 #include <stdlib.h>
19 #include <fcntl.h>
20 #include <errno.h>
21 #include <unistd.h>
22 #include <string.h>
23 #include <time.h>
24 #include <net/hton.h>
25 #include <net/gen/in.h>
26 #include <net/gen/inet.h>
27 #include <net/gen/tcp.h>
29 #include "ftpd.h"
30 #include "access.h"
31 #include "file.h"
32 #include "net.h"
34 _PROTOTYPE(static int fdxcmd, (int cmd, char *arg));
35 _PROTOTYPE(static int endfdxcmd, (int fd));
36 _PROTOTYPE(static int asciisize, (char *filename, unsigned long *filesize));
37 _PROTOTYPE(static int cnvtfile, (char *name, char **name2));
38 _PROTOTYPE(static int procfile, (char *name));
39 _PROTOTYPE(static unsigned long fsize, (char *fname));
40 _PROTOTYPE(static int sendfile, (char *name, int xmode));
41 _PROTOTYPE(static int recvfile, (char *name, int xmode));
42 _PROTOTYPE(static char *uniqname, (void));
43 _PROTOTYPE(static int docrc, (char *buff, int xmode));
44 _PROTOTYPE(static int dofdet, (char *buff));
45 _PROTOTYPE(static char *path, (char *fname));
47 #define SEND_FILE 0
48 #define SEND_NLST 1
49 #define SEND_LIST 2
51 #define RECV_FILE 0
52 #define RECV_APND 1
53 #define RECV_UNIQ 2
55 #define CNVT_ERROR 0
56 #define CNVT_NONE 1
57 #define CNVT_TAR 2
58 #define CNVT_TAR_Z 3
59 #define CNVT_COMP 4
60 #define CNVT_TAR_GZ 5
61 #define CNVT_GZIP 6
62 #define CNVT_UNCOMP 7
65 #define PROG_FTPDSH "ftpdsh"
66 #define CMD_NLST 1
67 #define CMD_LIST 2
68 #define CMD_CRC 3
70 static char *msg550 = "550 %s %s.\r\n";
72 static unsigned long file_restart = 0;
74 static char rnfr[256];
75 static char buffer[8192];
76 static char bufout[8192];
78 static cmdpid = -1;
80 /* allocate, we don't need no stink'n allocate */
81 int doALLO(buff)
82 char *buff;
84 printf("202 ALLO command not needed at this site.\r\n");
86 return(GOOD);
89 /* append to a file if it exists */
90 int doAPPE(buff)
91 char *buff;
93 return(recvfile(buff, RECV_APND));
96 /* change to parent directory */
97 int doCDUP(buff)
98 char *buff;
100 if(ChkLoggedIn())
101 return(GOOD);
103 return(doCWD(".."));
106 /* change directory */
107 int doCWD(buff)
108 char *buff;
110 if(ChkLoggedIn())
111 return(GOOD);
113 if(chdir(buff))
114 printf(msg550, buff, strerror(errno));
115 else {
116 showmsg("250", ".ftpd_msg");
117 printf("250 %s command okay.\r\n", line);
120 return(GOOD);
123 /* remove a file */
124 int doDELE(buff)
125 char *buff;
127 if(ChkLoggedIn())
128 return(GOOD);
130 if(anonymous) {
131 printf("550 Command not allowed for anonymous user\r\n");
132 return(GOOD);
135 if(unlink(buff))
136 printf(msg550, buff, strerror(errno));
137 else {
138 printf("250 File \"%s\" deleted.\r\n", buff);
139 logit("DELE", path(buff));
142 return(GOOD);
145 /* directory listing */
146 int doLIST(buff)
147 char *buff;
149 file_restart = 0;
151 return(sendfile(buff, SEND_LIST));
154 /* file modification time, btw when will this be put into an RFC */
155 int doMDTM(buff)
156 char *buff;
158 struct stat st;
159 struct tm *t;
161 if(ChkLoggedIn())
162 return(GOOD);
164 if(stat(buff, &st)) {
165 printf(msg550, buff, strerror(errno));
166 return(GOOD);
169 if((st.st_mode & S_IFMT) != S_IFREG) {
170 printf("550 Not a regular file.\r\n");
171 return(GOOD);
174 t = gmtime(&st.st_mtime);
176 printf("215 %04d%02d%02d%02d%02d%02d\r\n",
177 t->tm_year+1900, t->tm_mon+1, t->tm_mday,
178 t->tm_hour, t->tm_min, t->tm_sec);
180 return(GOOD);
183 /* mode */
184 int doMODE(buff)
185 char *buff;
187 switch(*buff) {
188 case 'b':
189 case 'B':
190 printf("200 Mode set to %c.\r\n", *buff);
191 mode = MODE_B;
192 break;
193 case 's':
194 case 'S':
195 printf("200 Mode set to %c.\r\n", *buff);
196 mode = MODE_S;
197 break;
198 default:
199 printf("501 Unknown mode %c.\r\n", *buff);
202 return(GOOD);
205 /* make a directory */
206 int doMKD(buff)
207 char *buff;
209 if(ChkLoggedIn())
210 return(GOOD);
212 if(anonymous) {
213 printf("550 Command not allowed for anonymous user\r\n");
214 return(GOOD);
217 if(mkdir(buff, 0777))
218 printf(msg550, buff, strerror(errno));
219 else {
220 printf("257 \"%s\" directory created.\r\n", buff);
221 logit("MKD ", path(buff));
224 return(GOOD);
227 /* name listing */
228 int doNLST(buff)
229 char *buff;
231 file_restart = 0;
233 return(sendfile(buff, SEND_NLST));
236 /* where are we */
237 int doPWD(buff)
238 char *buff;
240 char dir[128];
242 if(ChkLoggedIn())
243 return(GOOD);
245 if(getcwd(dir, sizeof(dir)) == (char *)NULL)
246 printf(msg550, buff, strerror(errno));
247 else
248 printf("257 \"%s\" is current directory.\r\n", dir);
250 return(GOOD);
253 /* restart command */
254 int doREST(buff)
255 char *buff;
257 if(ChkLoggedIn())
258 return(GOOD);
260 file_restart = atol(buff);
262 printf("350 Next file transfer will restart at %lu.\r\n", file_restart);
264 return(GOOD);
267 /* they want a file */
268 int doRETR(buff)
269 char *buff;
271 return(sendfile(buff, SEND_FILE));
274 /* remove a directory */
275 int doRMD(buff)
276 char *buff;
278 if(ChkLoggedIn())
279 return(GOOD);
281 if(anonymous) {
282 printf("550 Command not allowed for anonymous user\r\n");
283 return(GOOD);
286 if(rmdir(buff))
287 printf(msg550, buff, strerror(errno));
288 else {
289 printf("250 Directory \"%s\" deleted.\r\n", buff);
290 logit("RMD ", path(buff));
293 return(GOOD);
296 /* rename from */
297 int doRNFR(buff)
298 char *buff;
300 if(ChkLoggedIn())
301 return(GOOD);
303 if(anonymous) {
304 printf("550 Command not allowed for anonymous user\r\n");
305 return(GOOD);
308 strncpy(rnfr, buff, sizeof(rnfr));
309 rnfr[sizeof(rnfr)-1] = '\0';
311 printf("350 Got RNFR waiting for RNTO.\r\n");
313 return(GOOD);
316 /* rename to */
317 int doRNTO(buff)
318 char *buff;
320 if(ChkLoggedIn())
321 return(GOOD);
323 if(anonymous) {
324 printf("550 Command not allowed for anonymous user\r\n");
325 return(GOOD);
328 if(rnfr[0] == '\0') {
329 printf("550 Rename failed.\r\n");
330 return(GOOD);
333 if(rename(rnfr, buff) < 0)
334 printf("550 Rename failed. Error %s\r\n", strerror(errno));
335 else {
336 printf("250 Renamed %s to %s.\r\n", rnfr, buff);
337 logit("RNFR", path(rnfr));
338 logit("RNTO", path(buff));
341 rnfr[0] = '\0';
343 return(GOOD);
346 /* xmode = 0 for multiline crc, xmode <> 0 for single file single line crc */
347 static int docrc(buff, xmode)
348 char *buff;
349 int xmode;
351 unsigned short cs;
352 long fs;
353 int fd;
354 int s;
355 char *p;
357 if((fd = fdxcmd(CMD_CRC, buff)) < 0) {
358 printf("501 Could not obtain CRC.\r\n");
359 return(GOOD);
362 if(xmode == 0)
363 printf("202-SITE CRC \"%s\"\r\n", buff);
365 while(1) {
366 p = buffer;
367 while(1) {
368 if((s = read(fd, p, 1)) != 1) {
369 if(xmode == 0)
370 printf("202 SITE CRC DONE.\r\n");
371 else
372 printf("501 Could not obtain CRC.\r\n");
373 endfdxcmd(fd);
374 return(GOOD);
376 if(*p == '\n') {
377 *p++ = '\r';
378 *p++ = '\n';
379 *p = '\0';
380 break;
382 p++;
384 if(xmode != 0)
385 break;
386 printf(" %s", buffer);
389 cs = atoi(buffer);
391 fs = atol(buffer+6);
393 printf("202 CRC %05u %ld.\r\n", cs, fs);
395 endfdxcmd(fd);
397 return(GOOD);
400 /* site specific */
401 int doSITE(buff)
402 char *buff;
404 char *args;
406 if(ChkLoggedIn())
407 return(GOOD);
410 strncpy(line, buff, sizeof(line));
411 line[sizeof(line)-1] = '\0';
413 cvtline(&args);
415 if(!strcmp(line, "CRC") || !strcmp(line, "CCRC"))
416 return(docrc(args, strcmp(line, "CRC")));
418 if(!strcmp(line, "FDET"))
419 return(dofdet(args));
421 printf("501 Unknown SITE command %s.\r\n", line);
423 return(GOOD);
426 static unsigned long fsize(fname)
427 char *fname;
429 struct stat st;
430 unsigned long fs = 0L;
432 if(stat(fname, &st))
433 return(fs);
435 if((st.st_mode & S_IFMT) != S_IFREG)
436 return(fs);
438 if(type == TYPE_A)
439 return(fs);
441 fs = st.st_size;
443 return(fs);
446 /* file size, btw when will this be put into an RFC */
447 int doSIZE(buff)
448 char *buff;
450 struct stat st;
451 unsigned long filesize;
453 if(ChkLoggedIn())
454 return(GOOD);
456 if(stat(buff, &st)) {
457 printf(msg550, buff, strerror(errno));
458 return(GOOD);
461 if((st.st_mode & S_IFMT) != S_IFREG) {
462 printf("550 Not a regular file.\r\n");
463 return(GOOD);
466 filesize = st.st_size;
468 if(type == TYPE_A)
469 if(asciisize(buff, &filesize))
470 return(GOOD);
472 printf("215 %lu\r\n", filesize);
474 return(GOOD);
477 /* server status, or file status */
478 int doSTAT(buff)
479 char *buff;
481 time_t now;
482 struct tm *tm;
483 int fd;
484 int s;
486 if(!*buff) {
487 (void) time(&now);
488 tm = localtime(&now);
489 printf("211-%s(%s:%u) FTP server status:\r\n",
490 myhostname, inet_ntoa(myipaddr), ntohs(myport));
491 printf(" Version %s ", FtpdVersion);
492 printf("%s, %02d %s %d %02d:%02d:%02d %s\r\n", days[tm->tm_wday],
493 tm->tm_mday, months[tm->tm_mon], 1900+tm->tm_year,
494 tm->tm_hour, tm->tm_min, tm->tm_sec, tzname[tm->tm_isdst]);
495 printf(" Connected to %s:%u\r\n", inet_ntoa(rmtipaddr), ntohs(rmtport));
496 if(!loggedin)
497 printf(" Not logged in\r\n");
498 else
499 printf(" Logged in %s\r\n", username);
500 printf(" MODE: %s\r\n",(mode == MODE_B) ? "Block" : "Stream");
501 printf(" TYPE: %s\r\n",(type == TYPE_A) ? "Ascii" : "Binary");
502 printf("211 End of status\r\n");
503 return(GOOD);
506 if(ChkLoggedIn())
507 return(GOOD);
509 printf("211-Status of %s:\r\n", buff);
511 if((fd = fdxcmd(CMD_LIST, buff)) < 0)
512 printf(" Could not retrieve status");
513 else {
514 while((s = read(fd, buffer, 1)) == 1) {
515 if(*buffer == '\n')
516 printf("\r\n");
517 else
518 printf("%c", *buffer);
520 endfdxcmd(fd);
523 printf("211 End of status\r\n");
525 return(GOOD);
528 /* hey look, we're getting a file */
529 int doSTOR(buff)
530 char *buff;
532 return(recvfile(buff, RECV_FILE));
535 /* hey, get a file unique */
536 int doSTOU(buff)
537 char *buff;
539 return(recvfile(buff, RECV_UNIQ));
542 /* structure */
543 int doSTRU(buff)
544 char *buff;
546 switch(*buff) {
547 case 'f':
548 case 'F':
549 printf("200 Structure set to %c.\r\n", *buff);
550 break;
551 default:
552 printf("501 Unknown structure %c.\r\n", *buff);
555 return(GOOD);
558 /* we're UNIX and proud of it! */
559 int doSYST(buff)
560 char *buff;
562 printf("215 UNIX Type: L8\r\n");
564 return(GOOD);
567 /* change transfer type */
568 int doTYPE(buff)
569 char *buff;
571 if(*(buff+1) != '\0') {
572 printf("501 Syntax error in parameters.\r\n");
573 return(GOOD);
576 switch(*buff) {
577 case 'A':
578 case 'a':
579 type = TYPE_A;
580 printf("200 Type set to A.\r\n");
581 break;
582 case 'I':
583 case 'i':
584 type = TYPE_I;
585 printf("200 Type set to I.\r\n");
586 break;
587 default:
588 printf("501 Invalid type %c.\r\n", *buff);
591 return(GOOD);
594 static int fdxcmd(cmd, arg)
595 int cmd;
596 char *arg;
598 char xcmd[3];
599 char *argv[5];
600 int fds[2];
601 char *smallenv[] = { "PATH=/bin:/usr/bin:/usr/local/bin", NULL, NULL };
603 if((smallenv[1] = getenv("TZ")) != NULL) smallenv[1] -= 3; /* ouch... */
605 sprintf(xcmd, "%d", cmd);
607 argv[0] = PROG_FTPDSH;
608 argv[1] = xcmd;
609 argv[2] = arg;
610 argv[3] = (char *)NULL;
612 if(pipe(fds) < 0)
613 return(-1);
615 if((cmdpid = fork()) < 0) {
616 close(fds[0]);
617 close(fds[1]);
618 return(-1);
621 if(cmdpid == 0) { /* Child */
622 close(fds[0]);
623 close(0);
624 open("/dev/null", O_RDONLY);
625 dup2(fds[1], 1);
626 dup2(fds[1], 2);
627 close(fds[1]);
628 sprintf(argv[0], "/bin/%s", PROG_FTPDSH);
629 execve(argv[0], argv, smallenv);
630 sprintf(argv[0], "/usr/bin/%s", PROG_FTPDSH);
631 execve(argv[0], argv, smallenv);
632 sprintf(argv[0], "/usr/local/bin/%s", PROG_FTPDSH);
633 execve(argv[0], argv, smallenv);
634 exit(0);
637 close(fds[1]);
639 return(fds[0]);
642 /* Same as close if not cmd child started */
643 static int endfdxcmd(fd)
644 int fd;
646 int s;
647 int cs;
649 close(fd);
651 if(cmdpid == -1)
652 return(0);
654 s = waitpid(cmdpid, &cs, 0);
656 cmdpid = -1;
658 return(0);
661 /* returns -1 = size could not be determined, */
662 /* 0 = size determined and in filesize */
663 static int asciisize(filename, filesize)
664 char *filename;
665 unsigned long *filesize;
667 unsigned long count;
668 int fd;
669 char *p, *pp;
670 int cnt;
672 if((fd = open(filename, O_RDONLY)) < 0) {
673 printf(msg550, filename, strerror(errno));
674 return(-1);
677 count = 0;
679 while((cnt = read(fd, buffer, sizeof(buffer))) > 0) {
680 count += cnt;
681 p = buffer;
682 while(cnt > 0)
683 if((pp = memchr(p, '\n', cnt)) != (char *)NULL) {
684 count++;
685 cnt = cnt - 1 - (pp - p);
686 p = pp + 1;
687 } else
688 break;
691 if(cnt == 0) {
692 *filesize = count;
693 close(fd);
694 return(0);
697 printf(msg550, filename, strerror(errno));
699 close(fd);
701 return(-1);
704 /* see if we need to run a command to convert the file */
705 static int cnvtfile(name, name2)
706 char *name;
707 char **name2;
709 struct stat st;
710 static char fname[256];
711 char *p;
712 int cmode;
714 if(!stat(name, &st)) /* file exists can't be a conversion */
715 if((st.st_mode & S_IFMT) != S_IFREG) { /* must be regular file */
716 printf("550 Not a regular file.\r\n");
717 return(CNVT_ERROR);
718 } else
719 return(CNVT_NONE);
721 if(errno != ENOENT) { /* doesn't exist is okay, others are not */
722 printf(msg550, name, strerror(errno));
723 return(CNVT_ERROR);
726 /* find out what kind of conversion */
727 strncpy(fname, name, sizeof(fname));
728 fname[sizeof(fname)-1] = '\0';
730 p = fname + strlen(fname);
731 cmode = CNVT_ERROR;
732 while(p > fname && cmode == CNVT_ERROR) {
733 if(*p == '.') {
734 if(!strcmp(p, ".tar"))
735 cmode = CNVT_TAR;
736 else
737 if(!strcmp(p, ".tar.Z"))
738 cmode = CNVT_TAR_Z;
739 else
740 if(!strcmp(p, ".Z"))
741 cmode = CNVT_COMP;
742 else
743 if(!strcmp(p, ".tar.gz"))
744 cmode = CNVT_TAR_GZ;
745 else
746 if(!strcmp(p, ".gz"))
747 cmode = CNVT_GZIP;
749 if (cmode != CNVT_ERROR) {
750 /* is there a file to convert? */
751 *p = '\0';
752 if (!stat(fname, &st)) break;
753 *p = '.';
754 cmode = CNVT_ERROR;
757 p--;
760 if(cmode == CNVT_ERROR) {
761 printf(msg550, fname, strerror(errno));
762 return(CNVT_ERROR);
765 if(cmode == CNVT_COMP || cmode == CNVT_GZIP || cmode == CNVT_UNCOMP)
766 if((st.st_mode & S_IFMT) != S_IFREG) {
767 printf("550 Not a regular file.\r\n");
768 return(CNVT_ERROR);
771 *name2 = fname;
773 return(cmode);
776 static int procfile(name)
777 char *name;
779 int cmd;
780 int fd;
781 char *name2;
783 cmd = cnvtfile(name, &name2);
785 switch(cmd) {
786 case CNVT_TAR:
787 fd = fdxcmd(cmd + 10, name2);
788 break;
789 case CNVT_TAR_Z:
790 fd = fdxcmd(cmd + 10, name2);
791 break;
792 case CNVT_COMP:
793 fd = fdxcmd(cmd + 10, name2);
794 break;
795 case CNVT_TAR_GZ:
796 fd = fdxcmd(cmd + 10, name2);
797 break;
798 case CNVT_GZIP:
799 fd = fdxcmd(cmd + 10, name2);
800 break;
801 case CNVT_UNCOMP:
802 fd = fdxcmd(cmd + 10, name2);
803 break;
804 case CNVT_NONE:
805 fd = open(name, O_RDONLY);
806 break;
807 case CNVT_ERROR:
808 default:
809 return(-1);
812 if(fd < 0)
813 printf(msg550, name, strerror(errno));
815 return(fd);
818 /* oh no, they're taking a file */
819 static int sendfile(name, xmode)
820 char *name;
821 int xmode;
823 char *fname;
824 int fd, s;
825 time_t datastart, dataend;
826 unsigned long datacount;
827 long kbs;
828 char c;
829 char *p;
830 char *op, *ope;
831 off_t sp;
832 int doascii;
833 unsigned long fs;
834 char block[3];
836 if(ChkLoggedIn())
837 return(GOOD);
839 switch(xmode) {
840 case SEND_NLST:
841 fname = "NLST";
842 fd = fdxcmd(CMD_NLST, name);
843 if(fd < 0)
844 printf(msg550, name, strerror(errno));
845 break;
846 case SEND_LIST:
847 fname = "LIST";
848 fd = fdxcmd(CMD_LIST, name);
849 if(fd < 0)
850 printf(msg550, name, strerror(errno));
851 break;
852 default:
853 fname = name;
854 fd = procfile(name);
855 if(fd < 0)
856 logit("FAIL", path(fname));
857 else
858 logit("SEND", path(fname));
861 if(fd < 0)
862 return(GOOD);
864 /* set file position at approriate spot */
865 if(file_restart) {
866 if(type == TYPE_A) {
867 sp = 0;
868 while(sp < file_restart) {
869 sp++;
870 s = read(fd, buffer, 1);
871 if(s < 0) {
872 printf(msg550, fname, strerror(errno));
873 endfdxcmd(fd);
874 file_restart = 0;
875 return(GOOD);
877 if(s == 0) break;
878 if(*buffer == '\n')
879 sp++;
881 } else {
882 sp = lseek(fd, file_restart, SEEK_SET);
883 if(sp == -1) {
884 printf(msg550, fname, strerror(errno));
885 endfdxcmd(fd);
886 file_restart = 0;
887 return(GOOD);
890 if(sp != file_restart) {
891 printf("550 File restart point error. %lu not %lu\r\n", sp, file_restart);
892 endfdxcmd(fd);
893 file_restart = 0;
894 return(GOOD);
897 file_restart = 0;
899 fs = fsize(fname);
900 if(fs == 0L)
901 printf("%03d File %s okay. Opening data connection.\r\n",
902 ftpdata_fd >= 0 ? 125 : 150, fname);
903 else
904 printf("%03d Opening %s mode data connection for %s (%ld bytes).\r\n",
905 ftpdata_fd >= 0 ? 125 : 150,
906 type == TYPE_A ? "ASCII" : "BINARY",
907 fname, fs);
908 fflush(stdout);
910 #ifdef DEBUG
911 fprintf(logfile, "After 125/150 b4 DataConnect\n");
912 fflush(logfile);
913 #endif
915 if(DataConnect()) {
916 endfdxcmd(fd);
917 return(GOOD);
920 #ifdef DEBUG
921 fprintf(logfile, "After DataConnect\n");
922 fflush(logfile);
923 fprintf(logfile, "ftpd: parent %d start sendfile \n", getpid());
924 fflush(logfile);
925 #endif
927 /* start transfer */
928 doascii = (type == TYPE_A) ||
929 ((xmode == SEND_LIST) || (xmode == SEND_NLST)); /* per RFC1123 4.1.2.7 */
930 datacount = 0;
931 time(&datastart);
932 op = bufout; ope = bufout + sizeof(bufout) - 3;
933 while((s = read(fd, buffer, sizeof(buffer))) > 0) {
934 #ifdef DEBUG
935 fprintf(logfile, "sendfile read %d\n", s); fflush(logfile);
936 #endif
937 datacount += s;
938 if(doascii) {
939 p = buffer;
940 while(s-- > 0) {
941 c = *p++;
942 if(c == '\n') {
943 *op++ = '\r';
944 datacount++;
946 *op++ = c;
947 if(op >= ope) {
948 if(mode == MODE_B) {
949 block[0] = '\0';
950 *(u16_t *)&block[1] = htons(op - bufout);
951 write(ftpdata_fd, block, sizeof(block));
953 write(ftpdata_fd, bufout, op - bufout);
954 op = bufout;
957 } else {
958 if(mode == MODE_B) {
959 block[0] = '\0';
960 *(u16_t *)&block[1] = htons(s);
961 write(ftpdata_fd, block, sizeof(block));
963 s = write(ftpdata_fd, buffer, s);
966 if(op > bufout) {
967 if(mode == MODE_B) {
968 block[0] = MODE_B_EOF;
969 *(u16_t *)&block[1] = htons(op - bufout);
970 write(ftpdata_fd, block, sizeof(block));
972 write(ftpdata_fd, bufout, op - bufout);
973 } else
974 if(mode == MODE_B) {
975 block[0] = MODE_B_EOF;
976 *(u16_t *)&block[1] = htons(0);
977 write(ftpdata_fd, block, sizeof(block));
979 time(&dataend);
981 #ifdef DEBUG
982 fprintf(logfile, "ftpd: parent %d end sendfile \n", getpid());
983 fflush(logfile);
984 #endif
986 endfdxcmd(fd);
987 if(mode != MODE_B) {
988 close(ftpdata_fd);
989 ftpdata_fd = -1;
992 if(dataend == datastart) dataend++;
993 kbs = (datacount * 100 / (dataend - datastart)) / 1024;
995 if(s < 0)
996 printf("451 Transfer aborted.\r\n");
997 else
998 printf("%03d Transfer finished successfully. %ld.%02d KB/s\r\n",
999 mode == MODE_B ? 250 : 226,
1000 (long)(kbs / 100), (int)(kbs % 100));
1002 return(GOOD);
1005 static int recvfile(name, xmode)
1006 char *name;
1007 int xmode;
1009 char *fname;
1010 time_t datastart, dataend;
1011 unsigned long datacount;
1012 long kbs;
1013 char c;
1014 char *p;
1015 char *op, *ope;
1016 int fd, oflag;
1017 int s;
1018 int gotcr;
1019 off_t sp;
1020 char block[3];
1021 unsigned short cnt;
1023 if(ChkLoggedIn())
1024 return(GOOD);
1026 fname = name;
1028 switch(xmode) {
1029 case RECV_APND:
1030 oflag = O_WRONLY | O_APPEND;
1031 break;
1032 case RECV_UNIQ:
1033 fname = uniqname();
1034 oflag = O_WRONLY | O_CREAT;
1035 break;
1036 default:
1037 oflag = O_WRONLY | O_CREAT | O_TRUNC;
1040 if(file_restart)
1041 oflag = O_RDWR;
1043 fd = open(fname, oflag, (anonymous ? 0000:0600));
1045 if(fd < 0) {
1046 printf(msg550, fname, strerror(errno));
1047 return(GOOD);
1050 /* log the received file */
1051 logit("RECV", path(fname));
1053 /* set file position at approriate spot */
1054 if(file_restart) {
1055 if(type == TYPE_A) {
1056 sp = 0;
1057 while(sp < file_restart) {
1058 sp++;
1059 s = read(fd, buffer, 1);
1060 if(s < 0) {
1061 printf(msg550, fname, strerror(errno));
1062 close(fd);
1063 file_restart = 0;
1064 return(GOOD);
1066 if(s == 0) break;
1067 if(*buffer == '\n')
1068 sp++;
1070 } else {
1071 sp = lseek(fd, file_restart, SEEK_SET);
1072 if(sp == -1) {
1073 printf(msg550, fname, strerror(errno));
1074 close(fd);
1075 file_restart = 0;
1076 return(GOOD);
1079 if(sp != file_restart) {
1080 printf("550 File restart point error. %lu not %lu\r\n", sp, file_restart);
1081 close(fd);
1082 file_restart = 0;
1083 return(GOOD);
1086 file_restart = 0;
1088 if(xmode == RECV_UNIQ)
1089 printf("%03d FILE: %s\r\n",
1090 ftpdata_fd >= 0 ? 125 : 150, fname); /* per RFC1123 4.1.2.9 */
1091 else
1092 printf("%03d File %s okay. Opening data connection.\r\n",
1093 ftpdata_fd >= 0 ? 125 : 150, fname);
1094 fflush(stdout);
1096 if(DataConnect()) {
1097 close(fd);
1098 return(GOOD);
1101 #ifdef DEBUG
1102 fprintf(logfile, "ftpd: parent %d start recvfile \n", getpid());
1103 fflush(logfile);
1104 #endif
1106 /* start receiving file */
1107 datacount = 0;
1108 gotcr = 0;
1109 op = bufout; ope = bufout + sizeof(bufout) - 3;
1110 cnt = 0;
1111 time(&datastart);
1112 while(1) {
1113 if(mode != MODE_B)
1114 cnt = sizeof(buffer);
1115 else
1116 if(cnt == 0) {
1117 s = read(ftpdata_fd, block, sizeof(block));
1118 cnt = ntohs(*(u16_t *)&block[1]);
1119 s = 0;
1120 if(cnt == 0 && block[0] & MODE_B_EOF)
1121 break;
1123 s = read(ftpdata_fd, buffer, cnt > sizeof(buffer) ? sizeof(buffer) : cnt);
1124 if(s <= 0) break;
1125 cnt -= s;
1126 datacount += (long)s;
1127 if(type == TYPE_A) {
1128 p = buffer;
1129 while(s-- > 0) {
1130 c = *p++;
1131 if(gotcr) {
1132 gotcr = 0;
1133 if(c != '\n')
1134 *op++ = '\r';
1136 if(c == '\r')
1137 gotcr = 1;
1138 else
1139 *op++ = c;
1140 if(op >= ope) {
1141 write(fd, bufout, op - bufout);
1142 op = bufout;
1145 } else
1146 write(fd, buffer, s);
1147 if(cnt == 0 && mode == MODE_B && block[0] & MODE_B_EOF) {
1148 s = 0;
1149 break;
1152 if(gotcr)
1153 *op++ = '\r';
1154 if(op > bufout)
1155 write(fd, bufout, op - bufout);
1156 time(&dataend);
1158 #ifdef DEBUG
1159 fprintf(logfile, "ftpd: parent %d end recvfile \n", getpid());
1160 fflush(logfile);
1161 #endif
1163 close(fd);
1164 if(mode != MODE_B) {
1165 close(ftpdata_fd);
1166 ftpdata_fd = -1;
1169 if(dataend == datastart) dataend++;
1170 kbs = (datacount * 100 / (dataend - datastart)) / 1024;
1172 if((mode == MODE_B && cnt != 0) || s != 0)
1173 printf("451 Transfer aborted.\r\n");
1174 else {
1175 printf("%03d Transfer finished successfully. ",
1176 mode == MODE_B ? 250 : 226);
1177 if(xmode == RECV_UNIQ)
1178 printf("Unique file %s. ", fname);
1179 printf("%ld.%02d KB/s\r\n", (long)(kbs / 100), (int)(kbs % 100));
1182 return(GOOD);
1185 static char *uniqname()
1187 static char uniq[32];
1188 int i;
1189 struct stat st;
1191 for(i = 0; i < 1000; i++) {
1192 sprintf(uniq, "ftpd%d%d", getpid(), i);
1193 if(stat(uniq, &st) == -1)
1194 return(uniq);
1196 return(uniq);
1199 static char *spath[256];
1200 static char *path(fname)
1201 char *fname;
1203 char dir[128];
1205 if(getcwd(dir, sizeof(dir)) == (char *)NULL)
1206 sprintf(dir, "???");
1208 if(fname[0] == '/')
1209 sprintf((char *)spath, "%s%s", newroot, fname);
1210 else
1211 if(dir[1] == '\0')
1212 sprintf((char *)spath, "%s%s%s", newroot, dir, fname);
1213 else
1214 sprintf((char *)spath, "%s%s/%s", newroot, dir, fname);
1216 return((char *)spath);
1219 /* do file detail */
1220 static int dofdet(buff)
1221 char *buff;
1223 struct stat st;
1224 char ft;
1226 if(ChkLoggedIn())
1227 return(GOOD);
1229 if(stat(buff, &st)) {
1230 printf("501 Could not obtain file detail.\r\n");
1231 return(GOOD);
1233 switch(st.st_mode & S_IFMT) {
1234 case S_IFIFO: ft = 'p'; break;
1235 case S_IFCHR: ft = 'c'; break;
1236 case S_IFDIR: ft = 'd'; break;
1237 case S_IFBLK: ft = 'b'; break;
1238 case S_IFREG: ft = 'f'; break;
1239 default: ft = '?'; break;
1241 printf("202 %c %u %u %u %u %u %lu %lu\r\n",
1242 ft, /* file type */
1243 st.st_rdev >> 8, /* Major */
1244 st.st_rdev & 0xff, /* Minor */
1245 st.st_uid, /* UID */
1246 st.st_gid, /* GID */
1247 st.st_mode, /* File Modes */
1248 st.st_size, /* SIZE */
1249 st.st_mtime); /* Mod Time */
1251 return(GOOD);