13 #include <sys/socket.h>
14 #define closesocket close
26 static struct stream_priv_s
{
37 } stream_priv_dflts
= {
38 "anonymous","no@spam",
52 #define ST_OFF(f) M_ST_OFF(struct stream_priv_s,f)
54 static const m_option_t stream_opts_fields
[] = {
55 {"username", ST_OFF(user
), CONF_TYPE_STRING
, 0, 0 ,0, NULL
},
56 {"password", ST_OFF(pass
), CONF_TYPE_STRING
, 0, 0 ,0, NULL
},
57 {"hostname", ST_OFF(host
), CONF_TYPE_STRING
, 0, 0 ,0, NULL
},
58 {"port", ST_OFF(port
), CONF_TYPE_INT
, 0, 0 ,65635, NULL
},
59 {"filename", ST_OFF(filename
), CONF_TYPE_STRING
, 0, 0 ,0, NULL
},
60 { NULL
, NULL
, 0, 0, 0, 0, NULL
}
62 static struct m_struct_st stream_opts
= {
64 sizeof(struct stream_priv_s
),
69 #define TELNET_IAC 255 /* interpret as command: */
70 #define TELNET_IP 244 /* interrupt process--permanently */
71 #define TELNET_SYNCH 242 /* for telfunc calls */
73 // Check if there is something to read on a fd. This avoid hanging
74 // forever if the network stop responding.
75 static int fd_can_read(int fd
,int timeout
) {
84 return (select(fd
+1, &fds
, NULL
, NULL
, &tv
) > 0);
90 * return -1 on error or bytecount
92 static int readline(char *buf
,int max
,struct stream_priv_s
*ctl
)
99 if (ctl
->cavail
> 0) {
100 x
= (max
>= ctl
->cavail
) ? ctl
->cavail
: max
-1;
101 end
= memccpy(bp
,ctl
->cget
,'\n',x
);
112 if (strcmp(bp
,"\r\n") == 0) {
124 if (ctl
->cput
== ctl
->cget
) {
125 ctl
->cput
= ctl
->cget
= ctl
->buf
;
127 ctl
->cleft
= BUFSIZE
;
135 if(!fd_can_read(ctl
->handle
, 15)) {
136 mp_msg(MSGT_OPEN
,MSGL_ERR
, "[ftp] read timed out\n");
141 if ((x
= recv(ctl
->handle
,ctl
->cput
,ctl
->cleft
,0)) == -1) {
142 mp_msg(MSGT_STREAM
,MSGL_ERR
, "[ftp] read error: %s\n",strerror(errno
));
157 * read a response from the server
159 * return 0 if first char doesn't match
160 * return 1 if first char matches
162 static int readresp(struct stream_priv_s
* ctl
,char* rsp
)
164 static char response
[256];
168 if (readline(response
,256,ctl
) == -1)
171 r
= atoi(response
)/100;
172 if(rsp
) strcpy(rsp
,response
);
174 mp_msg(MSGT_STREAM
,MSGL_V
, "[ftp] < %s",response
);
176 if (response
[3] == '-') {
177 strncpy(match
,response
,3);
181 if (readline(response
,256,ctl
) == -1) {
182 mp_msg(MSGT_OPEN
,MSGL_ERR
, "[ftp] Control socket read failed\n");
185 mp_msg(MSGT_OPEN
,MSGL_V
, "[ftp] < %s",response
);
186 } while (strncmp(response
,match
,4));
192 static int FtpSendCmd(const char *cmd
, struct stream_priv_s
*nControl
,char* rsp
)
195 int hascrlf
= cmd
[l
- 2] == '\r' && cmd
[l
- 1] == '\n';
197 if(hascrlf
&& l
== 2) mp_msg(MSGT_STREAM
,MSGL_V
, "\n");
198 else mp_msg(MSGT_STREAM
,MSGL_V
, "[ftp] > %s",cmd
);
200 int s
= send(nControl
->handle
,cmd
,l
,0);
203 mp_msg(MSGT_OPEN
,MSGL_ERR
, "[ftp] write error: %s\n",strerror(errno
));
212 return readresp(nControl
,rsp
);
214 return FtpSendCmd("\r\n", nControl
, rsp
);
217 static int FtpOpenPort(struct stream_priv_s
* p
) {
223 resp
= FtpSendCmd("PASV",p
,rsp_txt
);
225 mp_msg(MSGT_OPEN
,MSGL_WARN
, "[ftp] command 'PASV' failed: %s\n",rsp_txt
);
229 par
= strchr(rsp_txt
,'(');
231 if(!par
|| !par
[0] || !par
[1]) {
232 mp_msg(MSGT_OPEN
,MSGL_ERR
, "[ftp] invalid server response: %s ??\n",rsp_txt
);
236 sscanf(par
+1,"%u,%u,%u,%u,%u,%u",&num
[0],&num
[1],&num
[2],
237 &num
[3],&num
[4],&num
[5]);
238 snprintf(str
,127,"%d.%d.%d.%d",num
[0],num
[1],num
[2],num
[3]);
239 fd
= connect2Server(str
,(num
[4]<<8)+num
[5],0);
242 mp_msg(MSGT_OPEN
,MSGL_ERR
, "[ftp] failed to create data connection\n");
247 static int FtpOpenData(stream_t
* s
,size_t newpos
) {
248 struct stream_priv_s
* p
= s
->priv
;
250 char str
[256],rsp_txt
[256];
252 // Open a new connection
253 s
->fd
= FtpOpenPort(p
);
255 if(s
->fd
< 0) return 0;
258 snprintf(str
,255,"REST %"PRId64
, (int64_t)newpos
);
260 resp
= FtpSendCmd(str
,p
,rsp_txt
);
262 mp_msg(MSGT_OPEN
,MSGL_WARN
, "[ftp] command '%s' failed: %s\n",str
,rsp_txt
);
268 snprintf(str
,255,"RETR %s",p
->filename
);
269 resp
= FtpSendCmd(str
,p
,rsp_txt
);
272 mp_msg(MSGT_OPEN
,MSGL_ERR
, "[ftp] command '%s' failed: %s\n",str
,rsp_txt
);
280 static int fill_buffer(stream_t
*s
, char* buffer
, int max_len
){
283 if(s
->fd
< 0 && !FtpOpenData(s
,s
->pos
))
286 if(!fd_can_read(s
->fd
, 15)) {
287 mp_msg(MSGT_OPEN
,MSGL_ERR
, "[ftp] read timed out\n");
291 r
= recv(s
->fd
,buffer
,max_len
,0);
292 return (r
<= 0) ? -1 : r
;
295 static int seek(stream_t
*s
,off_t newpos
) {
296 struct stream_priv_s
* p
= s
->priv
;
300 if(s
->pos
> s
->end_pos
) {
305 // Check to see if the server did not already terminate the transfer
306 if(fd_can_read(p
->handle
, 0)) {
307 if(readresp(p
,rsp_txt
) != 2)
308 mp_msg(MSGT_OPEN
,MSGL_WARN
, "[ftp] Warning the server didn't finished the transfer correctly: %s\n",rsp_txt
);
313 // Close current download
315 static const char pre_cmd
[]={TELNET_IAC
,TELNET_IP
,TELNET_IAC
,TELNET_SYNCH
};
318 // First close the fd
322 // Send send the telnet sequence needed to make the server react
324 // Dunno if this is really needed, lftp have it. I let
325 // it here in case it turn out to be needed on some other OS
326 //fl=fcntl(p->handle,F_GETFL);
327 //fcntl(p->handle,F_SETFL,fl&~O_NONBLOCK);
329 // send only first byte as OOB due to OOB braindamage in many unices
330 send(p
->handle
,pre_cmd
,1,MSG_OOB
);
331 send(p
->handle
,pre_cmd
+1,sizeof(pre_cmd
)-1,0);
333 //fcntl(p->handle,F_SETFL,fl);
335 // Get the 426 Transfer aborted
336 // Or the 226 Transfer complete
337 resp
= readresp(p
,rsp_txt
);
338 if(resp
!= 4 && resp
!= 2) {
339 mp_msg(MSGT_OPEN
,MSGL_ERR
, "[ftp] Server didn't abort correctly: %s\n",rsp_txt
);
343 // Send the ABOR command
344 // Ignore the return code as sometimes it fail with "nothing to abort"
345 FtpSendCmd("ABOR",p
,rsp_txt
);
347 return FtpOpenData(s
,newpos
);
351 static void close_f(stream_t
*s
) {
352 struct stream_priv_s
* p
= s
->priv
;
361 FtpSendCmd("QUIT",p
,NULL
);
363 if(p
->handle
) closesocket(p
->handle
);
364 if(p
->buf
) free(p
->buf
);
366 m_struct_free(&stream_opts
,p
);
371 static int open_f(stream_t
*stream
,int mode
, void* opts
, int* file_format
) {
373 struct stream_priv_s
* p
= (struct stream_priv_s
*)opts
;
374 char str
[256],rsp_txt
[256];
376 if(mode
!= STREAM_READ
) {
377 mp_msg(MSGT_OPEN
,MSGL_ERR
, "[ftp] Unknown open mode %d\n",mode
);
378 m_struct_free(&stream_opts
,opts
);
379 return STREAM_UNSUPPORTED
;
382 if(!p
->filename
|| !p
->host
) {
383 mp_msg(MSGT_OPEN
,MSGL_ERR
, "[ftp] Bad url\n");
384 m_struct_free(&stream_opts
,opts
);
388 // Open the control connection
389 p
->handle
= connect2Server(p
->host
,p
->port
,1);
392 m_struct_free(&stream_opts
,opts
);
396 // We got a connection, let's start serious things
399 p
->buf
= malloc(BUFSIZE
);
401 if (readresp(p
, NULL
) == 0) {
403 m_struct_free(&stream_opts
,opts
);
408 snprintf(str
,255,"USER %s",p
->user
);
409 resp
= FtpSendCmd(str
,p
,rsp_txt
);
413 snprintf(str
,255,"PASS %s",p
->pass
);
414 resp
= FtpSendCmd(str
,p
,rsp_txt
);
416 mp_msg(MSGT_OPEN
,MSGL_ERR
, "[ftp] command '%s' failed: %s\n",str
,rsp_txt
);
420 } else if(resp
!= 2) {
421 mp_msg(MSGT_OPEN
,MSGL_ERR
, "[ftp] command '%s' failed: %s\n",str
,rsp_txt
);
426 // Set the transfer type
427 resp
= FtpSendCmd("TYPE I",p
,rsp_txt
);
429 mp_msg(MSGT_OPEN
,MSGL_WARN
, "[ftp] command 'TYPE I' failed: %s\n",rsp_txt
);
435 snprintf(str
,255,"SIZE %s",p
->filename
);
436 resp
= FtpSendCmd(str
,p
,rsp_txt
);
438 mp_msg(MSGT_OPEN
,MSGL_WARN
, "[ftp] command '%s' failed: %s\n",str
,rsp_txt
);
441 sscanf(rsp_txt
,"%d %d",&dummy
,&len
);
446 stream
->end_pos
= len
;
449 // The data connection is really opened only at the first
450 // read/seek. This must be done when the cache is used
451 // because the connection would stay open in the main process,
452 // preventing correct abort with many servers.
455 stream
->fill_buffer
= fill_buffer
;
456 stream
->close
= close_f
;
461 const stream_info_t stream_info_ftp
= {
462 "File Transfer Protocol",
465 "reuse a bit of code from ftplib written by Thomas Pfau",
469 1 // Urls are an option string