15 #include <sys/socket.h>
16 #define closesocket close
27 static struct stream_priv_s
{
38 } stream_priv_dflts
= {
39 "anonymous","no@spam",
53 #define ST_OFF(f) M_ST_OFF(struct stream_priv_s,f)
55 static m_option_t stream_opts_fields
[] = {
56 {"username", ST_OFF(user
), CONF_TYPE_STRING
, 0, 0 ,0, NULL
},
57 {"password", ST_OFF(pass
), CONF_TYPE_STRING
, 0, 0 ,0, NULL
},
58 {"hostname", ST_OFF(host
), CONF_TYPE_STRING
, 0, 0 ,0, NULL
},
59 {"port", ST_OFF(port
), CONF_TYPE_INT
, 0, 0 ,65635, NULL
},
60 {"filename", ST_OFF(filename
), CONF_TYPE_STRING
, 0, 0 ,0, NULL
},
61 { NULL
, NULL
, 0, 0, 0, 0, NULL
}
63 static struct m_struct_st stream_opts
= {
65 sizeof(struct stream_priv_s
),
70 #define TELNET_IAC 255 /* interpret as command: */
71 #define TELNET_IP 244 /* interrupt process--permanently */
72 #define TELNET_SYNCH 242 /* for telfunc calls */
74 // Check if there is something to read on a fd. This avoid hanging
75 // forever if the network stop responding.
76 static int fd_can_read(int fd
,int timeout
) {
85 return (select(fd
+1, &fds
, NULL
, NULL
, &tv
) > 0);
91 * return -1 on error or bytecount
93 static int readline(char *buf
,int max
,struct stream_priv_s
*ctl
)
100 if (ctl
->cavail
> 0) {
101 x
= (max
>= ctl
->cavail
) ? ctl
->cavail
: max
-1;
102 end
= memccpy(bp
,ctl
->cget
,'\n',x
);
113 if (strcmp(bp
,"\r\n") == 0) {
125 if (ctl
->cput
== ctl
->cget
) {
126 ctl
->cput
= ctl
->cget
= ctl
->buf
;
128 ctl
->cleft
= BUFSIZE
;
136 if(!fd_can_read(ctl
->handle
, 15)) {
137 mp_msg(MSGT_OPEN
,MSGL_ERR
, "[ftp] read timed out\n");
142 if ((x
= recv(ctl
->handle
,ctl
->cput
,ctl
->cleft
,0)) == -1) {
143 mp_msg(MSGT_STREAM
,MSGL_ERR
, "[ftp] read error: %s\n",strerror(errno
));
158 * read a response from the server
160 * return 0 if first char doesn't match
161 * return 1 if first char matches
163 static int readresp(struct stream_priv_s
* ctl
,char* rsp
)
165 static char response
[256];
169 if (readline(response
,256,ctl
) == -1)
172 r
= atoi(response
)/100;
173 if(rsp
) strcpy(rsp
,response
);
175 mp_msg(MSGT_STREAM
,MSGL_V
, "[ftp] < %s",response
);
177 if (response
[3] == '-') {
178 strncpy(match
,response
,3);
182 if (readline(response
,256,ctl
) == -1) {
183 mp_msg(MSGT_OPEN
,MSGL_ERR
, "[ftp] Control socket read failed\n");
186 mp_msg(MSGT_OPEN
,MSGL_V
, "[ftp] < %s",response
);
187 } while (strncmp(response
,match
,4));
193 static int FtpSendCmd(const char *cmd
, struct stream_priv_s
*nControl
,char* rsp
)
196 int hascrlf
= cmd
[l
- 2] == '\r' && cmd
[l
- 1] == '\n';
198 if(hascrlf
&& l
== 2) mp_msg(MSGT_STREAM
,MSGL_V
, "\n");
199 else mp_msg(MSGT_STREAM
,MSGL_V
, "[ftp] > %s",cmd
);
201 int s
= send(nControl
->handle
,cmd
,l
,0);
204 mp_msg(MSGT_OPEN
,MSGL_ERR
, "[ftp] write error: %s\n",strerror(errno
));
213 return readresp(nControl
,rsp
);
215 return FtpSendCmd("\r\n", nControl
, rsp
);
218 static int FtpOpenPort(struct stream_priv_s
* p
) {
224 resp
= FtpSendCmd("PASV",p
,rsp_txt
);
226 mp_msg(MSGT_OPEN
,MSGL_WARN
, "[ftp] command 'PASV' failed: %s\n",rsp_txt
);
230 par
= strchr(rsp_txt
,'(');
232 if(!par
|| !par
[0] || !par
[1]) {
233 mp_msg(MSGT_OPEN
,MSGL_ERR
, "[ftp] invalid server response: %s ??\n",rsp_txt
);
237 sscanf(par
+1,"%u,%u,%u,%u,%u,%u",&num
[0],&num
[1],&num
[2],
238 &num
[3],&num
[4],&num
[5]);
239 snprintf(str
,127,"%d.%d.%d.%d",num
[0],num
[1],num
[2],num
[3]);
240 fd
= connect2Server(str
,(num
[4]<<8)+num
[5],0);
243 mp_msg(MSGT_OPEN
,MSGL_ERR
, "[ftp] failed to create data connection\n");
248 static int FtpOpenData(stream_t
* s
,size_t newpos
) {
249 struct stream_priv_s
* p
= s
->priv
;
251 char str
[256],rsp_txt
[256];
253 // Open a new connection
254 s
->fd
= FtpOpenPort(p
);
256 if(s
->fd
< 0) return 0;
259 snprintf(str
,255,"REST %"PRId64
, (int64_t)newpos
);
261 resp
= FtpSendCmd(str
,p
,rsp_txt
);
263 mp_msg(MSGT_OPEN
,MSGL_WARN
, "[ftp] command '%s' failed: %s\n",str
,rsp_txt
);
269 snprintf(str
,255,"RETR %s",p
->filename
);
270 resp
= FtpSendCmd(str
,p
,rsp_txt
);
273 mp_msg(MSGT_OPEN
,MSGL_ERR
, "[ftp] command '%s' failed: %s\n",str
,rsp_txt
);
281 static int fill_buffer(stream_t
*s
, char* buffer
, int max_len
){
284 if(s
->fd
< 0 && !FtpOpenData(s
,s
->pos
))
287 if(!fd_can_read(s
->fd
, 15)) {
288 mp_msg(MSGT_OPEN
,MSGL_ERR
, "[ftp] read timed out\n");
292 r
= recv(s
->fd
,buffer
,max_len
,0);
293 return (r
<= 0) ? -1 : r
;
296 static int seek(stream_t
*s
,off_t newpos
) {
297 struct stream_priv_s
* p
= s
->priv
;
301 if(s
->pos
> s
->end_pos
) {
306 // Check to see if the server doesn't alredy terminated the transfert
307 if(fd_can_read(p
->handle
, 0)) {
308 if(readresp(p
,rsp_txt
) != 2)
309 mp_msg(MSGT_OPEN
,MSGL_WARN
, "[ftp] Warning the server didn't finished the transfert correctly: %s\n",rsp_txt
);
314 // Close current download
316 static const char pre_cmd
[]={TELNET_IAC
,TELNET_IP
,TELNET_IAC
,TELNET_SYNCH
};
319 // First close the fd
323 // Send send the telnet sequence needed to make the server react
325 // Dunno if this is really needed, lftp have it. I let
326 // it here in case it turn out to be needed on some other OS
327 //fl=fcntl(p->handle,F_GETFL);
328 //fcntl(p->handle,F_SETFL,fl&~O_NONBLOCK);
330 // send only first byte as OOB due to OOB braindamage in many unices
331 send(p
->handle
,pre_cmd
,1,MSG_OOB
);
332 send(p
->handle
,pre_cmd
+1,sizeof(pre_cmd
)-1,0);
334 //fcntl(p->handle,F_SETFL,fl);
336 // Get the 426 Transfer aborted
337 // Or the 226 Transfer complete
338 resp
= readresp(p
,rsp_txt
);
339 if(resp
!= 4 && resp
!= 2) {
340 mp_msg(MSGT_OPEN
,MSGL_ERR
, "[ftp] Server didn't abort correctly: %s\n",rsp_txt
);
344 // Send the ABOR command
345 // Ignore the return code as sometimes it fail with "nothing to abort"
346 FtpSendCmd("ABOR",p
,rsp_txt
);
348 return FtpOpenData(s
,newpos
);
352 static void close_f(stream_t
*s
) {
353 struct stream_priv_s
* p
= s
->priv
;
362 FtpSendCmd("QUIT",p
,NULL
);
364 if(p
->handle
) closesocket(p
->handle
);
365 if(p
->buf
) free(p
->buf
);
367 m_struct_free(&stream_opts
,p
);
372 static int open_f(stream_t
*stream
,int mode
, void* opts
, int* file_format
) {
374 struct stream_priv_s
* p
= (struct stream_priv_s
*)opts
;
375 char str
[256],rsp_txt
[256];
377 if(mode
!= STREAM_READ
) {
378 mp_msg(MSGT_OPEN
,MSGL_ERR
, "[ftp] Unknown open mode %d\n",mode
);
379 m_struct_free(&stream_opts
,opts
);
380 return STREAM_UNSUPORTED
;
383 if(!p
->filename
|| !p
->host
) {
384 mp_msg(MSGT_OPEN
,MSGL_ERR
, "[ftp] Bad url\n");
385 m_struct_free(&stream_opts
,opts
);
389 // Open the control connection
390 p
->handle
= connect2Server(p
->host
,p
->port
,1);
393 m_struct_free(&stream_opts
,opts
);
397 // We got a connection, let's start serious things
400 p
->buf
= malloc(BUFSIZE
);
402 if (readresp(p
, NULL
) == 0) {
404 m_struct_free(&stream_opts
,opts
);
409 snprintf(str
,255,"USER %s",p
->user
);
410 resp
= FtpSendCmd(str
,p
,rsp_txt
);
414 snprintf(str
,255,"PASS %s",p
->pass
);
415 resp
= FtpSendCmd(str
,p
,rsp_txt
);
417 mp_msg(MSGT_OPEN
,MSGL_ERR
, "[ftp] command '%s' failed: %s\n",str
,rsp_txt
);
421 } else if(resp
!= 2) {
422 mp_msg(MSGT_OPEN
,MSGL_ERR
, "[ftp] command '%s' failed: %s\n",str
,rsp_txt
);
427 // Set the transfert type
428 resp
= FtpSendCmd("TYPE I",p
,rsp_txt
);
430 mp_msg(MSGT_OPEN
,MSGL_WARN
, "[ftp] command 'TYPE I' failed: %s\n",rsp_txt
);
436 snprintf(str
,255,"SIZE %s",p
->filename
);
437 resp
= FtpSendCmd(str
,p
,rsp_txt
);
439 mp_msg(MSGT_OPEN
,MSGL_WARN
, "[ftp] command '%s' failed: %s\n",str
,rsp_txt
);
442 sscanf(rsp_txt
,"%d %d",&dummy
,&len
);
447 stream
->end_pos
= len
;
450 // The data connection is really opened only at the first
451 // read/seek. This must be done when the cache is used
452 // because the connection would stay open in the main process,
453 // preventing correct abort with many servers.
456 stream
->fill_buffer
= fill_buffer
;
457 stream
->close
= close_f
;
462 stream_info_t stream_info_ftp
= {
463 "File Transfer Protocol",
466 "reuse a bit of code from ftplib written by Thomas Pfau",
470 1 // Urls are an option string