1 /* file.c Copyright 1992-2000 by Michael Temari All Rights Reserved
3 * This file is part of ftpd.
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>
25 #include <net/gen/in.h>
26 #include <net/gen/inet.h>
27 #include <net/gen/tcp.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
));
65 #define PROG_FTPDSH "ftpdsh"
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];
80 /* allocate, we don't need no stink'n allocate */
84 printf("202 ALLO command not needed at this site.\r\n");
89 /* append to a file if it exists */
93 return(recvfile(buff
, RECV_APND
));
96 /* change to parent directory */
106 /* change directory */
114 printf(msg550
, buff
, strerror(errno
));
116 showmsg("250", ".ftpd_msg");
117 printf("250 %s command okay.\r\n", line
);
131 printf("550 Command not allowed for anonymous user\r\n");
136 printf(msg550
, buff
, strerror(errno
));
138 printf("250 File \"%s\" deleted.\r\n", buff
);
139 logit("DELE", path(buff
));
145 /* directory listing */
151 return(sendfile(buff
, SEND_LIST
));
154 /* file modification time, btw when will this be put into an RFC */
164 if(stat(buff
, &st
)) {
165 printf(msg550
, buff
, strerror(errno
));
169 if((st
.st_mode
& S_IFMT
) != S_IFREG
) {
170 printf("550 Not a regular file.\r\n");
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
);
190 printf("200 Mode set to %c.\r\n", *buff
);
195 printf("200 Mode set to %c.\r\n", *buff
);
199 printf("501 Unknown mode %c.\r\n", *buff
);
205 /* make a directory */
213 printf("550 Command not allowed for anonymous user\r\n");
217 if(mkdir(buff
, 0777))
218 printf(msg550
, buff
, strerror(errno
));
220 printf("257 \"%s\" directory created.\r\n", buff
);
221 logit("MKD ", path(buff
));
233 return(sendfile(buff
, SEND_NLST
));
245 if(getcwd(dir
, sizeof(dir
)) == (char *)NULL
)
246 printf(msg550
, buff
, strerror(errno
));
248 printf("257 \"%s\" is current directory.\r\n", dir
);
253 /* restart command */
260 file_restart
= atol(buff
);
262 printf("350 Next file transfer will restart at %lu.\r\n", file_restart
);
267 /* they want a file */
271 return(sendfile(buff
, SEND_FILE
));
274 /* remove a directory */
282 printf("550 Command not allowed for anonymous user\r\n");
287 printf(msg550
, buff
, strerror(errno
));
289 printf("250 Directory \"%s\" deleted.\r\n", buff
);
290 logit("RMD ", path(buff
));
304 printf("550 Command not allowed for anonymous user\r\n");
308 strncpy(rnfr
, buff
, sizeof(rnfr
));
309 rnfr
[sizeof(rnfr
)-1] = '\0';
311 printf("350 Got RNFR waiting for RNTO.\r\n");
324 printf("550 Command not allowed for anonymous user\r\n");
328 if(rnfr
[0] == '\0') {
329 printf("550 Rename failed.\r\n");
333 if(rename(rnfr
, buff
) < 0)
334 printf("550 Rename failed. Error %s\r\n", strerror(errno
));
336 printf("250 Renamed %s to %s.\r\n", rnfr
, buff
);
337 logit("RNFR", path(rnfr
));
338 logit("RNTO", path(buff
));
346 /* xmode = 0 for multiline crc, xmode <> 0 for single file single line crc */
347 static int docrc(buff
, xmode
)
357 if((fd
= fdxcmd(CMD_CRC
, buff
)) < 0) {
358 printf("501 Could not obtain CRC.\r\n");
363 printf("202-SITE CRC \"%s\"\r\n", buff
);
368 if((s
= read(fd
, p
, 1)) != 1) {
370 printf("202 SITE CRC DONE.\r\n");
372 printf("501 Could not obtain CRC.\r\n");
386 printf(" %s", buffer
);
393 printf("202 CRC %05u %ld.\r\n", cs
, fs
);
410 strncpy(line
, buff
, sizeof(line
));
411 line
[sizeof(line
)-1] = '\0';
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
);
426 static unsigned long fsize(fname
)
430 unsigned long fs
= 0L;
435 if((st
.st_mode
& S_IFMT
) != S_IFREG
)
446 /* file size, btw when will this be put into an RFC */
451 unsigned long filesize
;
456 if(stat(buff
, &st
)) {
457 printf(msg550
, buff
, strerror(errno
));
461 if((st
.st_mode
& S_IFMT
) != S_IFREG
) {
462 printf("550 Not a regular file.\r\n");
466 filesize
= st
.st_size
;
469 if(asciisize(buff
, &filesize
))
472 printf("215 %lu\r\n", filesize
);
477 /* server status, or file status */
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
));
497 printf(" Not logged in\r\n");
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");
509 printf("211-Status of %s:\r\n", buff
);
511 if((fd
= fdxcmd(CMD_LIST
, buff
)) < 0)
512 printf(" Could not retrieve status");
514 while((s
= read(fd
, buffer
, 1)) == 1) {
518 printf("%c", *buffer
);
523 printf("211 End of status\r\n");
528 /* hey look, we're getting a file */
532 return(recvfile(buff
, RECV_FILE
));
535 /* hey, get a file unique */
539 return(recvfile(buff
, RECV_UNIQ
));
549 printf("200 Structure set to %c.\r\n", *buff
);
552 printf("501 Unknown structure %c.\r\n", *buff
);
558 /* we're UNIX and proud of it! */
562 printf("215 UNIX Type: L8\r\n");
567 /* change transfer type */
571 if(*(buff
+1) != '\0') {
572 printf("501 Syntax error in parameters.\r\n");
580 printf("200 Type set to A.\r\n");
585 printf("200 Type set to I.\r\n");
588 printf("501 Invalid type %c.\r\n", *buff
);
594 static int fdxcmd(cmd
, arg
)
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
;
610 argv
[3] = (char *)NULL
;
615 if((cmdpid
= fork()) < 0) {
621 if(cmdpid
== 0) { /* Child */
624 open("/dev/null", O_RDONLY
);
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
);
642 /* Same as close if not cmd child started */
643 static int endfdxcmd(fd
)
654 s
= waitpid(cmdpid
, &cs
, 0);
661 /* returns -1 = size could not be determined, */
662 /* 0 = size determined and in filesize */
663 static int asciisize(filename
, filesize
)
665 unsigned long *filesize
;
672 if((fd
= open(filename
, O_RDONLY
)) < 0) {
673 printf(msg550
, filename
, strerror(errno
));
679 while((cnt
= read(fd
, buffer
, sizeof(buffer
))) > 0) {
683 if((pp
= memchr(p
, '\n', cnt
)) != (char *)NULL
) {
685 cnt
= cnt
- 1 - (pp
- p
);
697 printf(msg550
, filename
, strerror(errno
));
704 /* see if we need to run a command to convert the file */
705 static int cnvtfile(name
, name2
)
710 static char fname
[256];
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");
721 if(errno
!= ENOENT
) { /* doesn't exist is okay, others are not */
722 printf(msg550
, name
, strerror(errno
));
726 /* find out what kind of conversion */
727 strncpy(fname
, name
, sizeof(fname
));
728 fname
[sizeof(fname
)-1] = '\0';
730 p
= fname
+ strlen(fname
);
732 while(p
> fname
&& cmode
== CNVT_ERROR
) {
734 if(!strcmp(p
, ".tar"))
737 if(!strcmp(p
, ".tar.Z"))
743 if(!strcmp(p
, ".tar.gz"))
746 if(!strcmp(p
, ".gz"))
749 if (cmode
!= CNVT_ERROR
) {
750 /* is there a file to convert? */
752 if (!stat(fname
, &st
)) break;
760 if(cmode
== CNVT_ERROR
) {
761 printf(msg550
, fname
, strerror(errno
));
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");
776 static int procfile(name
)
783 cmd
= cnvtfile(name
, &name2
);
787 fd
= fdxcmd(cmd
+ 10, name2
);
790 fd
= fdxcmd(cmd
+ 10, name2
);
793 fd
= fdxcmd(cmd
+ 10, name2
);
796 fd
= fdxcmd(cmd
+ 10, name2
);
799 fd
= fdxcmd(cmd
+ 10, name2
);
802 fd
= fdxcmd(cmd
+ 10, name2
);
805 fd
= open(name
, O_RDONLY
);
813 printf(msg550
, name
, strerror(errno
));
818 /* oh no, they're taking a file */
819 static int sendfile(name
, xmode
)
825 time_t datastart
, dataend
;
826 unsigned long datacount
;
842 fd
= fdxcmd(CMD_NLST
, name
);
844 printf(msg550
, name
, strerror(errno
));
848 fd
= fdxcmd(CMD_LIST
, name
);
850 printf(msg550
, name
, strerror(errno
));
856 logit("FAIL", path(fname
));
858 logit("SEND", path(fname
));
864 /* set file position at approriate spot */
868 while(sp
< file_restart
) {
870 s
= read(fd
, buffer
, 1);
872 printf(msg550
, fname
, strerror(errno
));
882 sp
= lseek(fd
, file_restart
, SEEK_SET
);
884 printf(msg550
, fname
, strerror(errno
));
890 if(sp
!= file_restart
) {
891 printf("550 File restart point error. %lu not %lu\r\n", sp
, file_restart
);
901 printf("%03d File %s okay. Opening data connection.\r\n",
902 ftpdata_fd
>= 0 ? 125 : 150, fname
);
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",
911 fprintf(logfile
, "After 125/150 b4 DataConnect\n");
921 fprintf(logfile
, "After DataConnect\n");
923 fprintf(logfile
, "ftpd: parent %d start sendfile \n", getpid());
928 doascii
= (type
== TYPE_A
) ||
929 ((xmode
== SEND_LIST
) || (xmode
== SEND_NLST
)); /* per RFC1123 4.1.2.7 */
932 op
= bufout
; ope
= bufout
+ sizeof(bufout
) - 3;
933 while((s
= read(fd
, buffer
, sizeof(buffer
))) > 0) {
935 fprintf(logfile
, "sendfile read %d\n", s
); fflush(logfile
);
950 *(u16_t
*)&block
[1] = htons(op
- bufout
);
951 write(ftpdata_fd
, block
, sizeof(block
));
953 write(ftpdata_fd
, bufout
, op
- bufout
);
960 *(u16_t
*)&block
[1] = htons(s
);
961 write(ftpdata_fd
, block
, sizeof(block
));
963 s
= write(ftpdata_fd
, buffer
, s
);
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
);
975 block
[0] = MODE_B_EOF
;
976 *(u16_t
*)&block
[1] = htons(0);
977 write(ftpdata_fd
, block
, sizeof(block
));
982 fprintf(logfile
, "ftpd: parent %d end sendfile \n", getpid());
992 if(dataend
== datastart
) dataend
++;
993 kbs
= (datacount
* 100 / (dataend
- datastart
)) / 1024;
996 printf("451 Transfer aborted.\r\n");
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));
1005 static int recvfile(name
, xmode
)
1010 time_t datastart
, dataend
;
1011 unsigned long datacount
;
1030 oflag
= O_WRONLY
| O_APPEND
;
1034 oflag
= O_WRONLY
| O_CREAT
;
1037 oflag
= O_WRONLY
| O_CREAT
| O_TRUNC
;
1043 fd
= open(fname
, oflag
, (anonymous
? 0000:0600));
1046 printf(msg550
, fname
, strerror(errno
));
1050 /* log the received file */
1051 logit("RECV", path(fname
));
1053 /* set file position at approriate spot */
1055 if(type
== TYPE_A
) {
1057 while(sp
< file_restart
) {
1059 s
= read(fd
, buffer
, 1);
1061 printf(msg550
, fname
, strerror(errno
));
1071 sp
= lseek(fd
, file_restart
, SEEK_SET
);
1073 printf(msg550
, fname
, strerror(errno
));
1079 if(sp
!= file_restart
) {
1080 printf("550 File restart point error. %lu not %lu\r\n", sp
, file_restart
);
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 */
1092 printf("%03d File %s okay. Opening data connection.\r\n",
1093 ftpdata_fd
>= 0 ? 125 : 150, fname
);
1102 fprintf(logfile
, "ftpd: parent %d start recvfile \n", getpid());
1106 /* start receiving file */
1109 op
= bufout
; ope
= bufout
+ sizeof(bufout
) - 3;
1114 cnt
= sizeof(buffer
);
1117 s
= read(ftpdata_fd
, block
, sizeof(block
));
1118 cnt
= ntohs(*(u16_t
*)&block
[1]);
1120 if(cnt
== 0 && block
[0] & MODE_B_EOF
)
1123 s
= read(ftpdata_fd
, buffer
, cnt
> sizeof(buffer
) ? sizeof(buffer
) : cnt
);
1126 datacount
+= (long)s
;
1127 if(type
== TYPE_A
) {
1141 write(fd
, bufout
, op
- bufout
);
1146 write(fd
, buffer
, s
);
1147 if(cnt
== 0 && mode
== MODE_B
&& block
[0] & MODE_B_EOF
) {
1155 write(fd
, bufout
, op
- bufout
);
1159 fprintf(logfile
, "ftpd: parent %d end recvfile \n", getpid());
1164 if(mode
!= MODE_B
) {
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");
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));
1185 static char *uniqname()
1187 static char uniq
[32];
1191 for(i
= 0; i
< 1000; i
++) {
1192 sprintf(uniq
, "ftpd%d%d", getpid(), i
);
1193 if(stat(uniq
, &st
) == -1)
1199 static char *spath
[256];
1200 static char *path(fname
)
1205 if(getcwd(dir
, sizeof(dir
)) == (char *)NULL
)
1206 sprintf(dir
, "???");
1209 sprintf((char *)spath
, "%s%s", newroot
, fname
);
1212 sprintf((char *)spath
, "%s%s%s", newroot
, dir
, fname
);
1214 sprintf((char *)spath
, "%s%s/%s", newroot
, dir
, fname
);
1216 return((char *)spath
);
1219 /* do file detail */
1220 static int dofdet(buff
)
1229 if(stat(buff
, &st
)) {
1230 printf("501 Could not obtain file detail.\r\n");
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",
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 */