subassconvert: do not escape likely ASS override tags
[mplayer.git] / stream / stream_ftp.c
blob63355f4a2ab4dd0e4ad0e1a9d7c4bbbdb7a261ee
1 /*
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.
19 #include "config.h"
21 #include <stdlib.h>
22 #include <stdio.h>
24 #include <sys/types.h>
25 #include <sys/stat.h>
26 #include <fcntl.h>
27 #include <unistd.h>
28 #include <errno.h>
29 #if !HAVE_WINSOCK2_H
30 #include <sys/socket.h>
31 #else
32 #include <winsock2.h>
33 #endif
35 #include <libavutil/common.h>
37 #include "mp_msg.h"
38 #include "network.h"
39 #include "stream.h"
40 #include "m_option.h"
41 #include "m_struct.h"
42 #include "tcp.h"
44 static struct stream_priv_s {
45 char* user;
46 char* pass;
47 char* host;
48 int port;
49 char* filename;
51 char *cput,*cget;
52 int handle;
53 int cavail,cleft;
54 char *buf;
55 char *cmd_buf;
56 } stream_priv_dflts = {
57 .user = "anonymous",
58 .pass = "no@spam",
59 .port = 21,
60 .handle = -1,
63 #define CMD_BUFSIZE 8192
65 #define BUFSIZE 2048
67 #define ST_OFF(f) M_ST_OFF(struct stream_priv_s,f)
68 /// URL definition
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 = {
78 "ftp",
79 sizeof(struct stream_priv_s),
80 &stream_priv_dflts,
81 stream_opts_fields
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) {
91 fd_set fds;
92 struct timeval tv;
94 FD_ZERO(&fds);
95 FD_SET(fd,&fds);
96 tv.tv_sec = timeout;
97 tv.tv_usec = 0;
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
108 * line.
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)
117 int x,retval = 0;
118 char *end,*bp=buf;
119 int eof = 0;
121 if (max <= 0) {
122 return -1;
124 *bp = '\0';
126 do {
127 if (ctl->cavail > 0) {
128 x = FFMIN(ctl->cavail, max-1);
129 end = memccpy(bp,ctl->cget,'\n',x);
130 if (end != NULL)
131 x = end - bp;
132 retval += x;
133 bp += x;
134 *bp = '\0';
135 max -= x;
136 ctl->cget += x;
137 ctl->cavail -= x;
138 if (end != NULL) {
139 bp -= 2;
140 if (strcmp(bp,"\r\n") == 0) {
141 *bp++ = '\n';
142 *bp++ = '\0';
143 --retval;
145 break;
148 if (max == 1) {
149 char *q = memchr(ctl->cget, '\n', ctl->cavail);
151 if (q) { // found EOL: update state and return
152 ++q;
153 ctl->cavail -= q - ctl->cget;
154 ctl->cget = q;
156 break;
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;
164 ctl->cavail = 0;
165 ctl->cleft = BUFSIZE;
167 if(eof) {
168 if (retval == 0)
169 retval = -1;
170 break;
173 if(!fd_can_read(ctl->handle, 15)) {
174 mp_msg(MSGT_OPEN,MSGL_ERR, "[ftp] read timed out\n");
175 retval = -1;
176 break;
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));
181 retval = -1;
182 break;
184 if (x == 0)
185 eof = 1;
186 ctl->cleft -= x;
187 ctl->cavail += x;
188 ctl->cput += x;
189 } while (1);
191 return retval;
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];
203 char match[5];
204 int r, len;
206 len = readline(response,256,ctl);
207 if (rsp) strcpy(rsp,response);
208 if (len == -1)
209 return 0;
211 r = atoi(response)/100;
213 mp_msg(MSGT_STREAM,MSGL_V, "[ftp] < %s",response);
215 if (response[3] == '-') {
216 strncpy(match,response,3);
217 match[3] = ' ';
218 match[4] = '\0';
219 do {
220 if (readline(response,256,ctl) == -1) {
221 mp_msg(MSGT_OPEN,MSGL_ERR, "[ftp] Control socket read failed\n");
222 return 0;
224 mp_msg(MSGT_OPEN,MSGL_V, "[ftp] < %s",response);
225 } while (strncmp(response,match,4));
227 return r;
231 static int FtpSendCmd(const char *cmd, struct stream_priv_s *nControl,char* rsp)
233 int l = strlen(cmd);
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);
238 while(l > 0) {
239 int s = send(nControl->handle,cmd,l,DEFAULT_SEND_FLAGS);
241 if(s <= 0) {
242 mp_msg(MSGT_OPEN,MSGL_ERR, "[ftp] write error: %s\n",strerror(errno));
243 return 0;
246 cmd += s;
247 l -= s;
250 if (hascrlf)
251 return readresp(nControl,rsp);
252 else
253 return FtpSendCmd("\r\n", nControl, rsp);
256 static int FtpOpenPort(struct stream_priv_s* p) {
257 int resp,fd;
258 char rsp_txt[256];
259 char* par,str[128];
260 int num[6];
262 resp = FtpSendCmd("PASV",p,rsp_txt);
263 if(resp != 2) {
264 mp_msg(MSGT_OPEN,MSGL_WARN, "[ftp] command 'PASV' failed: %s\n",rsp_txt);
265 return 0;
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);
272 return 0;
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);
280 if(fd < 0)
281 mp_msg(MSGT_OPEN,MSGL_ERR, "[ftp] failed to create data connection\n");
283 return fd;
286 static int FtpOpenData(stream_t* s,off_t newpos) {
287 struct stream_priv_s* p = s->priv;
288 int resp;
289 char rsp_txt[256];
291 // Open a new connection
292 s->fd = FtpOpenPort(p);
294 if(s->fd < 0) return 0;
296 if(newpos > 0) {
297 snprintf(p->cmd_buf,CMD_BUFSIZE,"REST %"PRId64, (int64_t)newpos);
299 resp = FtpSendCmd(p->cmd_buf,p,rsp_txt);
300 if(resp != 3) {
301 mp_msg(MSGT_OPEN,MSGL_WARN, "[ftp] command '%s' failed: %s\n",p->cmd_buf,rsp_txt);
302 newpos = 0;
306 // Get the file
307 snprintf(p->cmd_buf,CMD_BUFSIZE,"RETR %s",p->filename);
308 resp = FtpSendCmd(p->cmd_buf,p,rsp_txt);
310 if(resp != 1) {
311 mp_msg(MSGT_OPEN,MSGL_ERR, "[ftp] command '%s' failed: %s\n",p->cmd_buf,rsp_txt);
312 return 0;
315 s->pos = newpos;
316 return 1;
319 static int fill_buffer(stream_t *s, char* buffer, int max_len){
320 int r;
322 if(s->fd < 0 && !FtpOpenData(s,s->pos))
323 return -1;
325 if(!fd_can_read(s->fd, 15)) {
326 mp_msg(MSGT_OPEN,MSGL_ERR, "[ftp] read timed out\n");
327 return -1;
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;
336 int resp;
337 char rsp_txt[256];
339 if(s->pos > s->end_pos) {
340 s->eof=1;
341 return 0;
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);
348 closesocket(s->fd);
349 s->fd = -1;
352 // Close current download
353 if(s->fd >= 0) {
354 static const char pre_cmd[]={TELNET_IAC,TELNET_IP,TELNET_IAC,TELNET_SYNCH};
355 //int fl;
357 // First close the fd
358 closesocket(s->fd);
359 s->fd = -1;
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);
379 s->eof = 1;
380 return 0;
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;
393 if(!p) return;
395 if(s->fd >= 0) {
396 closesocket(s->fd);
397 s->fd = -1;
400 if (p->handle >= 0) {
401 FtpSendCmd("QUIT", p, NULL);
402 closesocket(p->handle);
405 free(p->buf);
406 free(p->cmd_buf);
408 m_struct_free(&stream_opts,p);
413 static int open_f(stream_t *stream,int mode, void* opts, int* file_format) {
414 int resp;
415 int64_t len = 0;
416 struct stream_priv_s* p = opts;
417 char rsp_txt[256];
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);
428 return STREAM_ERROR;
431 // Allocate buffers
432 p->buf = malloc(BUFSIZE);
433 p->cmd_buf = malloc(CMD_BUFSIZE);
435 if (!p->buf || !p->cmd_buf) {
436 close_f(stream);
437 m_struct_free(&stream_opts,opts);
438 return STREAM_ERROR;
441 // Open the control connection
442 p->handle = connect2Server(p->host,p->port,1);
444 if(p->handle < 0) {
445 m_struct_free(&stream_opts,opts);
446 return STREAM_ERROR;
449 // We got a connection, let's start serious things
450 stream->fd = -1;
451 stream->priv = p;
453 if (readresp(p, NULL) == 0) {
454 close_f(stream);
455 return STREAM_ERROR;
458 // Login
459 snprintf(p->cmd_buf,CMD_BUFSIZE,"USER %s",p->user);
460 resp = FtpSendCmd(p->cmd_buf,p,rsp_txt);
462 // password needed
463 if(resp == 3) {
464 snprintf(p->cmd_buf,CMD_BUFSIZE,"PASS %s",p->pass);
465 resp = FtpSendCmd(p->cmd_buf,p,rsp_txt);
466 if(resp != 2) {
467 mp_msg(MSGT_OPEN,MSGL_ERR, "[ftp] command '%s' failed: %s\n",p->cmd_buf,rsp_txt);
468 close_f(stream);
469 return STREAM_ERROR;
471 } else if(resp != 2) {
472 mp_msg(MSGT_OPEN,MSGL_ERR, "[ftp] command '%s' failed: %s\n",p->cmd_buf,rsp_txt);
473 close_f(stream);
474 return STREAM_ERROR;
477 // Set the transfer type
478 resp = FtpSendCmd("TYPE I",p,rsp_txt);
479 if(resp != 2) {
480 mp_msg(MSGT_OPEN,MSGL_WARN, "[ftp] command 'TYPE I' failed: %s\n",rsp_txt);
481 close_f(stream);
482 return STREAM_ERROR;
485 // Get the filesize
486 snprintf(p->cmd_buf,CMD_BUFSIZE,"SIZE %s",p->filename);
487 resp = FtpSendCmd(p->cmd_buf,p,rsp_txt);
488 if(resp != 2) {
489 mp_msg(MSGT_OPEN,MSGL_WARN, "[ftp] command '%s' failed: %s\n",p->cmd_buf,rsp_txt);
490 } else {
491 int dummy;
492 sscanf(rsp_txt,"%d %"SCNd64,&dummy,&len);
495 if(len > 0) {
496 stream->seek = seek;
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.
504 stream->fd = -1;
505 stream->priv = p;
506 stream->fill_buffer = fill_buffer;
507 stream->close = close_f;
508 stream->type = STREAMTYPE_STREAM;
510 return STREAM_OK;
513 const stream_info_t stream_info_ftp = {
514 "File Transfer Protocol",
515 "ftp",
516 "Albeu",
517 "reuse a bit of code from ftplib written by Thomas Pfau",
518 open_f,
519 { "ftp", NULL },
520 &stream_opts,
521 1 // Urls are an option string