]> git.sesse.net Git - vlc/blob - modules/access/ftp.c
Sorry to be so pedantic - grammar (and spelling)
[vlc] / modules / access / ftp.c
1 /*****************************************************************************
2  * ftp.c:
3  *****************************************************************************
4  * Copyright (C) 2001-2003 VideoLAN
5  * $Id: ftp.c,v 1.23 2004/01/03 18:41:36 rocky Exp $
6  *
7  * Authors: Laurent Aimar <fenrir@via.ecp.fr>
8  *
9  * This program is free software; you can redistribute it and/or modify
10  * it under the terms of the GNU General Public License as published by
11  * the Free Software Foundation; either version 2 of the License, or
12  * (at your option) any later version.
13  *
14  * This program is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17  * GNU General Public License for more details.
18  *
19  * You should have received a copy of the GNU General Public License
20  * along with this program; if not, write to the Free Software
21  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111, USA.
22  *****************************************************************************/
23
24 /*****************************************************************************
25  * Preamble
26  *****************************************************************************/
27 #include <stdlib.h>
28 #include <sys/types.h>
29 #include <sys/time.h>
30 #include <sys/stat.h>
31 #include <errno.h>
32 #include <fcntl.h>
33
34 #include <vlc/vlc.h>
35 #include <vlc/input.h>
36
37 #ifdef HAVE_SYS_TIME_H
38 #    include <sys/time.h>
39 #endif
40
41 #ifdef HAVE_UNISTD_H
42 #   include <unistd.h>
43 #endif
44
45 #ifdef WIN32
46 #   include <winsock2.h>
47 #   include <ws2tcpip.h>
48 #   ifndef IN_MULTICAST
49 #       define IN_MULTICAST(a) IN_CLASSD(a)
50 #   endif
51 #else
52 #   include <sys/socket.h>
53 #   include <netinet/in.h>
54 #   if HAVE_ARPA_INET_H
55 #      include <arpa/inet.h>
56 #   elif defined( SYS_BEOS )
57 #      include <net/netdb.h>
58 #   endif
59 #endif
60
61 #include "network.h"
62
63 /*****************************************************************************
64  * Local prototypes
65  *****************************************************************************/
66 static int     Open     ( vlc_object_t * );
67 static void    Close    ( vlc_object_t * );
68
69 static ssize_t Read     ( input_thread_t * p_input, byte_t * p_buffer,
70                           size_t i_len );
71 static void    Seek     ( input_thread_t *, off_t );
72
73
74 static ssize_t NetRead ( input_thread_t *, input_socket_t *, byte_t *, size_t );
75 static void    NetClose( input_thread_t *, input_socket_t *);
76
77 static int  ftp_SendCommand( input_thread_t *, char *, ... );
78 static int  ftp_ReadCommand( input_thread_t *, int *, char ** );
79 static int  ftp_StartStream( input_thread_t *, off_t );
80 static int  ftp_StopStream ( input_thread_t *);
81
82 /*****************************************************************************
83  * Module descriptor
84  *****************************************************************************/
85 #define CACHING_TEXT N_("Caching value in ms")
86 #define CACHING_LONGTEXT N_( \
87     "Allows you to modify the default caching value for ftp streams. This " \
88     "value should be set in millisecond units." )
89
90 vlc_module_begin();
91     set_description( _("FTP input") );
92     set_capability( "access", 0 );
93     add_category_hint( "stream", NULL, VLC_FALSE );
94         add_integer( "ftp-caching", 2 * DEFAULT_PTS_DELAY / 1000, NULL,
95                      CACHING_TEXT, CACHING_LONGTEXT, VLC_TRUE );
96         add_string( "ftp-user", "anonymous", NULL, "ftp user name", "ftp user name", VLC_FALSE );
97         add_string( "ftp-pwd", "anonymous@dummy.org", NULL, "ftp password", "ftp password, be careful with that option...", VLC_FALSE );
98         add_string( "ftp-account", "anonymous", NULL, "ftp account", "ftp account", VLC_FALSE );
99     add_shortcut( "ftp" );
100     set_callbacks( Open, Close );
101 vlc_module_end();
102
103 /* url: [/]host[:port][/path] */
104 typedef struct url_s
105 {
106     char    *psz_server_addr;
107     int     i_server_port;
108
109     char    *psz_bind_addr;
110     int     i_bind_port;
111
112     char    *psz_path;
113
114     /* private */
115     char *psz_private;
116 } url_t;
117
118 static void ftp_ParseURL( url_t *, char * );
119
120 #define FREE( p ) if( p ) free( p )
121
122 typedef struct access_s
123 {
124     input_socket_t  socket_cmd;
125     input_socket_t  socket_data;
126
127     url_t           url;                        /* connect to this server */
128
129     off_t           i_filesize;
130
131     int             i_eos;
132
133 } access_t;
134
135
136 /****************************************************************************
137  ****************************************************************************
138  *******************                                      *******************
139  *******************       Main functions                 *******************
140  *******************                                      *******************
141  ****************************************************************************
142  ****************************************************************************/
143
144 /****************************************************************************
145  * Open: connect to ftp server and ask for file
146  ****************************************************************************/
147 static int Open( vlc_object_t *p_this )
148 {
149     input_thread_t  *p_input = (input_thread_t*)p_this;
150
151     access_t    *p_access;
152     char        *psz_network;
153
154     module_t            *p_network;
155     network_socket_t    socket_desc;
156     url_t               *p_url;
157
158     int                 i_answer;
159     char                *psz_user, *psz_pwd, *psz_account;
160
161     char                *psz_arg;
162
163     /* *** allocate p_access_data *** */
164     p_input->p_access_data =
165         (void*)p_access = malloc( sizeof( access_t ) );
166     memset( p_access, 0, sizeof( access_t ) );
167     p_url = &p_access->url;
168
169     /* *** Parse URL and get server addr/port and path *** */
170     ftp_ParseURL( p_url, p_input->psz_name );
171
172     if( p_url->psz_server_addr == NULL ||
173         !( *p_url->psz_server_addr ) )
174     {
175         FREE( p_url->psz_private );
176         msg_Err( p_input, "invalid server name" );
177         return( -1 );
178     }
179     if( p_url->i_server_port == 0 )
180     {
181         p_url->i_server_port = 21; /* default port */
182     }
183
184     /* 2: look at ip version ipv4/ipv6 */
185     psz_network = "";
186     if( config_GetInt( p_input, "ipv4" ) )
187     {
188         psz_network = "ipv4";
189     }
190     else if( config_GetInt( p_input, "ipv6" ) )
191     {
192         psz_network = "ipv6";
193     }
194
195     /* 3: Open a TCP connection with server *** */
196     msg_Dbg( p_input, "waiting for connection..." );
197     socket_desc.i_type = NETWORK_TCP;
198     socket_desc.psz_server_addr = p_url->psz_server_addr;
199     socket_desc.i_server_port   = p_url->i_server_port;
200     socket_desc.psz_bind_addr   = "";
201     socket_desc.i_bind_port     = 0;
202     socket_desc.i_ttl           = 0;
203     p_input->p_private = (void*)&socket_desc;
204     if( !( p_network = module_Need( p_input, "network", psz_network ) ) )
205     {
206         msg_Err( p_input, "failed to connect with server" );
207         FREE( p_access->url.psz_private );
208         FREE( p_input->p_access_data );
209         return( -1 );
210     }
211     module_Unneed( p_input, p_network );
212     p_access->socket_cmd.i_handle = socket_desc.i_handle;
213     p_input->i_mtu    = socket_desc.i_mtu;
214     msg_Dbg( p_input,
215              "connection with \"%s:%d\" successful",
216              p_url->psz_server_addr,
217              p_url->i_server_port );
218
219
220     for( ;; )
221     {
222         if( ftp_ReadCommand( p_input, &i_answer, NULL ) < 0)
223         {
224             msg_Err( p_input, "failed to get answer" );
225             goto exit_error;
226         }
227         if( i_answer / 100 != 1 )
228         {
229             break;
230         }
231     }
232
233     if( i_answer / 100 != 2 )
234     {
235         msg_Err( p_input, "connection rejected" );
236         goto exit_error;
237     }
238     else
239     {
240         msg_Dbg( p_input, "connection accepted (%d)", i_answer );
241     }
242
243     psz_user = config_GetPsz( p_input, "ftp-user" );
244     if( ftp_SendCommand( p_input, "USER %s", psz_user ) < 0 )
245     {
246         FREE( psz_user );
247         goto exit_error;
248     }
249     FREE( psz_user );
250
251     if( ftp_ReadCommand( p_input, &i_answer, NULL ) < 0)
252     {
253         msg_Err( p_input, "failed to get answer" );
254         goto exit_error;
255     }
256     switch( i_answer / 100 )
257     {
258         case 2:
259             msg_Dbg( p_input, "user accepted" );
260             break;
261         case 3:
262             msg_Dbg( p_input, "password needed" );
263             psz_pwd = config_GetPsz( p_input, "ftp-pwd" );
264             if( ftp_SendCommand( p_input, "PASS %s", psz_pwd ) < 0 )
265             {
266                 FREE( psz_pwd );
267                 goto exit_error;
268             }
269             FREE( psz_pwd );
270             if( ftp_ReadCommand( p_input, &i_answer, NULL ) < 0)
271             {
272                 msg_Err( p_input, "failed to get answer" );
273                 goto exit_error;
274             }
275             switch( i_answer / 100 )
276             {
277                 case 2:
278                     msg_Dbg( p_input, "password accepted" );
279                     break;
280                 case 3:
281                     msg_Dbg( p_input, "account needed" );
282                     psz_account = config_GetPsz( p_input, "ftp-account" );
283                     if( ftp_SendCommand( p_input, "ACCT %s", psz_account ) < 0 )
284                     {
285                         FREE( psz_account );
286                         goto exit_error;
287                     }
288                     FREE( psz_account );
289                     if( ftp_ReadCommand( p_input, &i_answer, NULL ) < 0)
290                     {
291                         msg_Err( p_input, "failed to get answer" );
292                         goto exit_error;
293                     }
294                     if( i_answer / 100 != 2 )
295                     {
296                         msg_Err( p_input, "account rejected" );
297                         goto exit_error;
298                     }
299                     else
300                     {
301                         msg_Dbg( p_input, "account accepted" );
302                     }
303                     break;
304                 default:
305                     msg_Err( p_input, "password rejected" );
306                     goto exit_error;
307             }
308             break;
309         default:
310             msg_Err( p_input, "user rejected" );
311             goto exit_error;
312     }
313
314     if( ftp_SendCommand( p_input, "TYPE I" ) < 0 )
315     {
316         msg_Err( p_input, "cannot set binary transfert mode" );
317         goto exit_error;
318     }
319     if( ftp_ReadCommand( p_input, &i_answer, NULL ) != 2 )
320     {
321         msg_Err( p_input, "cannot set binary transfert mode" );
322         goto exit_error;
323     }
324
325     /* get size */
326     if( ftp_SendCommand( p_input, "SIZE %s", p_url->psz_path ) < 0 )
327     {
328         msg_Err( p_input, "cannot get file size" );
329         goto exit_error;
330     }
331     if( ftp_ReadCommand( p_input, &i_answer, &psz_arg ) != 2 )
332     {
333         msg_Err( p_input, "cannot get file size" );
334         goto exit_error;
335     }
336
337     p_access->i_filesize = atoll( psz_arg + 4 );
338
339     msg_Dbg( p_input, "file size: "I64Fd, p_access->i_filesize );
340     FREE( psz_arg );
341
342     if( ftp_StartStream( p_input, 0 ) < 0 )
343     {
344         msg_Err( p_input, "cannot retrieve file" );
345         goto exit_error;
346     }
347     /* *** set exported functions *** */
348     p_input->pf_read = Read;
349     p_input->pf_seek = Seek;
350     p_input->pf_set_program = input_SetProgram;
351     p_input->pf_set_area = NULL;
352
353     p_input->p_private = NULL;
354
355     /* *** finished to set some variable *** */
356     vlc_mutex_lock( &p_input->stream.stream_lock );
357     p_input->stream.b_pace_control = 1;
358     p_input->stream.p_selected_area->i_tell = 0;
359     p_input->stream.b_seekable = 1;
360     p_input->stream.p_selected_area->i_size = p_access->i_filesize;
361     p_input->stream.i_method = INPUT_METHOD_NETWORK;
362     vlc_mutex_unlock( &p_input->stream.stream_lock );
363
364     /* Update default_pts to a suitable value for ftp access */
365     p_input->i_pts_delay = config_GetInt( p_input, "ftp-caching" ) * 1000;
366
367     return( 0 );
368
369 exit_error:
370     NetClose( p_input, &p_access->socket_cmd );
371     FREE( p_access->url.psz_private );
372     FREE( p_input->p_access_data );
373     return( -1 );
374 }
375
376 /*****************************************************************************
377  * Close: free unused data structures
378  *****************************************************************************/
379 static void Close( vlc_object_t *p_this )
380 {
381     input_thread_t  *p_input = (input_thread_t *)p_this;
382     access_t        *p_access = (access_t*)p_input->p_access_data;
383
384     msg_Dbg( p_input, "stopping stream" );
385     ftp_StopStream( p_input );
386
387     if( ftp_SendCommand( p_input, "QUIT" ) < 0 )
388     {
389         msg_Err( p_input, "cannot quit" );
390     }
391     else
392     {
393         ftp_ReadCommand( p_input, NULL, NULL );
394     }
395
396
397     NetClose( p_input, &p_access->socket_cmd );
398
399     /* free memory */
400     FREE( p_access->url.psz_private );
401 }
402
403 /*****************************************************************************
404  * Seek: try to go at the right place
405  *****************************************************************************/
406 static void Seek( input_thread_t * p_input, off_t i_pos )
407 {
408     //access_t    *p_access = (access_t*)p_input->p_access_data;
409     if( i_pos < 0 )
410     {
411         return;
412     }
413     vlc_mutex_lock( &p_input->stream.stream_lock );
414
415     msg_Dbg( p_input, "seeking to "I64Fd, i_pos );
416
417     ftp_StopStream( p_input );
418     ftp_StartStream( p_input, i_pos );
419
420     p_input->stream.p_selected_area->i_tell = i_pos;
421     vlc_mutex_unlock( &p_input->stream.stream_lock );
422 }
423
424 static ssize_t Read     ( input_thread_t * p_input, byte_t * p_buffer,
425                           size_t i_len )
426 {
427     access_t    *p_access = (access_t*)p_input->p_access_data;
428     size_t      i_data;
429
430     i_data = NetRead( p_input, &p_access->socket_data, p_buffer, i_len );
431
432     return( i_data );
433 }
434
435 static int  ftp_SendCommand( input_thread_t *p_input, char *psz_fmt, ... )
436 {
437     access_t        *p_access = (access_t*)p_input->p_access_data;
438     va_list args;
439     char    *psz_buffer;
440 #if !defined(HAVE_VASPRINTF) || defined(SYS_DARWIN) || defined(SYS_BEOS)
441         size_t  i_size;
442 #endif
443
444     va_start( args, psz_fmt );
445
446 #if defined(HAVE_VASPRINTF) && !defined(SYS_DARWIN) && !defined(SYS_BEOS)
447     vasprintf( &psz_buffer, psz_fmt, args );
448 #else
449     i_size = strlen( psz_fmt ) + 2048;
450     psz_buffer = calloc( i_size, sizeof( char ) );
451     vsnprintf( psz_buffer, i_size, psz_fmt, args );
452     psz_buffer[i_size - 1] = 0;
453 #endif
454     if( !strncmp( psz_buffer, "PASS", 4 ) )
455     {
456         msg_Dbg( p_input, "ftp_SendCommand:\"PASS xxx\"" );
457     }
458     else
459     {
460         msg_Dbg( p_input, "ftp_SendCommand:\"%s\"", psz_buffer );
461     }
462     psz_buffer = realloc( psz_buffer, strlen( psz_buffer ) + 3 );
463     strcat( psz_buffer, "\r\n" );
464     if( send( p_access->socket_cmd.i_handle,
465               psz_buffer,
466               strlen( psz_buffer ),
467               0 ) == -1 )
468     {
469         FREE( psz_buffer );
470         msg_Err( p_input, "failed to send command" );
471         return( -1 );
472     }
473     FREE( psz_buffer );
474
475     va_end( args );
476
477     return( 0 );
478 }
479
480 #define BLOCK_SIZE  1024
481 /* TODO support this s**t :
482  RFC 959 allows the client to send certain TELNET strings at any moment,
483  even in the middle of a request:
484
485  * \377\377.
486  * \377\376x where x is one byte.
487  * \377\375x where x is one byte. The server is obliged to send \377\374x
488  *                                immediately after reading x.
489  * \377\374x where x is one byte.
490  * \377\373x where x is one byte. The server is obliged to send \377\376x
491  *                                immediately after reading x.
492  * \377x for any other byte x.
493
494  These strings are not part of the requests, except in the case \377\377,
495  where the request contains one \377. */
496
497 static int  ftp_ReadCommand( input_thread_t *p_input,
498                              int *pi_answer, char **ppsz_answer )
499 {
500     access_t        *p_access = (access_t*)p_input->p_access_data;
501     uint8_t *p_buffer;
502     int     i_buffer;
503     int     i_buffer_size;
504
505     int i_answer;
506
507     i_buffer      = 0;
508     i_buffer_size = BLOCK_SIZE + 1;
509     p_buffer      = malloc( BLOCK_SIZE + 1);
510
511     for( ;; )
512     {
513         ssize_t i_read;
514         i_read = NetRead( p_input, &p_access->socket_cmd,
515                           p_buffer + i_buffer, BLOCK_SIZE );
516         if( i_read <= 0 || p_input->b_die || p_input->b_error )
517         {
518             free( p_buffer );
519             if( pi_answer )   *pi_answer    = 500;
520             if( ppsz_answer ) *ppsz_answer  = NULL;
521             return( -1 );
522         }
523         if( i_read == 0 )
524         {
525 //            continue;
526         }
527         i_buffer += i_read;
528         if( i_read < BLOCK_SIZE )
529         {
530             p_buffer[i_buffer] = '\0';
531             break;
532         }
533         i_buffer_size += BLOCK_SIZE;
534         p_buffer = realloc( p_buffer, i_buffer_size );
535     }
536
537     if( i_buffer < 3 )
538     {
539         goto exit_error;
540     }
541
542     i_answer = atoi( p_buffer );
543
544     if( pi_answer ) *pi_answer = i_answer;
545     if( ppsz_answer )
546     {
547         *ppsz_answer = p_buffer;
548     }
549     else
550     {
551         free( p_buffer );
552     }
553     return( i_answer / 100 );
554
555 exit_error:
556     free( p_buffer );
557     if( pi_answer )   *pi_answer    = 500;
558     if( ppsz_answer ) *ppsz_answer  = NULL;
559     return( -1 );
560 }
561
562 static int  ftp_StartStream( input_thread_t *p_input, off_t i_start )
563 {
564     access_t        *p_access = (access_t*)p_input->p_access_data;
565
566     char psz_ip[1000];
567     int  i_answer;
568     char *psz_arg, *psz_parser;
569     int  a1,a2,a3,a4;
570     int  p1,p2;
571     int  i_port;
572     module_t            *p_network;
573     network_socket_t    socket_desc;
574
575     if( ftp_SendCommand( p_input, "PASV" ) < 0 )
576     {
577         msg_Err( p_input, "cannot set passive transfert mode" );
578         return( -1 );
579     }
580     if( ftp_ReadCommand( p_input, &i_answer, &psz_arg ) != 2 )
581     {
582         msg_Err( p_input, "cannot set passive transfert mode" );
583         return( -1 );
584     }
585     psz_parser = strchr( psz_arg, '(' );
586     if( !psz_parser || sscanf( psz_parser, "(%d,%d,%d,%d,%d,%d", &a1, &a2, &a3, &a4, &p1, &p2 ) < 6 )
587     {
588         FREE( psz_arg );
589         msg_Err( p_input, "cannot get ip/port for passive transfert mode" );
590         return( -1 );
591     }
592     FREE( psz_arg );
593
594     sprintf( psz_ip, "%d.%d.%d.%d", a1, a2, a3, a4 );
595     i_port = p1 * 256 + p2;
596     msg_Dbg( p_input, "ip:%s port:%d", psz_ip, i_port );
597
598     if( ftp_SendCommand( p_input, "TYPE I" ) < 0 )
599     {
600         msg_Err( p_input, "cannot set binary transfert mode" );
601         return( -1 );
602     }
603     if( ftp_ReadCommand( p_input, &i_answer, NULL ) != 2 )
604     {
605         msg_Err( p_input, "cannot set binary transfert mode" );
606         return( -1 );
607     }
608
609
610     if( i_start > 0 )
611     {
612         if( ftp_SendCommand( p_input, "REST "I64Fu, i_start ) < 0 )
613         {
614             msg_Err( p_input, "cannot set restart point" );
615             return( -1 );
616         }
617         if( ftp_ReadCommand( p_input, &i_answer, NULL ) > 3 )
618         {
619             msg_Err( p_input, "cannot set restart point" );
620             return( -1 );
621         }
622     }
623
624     msg_Dbg( p_input, "waiting for data connection..." );
625     socket_desc.i_type = NETWORK_TCP;
626     socket_desc.psz_server_addr = psz_ip;
627     socket_desc.i_server_port   = i_port;
628     socket_desc.psz_bind_addr   = "";
629     socket_desc.i_bind_port     = 0;
630     socket_desc.i_ttl           = 0;
631     p_input->p_private = (void*)&socket_desc;
632     if( !( p_network = module_Need( p_input, "network", "" ) ) )
633     {
634         msg_Err( p_input, "failed to connect with server" );
635         return( -1 );
636     }
637     module_Unneed( p_input, p_network );
638     p_access->socket_data.i_handle = socket_desc.i_handle;
639     p_input->i_mtu    = socket_desc.i_mtu;
640     msg_Dbg( p_input,
641              "connection with \"%s:%d\" successful",
642              psz_ip, i_port );
643
644     if( ftp_SendCommand( p_input, "RETR %s", p_access->url.psz_path ) < 0 )
645     {
646         msg_Err( p_input, "cannot retreive file" );
647         return( -1 );
648     }
649     /* "1xx" message */
650     if( ftp_ReadCommand( p_input, &i_answer, NULL ) > 2 )
651     {
652         msg_Err( p_input, "cannot retreive file" );
653         return( -1 );
654     }
655
656     return( 0 );
657 }
658
659 static int  ftp_StopStream ( input_thread_t *p_input)
660 {
661     access_t        *p_access = (access_t*)p_input->p_access_data;
662
663     int i_answer;
664
665     NetClose( p_input, &p_access->socket_data );
666
667     if( ftp_SendCommand( p_input, "ABOR" ) < 0 )
668     {
669         msg_Err( p_input, "cannot abord file" );
670     }
671     else
672     {
673         ftp_ReadCommand( p_input, &i_answer, NULL );
674         ftp_ReadCommand( p_input, &i_answer, NULL );
675     }
676
677     return( 0 );
678 }
679
680 /****************************************************************************
681  *
682  ****************************************************************************/
683 static void ftp_ParseURL( url_t *p_url, char *psz_url )
684 {
685     char *psz_parser;
686     char *psz_server_port;
687
688     p_url->psz_private = strdup( psz_url );
689
690     psz_parser = p_url->psz_private;
691
692     while( *psz_parser == '/' )
693     {
694         psz_parser++;
695     }
696     p_url->psz_server_addr = psz_parser;
697
698     while( *psz_parser &&
699            *psz_parser != ':' &&  *psz_parser != '/' && *psz_parser != '@' )
700     {
701         psz_parser++;
702     }
703
704     if( *psz_parser == ':' )
705     {
706         *psz_parser = '\0';
707         psz_parser++;
708         psz_server_port = psz_parser;
709
710         while( *psz_parser && *psz_parser != '/' )
711         {
712             psz_parser++;
713         }
714     }
715     else
716     {
717         psz_server_port = "";
718     }
719
720     if( *psz_parser == '@' )
721     {
722         char *psz_bind_port;
723
724         *psz_parser = '\0';
725         psz_parser++;
726
727         p_url->psz_bind_addr = psz_parser;
728
729         while( *psz_parser && *psz_parser != ':' && *psz_parser != '/' )
730         {
731             psz_parser++;
732         }
733
734         if( *psz_parser == ':' )
735         {
736             *psz_parser = '\0';
737             psz_parser++;
738             psz_bind_port = psz_parser;
739
740             while( *psz_parser && *psz_parser != '/' )
741             {
742                 psz_parser++;
743             }
744         }
745         else
746         {
747             psz_bind_port = "";
748         }
749         if( *psz_bind_port )
750         {
751             p_url->i_bind_port = strtol( psz_bind_port, &psz_parser, 10 );
752         }
753         else
754         {
755             p_url->i_bind_port = 0;
756         }
757     }
758     else
759     {
760         p_url->psz_bind_addr = "";
761         p_url->i_bind_port = 0;
762     }
763
764     if( *psz_parser == '/' )
765     {
766         *psz_parser = '\0';
767         psz_parser++;
768         p_url->psz_path = psz_parser;
769     }
770
771     if( *psz_server_port )
772     {
773         p_url->i_server_port = strtol( psz_server_port, &psz_parser, 10 );
774     }
775     else
776     {
777         p_url->i_server_port = 0;
778     }
779 }
780
781 /*****************************************************************************
782  * Read: read on a file descriptor, checking b_die periodically
783  *****************************************************************************/
784 static ssize_t NetRead( input_thread_t *p_input,
785                         input_socket_t *p_socket,
786                         byte_t *p_buffer, size_t i_len )
787 {
788 #ifdef UNDER_CE
789     return -1;
790
791 #else
792     struct timeval  timeout;
793     fd_set          fds;
794     ssize_t         i_recv;
795     int             i_ret;
796
797     /* Initialize file descriptor set */
798     FD_ZERO( &fds );
799     FD_SET( p_socket->i_handle, &fds );
800
801     /* We'll wait 1 second if nothing happens */
802     timeout.tv_sec  = 1;
803     timeout.tv_usec = 0;
804
805     /* Find if some data is available */
806     while( (i_ret = select( p_socket->i_handle + 1, &fds,
807                             NULL, NULL, &timeout )) == 0
808            || (i_ret < 0 && errno == EINTR) )
809     {
810         FD_ZERO( &fds );
811         FD_SET( p_socket->i_handle, &fds );
812         timeout.tv_sec  = 1;
813         timeout.tv_usec = 0;
814
815         if( p_input->b_die || p_input->b_error )
816         {
817             return 0;
818         }
819     }
820
821     if( i_ret < 0 )
822     {
823         msg_Err( p_input, "network select error (%s)", strerror(errno) );
824         return -1;
825     }
826
827     i_recv = recv( p_socket->i_handle, p_buffer, i_len, 0 );
828
829     if( i_recv < 0 )
830     {
831         msg_Err( p_input, "recv failed (%s)", strerror(errno) );
832     }
833
834     return i_recv;
835
836 #endif
837 }
838
839 static void NetClose( input_thread_t *p_input, input_socket_t *p_socket )
840 {
841 #if defined( UNDER_CE )
842     CloseHandle( (HANDLE)p_socket->i_handle );
843 #elif defined( WIN32 )
844     closesocket( p_socket->i_handle );
845 #else
846     close( p_socket->i_handle );
847 #endif
848 }
849