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