diff --git a/stream/stream_ftp.c b/stream/stream_ftp.c index 8485e0e969..6879881b8e 100644 --- a/stream/stream_ftp.c +++ b/stream/stream_ftp.c @@ -32,6 +32,8 @@ #include #endif +#include + #include "core/mp_msg.h" #include "network.h" #include "stream.h" @@ -50,6 +52,7 @@ static struct stream_priv_s { int handle; int cavail,cleft; char *buf; + char *cmd_buf; } stream_priv_dflts = { "anonymous","no@spam", NULL, @@ -58,11 +61,14 @@ static struct stream_priv_s { NULL, NULL, - 0, + -1, 0,0, - NULL + NULL, + NULL, }; +#define CMD_BUFSIZE 8192 + #define BUFSIZE 2048 #define ST_OFF(f) M_ST_OFF(struct stream_priv_s,f) @@ -103,6 +109,14 @@ static int fd_can_read(int fd,int timeout) { /* * read a line of text * + * If the line is too long to fit in the buffer, provided via parameters + * buf and max, the remaining characters are skipped. So the next call to + * this function is synchronized to the start of the following response + * line. + * + * The parameter buf will always be initialized as long as max is bigger + * then 1. If nothing is read it will contain an empty string. + * * return -1 on error or bytecount */ static int readline(char *buf,int max,struct stream_priv_s *ctl) @@ -111,9 +125,14 @@ static int readline(char *buf,int max,struct stream_priv_s *ctl) char *end,*bp=buf; int eof = 0; + if (max <= 0) { + return -1; + } + *bp = '\0'; + do { if (ctl->cavail > 0) { - x = (max >= ctl->cavail) ? ctl->cavail : max-1; + x = FFMIN(ctl->cavail, max-1); end = memccpy(bp,ctl->cget,'\n',x); if (end != NULL) x = end - bp; @@ -134,8 +153,18 @@ static int readline(char *buf,int max,struct stream_priv_s *ctl) } } if (max == 1) { - *buf = '\0'; - break; + char *q = memchr(ctl->cget, '\n', ctl->cavail); + + if (q) { // found EOL: update state and return + ++q; + ctl->cavail -= q - ctl->cget; + ctl->cget = q; + + break; + } + + // receive more data to find end of current line + ctl->cget = ctl->cput; } if (ctl->cput == ctl->cget) { ctl->cput = ctl->cget = ctl->buf; @@ -179,13 +208,14 @@ static int readresp(struct stream_priv_s* ctl,char* rsp) { static char response[256]; char match[5]; - int r; + int r, len; - if (readline(response,256,ctl) == -1) + len = readline(response,256,ctl); + if (rsp) strcpy(rsp,response); + if (len == -1) return 0; r = atoi(response)/100; - if(rsp) strcpy(rsp,response); mp_msg(MSGT_STREAM,MSGL_V, "[ftp] < %s",response); @@ -263,7 +293,7 @@ static int FtpOpenPort(struct stream_priv_s* p) { static int FtpOpenData(stream_t* s,int64_t newpos) { struct stream_priv_s* p = s->priv; int resp; - char str[256],rsp_txt[256]; + char rsp_txt[256]; // Open a new connection s->fd = FtpOpenPort(p); @@ -271,21 +301,21 @@ static int FtpOpenData(stream_t* s,int64_t newpos) { if(s->fd < 0) return 0; if(newpos > 0) { - snprintf(str,255,"REST %"PRId64, (int64_t)newpos); + snprintf(p->cmd_buf,CMD_BUFSIZE - 1,"REST %"PRId64, (int64_t)newpos); - resp = FtpSendCmd(str,p,rsp_txt); + resp = FtpSendCmd(p->cmd_buf,p,rsp_txt); if(resp != 3) { - mp_msg(MSGT_OPEN,MSGL_WARN, "[ftp] command '%s' failed: %s\n",str,rsp_txt); + mp_msg(MSGT_OPEN,MSGL_WARN, "[ftp] command '%s' failed: %s\n",p->cmd_buf,rsp_txt); newpos = 0; } } // Get the file - snprintf(str,255,"RETR %s",p->filename); - resp = FtpSendCmd(str,p,rsp_txt); + snprintf(p->cmd_buf,CMD_BUFSIZE - 1,"RETR %s",p->filename); + resp = FtpSendCmd(p->cmd_buf,p,rsp_txt); if(resp != 1) { - mp_msg(MSGT_OPEN,MSGL_ERR, "[ftp] command '%s' failed: %s\n",str,rsp_txt); + mp_msg(MSGT_OPEN,MSGL_ERR, "[ftp] command '%s' failed: %s\n",p->cmd_buf,rsp_txt); return 0; } @@ -333,7 +363,7 @@ static int seek(stream_t *s,int64_t newpos) { // First close the fd closesocket(s->fd); - s->fd = 0; + s->fd = -1; // Send send the telnet sequence needed to make the server react @@ -369,15 +399,18 @@ static void close_f(stream_t *s) { if(!p) return; - if(s->fd > 0) { + if(s->fd >= 0) { closesocket(s->fd); - s->fd = 0; + s->fd = -1; } - FtpSendCmd("QUIT",p,NULL); + if (p->handle >= 0) { + FtpSendCmd("QUIT", p, NULL); + closesocket(p->handle); + } - if(p->handle) closesocket(p->handle); free(p->buf); + free(p->cmd_buf); m_struct_free(&stream_opts,p); } @@ -388,7 +421,7 @@ static int open_f(stream_t *stream,int mode, void* opts, int* file_format) { int resp; int64_t len = 0; struct stream_priv_s* p = (struct stream_priv_s*)opts; - char str[256],rsp_txt[256]; + char rsp_txt[256]; if(mode != STREAM_READ) { mp_msg(MSGT_OPEN,MSGL_ERR, "[ftp] Unknown open mode %d\n",mode); @@ -402,6 +435,16 @@ static int open_f(stream_t *stream,int mode, void* opts, int* file_format) { return STREAM_ERROR; } + // Allocate buffers + p->buf = malloc(BUFSIZE); + p->cmd_buf = malloc(CMD_BUFSIZE); + + if (!p->buf || !p->cmd_buf) { + close_f(stream); + m_struct_free(&stream_opts,opts); + return STREAM_ERROR; + } + // Open the control connection p->handle = connect2Server(p->host,p->port,1); @@ -413,7 +456,6 @@ static int open_f(stream_t *stream,int mode, void* opts, int* file_format) { // We got a connection, let's start serious things stream->fd = -1; stream->priv = p; - p->buf = malloc(BUFSIZE); if (readresp(p, NULL) == 0) { close_f(stream); @@ -421,20 +463,20 @@ static int open_f(stream_t *stream,int mode, void* opts, int* file_format) { } // Login - snprintf(str,255,"USER %s",p->user); - resp = FtpSendCmd(str,p,rsp_txt); + snprintf(p->cmd_buf,CMD_BUFSIZE - 1,"USER %s",p->user); + resp = FtpSendCmd(p->cmd_buf,p,rsp_txt); // password needed if(resp == 3) { - snprintf(str,255,"PASS %s",p->pass); - resp = FtpSendCmd(str,p,rsp_txt); + snprintf(p->cmd_buf,CMD_BUFSIZE - 1,"PASS %s",p->pass); + resp = FtpSendCmd(p->cmd_buf,p,rsp_txt); if(resp != 2) { - mp_msg(MSGT_OPEN,MSGL_ERR, "[ftp] command '%s' failed: %s\n",str,rsp_txt); + mp_msg(MSGT_OPEN,MSGL_ERR, "[ftp] command '%s' failed: %s\n",p->cmd_buf,rsp_txt); close_f(stream); return STREAM_ERROR; } } else if(resp != 2) { - mp_msg(MSGT_OPEN,MSGL_ERR, "[ftp] command '%s' failed: %s\n",str,rsp_txt); + mp_msg(MSGT_OPEN,MSGL_ERR, "[ftp] command '%s' failed: %s\n",p->cmd_buf,rsp_txt); close_f(stream); return STREAM_ERROR; } @@ -448,10 +490,10 @@ static int open_f(stream_t *stream,int mode, void* opts, int* file_format) { } // Get the filesize - snprintf(str,255,"SIZE %s",p->filename); - resp = FtpSendCmd(str,p,rsp_txt); + snprintf(p->cmd_buf,CMD_BUFSIZE - 1,"SIZE %s",p->filename); + resp = FtpSendCmd(p->cmd_buf,p,rsp_txt); if(resp != 2) { - mp_msg(MSGT_OPEN,MSGL_WARN, "[ftp] command '%s' failed: %s\n",str,rsp_txt); + mp_msg(MSGT_OPEN,MSGL_WARN, "[ftp] command '%s' failed: %s\n",p->cmd_buf,rsp_txt); } else { int dummy; sscanf(rsp_txt,"%d %"SCNd64,&dummy,&len);