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 */
77
* return -1 on error or bytecount
79
static int readline(char *buf,int max,struct stream_priv_s *ctl)
86
if (ctl->cavail > 0) {
87
x = (max >= ctl->cavail) ? ctl->cavail : max-1;
88
end = memccpy(bp,ctl->cget,'\n',x);
99
if (strcmp(bp,"\r\n") == 0) {
111
if (ctl->cput == ctl->cget) {
112
ctl->cput = ctl->cget = ctl->buf;
114
ctl->cleft = BUFSIZE;
121
if ((x = recv(ctl->handle,ctl->cput,ctl->cleft,0)) == -1) {
122
mp_msg(MSGT_STREAM,MSGL_ERR, "[ftp] read error: %s\n",strerror(errno));
137
* read a response from the server
139
* return 0 if first char doesn't match
140
* return 1 if first char matches
142
static int readresp(struct stream_priv_s* ctl,char* rsp)
144
static char response[256];
148
if (readline(response,256,ctl) == -1)
151
r = atoi(response)/100;
152
if(rsp) strcpy(rsp,response);
154
mp_msg(MSGT_STREAM,MSGL_V, "[ftp] < %s",response);
156
if (response[3] == '-') {
157
strncpy(match,response,3);
161
if (readline(response,256,ctl) == -1) {
162
mp_msg(MSGT_OPEN,MSGL_ERR, "[ftp] Control socket read failed\n");
165
mp_msg(MSGT_OPEN,MSGL_V, "[ftp] < %s",response);
166
} while (strncmp(response,match,4));
172
static int FtpSendCmd(const char *cmd, struct stream_priv_s *nControl,char* rsp)
175
int hascrlf = cmd[l - 2] == '\r' && cmd[l - 1] == '\n';
177
mp_msg(MSGT_STREAM,MSGL_V, "[ftp] > %s",cmd);
179
int s = send(nControl->handle,cmd,l,0);
182
mp_msg(MSGT_OPEN,MSGL_ERR, "[ftp] write error: %s\n",strerror(errno));
191
return readresp(nControl,rsp);
193
return FtpSendCmd("\r\n", nControl, rsp);
196
static int FtpOpenPort(struct stream_priv_s* p) {
202
resp = FtpSendCmd("PASV",p,rsp_txt);
204
mp_msg(MSGT_OPEN,MSGL_WARN, "[ftp] command 'PASV' failed: %s\n",rsp_txt);
208
par = strchr(rsp_txt,'(');
210
if(!par || !par[0] || !par[1]) {
211
mp_msg(MSGT_OPEN,MSGL_ERR, "[ftp] invalid server response: %s ??\n",rsp_txt);
215
sscanf(par+1,"%u,%u,%u,%u,%u,%u",&num[0],&num[1],&num[2],
216
&num[3],&num[4],&num[5]);
217
snprintf(str,127,"%d.%d.%d.%d",num[0],num[1],num[2],num[3]);
218
fd = connect2Server(str,(num[4]<<8)+num[5],0);
221
mp_msg(MSGT_OPEN,MSGL_ERR, "[ftp] failed to create data connection\n");
228
static int fill_buffer(stream_t *s, char* buffer, int max_len){
233
// Check if there is something to read. This avoid hang
234
// forever if the network stop responding.
240
if(select(s->fd+1, &fds, NULL, NULL, &tv) < 1) {
241
mp_msg(MSGT_OPEN,MSGL_ERR, "[ftp] read timed out\n");
245
r = recv(s->fd,buffer,max_len,0);
246
return (r <= 0) ? -1 : r;
249
static int seek(stream_t *s,off_t newpos) {
250
struct stream_priv_s* p = s->priv;
252
char str[256],rsp_txt[256];
256
if(s->pos > s->end_pos) {
262
FD_SET(p->handle,&fds);
263
tv.tv_sec = tv.tv_usec = 0;
265
// Check to see if the server doesn't alredy terminated the transfert
266
if(select(p->handle+1, &fds, NULL, NULL, &tv) > 0) {
267
if(readresp(p,rsp_txt) != 2)
268
mp_msg(MSGT_OPEN,MSGL_WARN, "[ftp] Warning the server didn't finished the transfert correctly: %s\n",rsp_txt);
273
// Close current download
275
static const char pre_cmd[]={TELNET_IAC,TELNET_IP,TELNET_IAC,TELNET_SYNCH};
278
// First close the fd
282
// Send send the telnet sequence needed to make the server react
284
// Dunno if this is really needed, lftp have it. I let
285
// it here in case it turn out to be needed on some other OS
286
//fl=fcntl(p->handle,F_GETFL);
287
//fcntl(p->handle,F_SETFL,fl&~O_NONBLOCK);
289
// send only first byte as OOB due to OOB braindamage in many unices
290
send(p->handle,pre_cmd,1,MSG_OOB);
291
send(p->handle,pre_cmd+1,sizeof(pre_cmd)-1,0);
293
//fcntl(p->handle,F_SETFL,fl);
295
// Get the 426 Transfer aborted
296
// Or the 226 Transfer complete
297
resp = readresp(p,rsp_txt);
298
if(resp != 4 && resp != 2) {
299
mp_msg(MSGT_OPEN,MSGL_ERR, "[ftp] Server didn't abort correctly: %s\n",rsp_txt);
303
// Send the ABOR command
304
// Ignore the return code as sometimes it fail with "nothing to abort"
305
FtpSendCmd("ABOR",p,rsp_txt);
308
// Open a new connection
309
s->fd = FtpOpenPort(p);
314
snprintf(str,255,"REST %"PRId64, (int64_t)newpos);
316
resp = FtpSendCmd(str,p,rsp_txt);
318
mp_msg(MSGT_OPEN,MSGL_WARN, "[ftp] command '%s' failed: %s\n",str,rsp_txt);
324
snprintf(str,255,"RETR %s",p->filename);
325
resp = FtpSendCmd(str,p,rsp_txt);
328
mp_msg(MSGT_OPEN,MSGL_ERR, "[ftp] command '%s' failed: %s\n",str,rsp_txt);
337
static void close_f(stream_t *s) {
338
struct stream_priv_s* p = s->priv;
347
FtpSendCmd("QUIT",p,NULL);
349
if(p->handle) closesocket(p->handle);
350
if(p->buf) free(p->buf);
352
m_struct_free(&stream_opts,p);
357
static int open_f(stream_t *stream,int mode, void* opts, int* file_format) {
359
struct stream_priv_s* p = (struct stream_priv_s*)opts;
360
char str[256],rsp_txt[256];
362
if(mode != STREAM_READ) {
363
mp_msg(MSGT_OPEN,MSGL_ERR, "[ftp] Unknown open mode %d\n",mode);
364
m_struct_free(&stream_opts,opts);
365
return STREAM_UNSUPORTED;
368
if(!p->filename || !p->host) {
369
mp_msg(MSGT_OPEN,MSGL_ERR, "[ftp] Bad url\n");
370
m_struct_free(&stream_opts,opts);
374
// Open the control connection
375
p->handle = connect2Server(p->host,p->port,1);
378
m_struct_free(&stream_opts,opts);
382
// We got a connection, let's start serious things
385
p->buf = malloc(BUFSIZE);
387
if (readresp(p, NULL) == 0) {
389
m_struct_free(&stream_opts,opts);
394
snprintf(str,255,"USER %s",p->user);
395
resp = FtpSendCmd(str,p,rsp_txt);
399
snprintf(str,255,"PASS %s",p->pass);
400
resp = FtpSendCmd(str,p,rsp_txt);
402
mp_msg(MSGT_OPEN,MSGL_ERR, "[ftp] command '%s' failed: %s\n",str,rsp_txt);
406
} else if(resp != 2) {
407
mp_msg(MSGT_OPEN,MSGL_ERR, "[ftp] command '%s' failed: %s\n",str,rsp_txt);
412
// Set the transfert type
413
resp = FtpSendCmd("TYPE I",p,rsp_txt);
415
mp_msg(MSGT_OPEN,MSGL_WARN, "[ftp] command 'TYPE I' failed: %s\n",rsp_txt);
421
snprintf(str,255,"SIZE %s",p->filename);
422
resp = FtpSendCmd(str,p,rsp_txt);
424
mp_msg(MSGT_OPEN,MSGL_WARN, "[ftp] command '%s' failed: %s\n",str,rsp_txt);
427
sscanf(rsp_txt,"%d %d",&dummy,&len);
430
// Start the data connection
431
stream->fd = FtpOpenPort(p);
432
if(stream->fd <= 0) {
438
snprintf(str,255,"RETR %s",p->filename);
439
resp = FtpSendCmd(str,p,rsp_txt);
442
mp_msg(MSGT_OPEN,MSGL_WARN, "[ftp] command '%s' failed: %s\n",str,rsp_txt);
450
stream->end_pos = len;
454
stream->fill_buffer = fill_buffer;
455
stream->close = close_f;
460
stream_info_t stream_info_ftp = {
461
"File Transfer Protocol",
464
"reuse a bit of code from ftplib written by Thomas Pfau",
468
1 // Urls are an option string