~ubuntu-dev/mplayer/ubuntu-feisty

« back to all changes in this revision

Viewing changes to libmpdemux/stream_ftp.c

  • Committer: Reinhard Tartler
  • Date: 2006-07-08 08:45:33 UTC
  • Revision ID: siretart@tauware.de-20060708084533-dbc155bde7122e78
imported mplayer_0.99+1.0pre7try2+cvs20060117

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
 
 
2
#include "config.h"
 
3
 
 
4
#ifdef HAVE_FTP
 
5
 
 
6
#include <stdlib.h>
 
7
#include <stdio.h>
 
8
 
 
9
#include <sys/types.h>
 
10
#include <sys/stat.h>
 
11
#include <fcntl.h>
 
12
#include <unistd.h>
 
13
#include <errno.h>
 
14
#ifndef HAVE_WINSOCK2
 
15
#include <sys/socket.h>
 
16
#define closesocket close
 
17
#else
 
18
#include <winsock2.h>
 
19
#endif
 
20
 
 
21
#include "mp_msg.h"
 
22
#include "stream.h"
 
23
#include "help_mp.h"
 
24
#include "m_option.h"
 
25
#include "m_struct.h"
 
26
 
 
27
static struct stream_priv_s {
 
28
  char* user;
 
29
  char* pass;
 
30
  char* host;
 
31
  int port;
 
32
  char* filename;
 
33
 
 
34
  char *cput,*cget;
 
35
  int handle;
 
36
  int cavail,cleft;
 
37
  char *buf;
 
38
} stream_priv_dflts = {
 
39
  "anonymous","no@spam",
 
40
  NULL,
 
41
  21,
 
42
  NULL,
 
43
  NULL,
 
44
  NULL,
 
45
 
 
46
  0,
 
47
  0,0,
 
48
  NULL
 
49
};
 
50
 
 
51
#define BUFSIZE 2048
 
52
 
 
53
#define ST_OFF(f) M_ST_OFF(struct stream_priv_s,f)
 
54
/// URL definition
 
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 }
 
62
};
 
63
static struct m_struct_st stream_opts = {
 
64
  "ftp",
 
65
  sizeof(struct stream_priv_s),
 
66
  &stream_priv_dflts,
 
67
  stream_opts_fields
 
68
};
 
69
 
 
70
#define TELNET_IAC      255             /* interpret as command: */
 
71
#define TELNET_IP       244             /* interrupt process--permanently */
 
72
#define TELNET_SYNCH    242             /* for telfunc calls */
 
73
 
 
74
/*
 
75
 * read a line of text
 
76
 *
 
77
 * return -1 on error or bytecount
 
78
 */
 
79
static int readline(char *buf,int max,struct stream_priv_s *ctl)
 
80
{
 
81
    int x,retval = 0;
 
82
    char *end,*bp=buf;
 
83
    int eof = 0;
 
84
 
 
85
    do {
 
86
      if (ctl->cavail > 0) {
 
87
        x = (max >= ctl->cavail) ? ctl->cavail : max-1;
 
88
        end = memccpy(bp,ctl->cget,'\n',x);
 
89
        if (end != NULL)
 
90
          x = end - bp;
 
91
        retval += x;
 
92
        bp += x;
 
93
        *bp = '\0';
 
94
        max -= x;
 
95
        ctl->cget += x;
 
96
        ctl->cavail -= x;
 
97
        if (end != NULL) {
 
98
          bp -= 2;
 
99
          if (strcmp(bp,"\r\n") == 0) {
 
100
            *bp++ = '\n';
 
101
            *bp++ = '\0';
 
102
            --retval;
 
103
          }
 
104
          break;
 
105
        }
 
106
      }
 
107
      if (max == 1) {
 
108
        *buf = '\0';
 
109
        break;
 
110
      }
 
111
      if (ctl->cput == ctl->cget) {
 
112
        ctl->cput = ctl->cget = ctl->buf;
 
113
        ctl->cavail = 0;
 
114
        ctl->cleft = BUFSIZE;
 
115
      }
 
116
      if(eof) {
 
117
        if (retval == 0)
 
118
          retval = -1;
 
119
        break;
 
120
      }
 
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));
 
123
        retval = -1;
 
124
        break;
 
125
      }
 
126
      if (x == 0)
 
127
        eof = 1;
 
128
      ctl->cleft -= x;
 
129
      ctl->cavail += x;
 
130
      ctl->cput += x;
 
131
    } while (1);
 
132
    
 
133
    return retval;
 
134
}
 
135
 
 
136
/*
 
137
 * read a response from the server
 
138
 *
 
139
 * return 0 if first char doesn't match
 
140
 * return 1 if first char matches
 
141
 */
 
142
static int readresp(struct stream_priv_s* ctl,char* rsp)
 
143
{
 
144
    static char response[256];
 
145
    char match[5];
 
146
    int r;
 
147
 
 
148
    if (readline(response,256,ctl) == -1)
 
149
      return 0;
 
150
 
 
151
    r = atoi(response)/100;
 
152
    if(rsp) strcpy(rsp,response);
 
153
 
 
154
    mp_msg(MSGT_STREAM,MSGL_V, "[ftp] < %s",response);
 
155
 
 
156
    if (response[3] == '-') {
 
157
      strncpy(match,response,3);
 
158
      match[3] = ' ';
 
159
      match[4] = '\0';
 
160
      do {
 
161
        if (readline(response,256,ctl) == -1) {
 
162
          mp_msg(MSGT_OPEN,MSGL_ERR, "[ftp] Control socket read failed\n");
 
163
          return 0;
 
164
        }
 
165
        mp_msg(MSGT_OPEN,MSGL_V, "[ftp] < %s",response);
 
166
      } while (strncmp(response,match,4));
 
167
    }
 
168
    return r;
 
169
}
 
170
 
 
171
 
 
172
static int FtpSendCmd(const char *cmd, struct stream_priv_s *nControl,char* rsp)
 
173
{
 
174
  int l = strlen(cmd);
 
175
  int hascrlf = cmd[l - 2] == '\r' && cmd[l - 1] == '\n';
 
176
 
 
177
  mp_msg(MSGT_STREAM,MSGL_V, "[ftp] > %s",cmd);
 
178
  while(l > 0) {
 
179
    int s = send(nControl->handle,cmd,l,0);
 
180
 
 
181
    if(s <= 0) {
 
182
      mp_msg(MSGT_OPEN,MSGL_ERR, "[ftp] write error: %s\n",strerror(errno));
 
183
      return 0;
 
184
    }
 
185
    
 
186
    cmd += s;
 
187
    l -= s;
 
188
  }
 
189
    
 
190
  if (hascrlf)  
 
191
    return readresp(nControl,rsp);
 
192
  else
 
193
    return FtpSendCmd("\r\n", nControl, rsp);
 
194
}
 
195
 
 
196
static int FtpOpenPort(struct stream_priv_s* p) {
 
197
  int resp,fd;
 
198
  char rsp_txt[256];
 
199
  char* par,str[128];
 
200
  int num[6];
 
201
 
 
202
  resp = FtpSendCmd("PASV",p,rsp_txt);
 
203
  if(resp != 2) {
 
204
    mp_msg(MSGT_OPEN,MSGL_WARN, "[ftp] command 'PASV' failed: %s\n",rsp_txt);
 
205
    return 0;
 
206
  }
 
207
  
 
208
  par = strchr(rsp_txt,'(');
 
209
  
 
210
  if(!par || !par[0] || !par[1]) {
 
211
    mp_msg(MSGT_OPEN,MSGL_ERR, "[ftp] invalid server response: %s ??\n",rsp_txt);
 
212
    return 0;
 
213
  }
 
214
 
 
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);
 
219
 
 
220
  if(fd <= 0) {
 
221
    mp_msg(MSGT_OPEN,MSGL_ERR, "[ftp] failed to create data connection\n");
 
222
    return 0;
 
223
  }
 
224
 
 
225
  return fd;
 
226
}
 
227
 
 
228
static int fill_buffer(stream_t *s, char* buffer, int max_len){
 
229
  fd_set fds;
 
230
  struct timeval tv;
 
231
  int r;
 
232
 
 
233
  // Check if there is something to read. This avoid hang
 
234
  // forever if the network stop responding.
 
235
  FD_ZERO(&fds);
 
236
  FD_SET(s->fd,&fds);
 
237
  tv.tv_sec = 15;
 
238
  tv.tv_usec = 0;
 
239
 
 
240
  if(select(s->fd+1, &fds, NULL, NULL, &tv) < 1) {
 
241
    mp_msg(MSGT_OPEN,MSGL_ERR, "[ftp] read timed out\n");
 
242
    return -1;
 
243
  }
 
244
 
 
245
  r = recv(s->fd,buffer,max_len,0);
 
246
  return (r <= 0) ? -1 : r;
 
247
}
 
248
 
 
249
static int seek(stream_t *s,off_t newpos) {
 
250
  struct stream_priv_s* p = s->priv;
 
251
  int resp;
 
252
  char str[256],rsp_txt[256];
 
253
  fd_set fds;
 
254
  struct timeval tv;
 
255
 
 
256
  if(s->pos > s->end_pos) {
 
257
    s->eof=1;
 
258
    return 0;
 
259
  }
 
260
 
 
261
  FD_ZERO(&fds);
 
262
  FD_SET(p->handle,&fds);
 
263
  tv.tv_sec = tv.tv_usec = 0;
 
264
 
 
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);
 
269
    closesocket(s->fd);
 
270
    s->fd = -1;
 
271
  }
 
272
 
 
273
  // Close current download
 
274
  if(s->fd >= 0) {
 
275
    static const char pre_cmd[]={TELNET_IAC,TELNET_IP,TELNET_IAC,TELNET_SYNCH};
 
276
    //int fl;
 
277
    
 
278
    // First close the fd
 
279
    closesocket(s->fd);
 
280
    s->fd = 0;
 
281
    
 
282
    // Send send the telnet sequence needed to make the server react
 
283
    
 
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);
 
288
 
 
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);
 
292
    
 
293
    //fcntl(p->handle,F_SETFL,fl);
 
294
 
 
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);
 
300
      s->eof = 1;
 
301
      return 0;
 
302
    }
 
303
    // Send the ABOR command
 
304
    // Ignore the return code as sometimes it fail with "nothing to abort"
 
305
    FtpSendCmd("ABOR",p,rsp_txt);
 
306
  }
 
307
 
 
308
  // Open a new connection
 
309
  s->fd = FtpOpenPort(p);
 
310
 
 
311
  if(!s->fd) return 0;
 
312
 
 
313
  if(newpos > 0) {
 
314
    snprintf(str,255,"REST %"PRId64, (int64_t)newpos);
 
315
 
 
316
    resp = FtpSendCmd(str,p,rsp_txt);
 
317
    if(resp != 3) {
 
318
      mp_msg(MSGT_OPEN,MSGL_WARN, "[ftp] command '%s' failed: %s\n",str,rsp_txt);
 
319
      newpos = 0;
 
320
    }
 
321
  }
 
322
 
 
323
  // Get the file
 
324
  snprintf(str,255,"RETR %s",p->filename);
 
325
  resp = FtpSendCmd(str,p,rsp_txt);
 
326
 
 
327
  if(resp != 1) {
 
328
    mp_msg(MSGT_OPEN,MSGL_ERR, "[ftp] command '%s' failed: %s\n",str,rsp_txt);
 
329
    return 0;
 
330
  }
 
331
 
 
332
  s->pos = newpos;
 
333
  return 1;
 
334
}
 
335
 
 
336
 
 
337
static void close_f(stream_t *s) {
 
338
  struct stream_priv_s* p = s->priv;
 
339
 
 
340
  if(!p) return;
 
341
 
 
342
  if(s->fd > 0) {
 
343
    closesocket(s->fd);
 
344
    s->fd = 0;
 
345
  }
 
346
 
 
347
  FtpSendCmd("QUIT",p,NULL);
 
348
 
 
349
  if(p->handle) closesocket(p->handle);
 
350
  if(p->buf) free(p->buf);
 
351
 
 
352
  m_struct_free(&stream_opts,p);
 
353
}
 
354
 
 
355
 
 
356
 
 
357
static int open_f(stream_t *stream,int mode, void* opts, int* file_format) {
 
358
  int len = 0,resp;
 
359
  struct stream_priv_s* p = (struct stream_priv_s*)opts;
 
360
  char str[256],rsp_txt[256];
 
361
 
 
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;
 
366
  }
 
367
 
 
368
  if(!p->filename || !p->host) {
 
369
    mp_msg(MSGT_OPEN,MSGL_ERR, "[ftp] Bad url\n");
 
370
    m_struct_free(&stream_opts,opts);
 
371
    return STREAM_ERROR;
 
372
  }
 
373
 
 
374
  // Open the control connection
 
375
  p->handle = connect2Server(p->host,p->port,1);
 
376
  
 
377
  if(p->handle < 0) {
 
378
    m_struct_free(&stream_opts,opts);
 
379
    return STREAM_ERROR;
 
380
  }
 
381
 
 
382
  // We got a connection, let's start serious things
 
383
  stream->fd = -1;
 
384
  stream->priv = p;
 
385
  p->buf = malloc(BUFSIZE);
 
386
 
 
387
  if (readresp(p, NULL) == 0) {
 
388
    close_f(stream);
 
389
    m_struct_free(&stream_opts,opts);
 
390
    return STREAM_ERROR;
 
391
  }
 
392
 
 
393
  // Login
 
394
  snprintf(str,255,"USER %s",p->user);
 
395
  resp = FtpSendCmd(str,p,rsp_txt);
 
396
 
 
397
  // password needed
 
398
  if(resp == 3) {
 
399
    snprintf(str,255,"PASS %s",p->pass);
 
400
    resp = FtpSendCmd(str,p,rsp_txt);
 
401
    if(resp != 2) {
 
402
      mp_msg(MSGT_OPEN,MSGL_ERR, "[ftp] command '%s' failed: %s\n",str,rsp_txt);
 
403
      close_f(stream);
 
404
      return STREAM_ERROR;
 
405
    }
 
406
  } else if(resp != 2) {
 
407
    mp_msg(MSGT_OPEN,MSGL_ERR, "[ftp] command '%s' failed: %s\n",str,rsp_txt);
 
408
    close_f(stream);
 
409
    return STREAM_ERROR;
 
410
  }
 
411
    
 
412
  // Set the transfert type
 
413
  resp = FtpSendCmd("TYPE I",p,rsp_txt);
 
414
  if(resp != 2) {
 
415
    mp_msg(MSGT_OPEN,MSGL_WARN, "[ftp] command 'TYPE I' failed: %s\n",rsp_txt);
 
416
    close_f(stream);
 
417
    return STREAM_ERROR;
 
418
  }
 
419
 
 
420
  // Get the filesize
 
421
  snprintf(str,255,"SIZE %s",p->filename);
 
422
  resp = FtpSendCmd(str,p,rsp_txt);
 
423
  if(resp != 2) {
 
424
    mp_msg(MSGT_OPEN,MSGL_WARN, "[ftp] command '%s' failed: %s\n",str,rsp_txt);
 
425
  } else {
 
426
    int dummy;
 
427
    sscanf(rsp_txt,"%d %d",&dummy,&len);
 
428
  }
 
429
 
 
430
  // Start the data connection
 
431
  stream->fd = FtpOpenPort(p);
 
432
  if(stream->fd <= 0) {
 
433
    close_f(stream);
 
434
    return STREAM_ERROR;
 
435
  }
 
436
 
 
437
  // Get the file
 
438
  snprintf(str,255,"RETR %s",p->filename);
 
439
  resp = FtpSendCmd(str,p,rsp_txt);
 
440
 
 
441
  if(resp != 1) {
 
442
    mp_msg(MSGT_OPEN,MSGL_WARN, "[ftp] command '%s' failed: %s\n",str,rsp_txt);
 
443
    close_f(stream);
 
444
    return STREAM_ERROR;
 
445
  }
 
446
  
 
447
 
 
448
  if(len > 0) {
 
449
    stream->seek = seek;
 
450
    stream->end_pos = len;
 
451
  }
 
452
 
 
453
  stream->priv = p;
 
454
  stream->fill_buffer = fill_buffer;
 
455
  stream->close = close_f;
 
456
 
 
457
  return STREAM_OK;
 
458
}
 
459
 
 
460
stream_info_t stream_info_ftp = {
 
461
  "File Transfer Protocol",
 
462
  "ftp",
 
463
  "Albeu",
 
464
  "reuse a bit of code from ftplib written by Thomas Pfau",
 
465
  open_f,
 
466
  { "ftp", NULL },
 
467
  &stream_opts,
 
468
  1 // Urls are an option string
 
469
};
 
470
 
 
471
#endif