source: trunk/minix/commands/ftpd200/file.c@ 9

Last change on this file since 9 was 9, checked in by Mattia Monga, 13 years ago

Minix 3.1.2a

File size: 22.8 KB
Line 
1/* file.c Copyright 1992-2000 by Michael Temari All Rights Reserved
2 *
3 * This file is part of ftpd.
4 *
5 * This file handles:
6 *
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
9 *
10 * 01/25/96 Initial Release Michael Temari
11 * 03/09/00 Michael Temari, <Michael@TemWare.Com>
12 */
13
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>
28
29#include "ftpd.h"
30#include "access.h"
31#include "file.h"
32#include "net.h"
33
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));
46
47#define SEND_FILE 0
48#define SEND_NLST 1
49#define SEND_LIST 2
50
51#define RECV_FILE 0
52#define RECV_APND 1
53#define RECV_UNIQ 2
54
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
63
64
65#define PROG_FTPDSH "ftpdsh"
66#define CMD_NLST 1
67#define CMD_LIST 2
68#define CMD_CRC 3
69
70static char *msg550 = "550 %s %s.\r\n";
71
72static unsigned long file_restart = 0;
73
74static char rnfr[256];
75static char buffer[8192];
76static char bufout[8192];
77
78static cmdpid = -1;
79
80/* allocate, we don't need no stink'n allocate */
81int doALLO(buff)
82char *buff;
83{
84 printf("202 ALLO command not needed at this site.\r\n");
85
86 return(GOOD);
87}
88
89/* append to a file if it exists */
90int doAPPE(buff)
91char *buff;
92{
93 return(recvfile(buff, RECV_APND));
94}
95
96/* change to parent directory */
97int doCDUP(buff)
98char *buff;
99{
100 if(ChkLoggedIn())
101 return(GOOD);
102
103 return(doCWD(".."));
104}
105
106/* change directory */
107int doCWD(buff)
108char *buff;
109{
110 if(ChkLoggedIn())
111 return(GOOD);
112
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);
118 }
119
120 return(GOOD);
121}
122
123/* remove a file */
124int doDELE(buff)
125char *buff;
126{
127 if(ChkLoggedIn())
128 return(GOOD);
129
130 if(anonymous) {
131 printf("550 Command not allowed for anonymous user\r\n");
132 return(GOOD);
133 }
134
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));
140 }
141
142 return(GOOD);
143}
144
145/* directory listing */
146int doLIST(buff)
147char *buff;
148{
149 file_restart = 0;
150
151 return(sendfile(buff, SEND_LIST));
152}
153
154/* file modification time, btw when will this be put into an RFC */
155int doMDTM(buff)
156char *buff;
157{
158struct stat st;
159struct tm *t;
160
161 if(ChkLoggedIn())
162 return(GOOD);
163
164 if(stat(buff, &st)) {
165 printf(msg550, buff, strerror(errno));
166 return(GOOD);
167 }
168
169 if((st.st_mode & S_IFMT) != S_IFREG) {
170 printf("550 Not a regular file.\r\n");
171 return(GOOD);
172 }
173
174 t = gmtime(&st.st_mtime);
175
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);
179
180 return(GOOD);
181}
182
183/* mode */
184int doMODE(buff)
185char *buff;
186{
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);
200 }
201
202 return(GOOD);
203}
204
205/* make a directory */
206int doMKD(buff)
207char *buff;
208{
209 if(ChkLoggedIn())
210 return(GOOD);
211
212 if(anonymous) {
213 printf("550 Command not allowed for anonymous user\r\n");
214 return(GOOD);
215 }
216
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));
222 }
223
224 return(GOOD);
225}
226
227/* name listing */
228int doNLST(buff)
229char *buff;
230{
231 file_restart = 0;
232
233 return(sendfile(buff, SEND_NLST));
234}
235
236/* where are we */
237int doPWD(buff)
238char *buff;
239{
240char dir[128];
241
242 if(ChkLoggedIn())
243 return(GOOD);
244
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);
249
250 return(GOOD);
251}
252
253/* restart command */
254int doREST(buff)
255char *buff;
256{
257 if(ChkLoggedIn())
258 return(GOOD);
259
260 file_restart = atol(buff);
261
262 printf("350 Next file transfer will restart at %lu.\r\n", file_restart);
263
264 return(GOOD);
265}
266
267/* they want a file */
268int doRETR(buff)
269char *buff;
270{
271 return(sendfile(buff, SEND_FILE));
272}
273
274/* remove a directory */
275int doRMD(buff)
276char *buff;
277{
278 if(ChkLoggedIn())
279 return(GOOD);
280
281 if(anonymous) {
282 printf("550 Command not allowed for anonymous user\r\n");
283 return(GOOD);
284 }
285
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));
291 }
292
293 return(GOOD);
294}
295
296/* rename from */
297int doRNFR(buff)
298char *buff;
299{
300 if(ChkLoggedIn())
301 return(GOOD);
302
303 if(anonymous) {
304 printf("550 Command not allowed for anonymous user\r\n");
305 return(GOOD);
306 }
307
308 strncpy(rnfr, buff, sizeof(rnfr));
309 rnfr[sizeof(rnfr)-1] = '\0';
310
311 printf("350 Got RNFR waiting for RNTO.\r\n");
312
313 return(GOOD);
314}
315
316/* rename to */
317int doRNTO(buff)
318char *buff;
319{
320 if(ChkLoggedIn())
321 return(GOOD);
322
323 if(anonymous) {
324 printf("550 Command not allowed for anonymous user\r\n");
325 return(GOOD);
326 }
327
328 if(rnfr[0] == '\0') {
329 printf("550 Rename failed.\r\n");
330 return(GOOD);
331 }
332
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));
339 }
340
341 rnfr[0] = '\0';
342
343 return(GOOD);
344}
345
346/* xmode = 0 for multiline crc, xmode <> 0 for single file single line crc */
347static int docrc(buff, xmode)
348char *buff;
349int xmode;
350{
351unsigned short cs;
352long fs;
353int fd;
354int s;
355char *p;
356
357 if((fd = fdxcmd(CMD_CRC, buff)) < 0) {
358 printf("501 Could not obtain CRC.\r\n");
359 return(GOOD);
360 }
361
362 if(xmode == 0)
363 printf("202-SITE CRC \"%s\"\r\n", buff);
364
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);
375 }
376 if(*p == '\n') {
377 *p++ = '\r';
378 *p++ = '\n';
379 *p = '\0';
380 break;
381 }
382 p++;
383 }
384 if(xmode != 0)
385 break;
386 printf(" %s", buffer);
387 }
388
389 cs = atoi(buffer);
390
391 fs = atol(buffer+6);
392
393 printf("202 CRC %05u %ld.\r\n", cs, fs);
394
395 endfdxcmd(fd);
396
397 return(GOOD);
398}
399
400/* site specific */
401int doSITE(buff)
402char *buff;
403{
404char *args;
405
406 if(ChkLoggedIn())
407 return(GOOD);
408
409
410 strncpy(line, buff, sizeof(line));
411 line[sizeof(line)-1] = '\0';
412
413 cvtline(&args);
414
415 if(!strcmp(line, "CRC") || !strcmp(line, "CCRC"))
416 return(docrc(args, strcmp(line, "CRC")));
417
418 if(!strcmp(line, "FDET"))
419 return(dofdet(args));
420
421 printf("501 Unknown SITE command %s.\r\n", line);
422
423 return(GOOD);
424}
425
426static unsigned long fsize(fname)
427char *fname;
428{
429struct stat st;
430unsigned long fs = 0L;
431
432 if(stat(fname, &st))
433 return(fs);
434
435 if((st.st_mode & S_IFMT) != S_IFREG)
436 return(fs);
437
438 if(type == TYPE_A)
439 return(fs);
440
441 fs = st.st_size;
442
443 return(fs);
444}
445
446/* file size, btw when will this be put into an RFC */
447int doSIZE(buff)
448char *buff;
449{
450struct stat st;
451unsigned long filesize;
452
453 if(ChkLoggedIn())
454 return(GOOD);
455
456 if(stat(buff, &st)) {
457 printf(msg550, buff, strerror(errno));
458 return(GOOD);
459 }
460
461 if((st.st_mode & S_IFMT) != S_IFREG) {
462 printf("550 Not a regular file.\r\n");
463 return(GOOD);
464 }
465
466 filesize = st.st_size;
467
468 if(type == TYPE_A)
469 if(asciisize(buff, &filesize))
470 return(GOOD);
471
472 printf("215 %lu\r\n", filesize);
473
474 return(GOOD);
475}
476
477/* server status, or file status */
478int doSTAT(buff)
479char *buff;
480{
481time_t now;
482struct tm *tm;
483int fd;
484int s;
485
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);
504 }
505
506 if(ChkLoggedIn())
507 return(GOOD);
508
509 printf("211-Status of %s:\r\n", buff);
510
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);
519 }
520 endfdxcmd(fd);
521 }
522
523 printf("211 End of status\r\n");
524
525 return(GOOD);
526}
527
528/* hey look, we're getting a file */
529int doSTOR(buff)
530char *buff;
531{
532 return(recvfile(buff, RECV_FILE));
533}
534
535/* hey, get a file unique */
536int doSTOU(buff)
537char *buff;
538{
539 return(recvfile(buff, RECV_UNIQ));
540}
541
542/* structure */
543int doSTRU(buff)
544char *buff;
545{
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);
553 }
554
555 return(GOOD);
556}
557
558/* we're UNIX and proud of it! */
559int doSYST(buff)
560char *buff;
561{
562 printf("215 UNIX Type: L8\r\n");
563
564 return(GOOD);
565}
566
567/* change transfer type */
568int doTYPE(buff)
569char *buff;
570{
571 if(*(buff+1) != '\0') {
572 printf("501 Syntax error in parameters.\r\n");
573 return(GOOD);
574 }
575
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);
589 }
590
591 return(GOOD);
592}
593
594static int fdxcmd(cmd, arg)
595int cmd;
596char *arg;
597{
598char xcmd[3];
599char *argv[5];
600int fds[2];
601char *smallenv[] = { "PATH=/bin:/usr/bin:/usr/local/bin", NULL, NULL };
602
603 if((smallenv[1] = getenv("TZ")) != NULL) smallenv[1] -= 3; /* ouch... */
604
605 sprintf(xcmd, "%d", cmd);
606
607 argv[0] = PROG_FTPDSH;
608 argv[1] = xcmd;
609 argv[2] = arg;
610 argv[3] = (char *)NULL;
611
612 if(pipe(fds) < 0)
613 return(-1);
614
615 if((cmdpid = fork()) < 0) {
616 close(fds[0]);
617 close(fds[1]);
618 return(-1);
619 }
620
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);
635 }
636
637 close(fds[1]);
638
639 return(fds[0]);
640}
641
642/* Same as close if not cmd child started */
643static int endfdxcmd(fd)
644int fd;
645{
646int s;
647int cs;
648
649 close(fd);
650
651 if(cmdpid == -1)
652 return(0);
653
654 s = waitpid(cmdpid, &cs, 0);
655
656 cmdpid = -1;
657
658 return(0);
659}
660
661/* returns -1 = size could not be determined, */
662/* 0 = size determined and in filesize */
663static int asciisize(filename, filesize)
664char *filename;
665unsigned long *filesize;
666{
667unsigned long count;
668int fd;
669char *p, *pp;
670int cnt;
671
672 if((fd = open(filename, O_RDONLY)) < 0) {
673 printf(msg550, filename, strerror(errno));
674 return(-1);
675 }
676
677 count = 0;
678
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;
689 }
690
691 if(cnt == 0) {
692 *filesize = count;
693 close(fd);
694 return(0);
695 }
696
697 printf(msg550, filename, strerror(errno));
698
699 close(fd);
700
701 return(-1);
702}
703
704/* see if we need to run a command to convert the file */
705static int cnvtfile(name, name2)
706char *name;
707char **name2;
708{
709struct stat st;
710static char fname[256];
711char *p;
712int cmode;
713
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);
720
721 if(errno != ENOENT) { /* doesn't exist is okay, others are not */
722 printf(msg550, name, strerror(errno));
723 return(CNVT_ERROR);
724 }
725
726 /* find out what kind of conversion */
727 strncpy(fname, name, sizeof(fname));
728 fname[sizeof(fname)-1] = '\0';
729
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;
748
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;
755 }
756 }
757 p--;
758 }
759
760 if(cmode == CNVT_ERROR) {
761 printf(msg550, fname, strerror(errno));
762 return(CNVT_ERROR);
763 }
764
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);
769 }
770
771 *name2 = fname;
772
773 return(cmode);
774}
775
776static int procfile(name)
777char *name;
778{
779int cmd;
780int fd;
781char *name2;
782
783 cmd = cnvtfile(name, &name2);
784
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);
810 }
811
812 if(fd < 0)
813 printf(msg550, name, strerror(errno));
814
815 return(fd);
816}
817
818/* oh no, they're taking a file */
819static int sendfile(name, xmode)
820char *name;
821int xmode;
822{
823char *fname;
824int fd, s;
825time_t datastart, dataend;
826unsigned long datacount;
827long kbs;
828char c;
829char *p;
830char *op, *ope;
831off_t sp;
832int doascii;
833unsigned long fs;
834char block[3];
835
836 if(ChkLoggedIn())
837 return(GOOD);
838
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));
859 }
860
861 if(fd < 0)
862 return(GOOD);
863
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);
876 }
877 if(s == 0) break;
878 if(*buffer == '\n')
879 sp++;
880 }
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);
888 }
889 }
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);
895 }
896 }
897 file_restart = 0;
898
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);
909
910#ifdef DEBUG
911 fprintf(logfile, "After 125/150 b4 DataConnect\n");
912 fflush(logfile);
913#endif
914
915 if(DataConnect()) {
916 endfdxcmd(fd);
917 return(GOOD);
918 }
919
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
926
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++;
945 }
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));
952 }
953 write(ftpdata_fd, bufout, op - bufout);
954 op = bufout;
955 }
956 }
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));
962 }
963 s = write(ftpdata_fd, buffer, s);
964 }
965 }
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));
971 }
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));
978 }
979 time(&dataend);
980
981#ifdef DEBUG
982 fprintf(logfile, "ftpd: parent %d end sendfile \n", getpid());
983 fflush(logfile);
984#endif
985
986 endfdxcmd(fd);
987 if(mode != MODE_B) {
988 close(ftpdata_fd);
989 ftpdata_fd = -1;
990 }
991
992 if(dataend == datastart) dataend++;
993 kbs = (datacount * 100 / (dataend - datastart)) / 1024;
994
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));
1001
1002 return(GOOD);
1003}
1004
1005static int recvfile(name, xmode)
1006char *name;
1007int xmode;
1008{
1009char *fname;
1010time_t datastart, dataend;
1011unsigned long datacount;
1012long kbs;
1013char c;
1014char *p;
1015char *op, *ope;
1016int fd, oflag;
1017int s;
1018int gotcr;
1019off_t sp;
1020char block[3];
1021unsigned short cnt;
1022
1023 if(ChkLoggedIn())
1024 return(GOOD);
1025
1026 fname = name;
1027
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;
1038 }
1039
1040 if(file_restart)
1041 oflag = O_RDWR;
1042
1043 fd = open(fname, oflag, (anonymous ? 0000:0600));
1044
1045 if(fd < 0) {
1046 printf(msg550, fname, strerror(errno));
1047 return(GOOD);
1048 }
1049
1050 /* log the received file */
1051 logit("RECV", path(fname));
1052
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);
1065 }
1066 if(s == 0) break;
1067 if(*buffer == '\n')
1068 sp++;
1069 }
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);
1077 }
1078 }
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);
1084 }
1085 }
1086 file_restart = 0;
1087
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);
1095
1096 if(DataConnect()) {
1097 close(fd);
1098 return(GOOD);
1099 }
1100
1101#ifdef DEBUG
1102 fprintf(logfile, "ftpd: parent %d start recvfile \n", getpid());
1103 fflush(logfile);
1104#endif
1105
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;
1122 }
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';
1135 }
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;
1143 }
1144 }
1145 } else
1146 write(fd, buffer, s);
1147 if(cnt == 0 && mode == MODE_B && block[0] & MODE_B_EOF) {
1148 s = 0;
1149 break;
1150 }
1151 }
1152 if(gotcr)
1153 *op++ = '\r';
1154 if(op > bufout)
1155 write(fd, bufout, op - bufout);
1156 time(&dataend);
1157
1158#ifdef DEBUG
1159 fprintf(logfile, "ftpd: parent %d end recvfile \n", getpid());
1160 fflush(logfile);
1161#endif
1162
1163 close(fd);
1164 if(mode != MODE_B) {
1165 close(ftpdata_fd);
1166 ftpdata_fd = -1;
1167 }
1168
1169 if(dataend == datastart) dataend++;
1170 kbs = (datacount * 100 / (dataend - datastart)) / 1024;
1171
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));
1180 }
1181
1182 return(GOOD);
1183}
1184
1185static char *uniqname()
1186{
1187static char uniq[32];
1188int i;
1189struct stat st;
1190
1191 for(i = 0; i < 1000; i++) {
1192 sprintf(uniq, "ftpd%d%d", getpid(), i);
1193 if(stat(uniq, &st) == -1)
1194 return(uniq);
1195 }
1196 return(uniq);
1197}
1198
1199static char *spath[256];
1200static char *path(fname)
1201char *fname;
1202{
1203char dir[128];
1204
1205 if(getcwd(dir, sizeof(dir)) == (char *)NULL)
1206 sprintf(dir, "???");
1207
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);
1215
1216 return((char *)spath);
1217}
1218
1219/* do file detail */
1220static int dofdet(buff)
1221char *buff;
1222{
1223struct stat st;
1224char ft;
1225
1226 if(ChkLoggedIn())
1227 return(GOOD);
1228
1229 if(stat(buff, &st)) {
1230 printf("501 Could not obtain file detail.\r\n");
1231 return(GOOD);
1232 }
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;
1240 }
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 */
1250
1251 return(GOOD);
1252}
Note: See TracBrowser for help on using the repository browser.