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>
42 static struct stream_priv_s
{
53 } stream_priv_dflts
= {
54 "anonymous","no@spam",
68 #define ST_OFF(f) M_ST_OFF(struct stream_priv_s,f)
70 static const m_option_t stream_opts_fields
[] = {
71 {"username", ST_OFF(user
), CONF_TYPE_STRING
, 0, 0 ,0, NULL
},
72 {"password", ST_OFF(pass
), CONF_TYPE_STRING
, 0, 0 ,0, NULL
},
73 {"hostname", ST_OFF(host
), CONF_TYPE_STRING
, 0, 0 ,0, NULL
},
74 {"port", ST_OFF(port
), CONF_TYPE_INT
, 0, 0 ,65635, NULL
},
75 {"filename", ST_OFF(filename
), CONF_TYPE_STRING
, 0, 0 ,0, NULL
},
76 { NULL
, NULL
, 0, 0, 0, 0, NULL
}
78 static const struct m_struct_st stream_opts
= {
80 sizeof(struct stream_priv_s
),
85 #define TELNET_IAC 255 /* interpret as command: */
86 #define TELNET_IP 244 /* interrupt process--permanently */
87 #define TELNET_SYNCH 242 /* for telfunc calls */
89 // Check if there is something to read on a fd. This avoid hanging
90 // forever if the network stop responding.
91 static int fd_can_read(int fd
,int timeout
) {
100 return select(fd
+1, &fds
, NULL
, NULL
, &tv
) > 0;
104 * read a line of text
106 * return -1 on error or bytecount
108 static int readline(char *buf
,int max
,struct stream_priv_s
*ctl
)
115 if (ctl
->cavail
> 0) {
116 x
= (max
>= ctl
->cavail
) ? ctl
->cavail
: max
-1;
117 end
= memccpy(bp
,ctl
->cget
,'\n',x
);
128 if (strcmp(bp
,"\r\n") == 0) {
140 if (ctl
->cput
== ctl
->cget
) {
141 ctl
->cput
= ctl
->cget
= ctl
->buf
;
143 ctl
->cleft
= BUFSIZE
;
151 if(!fd_can_read(ctl
->handle
, 15)) {
152 mp_msg(MSGT_OPEN
,MSGL_ERR
, "[ftp] read timed out\n");
157 if ((x
= recv(ctl
->handle
,ctl
->cput
,ctl
->cleft
,0)) == -1) {
158 mp_msg(MSGT_STREAM
,MSGL_ERR
, "[ftp] read error: %s\n",strerror(errno
));
173 * read a response from the server
175 * return 0 if first char doesn't match
176 * return 1 if first char matches
178 static int readresp(struct stream_priv_s
* ctl
,char* rsp
)
180 static char response
[256];
184 if (readline(response
,256,ctl
) == -1)
187 r
= atoi(response
)/100;
188 if(rsp
) strcpy(rsp
,response
);
190 mp_msg(MSGT_STREAM
,MSGL_V
, "[ftp] < %s",response
);
192 if (response
[3] == '-') {
193 strncpy(match
,response
,3);
197 if (readline(response
,256,ctl
) == -1) {
198 mp_msg(MSGT_OPEN
,MSGL_ERR
, "[ftp] Control socket read failed\n");
201 mp_msg(MSGT_OPEN
,MSGL_V
, "[ftp] < %s",response
);
202 } while (strncmp(response
,match
,4));
208 static int FtpSendCmd(const char *cmd
, struct stream_priv_s
*nControl
,char* rsp
)
211 int hascrlf
= cmd
[l
- 2] == '\r' && cmd
[l
- 1] == '\n';
213 if(hascrlf
&& l
== 2) mp_msg(MSGT_STREAM
,MSGL_V
, "\n");
214 else mp_msg(MSGT_STREAM
,MSGL_V
, "[ftp] > %s",cmd
);
216 int s
= send(nControl
->handle
,cmd
,l
,DEFAULT_SEND_FLAGS
);
219 mp_msg(MSGT_OPEN
,MSGL_ERR
, "[ftp] write error: %s\n",strerror(errno
));
228 return readresp(nControl
,rsp
);
230 return FtpSendCmd("\r\n", nControl
, rsp
);
233 static int FtpOpenPort(struct stream_priv_s
* p
) {
239 resp
= FtpSendCmd("PASV",p
,rsp_txt
);
241 mp_msg(MSGT_OPEN
,MSGL_WARN
, "[ftp] command 'PASV' failed: %s\n",rsp_txt
);
245 par
= strchr(rsp_txt
,'(');
247 if(!par
|| !par
[0] || !par
[1]) {
248 mp_msg(MSGT_OPEN
,MSGL_ERR
, "[ftp] invalid server response: %s ??\n",rsp_txt
);
252 sscanf(par
+1,"%u,%u,%u,%u,%u,%u",&num
[0],&num
[1],&num
[2],
253 &num
[3],&num
[4],&num
[5]);
254 snprintf(str
,127,"%d.%d.%d.%d",num
[0],num
[1],num
[2],num
[3]);
255 fd
= connect2Server(str
,(num
[4]<<8)+num
[5],0);
258 mp_msg(MSGT_OPEN
,MSGL_ERR
, "[ftp] failed to create data connection\n");
263 static int FtpOpenData(stream_t
* s
,off_t newpos
) {
264 struct stream_priv_s
* p
= s
->priv
;
266 char str
[256],rsp_txt
[256];
268 // Open a new connection
269 s
->fd
= FtpOpenPort(p
);
271 if(s
->fd
< 0) return 0;
274 snprintf(str
,255,"REST %"PRId64
, (int64_t)newpos
);
276 resp
= FtpSendCmd(str
,p
,rsp_txt
);
278 mp_msg(MSGT_OPEN
,MSGL_WARN
, "[ftp] command '%s' failed: %s\n",str
,rsp_txt
);
284 snprintf(str
,255,"RETR %s",p
->filename
);
285 resp
= FtpSendCmd(str
,p
,rsp_txt
);
288 mp_msg(MSGT_OPEN
,MSGL_ERR
, "[ftp] command '%s' failed: %s\n",str
,rsp_txt
);
296 static int fill_buffer(stream_t
*s
, char* buffer
, int max_len
){
299 if(s
->fd
< 0 && !FtpOpenData(s
,s
->pos
))
302 if(!fd_can_read(s
->fd
, 15)) {
303 mp_msg(MSGT_OPEN
,MSGL_ERR
, "[ftp] read timed out\n");
307 r
= recv(s
->fd
,buffer
,max_len
,0);
308 return (r
<= 0) ? -1 : r
;
311 static int seek(stream_t
*s
,off_t newpos
) {
312 struct stream_priv_s
* p
= s
->priv
;
316 if(s
->pos
> s
->end_pos
) {
321 // Check to see if the server did not already terminate the transfer
322 if(fd_can_read(p
->handle
, 0)) {
323 if(readresp(p
,rsp_txt
) != 2)
324 mp_msg(MSGT_OPEN
,MSGL_WARN
, "[ftp] Warning the server didn't finished the transfer correctly: %s\n",rsp_txt
);
329 // Close current download
331 static const char pre_cmd
[]={TELNET_IAC
,TELNET_IP
,TELNET_IAC
,TELNET_SYNCH
};
334 // First close the fd
338 // Send send the telnet sequence needed to make the server react
340 // Dunno if this is really needed, lftp have it. I let
341 // it here in case it turn out to be needed on some other OS
342 //fl=fcntl(p->handle,F_GETFL);
343 //fcntl(p->handle,F_SETFL,fl&~O_NONBLOCK);
345 // send only first byte as OOB due to OOB braindamage in many unices
346 send(p
->handle
,pre_cmd
,1,MSG_OOB
|DEFAULT_SEND_FLAGS
);
347 send(p
->handle
,pre_cmd
+1,sizeof(pre_cmd
)-1,DEFAULT_SEND_FLAGS
);
349 //fcntl(p->handle,F_SETFL,fl);
351 // Get the 426 Transfer aborted
352 // Or the 226 Transfer complete
353 resp
= readresp(p
,rsp_txt
);
354 if(resp
!= 4 && resp
!= 2) {
355 mp_msg(MSGT_OPEN
,MSGL_ERR
, "[ftp] Server didn't abort correctly: %s\n",rsp_txt
);
359 // Send the ABOR command
360 // Ignore the return code as sometimes it fail with "nothing to abort"
361 FtpSendCmd("ABOR",p
,rsp_txt
);
363 return FtpOpenData(s
,newpos
);
367 static void close_f(stream_t
*s
) {
368 struct stream_priv_s
* p
= s
->priv
;
377 FtpSendCmd("QUIT",p
,NULL
);
379 if(p
->handle
) closesocket(p
->handle
);
382 m_struct_free(&stream_opts
,p
);
387 static int open_f(stream_t
*stream
,int mode
, void* opts
, int* file_format
) {
390 struct stream_priv_s
* p
= (struct stream_priv_s
*)opts
;
391 char str
[256],rsp_txt
[256];
393 if(mode
!= STREAM_READ
) {
394 mp_msg(MSGT_OPEN
,MSGL_ERR
, "[ftp] Unknown open mode %d\n",mode
);
395 m_struct_free(&stream_opts
,opts
);
396 return STREAM_UNSUPPORTED
;
399 if(!p
->filename
|| !p
->host
) {
400 mp_msg(MSGT_OPEN
,MSGL_ERR
, "[ftp] Bad url\n");
401 m_struct_free(&stream_opts
,opts
);
405 // Open the control connection
406 p
->handle
= connect2Server(p
->host
,p
->port
,1);
409 m_struct_free(&stream_opts
,opts
);
413 // We got a connection, let's start serious things
416 p
->buf
= malloc(BUFSIZE
);
418 if (readresp(p
, NULL
) == 0) {
424 snprintf(str
,255,"USER %s",p
->user
);
425 resp
= FtpSendCmd(str
,p
,rsp_txt
);
429 snprintf(str
,255,"PASS %s",p
->pass
);
430 resp
= FtpSendCmd(str
,p
,rsp_txt
);
432 mp_msg(MSGT_OPEN
,MSGL_ERR
, "[ftp] command '%s' failed: %s\n",str
,rsp_txt
);
436 } else if(resp
!= 2) {
437 mp_msg(MSGT_OPEN
,MSGL_ERR
, "[ftp] command '%s' failed: %s\n",str
,rsp_txt
);
442 // Set the transfer type
443 resp
= FtpSendCmd("TYPE I",p
,rsp_txt
);
445 mp_msg(MSGT_OPEN
,MSGL_WARN
, "[ftp] command 'TYPE I' failed: %s\n",rsp_txt
);
451 snprintf(str
,255,"SIZE %s",p
->filename
);
452 resp
= FtpSendCmd(str
,p
,rsp_txt
);
454 mp_msg(MSGT_OPEN
,MSGL_WARN
, "[ftp] command '%s' failed: %s\n",str
,rsp_txt
);
457 sscanf(rsp_txt
,"%d %"SCNd64
,&dummy
,&len
);
462 stream
->end_pos
= len
;
465 // The data connection is really opened only at the first
466 // read/seek. This must be done when the cache is used
467 // because the connection would stay open in the main process,
468 // preventing correct abort with many servers.
471 stream
->fill_buffer
= fill_buffer
;
472 stream
->close
= close_f
;
477 const stream_info_t stream_info_ftp
= {
478 "File Transfer Protocol",
481 "reuse a bit of code from ftplib written by Thomas Pfau",
485 1 // Urls are an option string