Mercurial > mplayer.hg
comparison libmpdemux/stream_ftp.c @ 10625:620cc649f519
ftp support. The change on connect2Server is needed bcs we need 2
different level of verbosity for control and data connections.
author | albeu |
---|---|
date | Fri, 15 Aug 2003 19:13:23 +0000 |
parents | |
children | f7df841c8b74 |
comparison
equal
deleted
inserted
replaced
10624:cdfd4a43c406 | 10625:620cc649f519 |
---|---|
1 | |
2 #include "config.h" | |
3 | |
4 #ifdef HAVE_FTP | |
5 | |
6 #include <stdlib.h> | |
7 #include <stdio.h> | |
8 | |
9 #include <sys/types.h> | |
10 #include <sys/stat.h> | |
11 #include <fcntl.h> | |
12 #include <unistd.h> | |
13 #include <errno.h> | |
14 | |
15 #include "mp_msg.h" | |
16 #include "stream.h" | |
17 #include "help_mp.h" | |
18 #include "../m_option.h" | |
19 #include "../m_struct.h" | |
20 | |
21 static struct stream_priv_s { | |
22 char* user; | |
23 char* pass; | |
24 char* host; | |
25 int port; | |
26 char* filename; | |
27 | |
28 char *cput,*cget; | |
29 int handle; | |
30 int cavail,cleft; | |
31 char *buf; | |
32 } stream_priv_dflts = { | |
33 "anonymous","no@spam", | |
34 NULL, | |
35 21, | |
36 NULL, | |
37 NULL, | |
38 NULL, | |
39 | |
40 0, | |
41 0,0, | |
42 NULL | |
43 }; | |
44 | |
45 #define BUFSIZE 2048 | |
46 | |
47 #define ST_OFF(f) M_ST_OFF(struct stream_priv_s,f) | |
48 /// URL definition | |
49 static m_option_t stream_opts_fields[] = { | |
50 {"username", ST_OFF(user), CONF_TYPE_STRING, 0, 0 ,0, NULL}, | |
51 {"password", ST_OFF(pass), CONF_TYPE_STRING, 0, 0 ,0, NULL}, | |
52 {"hostname", ST_OFF(host), CONF_TYPE_STRING, 0, 0 ,0, NULL}, | |
53 {"port", ST_OFF(port), CONF_TYPE_INT, 0, 0 ,65635, NULL}, | |
54 {"filename", ST_OFF(filename), CONF_TYPE_STRING, 0, 0 ,0, NULL}, | |
55 { NULL, NULL, 0, 0, 0, 0, NULL } | |
56 }; | |
57 static struct m_struct_st stream_opts = { | |
58 "ftp", | |
59 sizeof(struct stream_priv_s), | |
60 &stream_priv_dflts, | |
61 stream_opts_fields | |
62 }; | |
63 | |
64 #define TELNET_IAC 255 /* interpret as command: */ | |
65 #define TELNET_IP 244 /* interrupt process--permanently */ | |
66 #define TELNET_SYNCH 242 /* for telfunc calls */ | |
67 | |
68 /* | |
69 * read a line of text | |
70 * | |
71 * return -1 on error or bytecount | |
72 */ | |
73 static int readline(char *buf,int max,struct stream_priv_s *ctl) | |
74 { | |
75 int x,retval = 0; | |
76 char *end,*bp=buf; | |
77 int eof = 0; | |
78 | |
79 do { | |
80 if (ctl->cavail > 0) { | |
81 x = (max >= ctl->cavail) ? ctl->cavail : max-1; | |
82 end = memccpy(bp,ctl->cget,'\n',x); | |
83 if (end != NULL) | |
84 x = end - bp; | |
85 retval += x; | |
86 bp += x; | |
87 *bp = '\0'; | |
88 max -= x; | |
89 ctl->cget += x; | |
90 ctl->cavail -= x; | |
91 if (end != NULL) { | |
92 bp -= 2; | |
93 if (strcmp(bp,"\r\n") == 0) { | |
94 *bp++ = '\n'; | |
95 *bp++ = '\0'; | |
96 --retval; | |
97 } | |
98 break; | |
99 } | |
100 } | |
101 if (max == 1) { | |
102 *buf = '\0'; | |
103 break; | |
104 } | |
105 if (ctl->cput == ctl->cget) { | |
106 ctl->cput = ctl->cget = ctl->buf; | |
107 ctl->cavail = 0; | |
108 ctl->cleft = BUFSIZE; | |
109 } | |
110 if(eof) { | |
111 if (retval == 0) | |
112 retval = -1; | |
113 break; | |
114 } | |
115 if ((x = recv(ctl->handle,ctl->cput,ctl->cleft,0)) == -1) { | |
116 mp_msg(MSGT_STREAM,MSGL_ERR, "[ftp] read error: %s\n",strerror(errno)); | |
117 retval = -1; | |
118 break; | |
119 } | |
120 if (x == 0) | |
121 eof = 1; | |
122 ctl->cleft -= x; | |
123 ctl->cavail += x; | |
124 ctl->cput += x; | |
125 } while (1); | |
126 | |
127 return retval; | |
128 } | |
129 | |
130 /* | |
131 * read a response from the server | |
132 * | |
133 * return 0 if first char doesn't match | |
134 * return 1 if first char matches | |
135 */ | |
136 static int readresp(struct stream_priv_s* ctl,char* rsp) | |
137 { | |
138 static char response[256]; | |
139 char match[5]; | |
140 int r; | |
141 | |
142 if (readline(response,256,ctl) == -1) | |
143 return 0; | |
144 | |
145 r = atoi(response)/100; | |
146 if(rsp) strcpy(rsp,response); | |
147 | |
148 mp_msg(MSGT_STREAM,MSGL_V, "[ftp] < %s",response); | |
149 | |
150 if (response[3] == '-') { | |
151 strncpy(match,response,3); | |
152 match[3] = ' '; | |
153 match[4] = '\0'; | |
154 do { | |
155 if (readline(response,256,ctl) == -1) { | |
156 mp_msg(MSGT_OPEN,MSGL_ERR, "[ftp] Control socket read failed\n"); | |
157 return 0; | |
158 } | |
159 mp_msg(MSGT_OPEN,MSGL_V, "[ftp] < %s",response); | |
160 } while (strncmp(response,match,4)); | |
161 } | |
162 return r; | |
163 } | |
164 | |
165 | |
166 static int FtpSendCmd(const char *cmd, struct stream_priv_s *nControl,char* rsp) | |
167 { | |
168 int l = strlen(cmd); | |
169 | |
170 mp_msg(MSGT_STREAM,MSGL_V, "[ftp] > %s",cmd); | |
171 while(l > 0) { | |
172 int s = send(nControl->handle,cmd,l,0); | |
173 | |
174 if(s <= 0) { | |
175 mp_msg(MSGT_OPEN,MSGL_ERR, "[ftp] write error: %s\n",strerror(errno)); | |
176 return 0; | |
177 } | |
178 | |
179 cmd += s; | |
180 l -= s; | |
181 } | |
182 | |
183 return readresp(nControl,rsp); | |
184 } | |
185 | |
186 static int FtpOpenPort(struct stream_priv_s* p) { | |
187 int resp,fd; | |
188 char rsp_txt[256]; | |
189 char* par,str[128]; | |
190 int num[6]; | |
191 | |
192 resp = FtpSendCmd("PASV\r\n",p,rsp_txt); | |
193 if(resp != 2) { | |
194 mp_msg(MSGT_OPEN,MSGL_WARN, "[ftp] command 'PASV' failed: %s\n",rsp_txt); | |
195 return 0; | |
196 } | |
197 | |
198 par = strchr(rsp_txt,'('); | |
199 | |
200 if(!par || !par[0] || !par[1]) { | |
201 mp_msg(MSGT_OPEN,MSGL_ERR, "[ftp] invalid server response: %s ??\n",rsp_txt); | |
202 return 0; | |
203 } | |
204 | |
205 sscanf(par+1,"%u,%u,%u,%u,%u,%u",&num[0],&num[1],&num[2], | |
206 &num[3],&num[4],&num[5]); | |
207 snprintf(str,127,"%d.%d.%d.%d",num[0],num[1],num[2],num[3]); | |
208 fd = connect2Server(str,(num[4]<<8)+num[5],0); | |
209 | |
210 if(fd <= 0) { | |
211 mp_msg(MSGT_OPEN,MSGL_ERR, "[ftp] failed to create data connection\n"); | |
212 return 0; | |
213 } | |
214 | |
215 return fd; | |
216 } | |
217 | |
218 static int fill_buffer(stream_t *s, char* buffer, int max_len){ | |
219 fd_set fds; | |
220 struct timeval tv; | |
221 int r; | |
222 | |
223 // Check if there is something to read. This avoid hang | |
224 // forever if the network stop responding. | |
225 FD_ZERO(&fds); | |
226 FD_SET(s->fd,&fds); | |
227 tv.tv_sec = 15; | |
228 tv.tv_usec = 0; | |
229 | |
230 if(select(s->fd+1, &fds, NULL, NULL, &tv) < 1) { | |
231 mp_msg(MSGT_OPEN,MSGL_ERR, "[ftp] read timed out\n"); | |
232 return -1; | |
233 } | |
234 | |
235 r = recv(s->fd,buffer,max_len,0); | |
236 return (r <= 0) ? -1 : r; | |
237 } | |
238 | |
239 static int seek(stream_t *s,off_t newpos) { | |
240 struct stream_priv_s* p = s->priv; | |
241 int resp; | |
242 char str[256],rsp_txt[256]; | |
243 fd_set fds; | |
244 struct timeval tv; | |
245 | |
246 if(s->pos > s->end_pos) { | |
247 s->eof=1; | |
248 return 0; | |
249 } | |
250 | |
251 FD_ZERO(&fds); | |
252 FD_SET(p->handle,&fds); | |
253 tv.tv_sec = tv.tv_usec = 0; | |
254 | |
255 // Check to see if the server doesn't alredy terminated the transfert | |
256 if(select(p->handle+1, &fds, NULL, NULL, &tv) > 0) { | |
257 if(readresp(p,rsp_txt) != 2) | |
258 mp_msg(MSGT_OPEN,MSGL_WARN, "[ftp] Warning the server didn't finished the transfert correctly: %s\n",rsp_txt); | |
259 close(s->fd); | |
260 s->fd = -1; | |
261 } | |
262 | |
263 // Close current download | |
264 if(s->fd >= 0) { | |
265 static const char pre_cmd[]={TELNET_IAC,TELNET_IP,TELNET_IAC,TELNET_SYNCH}; | |
266 //int fl; | |
267 | |
268 // First close the fd | |
269 close(s->fd); | |
270 s->fd = 0; | |
271 | |
272 // Send send the telnet sequence needed to make the server react | |
273 | |
274 // Dunno if this is really needed, lftp have it. I let | |
275 // it here in case it turn out to be needed on some other OS | |
276 //fl=fcntl(p->handle,F_GETFL); | |
277 //fcntl(p->handle,F_SETFL,fl&~O_NONBLOCK); | |
278 | |
279 // send only first byte as OOB due to OOB braindamage in many unices | |
280 send(p->handle,pre_cmd,1,MSG_OOB); | |
281 send(p->handle,pre_cmd+1,sizeof(pre_cmd)-1,0); | |
282 | |
283 //fcntl(p->handle,F_SETFL,fl); | |
284 | |
285 // Get the 426 Transfer aborted | |
286 // Or the 226 Transfer complete | |
287 resp = readresp(p,rsp_txt); | |
288 if(resp != 4 && resp != 2) { | |
289 mp_msg(MSGT_OPEN,MSGL_ERR, "[ftp] Server didn't abort correctly: %s\n",rsp_txt); | |
290 s->eof = 1; | |
291 return 0; | |
292 } | |
293 // Send the ABOR command | |
294 // Ignore the return code as sometimes it fail with "nothing to abort" | |
295 FtpSendCmd("ABOR\n\r",p,rsp_txt); | |
296 } | |
297 | |
298 // Open a new connection | |
299 s->fd = FtpOpenPort(p); | |
300 | |
301 if(!s->fd) return 0; | |
302 | |
303 if(newpos > 0) { | |
304 #ifdef _LARGEFILE_SOURCE | |
305 snprintf(str,255,"REST %lld\r\n",newpos); | |
306 #else | |
307 snprintf(str,255,"REST %u\r\n",newpos); | |
308 #endif | |
309 | |
310 resp = FtpSendCmd(str,p,rsp_txt); | |
311 if(resp != 3) { | |
312 mp_msg(MSGT_OPEN,MSGL_WARN, "[ftp] command '%s' failed: %s\n",str,rsp_txt); | |
313 newpos = 0; | |
314 } | |
315 } | |
316 | |
317 // Get the file | |
318 snprintf(str,255,"RETR %s\r\n",p->filename); | |
319 resp = FtpSendCmd(str,p,rsp_txt); | |
320 | |
321 if(resp != 1) { | |
322 mp_msg(MSGT_OPEN,MSGL_ERR, "[ftp] command '%s' failed: %s\n",str,rsp_txt); | |
323 return 0; | |
324 } | |
325 | |
326 s->pos = newpos; | |
327 return 1; | |
328 } | |
329 | |
330 | |
331 static void close_f(stream_t *s) { | |
332 struct stream_priv_s* p = s->priv; | |
333 | |
334 if(!p) return; | |
335 | |
336 if(s->fd > 0) { | |
337 close(s->fd); | |
338 s->fd = 0; | |
339 } | |
340 | |
341 FtpSendCmd("QUIT\r\n",p,NULL); | |
342 | |
343 if(p->handle) close(p->handle); | |
344 if(p->buf) free(p->buf); | |
345 | |
346 m_struct_free(&stream_opts,p); | |
347 } | |
348 | |
349 | |
350 | |
351 static int open_f(stream_t *stream,int mode, void* opts, int* file_format) { | |
352 int len = 0,resp; | |
353 struct stream_priv_s* p = (struct stream_priv_s*)opts; | |
354 char str[256],rsp_txt[256]; | |
355 | |
356 if(mode != STREAM_READ) { | |
357 mp_msg(MSGT_OPEN,MSGL_ERR, "[ftp] Unknown open mode %d\n",mode); | |
358 m_struct_free(&stream_opts,opts); | |
359 return STREAM_UNSUPORTED; | |
360 } | |
361 | |
362 if(!p->filename || !p->host) { | |
363 mp_msg(MSGT_OPEN,MSGL_ERR, "[ftp] Bad url\n"); | |
364 m_struct_free(&stream_opts,opts); | |
365 return STREAM_ERROR; | |
366 } | |
367 | |
368 // Open the control connection | |
369 p->handle = connect2Server(p->host,p->port,1); | |
370 | |
371 if(p->handle < 0) { | |
372 m_struct_free(&stream_opts,opts); | |
373 return STREAM_ERROR; | |
374 } | |
375 | |
376 // We got a connection, let's start serious things | |
377 stream->fd = -1; | |
378 stream->priv = p; | |
379 p->buf = malloc(BUFSIZE); | |
380 | |
381 if (readresp(p, NULL) == 0) { | |
382 close_f(stream); | |
383 m_struct_free(&stream_opts,opts); | |
384 return STREAM_ERROR; | |
385 } | |
386 | |
387 // Login | |
388 snprintf(str,255,"USER %s\r\n",p->user); | |
389 resp = FtpSendCmd(str,p,rsp_txt); | |
390 | |
391 // password needed | |
392 if(resp == 3) { | |
393 snprintf(str,255,"PASS %s\r\n",p->pass); | |
394 resp = FtpSendCmd(str,p,rsp_txt); | |
395 if(resp != 2) { | |
396 mp_msg(MSGT_OPEN,MSGL_ERR, "[ftp] command '%s' failed: %s\n",str,rsp_txt); | |
397 close_f(stream); | |
398 return STREAM_ERROR; | |
399 } | |
400 } else if(resp != 2) { | |
401 mp_msg(MSGT_OPEN,MSGL_ERR, "[ftp] command '%s' failed: %s\n",str,rsp_txt); | |
402 close_f(stream); | |
403 return STREAM_ERROR; | |
404 } | |
405 | |
406 // Set the transfert type | |
407 resp = FtpSendCmd("TYPE I\r\n",p,rsp_txt); | |
408 if(resp != 2) { | |
409 mp_msg(MSGT_OPEN,MSGL_WARN, "[ftp] command 'TYPE I' failed: %s\n",rsp_txt); | |
410 close_f(stream); | |
411 return STREAM_ERROR; | |
412 } | |
413 | |
414 // Get the filesize | |
415 snprintf(str,255,"SIZE %s\r\n",p->filename); | |
416 resp = FtpSendCmd(str,p,rsp_txt); | |
417 if(resp != 2) { | |
418 mp_msg(MSGT_OPEN,MSGL_WARN, "[ftp] command '%s' failed: %s\n",str,rsp_txt); | |
419 } else { | |
420 int dummy; | |
421 sscanf(rsp_txt,"%d %d",&dummy,&len); | |
422 } | |
423 | |
424 // Start the data connection | |
425 stream->fd = FtpOpenPort(p); | |
426 if(stream->fd <= 0) { | |
427 close_f(stream); | |
428 return STREAM_ERROR; | |
429 } | |
430 | |
431 // Get the file | |
432 snprintf(str,255,"RETR %s\r\n",p->filename); | |
433 resp = FtpSendCmd(str,p,rsp_txt); | |
434 | |
435 if(resp != 1) { | |
436 mp_msg(MSGT_OPEN,MSGL_WARN, "[ftp] command '%s' failed: %s\n",str,rsp_txt); | |
437 close_f(stream); | |
438 return STREAM_ERROR; | |
439 } | |
440 | |
441 | |
442 if(len > 0) { | |
443 stream->seek = seek; | |
444 stream->end_pos = len; | |
445 } | |
446 | |
447 stream->priv = p; | |
448 stream->fill_buffer = fill_buffer; | |
449 stream->close = close_f; | |
450 | |
451 return STREAM_OK; | |
452 } | |
453 | |
454 stream_info_t stream_info_ftp = { | |
455 "File Transfer Protocol", | |
456 "ftp", | |
457 "Albeu", | |
458 "reuse a bit of code from ftplib written by Thomas Pfau", | |
459 open_f, | |
460 { "ftp", NULL }, | |
461 &stream_opts, | |
462 1 // Urls are an option string | |
463 }; | |
464 | |
465 #endif |