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>
43 static struct stream_priv_s
{
54 } stream_priv_dflts
= {
55 "anonymous","no@spam",
69 #define ST_OFF(f) M_ST_OFF(struct stream_priv_s,f)
71 static const m_option_t stream_opts_fields
[] = {
72 {"username", ST_OFF(user
), CONF_TYPE_STRING
, 0, 0 ,0, NULL
},
73 {"password", ST_OFF(pass
), CONF_TYPE_STRING
, 0, 0 ,0, NULL
},
74 {"hostname", ST_OFF(host
), CONF_TYPE_STRING
, 0, 0 ,0, NULL
},
75 {"port", ST_OFF(port
), CONF_TYPE_INT
, 0, 0 ,65635, NULL
},
76 {"filename", ST_OFF(filename
), CONF_TYPE_STRING
, 0, 0 ,0, NULL
},
77 { NULL
, NULL
, 0, 0, 0, 0, NULL
}
79 static const struct m_struct_st stream_opts
= {
81 sizeof(struct stream_priv_s
),
86 #define TELNET_IAC 255 /* interpret as command: */
87 #define TELNET_IP 244 /* interrupt process--permanently */
88 #define TELNET_SYNCH 242 /* for telfunc calls */
90 // Check if there is something to read on a fd. This avoid hanging
91 // forever if the network stop responding.
92 static int fd_can_read(int fd
,int timeout
) {
101 return select(fd
+1, &fds
, NULL
, NULL
, &tv
) > 0;
105 * read a line of text
107 * return -1 on error or bytecount
109 static int readline(char *buf
,int max
,struct stream_priv_s
*ctl
)
116 if (ctl
->cavail
> 0) {
117 x
= (max
>= ctl
->cavail
) ? ctl
->cavail
: max
-1;
118 end
= memccpy(bp
,ctl
->cget
,'\n',x
);
129 if (strcmp(bp
,"\r\n") == 0) {
141 if (ctl
->cput
== ctl
->cget
) {
142 ctl
->cput
= ctl
->cget
= ctl
->buf
;
144 ctl
->cleft
= BUFSIZE
;
152 if(!fd_can_read(ctl
->handle
, 15)) {
153 mp_msg(MSGT_OPEN
,MSGL_ERR
, "[ftp] read timed out\n");
158 if ((x
= recv(ctl
->handle
,ctl
->cput
,ctl
->cleft
,0)) == -1) {
159 mp_msg(MSGT_STREAM
,MSGL_ERR
, "[ftp] read error: %s\n",strerror(errno
));
174 * read a response from the server
176 * return 0 if first char doesn't match
177 * return 1 if first char matches
179 static int readresp(struct stream_priv_s
* ctl
,char* rsp
)
181 static char response
[256];
185 if (readline(response
,256,ctl
) == -1)
188 r
= atoi(response
)/100;
189 if(rsp
) strcpy(rsp
,response
);
191 mp_msg(MSGT_STREAM
,MSGL_V
, "[ftp] < %s",response
);
193 if (response
[3] == '-') {
194 strncpy(match
,response
,3);
198 if (readline(response
,256,ctl
) == -1) {
199 mp_msg(MSGT_OPEN
,MSGL_ERR
, "[ftp] Control socket read failed\n");
202 mp_msg(MSGT_OPEN
,MSGL_V
, "[ftp] < %s",response
);
203 } while (strncmp(response
,match
,4));
209 static int FtpSendCmd(const char *cmd
, struct stream_priv_s
*nControl
,char* rsp
)
212 int hascrlf
= cmd
[l
- 2] == '\r' && cmd
[l
- 1] == '\n';
214 if(hascrlf
&& l
== 2) mp_msg(MSGT_STREAM
,MSGL_V
, "\n");
215 else mp_msg(MSGT_STREAM
,MSGL_V
, "[ftp] > %s",cmd
);
217 int s
= send(nControl
->handle
,cmd
,l
,0);
220 mp_msg(MSGT_OPEN
,MSGL_ERR
, "[ftp] write error: %s\n",strerror(errno
));
229 return readresp(nControl
,rsp
);
231 return FtpSendCmd("\r\n", nControl
, rsp
);
234 static int FtpOpenPort(struct stream_priv_s
* p
) {
240 resp
= FtpSendCmd("PASV",p
,rsp_txt
);
242 mp_msg(MSGT_OPEN
,MSGL_WARN
, "[ftp] command 'PASV' failed: %s\n",rsp_txt
);
246 par
= strchr(rsp_txt
,'(');
248 if(!par
|| !par
[0] || !par
[1]) {
249 mp_msg(MSGT_OPEN
,MSGL_ERR
, "[ftp] invalid server response: %s ??\n",rsp_txt
);
253 sscanf(par
+1,"%u,%u,%u,%u,%u,%u",&num
[0],&num
[1],&num
[2],
254 &num
[3],&num
[4],&num
[5]);
255 snprintf(str
,127,"%d.%d.%d.%d",num
[0],num
[1],num
[2],num
[3]);
256 fd
= connect2Server(str
,(num
[4]<<8)+num
[5],0);
259 mp_msg(MSGT_OPEN
,MSGL_ERR
, "[ftp] failed to create data connection\n");
264 static int FtpOpenData(stream_t
* s
,off_t newpos
) {
265 struct stream_priv_s
* p
= s
->priv
;
267 char str
[256],rsp_txt
[256];
269 // Open a new connection
270 s
->fd
= FtpOpenPort(p
);
272 if(s
->fd
< 0) return 0;
275 snprintf(str
,255,"REST %"PRId64
, (int64_t)newpos
);
277 resp
= FtpSendCmd(str
,p
,rsp_txt
);
279 mp_msg(MSGT_OPEN
,MSGL_WARN
, "[ftp] command '%s' failed: %s\n",str
,rsp_txt
);
285 snprintf(str
,255,"RETR %s",p
->filename
);
286 resp
= FtpSendCmd(str
,p
,rsp_txt
);
289 mp_msg(MSGT_OPEN
,MSGL_ERR
, "[ftp] command '%s' failed: %s\n",str
,rsp_txt
);
297 static int fill_buffer(stream_t
*s
, char* buffer
, int max_len
){
300 if(s
->fd
< 0 && !FtpOpenData(s
,s
->pos
))
303 if(!fd_can_read(s
->fd
, 15)) {
304 mp_msg(MSGT_OPEN
,MSGL_ERR
, "[ftp] read timed out\n");
308 r
= recv(s
->fd
,buffer
,max_len
,0);
309 return (r
<= 0) ? -1 : r
;
312 static int seek(stream_t
*s
,off_t newpos
) {
313 struct stream_priv_s
* p
= s
->priv
;
317 if(s
->pos
> s
->end_pos
) {
322 // Check to see if the server did not already terminate the transfer
323 if(fd_can_read(p
->handle
, 0)) {
324 if(readresp(p
,rsp_txt
) != 2)
325 mp_msg(MSGT_OPEN
,MSGL_WARN
, "[ftp] Warning the server didn't finished the transfer correctly: %s\n",rsp_txt
);
330 // Close current download
332 static const char pre_cmd
[]={TELNET_IAC
,TELNET_IP
,TELNET_IAC
,TELNET_SYNCH
};
335 // First close the fd
339 // Send send the telnet sequence needed to make the server react
341 // Dunno if this is really needed, lftp have it. I let
342 // it here in case it turn out to be needed on some other OS
343 //fl=fcntl(p->handle,F_GETFL);
344 //fcntl(p->handle,F_SETFL,fl&~O_NONBLOCK);
346 // send only first byte as OOB due to OOB braindamage in many unices
347 send(p
->handle
,pre_cmd
,1,MSG_OOB
);
348 send(p
->handle
,pre_cmd
+1,sizeof(pre_cmd
)-1,0);
350 //fcntl(p->handle,F_SETFL,fl);
352 // Get the 426 Transfer aborted
353 // Or the 226 Transfer complete
354 resp
= readresp(p
,rsp_txt
);
355 if(resp
!= 4 && resp
!= 2) {
356 mp_msg(MSGT_OPEN
,MSGL_ERR
, "[ftp] Server didn't abort correctly: %s\n",rsp_txt
);
360 // Send the ABOR command
361 // Ignore the return code as sometimes it fail with "nothing to abort"
362 FtpSendCmd("ABOR",p
,rsp_txt
);
364 return FtpOpenData(s
,newpos
);
368 static void close_f(stream_t
*s
) {
369 struct stream_priv_s
* p
= s
->priv
;
378 FtpSendCmd("QUIT",p
,NULL
);
380 if(p
->handle
) closesocket(p
->handle
);
381 if(p
->buf
) free(p
->buf
);
383 m_struct_free(&stream_opts
,p
);
388 static int open_f(stream_t
*stream
,int mode
, void* opts
, int* file_format
) {
391 struct stream_priv_s
* p
= (struct stream_priv_s
*)opts
;
392 char str
[256],rsp_txt
[256];
394 if(mode
!= STREAM_READ
) {
395 mp_msg(MSGT_OPEN
,MSGL_ERR
, "[ftp] Unknown open mode %d\n",mode
);
396 m_struct_free(&stream_opts
,opts
);
397 return STREAM_UNSUPPORTED
;
400 if(!p
->filename
|| !p
->host
) {
401 mp_msg(MSGT_OPEN
,MSGL_ERR
, "[ftp] Bad url\n");
402 m_struct_free(&stream_opts
,opts
);
406 // Open the control connection
407 p
->handle
= connect2Server(p
->host
,p
->port
,1);
410 m_struct_free(&stream_opts
,opts
);
414 // We got a connection, let's start serious things
417 p
->buf
= malloc(BUFSIZE
);
419 if (readresp(p
, NULL
) == 0) {
421 m_struct_free(&stream_opts
,opts
);
426 snprintf(str
,255,"USER %s",p
->user
);
427 resp
= FtpSendCmd(str
,p
,rsp_txt
);
431 snprintf(str
,255,"PASS %s",p
->pass
);
432 resp
= FtpSendCmd(str
,p
,rsp_txt
);
434 mp_msg(MSGT_OPEN
,MSGL_ERR
, "[ftp] command '%s' failed: %s\n",str
,rsp_txt
);
438 } else if(resp
!= 2) {
439 mp_msg(MSGT_OPEN
,MSGL_ERR
, "[ftp] command '%s' failed: %s\n",str
,rsp_txt
);
444 // Set the transfer type
445 resp
= FtpSendCmd("TYPE I",p
,rsp_txt
);
447 mp_msg(MSGT_OPEN
,MSGL_WARN
, "[ftp] command 'TYPE I' failed: %s\n",rsp_txt
);
453 snprintf(str
,255,"SIZE %s",p
->filename
);
454 resp
= FtpSendCmd(str
,p
,rsp_txt
);
456 mp_msg(MSGT_OPEN
,MSGL_WARN
, "[ftp] command '%s' failed: %s\n",str
,rsp_txt
);
459 sscanf(rsp_txt
,"%d %"SCNd64
,&dummy
,&len
);
464 stream
->end_pos
= len
;
467 // The data connection is really opened only at the first
468 // read/seek. This must be done when the cache is used
469 // because the connection would stay open in the main process,
470 // preventing correct abort with many servers.
473 stream
->fill_buffer
= fill_buffer
;
474 stream
->close
= close_f
;
479 const stream_info_t stream_info_ftp
= {
480 "File Transfer Protocol",
483 "reuse a bit of code from ftplib written by Thomas Pfau",
487 1 // Urls are an option string