]> git.sesse.net Git - vlc/blob - modules/access/ftp.c
50f91be9fdc84bd19b0f71091d925e6a0e7a84e7
[vlc] / modules / access / ftp.c
1 /*****************************************************************************
2  * ftp.c:
3  *****************************************************************************
4  * Copyright (C) 2001, 2002 VideoLAN
5  * $Id: ftp.c,v 1.2 2002/12/16 15:17:12 fenrir 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;
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:%d", 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.p_selected_area->i_size = p_access->i_filesize;
375     p_input->stream.i_method = INPUT_METHOD_NETWORK;
376     vlc_mutex_unlock( &p_input->stream.stream_lock );
377
378     /* Update default_pts to a suitable value for ftp access */
379     p_input->i_pts_delay = config_GetInt( p_input, "ftp-caching" ) * 1000;
380
381     return( 0 );
382
383 exit_error:
384     NetClose( p_input, &p_access->socket_cmd );
385     FREE( p_access->url.psz_private );
386     FREE( p_input->p_access_data );
387     return( -1 );
388 }
389
390 /*****************************************************************************
391  * Close: free unused data structures
392  *****************************************************************************/
393 static void Close( vlc_object_t *p_this )
394 {
395     input_thread_t  *p_input = (input_thread_t *)p_this;
396     access_t        *p_access = (access_t*)p_input->p_access_data;
397
398     msg_Dbg( p_input, "stopping stream" );
399     ftp_StopStream( p_input );
400
401     if( ftp_SendCommand( p_input, "QUIT" ) < 0 )
402     {
403         msg_Err( p_input, "cannot quit" );
404     }
405     else
406     {
407         ftp_ReadCommand( p_input, NULL, NULL );
408     }
409
410
411     NetClose( p_input, &p_access->socket_cmd );
412
413     /* free memory */
414     FREE( p_access->url.psz_private );
415 }
416
417 /*****************************************************************************
418  * SetProgram: do nothing
419  *****************************************************************************/
420 static int SetProgram( input_thread_t * p_input,
421                        pgrm_descriptor_t * p_program )
422 {
423     return( 0 );
424 }
425
426 /*****************************************************************************
427  * Seek: try to go at the right place
428  *****************************************************************************/
429 static void Seek( input_thread_t * p_input, off_t i_pos )
430 {
431     //access_t    *p_access = (access_t*)p_input->p_access_data;
432     if( i_pos < 0 )
433     {
434         return;
435     }
436     vlc_mutex_lock( &p_input->stream.stream_lock );
437
438     msg_Dbg( p_input, "seeking to "I64Fd, i_pos );
439
440     ftp_StopStream( p_input );
441     ftp_StartStream( p_input, i_pos );
442
443     p_input->stream.p_selected_area->i_tell = i_pos;
444     vlc_mutex_unlock( &p_input->stream.stream_lock );
445 }
446
447 static int  Read        ( input_thread_t * p_input, byte_t * p_buffer,
448                           size_t i_len )
449 {
450     access_t    *p_access = (access_t*)p_input->p_access_data;
451     size_t      i_data;
452
453     i_data = NetRead( p_input, &p_access->socket_data, p_buffer, i_len );
454
455     return( i_data );
456 }
457
458 static int  ftp_SendCommand( input_thread_t *p_input, char *psz_fmt, ... )
459 {
460     access_t        *p_access = (access_t*)p_input->p_access_data;
461     va_list args;
462     char    *psz_buffer;
463 #if !defined(HAVE_VASPRINTF) || defined(SYS_DARWIN)
464         size_t  i_size;
465 #endif
466
467     va_start( args, psz_fmt );
468
469 #if defined(HAVE_VASPRINTF) && !defined(SYS_DARWIN)
470     vasprintf( &psz_buffer, psz_fmt, args );
471 #else
472     i_size = strlen( psz_fmt ) + 2048;
473     psz_buffer = calloc( i_size, sizeof( char ) );
474     vsnprintf( psz_buffer, i_size, psz_fmt, args );
475     psz_buffer[i_size - 1] = 0;
476 #endif
477     if( !strncmp( psz_buffer, "PASS", 4 ) )
478     {
479         msg_Dbg( p_input, "ftp_SendCommand:\"PASS xxx\"" );
480     }
481     else
482     {
483         msg_Dbg( p_input, "ftp_SendCommand:\"%s\"", psz_buffer );
484     }
485     psz_buffer = realloc( psz_buffer, strlen( psz_buffer ) + 3 );
486     strcat( psz_buffer, "\r\n" );
487     if( send( p_access->socket_cmd.i_handle,
488               psz_buffer,
489               strlen( psz_buffer ),
490               0 ) == -1 )
491     {
492         FREE( psz_buffer );
493         msg_Err( p_input, "failed to send command" );
494         return( -1 );
495     }
496     FREE( psz_buffer );
497
498     va_end( args );
499
500     return( 0 );
501 }
502
503 #define BLOCK_SIZE  1024
504 /* TODO support this s**t :
505  RFC 959 allows the client to send certain TELNET strings at any moment,
506  even in the middle of a request:
507
508  * \377\377.
509  * \377\376x where x is one byte.
510  * \377\375x where x is one byte. The server is obliged to send \377\374x
511  *                                immediately after reading x.
512  * \377\374x where x is one byte.
513  * \377\373x where x is one byte. The server is obliged to send \377\376x
514  *                                immediately after reading x.
515  * \377x for any other byte x.
516
517  These strings are not part of the requests, except in the case \377\377,
518  where the request contains one \377. */
519
520 static int  ftp_ReadCommand( input_thread_t *p_input,
521                              int *pi_answer, char **ppsz_answer )
522 {
523     access_t        *p_access = (access_t*)p_input->p_access_data;
524     uint8_t *p_buffer;
525     int     i_buffer;
526     int     i_buffer_size;
527
528     int i_answer;
529
530     i_buffer      = 0;
531     i_buffer_size = BLOCK_SIZE + 1;
532     p_buffer      = malloc( BLOCK_SIZE + 1);
533
534     for( ;; )
535     {
536         ssize_t i_read;
537         i_read = NetRead( p_input, &p_access->socket_cmd,
538                           p_buffer + i_buffer, BLOCK_SIZE );
539         if( i_read <= 0 || p_input->b_die || p_input->b_error )
540         {
541             free( p_buffer );
542             if( pi_answer )   *pi_answer    = 500;
543             if( ppsz_answer ) *ppsz_answer  = NULL;
544             return( -1 );
545         }
546         if( i_read == 0 )
547         {
548 //            continue;
549         }
550         i_buffer += i_read;
551         if( i_read < BLOCK_SIZE )
552         {
553             p_buffer[i_buffer] = '\0';
554             break;
555         }
556         i_buffer_size += BLOCK_SIZE;
557         p_buffer = realloc( p_buffer, i_buffer_size );
558     }
559
560     if( i_buffer < 3 )
561     {
562         goto exit_error;
563     }
564
565     i_answer = atoi( p_buffer );
566
567     if( pi_answer ) *pi_answer = i_answer;
568     if( ppsz_answer )
569     {
570         *ppsz_answer = p_buffer;
571     }
572     else
573     {
574         free( p_buffer );
575     }
576     return( i_answer / 100 );
577
578 exit_error:
579     free( p_buffer );
580     if( pi_answer )   *pi_answer    = 500;
581     if( ppsz_answer ) *ppsz_answer  = NULL;
582     return( -1 );
583 }
584
585 static int  ftp_StartStream( input_thread_t *p_input, off_t i_start )
586 {
587     access_t        *p_access = (access_t*)p_input->p_access_data;
588
589     char psz_ip[1000];
590     int  i_answer;
591     char *psz_arg, *psz_parser;
592     int  a1,a2,a3,a4;
593     int  p1,p2;
594     int  i_port;
595     module_t            *p_network;
596     network_socket_t    socket_desc;
597
598     if( ftp_SendCommand( p_input, "PASV" ) < 0 )
599     {
600         msg_Err( p_input, "cannot set passive transfert mode" );
601         return( -1 );
602     }
603     if( ftp_ReadCommand( p_input, &i_answer, &psz_arg ) != 2 )
604     {
605         msg_Err( p_input, "cannot set passive transfert mode" );
606         return( -1 );
607     }
608     psz_parser = strchr( psz_arg, '(' );
609     if( !psz_parser || sscanf( psz_parser, "(%d,%d,%d,%d,%d,%d", &a1, &a2, &a3, &a4, &p1, &p2 ) < 6 )
610     {
611         FREE( psz_arg );
612         msg_Err( p_input, "cannot get ip/port for passive transfert mode" );
613         return( -1 );
614     }
615     FREE( psz_arg );
616
617     sprintf( psz_ip, "%d.%d.%d.%d", a1, a2, a3, a4 );
618     i_port = p1 * 256 + p2;
619     msg_Dbg( p_input, "ip:%s port:%d", psz_ip, i_port );
620
621     if( ftp_SendCommand( p_input, "TYPE I" ) < 0 )
622     {
623         msg_Err( p_input, "cannot set binary transfert mode" );
624         return( -1 );
625     }
626     if( ftp_ReadCommand( p_input, &i_answer, NULL ) != 2 )
627     {
628         msg_Err( p_input, "cannot set binary transfert mode" );
629         return( -1 );
630     }
631
632
633     if( i_start > 0 )
634     {
635         if( ftp_SendCommand( p_input, "REST "I64Fu, i_start ) < 0 )
636         {
637             msg_Err( p_input, "cannot set restart point" );
638             return( -1 );
639         }
640         if( ftp_ReadCommand( p_input, &i_answer, NULL ) > 3 )
641         {
642             msg_Err( p_input, "cannot set restart point" );
643             return( -1 );
644         }
645     }
646
647     msg_Dbg( p_input, "waiting for data connection..." );
648     socket_desc.i_type = NETWORK_TCP;
649     socket_desc.psz_server_addr = psz_ip;
650     socket_desc.i_server_port   = i_port;
651     socket_desc.psz_bind_addr   = "";
652     socket_desc.i_bind_port     = 0;
653     p_input->p_private = (void*)&socket_desc;
654     if( !( p_network = module_Need( p_input, "network", "" ) ) )
655     {
656         msg_Err( p_input, "failed to connect with server" );
657         return( -1 );
658     }
659     module_Unneed( p_input, p_network );
660     p_access->socket_data.i_handle = socket_desc.i_handle;
661     p_input->i_mtu    = socket_desc.i_mtu;
662     msg_Dbg( p_input,
663              "connection with \"%s:%d\" successful",
664              psz_ip, i_port );
665
666     if( ftp_SendCommand( p_input, "RETR %s", p_access->url.psz_path ) < 0 )
667     {
668         msg_Err( p_input, "cannot retreive file" );
669         return( -1 );
670     }
671     /* "1xx" message */
672     if( ftp_ReadCommand( p_input, &i_answer, NULL ) > 2 )
673     {
674         msg_Err( p_input, "cannot retreive file" );
675         return( -1 );
676     }
677
678     return( 0 );
679 }
680
681 static int  ftp_StopStream ( input_thread_t *p_input)
682 {
683     access_t        *p_access = (access_t*)p_input->p_access_data;
684
685     int i_answer;
686
687     NetClose( p_input, &p_access->socket_data );
688
689     if( ftp_SendCommand( p_input, "ABOR" ) < 0 )
690     {
691         msg_Err( p_input, "cannot abord file" );
692     }
693     else
694     {
695         ftp_ReadCommand( p_input, &i_answer, NULL );
696         ftp_ReadCommand( p_input, &i_answer, NULL );
697     }
698
699     return( 0 );
700 }
701
702 /****************************************************************************
703  *
704  ****************************************************************************/
705 static void ftp_ParseURL( url_t *p_url, char *psz_url )
706 {
707     char *psz_parser;
708     char *psz_server_port;
709
710     p_url->psz_private = strdup( psz_url );
711
712     psz_parser = p_url->psz_private;
713
714     while( *psz_parser == '/' )
715     {
716         psz_parser++;
717     }
718     p_url->psz_server_addr = psz_parser;
719
720     while( *psz_parser &&
721            *psz_parser != ':' &&  *psz_parser != '/' && *psz_parser != '@' )
722     {
723         psz_parser++;
724     }
725
726     if( *psz_parser == ':' )
727     {
728         *psz_parser = '\0';
729         psz_parser++;
730         psz_server_port = psz_parser;
731
732         while( *psz_parser && *psz_parser != '/' )
733         {
734             psz_parser++;
735         }
736     }
737     else
738     {
739         psz_server_port = "";
740     }
741
742     if( *psz_parser == '@' )
743     {
744         char *psz_bind_port;
745
746         *psz_parser = '\0';
747         psz_parser++;
748
749         p_url->psz_bind_addr = psz_parser;
750
751         while( *psz_parser && *psz_parser != ':' && *psz_parser != '/' )
752         {
753             psz_parser++;
754         }
755
756         if( *psz_parser == ':' )
757         {
758             *psz_parser = '\0';
759             psz_parser++;
760             psz_bind_port = psz_parser;
761
762             while( *psz_parser && *psz_parser != '/' )
763             {
764                 psz_parser++;
765             }
766         }
767         else
768         {
769             psz_bind_port = "";
770         }
771         if( *psz_bind_port )
772         {
773             p_url->i_bind_port = strtol( psz_bind_port, &psz_parser, 10 );
774         }
775         else
776         {
777             p_url->i_bind_port = 0;
778         }
779     }
780     else
781     {
782         p_url->psz_bind_addr = "";
783         p_url->i_bind_port = 0;
784     }
785
786     if( *psz_parser == '/' )
787     {
788         *psz_parser = '\0';
789         psz_parser++;
790         p_url->psz_path = psz_parser;
791     }
792
793     if( *psz_server_port )
794     {
795         p_url->i_server_port = strtol( psz_server_port, &psz_parser, 10 );
796     }
797     else
798     {
799         p_url->i_server_port = 0;
800     }
801 }
802
803 /*****************************************************************************
804  * Read: read on a file descriptor, checking b_die periodically
805  *****************************************************************************/
806 static ssize_t NetRead( input_thread_t *p_input,
807                         input_socket_t *p_socket,
808                         byte_t *p_buffer, size_t i_len )
809 {
810 #ifdef UNDER_CE
811     return -1;
812
813 #else
814     struct timeval  timeout;
815     fd_set          fds;
816     int             i_ret;
817
818     /* Initialize file descriptor set */
819     FD_ZERO( &fds );
820     FD_SET( p_socket->i_handle, &fds );
821
822     /* We'll wait 1 second if nothing happens */
823     timeout.tv_sec  = 0;
824     timeout.tv_usec = 1000000;
825
826     /* Find if some data is available */
827     i_ret = select( p_socket->i_handle + 1, &fds,
828                     NULL, NULL, &timeout );
829
830     if( i_ret == -1 && errno != EINTR )
831     {
832         msg_Err( p_input, "network select error (%s)", strerror(errno) );
833     }
834     else if( i_ret > 0 )
835     {
836         ssize_t i_recv = recv( p_socket->i_handle, p_buffer, i_len, 0 );
837
838         if( i_recv < 0 )
839         {
840             msg_Err( p_input, "recv failed (%s)", strerror(errno) );
841         }
842
843         return i_recv;
844     }
845
846     return 0;
847
848 #endif
849 }
850
851 static void NetClose( input_thread_t *p_input, input_socket_t *p_socket )
852 {
853 #if defined( UNDER_CE )
854     CloseHandle( (HANDLE)p_socket->i_handle );
855 #elif defined( WIN32 )
856     closesocket( p_socket->i_handle );
857 #else
858     close( p_socket->i_handle );
859 #endif
860 }
861