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