1 /* ftpget.c - Get a remote file from FTP.
2 *
3 * Copyright 2013 Ranjan Kumar <ranjankumar.bth@gmail.com>
4 * Copyright 2013 Kyungwan Han <asura321@gmail.com>
5 *
6 * No Standard.
7 *
8 USE_FTPGET(NEWTOY(ftpget, "<2cvu:p:P#<0=21>65535", TOYFLAG_BIN))
9 USE_FTPGET(OLDTOY(ftpput, ftpget, TOYFLAG_BIN))
10
11 config FTPGET
12 bool "ftpget/ftpput"
13 default n
14 help
15 usage: ftpget [-cv] [-u USER -p PASSWORD -P PORT] HOST_NAME [LOCAL_FILENAME] REMOTE_FILENAME
16 usage: ftpput [-v] [-u USER -p PASSWORD -P PORT] HOST_NAME [REMOTE_FILENAME] LOCAL_FILENAME
17
18 ftpget - Get a remote file from FTP.
19 ftpput - Upload a local file on remote machine through FTP.
20
21 -c Continue previous transfer.
22 -v Verbose.
23 -u User name.
24 -p Password.
25 -P Port Number (default 21).
26 */
27 #define FOR_ftpget
28 #include "toys.h"
29
GLOBALS(long port;char * password;char * username;FILE * sockfp;int c;int isget;char buf[sizeof (struct sockaddr_storage)];)30 GLOBALS(
31 long port; // char *port;
32 char *password;
33 char *username;
34
35 FILE *sockfp;
36 int c;
37 int isget;
38 char buf[sizeof(struct sockaddr_storage)];
39 )
40
41 #define DATACONNECTION_OPENED 125
42 #define FTPFILE_STATUSOKAY 150
43 #define FTP_COMMAND_OKAY 200
44 #define FTPFILE_STATUS 213
45 #define FTPSERVER_READY 220
46 #define CLOSE_DATACONECTION 226
47 #define PASSIVE_MODE 227
48 #define USERLOGGED_SUCCESS 230
49 #define PASSWORD_REQUEST 331
50 #define REQUESTED_PENDINGACTION 350
51
52
53 static void setport(unsigned port_num)
54 {
55 int af = ((struct sockaddr *)TT.buf)->sa_family;
56
57 if (af == AF_INET) ((struct sockaddr_in*)TT.buf)->sin_port = port_num;
58 else if (af == AF_INET6) ((struct sockaddr_in6*)TT.buf)->sin6_port = port_num;
59 }
60
connect_to_stream()61 static int connect_to_stream()
62 {
63 int sockfd, af = ((struct sockaddr *)TT.buf)->sa_family;
64
65 sockfd = xsocket(af, SOCK_STREAM, 0);
66 if (connect(sockfd, (struct sockaddr*)TT.buf,((af == AF_INET)?
67 sizeof(struct sockaddr_in):sizeof(struct sockaddr_in6))) < 0) {
68 close(sockfd);
69 perror_exit("can't connect to remote host");
70 }
71 return sockfd;
72 }
73
74 //close ftp connection and print the message.
close_stream(char * msg_str)75 static void close_stream(char *msg_str)
76 {
77 char *str = toybuf; //toybuf holds response data.
78
79 //Remove garbage chars (from ' ' space to '\x7f') DEL remote server response.
80 while ((*str >= 0x20) && (*str < 0x7f)) str++;
81 *str = '\0';
82 if (TT.sockfp) fclose(TT.sockfp);
83 error_exit("%s server response: %s", (msg_str) ? msg_str:"", toybuf);
84 }
85
86 //send command to ftp and get return status.
get_ftp_response(char * command,char * param)87 static int get_ftp_response(char *command, char *param)
88 {
89 unsigned cmd_status = 0;
90 char *fmt = "%s %s\r\n";
91
92 if (command) {
93 if (!param) fmt += 3;
94 fprintf(TT.sockfp, fmt, command, param);
95 fflush(TT.sockfp);
96 if (toys.optflags & FLAG_v)
97 fprintf(stderr, "FTP Request: %s %s\r\n", command, param);
98 }
99
100 do {
101 if (!fgets(toybuf, sizeof(toybuf)-1, TT.sockfp)) close_stream(NULL);
102 } while (!isdigit(toybuf[0]) || toybuf[3] != ' ');
103
104 toybuf[3] = '\0';
105 cmd_status = atolx_range(toybuf, 0, INT_MAX);
106 toybuf[3] = ' ';
107 return cmd_status;
108 }
109
send_requests(void)110 static void send_requests(void)
111 {
112 int cmd_status = 0;
113
114 //FTP connection request.
115 if (get_ftp_response(NULL, NULL) != FTPSERVER_READY) close_stream(NULL);
116
117 //230 User authenticated, password please; 331 Password request.
118 cmd_status = get_ftp_response("USER", TT.username);
119 if (cmd_status == PASSWORD_REQUEST) { //user logged in. Need Password.
120 if (get_ftp_response("PASS", TT.password) != USERLOGGED_SUCCESS)
121 close_stream("PASS");
122 } else if (cmd_status == USERLOGGED_SUCCESS); //do nothing
123 else close_stream("USER");
124 //200 Type Binary. Command okay.
125 if (get_ftp_response("TYPE I", NULL) != FTP_COMMAND_OKAY)
126 close_stream("TYPE I");
127 }
128
get_sockaddr(char * host)129 static void get_sockaddr(char *host)
130 {
131 struct addrinfo hints, *result;
132 char port[6];
133 int status;
134
135 errno = 0;
136 snprintf(port, 6, "%ld", TT.port);
137
138 memset(&hints, 0 , sizeof(struct addrinfo));
139 hints.ai_family = AF_UNSPEC;
140 hints.ai_socktype = SOCK_STREAM;
141
142 status = getaddrinfo(host, port, &hints, &result);
143 if (status) error_exit("bad address '%s' : %s", host, gai_strerror(status));
144
145 memcpy(TT.buf, result->ai_addr, result->ai_addrlen);
146 freeaddrinfo(result);
147 }
148
149 // send commands to ftp fo PASV mode.
verify_pasv_mode(char * r_filename)150 static void verify_pasv_mode(char *r_filename)
151 {
152 char *pch;
153 unsigned portnum;
154
155 //vsftpd reply like:- "227 Entering Passive Mode (125,19,39,117,43,39)".
156 if (get_ftp_response("PASV", NULL) != PASSIVE_MODE) goto close_stream;
157
158 //Response is "NNN <some text> (N1,N2,N3,N4,P1,P2) garbage.
159 //Server's IP is N1.N2.N3.N4
160 //Server's port for data connection is P1*256+P2.
161 if (!(pch = strrchr(toybuf, ')'))) goto close_stream;
162 *pch = '\0';
163 if (!(pch = strrchr(toybuf, ','))) goto close_stream;
164 *pch = '\0';
165
166 portnum = atolx_range(pch + 1, 0, 255);
167
168 if (!(pch = strrchr(toybuf, ','))) goto close_stream;
169 *pch = '\0';
170 portnum = portnum + (atolx_range(pch + 1, 0, 255) * 256);
171 setport(htons(portnum));
172
173 if (TT.isget && get_ftp_response("SIZE", r_filename) != FTPFILE_STATUS)
174 TT.c = 0;
175 return;
176
177 close_stream:
178 close_stream("PASV");
179 }
180
181 /*
182 * verify the local file presence.
183 * if present, get the size of the file.
184 */
is_localfile_present(char * l_filename)185 static void is_localfile_present(char *l_filename)
186 {
187 struct stat sb;
188
189 if (stat(l_filename, &sb) < 0) perror_exit("stat");
190 //if local file present, then request for pending file action.
191 if (sb.st_size > 0) {
192 sprintf(toybuf, "REST %lu", (unsigned long) sb.st_size);
193 if (get_ftp_response(toybuf, NULL) != REQUESTED_PENDINGACTION) TT.c = 0;
194 } else TT.c = 0;
195 }
196
transfer_file(int local_fd,int remote_fd)197 static void transfer_file(int local_fd, int remote_fd)
198 {
199 int len, rfd = (TT.isget)?remote_fd:local_fd,
200 wfd = (TT.isget)?local_fd:remote_fd;
201
202 if (rfd < 0 || wfd < 0) error_exit("Error in file creation:");
203 while ((len = xread(rfd, toybuf, sizeof(toybuf)))) xwrite(wfd, toybuf, len);
204 }
205
get_file(char * l_filename,char * r_filename)206 static void get_file(char *l_filename, char *r_filename)
207 {
208 int local_fd = -1, remote_fd;
209
210 verify_pasv_mode(r_filename);
211 remote_fd = connect_to_stream(); //Connect to data socket.
212
213 //if local file name will be '-' then local fd will be stdout.
214 if ((l_filename[0] == '-') && !l_filename[1]) {
215 local_fd = 1; //file descriptor will become stdout.
216 TT.c = 0;
217 }
218
219 //if continue, check for local file existance.
220 if (TT.c) is_localfile_present(l_filename);
221
222 //verify the remote file presence.
223 if (get_ftp_response("RETR", r_filename) > FTPFILE_STATUSOKAY)
224 close_stream("RETR");
225
226 //if local fd is not stdout, create a file descriptor.
227 if (local_fd == -1) {
228 int flags = O_WRONLY;
229
230 flags |= (TT.c)? O_APPEND : (O_CREAT | O_TRUNC);
231 local_fd = xcreate((char *)l_filename, flags, 0666);
232 }
233 transfer_file(local_fd, remote_fd);
234 xclose(remote_fd);
235 xclose(local_fd);
236 if (get_ftp_response(NULL, NULL) != CLOSE_DATACONECTION) close_stream(NULL);
237 get_ftp_response("QUIT", NULL);
238 toys.exitval = EXIT_SUCCESS;
239 }
240
put_file(char * r_filename,char * l_filename)241 static void put_file(char *r_filename, char *l_filename)
242 {
243 int local_fd = 0, remote_fd;
244 unsigned cmd_status = 0;
245
246 verify_pasv_mode(r_filename);
247 remote_fd = connect_to_stream(); //Connect to data socket.
248
249 //open the local file for transfer.
250 if ((l_filename[0] != '-') || l_filename[1])
251 local_fd = xcreate((char *)l_filename, O_RDONLY, 0666);
252
253 //verify for the remote file status, Ok or Open: transfer File.
254 cmd_status = get_ftp_response("STOR", r_filename);
255 if ( (cmd_status == DATACONNECTION_OPENED) ||
256 (cmd_status == FTPFILE_STATUSOKAY)) {
257 transfer_file(local_fd, remote_fd);
258 if (get_ftp_response(NULL, NULL) != CLOSE_DATACONECTION) close_stream(NULL);
259 get_ftp_response("QUIT", NULL);
260 toys.exitval = EXIT_SUCCESS;
261 } else {
262 toys.exitval = EXIT_FAILURE;
263 close_stream("STOR");
264 }
265 xclose(remote_fd);
266 xclose(local_fd);
267 }
268
ftpget_main(void)269 void ftpget_main(void)
270 {
271 char **argv = toys.optargs; //host name + file name.
272
273 TT.isget = toys.which->name[3] == 'g';
274 TT.c = 1;
275 //if user name is not specified.
276 if (!(toys.optflags & FLAG_u) && (toys.optflags & FLAG_p))
277 error_exit("Missing username:");
278 //if user name and password is not specified in command line.
279 if (!(toys.optflags & FLAG_u) && !(toys.optflags & FLAG_p))
280 TT.username = TT.password ="anonymous";
281
282 //if continue is not in the command line argument.
283 if (TT.isget && !(toys.optflags & FLAG_c)) TT.c = 0;
284
285 if (toys.optflags & FLAG_v) fprintf(stderr, "Connecting to %s\n", argv[0]);
286 get_sockaddr(argv[0]);
287
288 TT.sockfp = xfdopen(connect_to_stream(), "r+");
289 send_requests();
290
291 if (TT.isget) get_file(argv[1], argv[2] ? argv[2] : argv[1]);
292 else put_file(argv[1], argv[2] ? argv[2] : argv[1]);
293 }
294