2 * This file is part of MPlayer.
4 * MPlayer is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version.
9 * MPlayer is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
14 * You should have received a copy of the GNU General Public License along
15 * with MPlayer; if not, write to the Free Software Foundation, Inc.,
16 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
24 #include <sys/types.h>
30 #include <sys/socket.h>
35 #include <libavutil/common.h>
44 static struct stream_priv_s
{
56 } stream_priv_dflts
= {
63 #define CMD_BUFSIZE 8192
67 #define ST_OFF(f) M_ST_OFF(struct stream_priv_s,f)
69 static const m_option_t stream_opts_fields
[] = {
70 {"username", ST_OFF(user
), CONF_TYPE_STRING
, 0, 0 ,0, NULL
},
71 {"password", ST_OFF(pass
), CONF_TYPE_STRING
, 0, 0 ,0, NULL
},
72 {"hostname", ST_OFF(host
), CONF_TYPE_STRING
, 0, 0 ,0, NULL
},
73 {"port", ST_OFF(port
), CONF_TYPE_INT
, 0, 0 ,65635, NULL
},
74 {"filename", ST_OFF(filename
), CONF_TYPE_STRING
, 0, 0 ,0, NULL
},
75 { NULL
, NULL
, 0, 0, 0, 0, NULL
}
77 static const struct m_struct_st stream_opts
= {
79 sizeof(struct stream_priv_s
),
84 #define TELNET_IAC 255 /* interpret as command: */
85 #define TELNET_IP 244 /* interrupt process--permanently */
86 #define TELNET_SYNCH 242 /* for telfunc calls */
88 // Check if there is something to read on a fd. This avoid hanging
89 // forever if the network stop responding.
90 static int fd_can_read(int fd
,int timeout
) {
99 return select(fd
+1, &fds
, NULL
, NULL
, &tv
) > 0;
103 * read a line of text
105 * If the line is too long to fit in the buffer, provided via parameters
106 * buf and max, the remaining characters are skipped. So the next call to
107 * this function is synchronized to the start of the following response
110 * The parameter buf will always be initialized as long as max is bigger
111 * then 1. If nothing is read it will contain an empty string.
113 * return -1 on error or bytecount
115 static int readline(char *buf
,int max
,struct stream_priv_s
*ctl
)
127 if (ctl
->cavail
> 0) {
128 x
= FFMIN(ctl
->cavail
, max
-1);
129 end
= memccpy(bp
,ctl
->cget
,'\n',x
);
140 if (strcmp(bp
,"\r\n") == 0) {
149 char *q
= memchr(ctl
->cget
, '\n', ctl
->cavail
);
151 if (q
) { // found EOL: update state and return
153 ctl
->cavail
-= q
- ctl
->cget
;
159 // receive more data to find end of current line
160 ctl
->cget
= ctl
->cput
;
162 if (ctl
->cput
== ctl
->cget
) {
163 ctl
->cput
= ctl
->cget
= ctl
->buf
;
165 ctl
->cleft
= BUFSIZE
;
173 if(!fd_can_read(ctl
->handle
, 15)) {
174 mp_msg(MSGT_OPEN
,MSGL_ERR
, "[ftp] read timed out\n");
179 if ((x
= recv(ctl
->handle
,ctl
->cput
,ctl
->cleft
,0)) == -1) {
180 mp_msg(MSGT_STREAM
,MSGL_ERR
, "[ftp] read error: %s\n",strerror(errno
));
195 * read a response from the server
197 * return 0 if first char doesn't match
198 * return 1 if first char matches
200 static int readresp(struct stream_priv_s
* ctl
,char* rsp
)
202 static char response
[256];
206 len
= readline(response
,256,ctl
);
207 if (rsp
) strcpy(rsp
,response
);
211 r
= atoi(response
)/100;
213 mp_msg(MSGT_STREAM
,MSGL_V
, "[ftp] < %s",response
);
215 if (response
[3] == '-') {
216 strncpy(match
,response
,3);
220 if (readline(response
,256,ctl
) == -1) {
221 mp_msg(MSGT_OPEN
,MSGL_ERR
, "[ftp] Control socket read failed\n");
224 mp_msg(MSGT_OPEN
,MSGL_V
, "[ftp] < %s",response
);
225 } while (strncmp(response
,match
,4));
231 static int FtpSendCmd(const char *cmd
, struct stream_priv_s
*nControl
,char* rsp
)
234 int hascrlf
= cmd
[l
- 2] == '\r' && cmd
[l
- 1] == '\n';
236 if(hascrlf
&& l
== 2) mp_msg(MSGT_STREAM
,MSGL_V
, "\n");
237 else mp_msg(MSGT_STREAM
,MSGL_V
, "[ftp] > %s",cmd
);
239 int s
= send(nControl
->handle
,cmd
,l
,DEFAULT_SEND_FLAGS
);
242 mp_msg(MSGT_OPEN
,MSGL_ERR
, "[ftp] write error: %s\n",strerror(errno
));
251 return readresp(nControl
,rsp
);
253 return FtpSendCmd("\r\n", nControl
, rsp
);
256 static int FtpOpenPort(struct stream_priv_s
* p
) {
262 resp
= FtpSendCmd("PASV",p
,rsp_txt
);
264 mp_msg(MSGT_OPEN
,MSGL_WARN
, "[ftp] command 'PASV' failed: %s\n",rsp_txt
);
268 par
= strchr(rsp_txt
,'(');
270 if(!par
|| !par
[0] || !par
[1]) {
271 mp_msg(MSGT_OPEN
,MSGL_ERR
, "[ftp] invalid server response: %s ??\n",rsp_txt
);
275 sscanf(par
+1,"%u,%u,%u,%u,%u,%u",&num
[0],&num
[1],&num
[2],
276 &num
[3],&num
[4],&num
[5]);
277 snprintf(str
,sizeof(str
),"%d.%d.%d.%d",num
[0],num
[1],num
[2],num
[3]);
278 fd
= connect2Server(str
,(num
[4]<<8)+num
[5],0);
281 mp_msg(MSGT_OPEN
,MSGL_ERR
, "[ftp] failed to create data connection\n");
286 static int FtpOpenData(stream_t
* s
,off_t newpos
) {
287 struct stream_priv_s
* p
= s
->priv
;
291 // Open a new connection
292 s
->fd
= FtpOpenPort(p
);
294 if(s
->fd
< 0) return 0;
297 snprintf(p
->cmd_buf
,CMD_BUFSIZE
,"REST %"PRId64
, (int64_t)newpos
);
299 resp
= FtpSendCmd(p
->cmd_buf
,p
,rsp_txt
);
301 mp_msg(MSGT_OPEN
,MSGL_WARN
, "[ftp] command '%s' failed: %s\n",p
->cmd_buf
,rsp_txt
);
307 snprintf(p
->cmd_buf
,CMD_BUFSIZE
,"RETR %s",p
->filename
);
308 resp
= FtpSendCmd(p
->cmd_buf
,p
,rsp_txt
);
311 mp_msg(MSGT_OPEN
,MSGL_ERR
, "[ftp] command '%s' failed: %s\n",p
->cmd_buf
,rsp_txt
);
319 static int fill_buffer(stream_t
*s
, char* buffer
, int max_len
){
322 if(s
->fd
< 0 && !FtpOpenData(s
,s
->pos
))
325 if(!fd_can_read(s
->fd
, 15)) {
326 mp_msg(MSGT_OPEN
,MSGL_ERR
, "[ftp] read timed out\n");
330 r
= recv(s
->fd
,buffer
,max_len
,0);
331 return (r
<= 0) ? -1 : r
;
334 static int seek(stream_t
*s
,off_t newpos
) {
335 struct stream_priv_s
* p
= s
->priv
;
339 if(s
->pos
> s
->end_pos
) {
344 // Check to see if the server did not already terminate the transfer
345 if(fd_can_read(p
->handle
, 0)) {
346 if(readresp(p
,rsp_txt
) != 2)
347 mp_msg(MSGT_OPEN
,MSGL_WARN
, "[ftp] Warning the server didn't finished the transfer correctly: %s\n",rsp_txt
);
352 // Close current download
354 static const char pre_cmd
[]={TELNET_IAC
,TELNET_IP
,TELNET_IAC
,TELNET_SYNCH
};
357 // First close the fd
361 // Send send the telnet sequence needed to make the server react
363 // Dunno if this is really needed, lftp have it. I let
364 // it here in case it turn out to be needed on some other OS
365 //fl=fcntl(p->handle,F_GETFL);
366 //fcntl(p->handle,F_SETFL,fl&~O_NONBLOCK);
368 // send only first byte as OOB due to OOB braindamage in many unices
369 send(p
->handle
,pre_cmd
,1,MSG_OOB
|DEFAULT_SEND_FLAGS
);
370 send(p
->handle
,pre_cmd
+1,sizeof(pre_cmd
)-1,DEFAULT_SEND_FLAGS
);
372 //fcntl(p->handle,F_SETFL,fl);
374 // Get the 426 Transfer aborted
375 // Or the 226 Transfer complete
376 resp
= readresp(p
,rsp_txt
);
377 if(resp
!= 4 && resp
!= 2) {
378 mp_msg(MSGT_OPEN
,MSGL_ERR
, "[ftp] Server didn't abort correctly: %s\n",rsp_txt
);
382 // Send the ABOR command
383 // Ignore the return code as sometimes it fail with "nothing to abort"
384 FtpSendCmd("ABOR",p
,rsp_txt
);
386 return FtpOpenData(s
,newpos
);
390 static void close_f(stream_t
*s
) {
391 struct stream_priv_s
* p
= s
->priv
;
400 if (p
->handle
>= 0) {
401 FtpSendCmd("QUIT", p
, NULL
);
402 closesocket(p
->handle
);
408 m_struct_free(&stream_opts
,p
);
413 static int open_f(stream_t
*stream
,int mode
, void* opts
, int* file_format
) {
416 struct stream_priv_s
* p
= opts
;
419 if(mode
!= STREAM_READ
) {
420 mp_msg(MSGT_OPEN
,MSGL_ERR
, "[ftp] Unknown open mode %d\n",mode
);
421 m_struct_free(&stream_opts
,opts
);
422 return STREAM_UNSUPPORTED
;
425 if(!p
->filename
|| !p
->host
) {
426 mp_msg(MSGT_OPEN
,MSGL_ERR
, "[ftp] Bad url\n");
427 m_struct_free(&stream_opts
,opts
);
432 p
->buf
= malloc(BUFSIZE
);
433 p
->cmd_buf
= malloc(CMD_BUFSIZE
);
435 if (!p
->buf
|| !p
->cmd_buf
) {
437 m_struct_free(&stream_opts
,opts
);
441 // Open the control connection
442 p
->handle
= connect2Server(p
->host
,p
->port
,1);
445 m_struct_free(&stream_opts
,opts
);
449 // We got a connection, let's start serious things
453 if (readresp(p
, NULL
) == 0) {
459 snprintf(p
->cmd_buf
,CMD_BUFSIZE
,"USER %s",p
->user
);
460 resp
= FtpSendCmd(p
->cmd_buf
,p
,rsp_txt
);
464 snprintf(p
->cmd_buf
,CMD_BUFSIZE
,"PASS %s",p
->pass
);
465 resp
= FtpSendCmd(p
->cmd_buf
,p
,rsp_txt
);
467 mp_msg(MSGT_OPEN
,MSGL_ERR
, "[ftp] command '%s' failed: %s\n",p
->cmd_buf
,rsp_txt
);
471 } else if(resp
!= 2) {
472 mp_msg(MSGT_OPEN
,MSGL_ERR
, "[ftp] command '%s' failed: %s\n",p
->cmd_buf
,rsp_txt
);
477 // Set the transfer type
478 resp
= FtpSendCmd("TYPE I",p
,rsp_txt
);
480 mp_msg(MSGT_OPEN
,MSGL_WARN
, "[ftp] command 'TYPE I' failed: %s\n",rsp_txt
);
486 snprintf(p
->cmd_buf
,CMD_BUFSIZE
,"SIZE %s",p
->filename
);
487 resp
= FtpSendCmd(p
->cmd_buf
,p
,rsp_txt
);
489 mp_msg(MSGT_OPEN
,MSGL_WARN
, "[ftp] command '%s' failed: %s\n",p
->cmd_buf
,rsp_txt
);
492 sscanf(rsp_txt
,"%d %"SCNd64
,&dummy
,&len
);
497 stream
->end_pos
= len
;
500 // The data connection is really opened only at the first
501 // read/seek. This must be done when the cache is used
502 // because the connection would stay open in the main process,
503 // preventing correct abort with many servers.
506 stream
->fill_buffer
= fill_buffer
;
507 stream
->close
= close_f
;
508 stream
->type
= STREAMTYPE_STREAM
;
513 const stream_info_t stream_info_ftp
= {
514 "File Transfer Protocol",
517 "reuse a bit of code from ftplib written by Thomas Pfau",
521 1 // Urls are an option string