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