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