diff --git a/libmpdemux/stream_ftp.c b/libmpdemux/stream_ftp.c index 7cc01004b6..2a23fd2d3d 100644 --- a/libmpdemux/stream_ftp.c +++ b/libmpdemux/stream_ftp.c @@ -71,6 +71,20 @@ static struct m_struct_st stream_opts = { #define TELNET_IP 244 /* interrupt process--permanently */ #define TELNET_SYNCH 242 /* for telfunc calls */ +// Check if there is something to read on a fd. This avoid hanging +// forever if the network stop responding. +static int fd_can_read(int fd,int timeout) { + fd_set fds; + struct timeval tv; + + FD_ZERO(&fds); + FD_SET(fd,&fds); + tv.tv_sec = timeout; + tv.tv_usec = 0; + + return (select(fd+1, &fds, NULL, NULL, &tv) > 0); +} + /* * read a line of text * @@ -118,6 +132,13 @@ static int readline(char *buf,int max,struct stream_priv_s *ctl) retval = -1; break; } + + if(!fd_can_read(ctl->handle, 15)) { + mp_msg(MSGT_OPEN,MSGL_ERR, "[ftp] read timed out\n"); + retval = -1; + break; + } + if ((x = recv(ctl->handle,ctl->cput,ctl->cleft,0)) == -1) { mp_msg(MSGT_STREAM,MSGL_ERR, "[ftp] read error: %s\n",strerror(errno)); retval = -1; @@ -174,7 +195,8 @@ static int FtpSendCmd(const char *cmd, struct stream_priv_s *nControl,char* rsp) int l = strlen(cmd); int hascrlf = cmd[l - 2] == '\r' && cmd[l - 1] == '\n'; - mp_msg(MSGT_STREAM,MSGL_V, "[ftp] > %s",cmd); + if(hascrlf && l == 2) mp_msg(MSGT_STREAM,MSGL_V, "\n"); + else mp_msg(MSGT_STREAM,MSGL_V, "[ftp] > %s",cmd); while(l > 0) { int s = send(nControl->handle,cmd,l,0); @@ -217,27 +239,52 @@ static int FtpOpenPort(struct stream_priv_s* p) { snprintf(str,127,"%d.%d.%d.%d",num[0],num[1],num[2],num[3]); fd = connect2Server(str,(num[4]<<8)+num[5],0); - if(fd <= 0) { + if(fd < 0) mp_msg(MSGT_OPEN,MSGL_ERR, "[ftp] failed to create data connection\n"); - return 0; - } return fd; } +static int FtpOpenData(stream_t* s,size_t newpos) { + struct stream_priv_s* p = s->priv; + int resp; + char str[256],rsp_txt[256]; + + // Open a new connection + s->fd = FtpOpenPort(p); + + if(s->fd < 0) return 0; + + if(newpos > 0) { + snprintf(str,255,"REST %"PRId64, (int64_t)newpos); + + resp = FtpSendCmd(str,p,rsp_txt); + if(resp != 3) { + mp_msg(MSGT_OPEN,MSGL_WARN, "[ftp] command '%s' failed: %s\n",str,rsp_txt); + newpos = 0; + } + } + + // Get the file + snprintf(str,255,"RETR %s",p->filename); + resp = FtpSendCmd(str,p,rsp_txt); + + if(resp != 1) { + mp_msg(MSGT_OPEN,MSGL_ERR, "[ftp] command '%s' failed: %s\n",str,rsp_txt); + return 0; + } + + s->pos = newpos; + return 1; +} + static int fill_buffer(stream_t *s, char* buffer, int max_len){ - fd_set fds; - struct timeval tv; int r; - // Check if there is something to read. This avoid hang - // forever if the network stop responding. - FD_ZERO(&fds); - FD_SET(s->fd,&fds); - tv.tv_sec = 15; - tv.tv_usec = 0; - - if(select(s->fd+1, &fds, NULL, NULL, &tv) < 1) { + if(s->fd < 0 && !FtpOpenData(s,s->pos)) + return -1; + + if(!fd_can_read(s->fd, 15)) { mp_msg(MSGT_OPEN,MSGL_ERR, "[ftp] read timed out\n"); return -1; } @@ -249,21 +296,15 @@ static int fill_buffer(stream_t *s, char* buffer, int max_len){ static int seek(stream_t *s,off_t newpos) { struct stream_priv_s* p = s->priv; int resp; - char str[256],rsp_txt[256]; - fd_set fds; - struct timeval tv; + char rsp_txt[256]; if(s->pos > s->end_pos) { s->eof=1; return 0; } - FD_ZERO(&fds); - FD_SET(p->handle,&fds); - tv.tv_sec = tv.tv_usec = 0; - // Check to see if the server doesn't alredy terminated the transfert - if(select(p->handle+1, &fds, NULL, NULL, &tv) > 0) { + if(fd_can_read(p->handle, 0)) { if(readresp(p,rsp_txt) != 2) mp_msg(MSGT_OPEN,MSGL_WARN, "[ftp] Warning the server didn't finished the transfert correctly: %s\n",rsp_txt); closesocket(s->fd); @@ -304,33 +345,7 @@ static int seek(stream_t *s,off_t newpos) { // Ignore the return code as sometimes it fail with "nothing to abort" FtpSendCmd("ABOR",p,rsp_txt); } - - // Open a new connection - s->fd = FtpOpenPort(p); - - if(!s->fd) return 0; - - if(newpos > 0) { - snprintf(str,255,"REST %"PRId64, (int64_t)newpos); - - resp = FtpSendCmd(str,p,rsp_txt); - if(resp != 3) { - mp_msg(MSGT_OPEN,MSGL_WARN, "[ftp] command '%s' failed: %s\n",str,rsp_txt); - newpos = 0; - } - } - - // Get the file - snprintf(str,255,"RETR %s",p->filename); - resp = FtpSendCmd(str,p,rsp_txt); - - if(resp != 1) { - mp_msg(MSGT_OPEN,MSGL_ERR, "[ftp] command '%s' failed: %s\n",str,rsp_txt); - return 0; - } - - s->pos = newpos; - return 1; + return FtpOpenData(s,newpos); } @@ -427,29 +442,16 @@ static int open_f(stream_t *stream,int mode, void* opts, int* file_format) { sscanf(rsp_txt,"%d %d",&dummy,&len); } - // Start the data connection - stream->fd = FtpOpenPort(p); - if(stream->fd <= 0) { - close_f(stream); - return STREAM_ERROR; - } - - // Get the file - snprintf(str,255,"RETR %s",p->filename); - resp = FtpSendCmd(str,p,rsp_txt); - - if(resp != 1) { - mp_msg(MSGT_OPEN,MSGL_WARN, "[ftp] command '%s' failed: %s\n",str,rsp_txt); - close_f(stream); - return STREAM_ERROR; - } - - if(len > 0) { stream->seek = seek; stream->end_pos = len; } + // The data connection is really opened only at the first + // read/seek. This must be done when the cache is used + // because the connection would stay open in the main process, + // preventing correct abort with many servers. + stream->fd = -1; stream->priv = p; stream->fill_buffer = fill_buffer; stream->close = close_f;