]> git.sesse.net Git - vlc/blob - modules/misc/sap.c
dd4f882758b9a7b201f64706f9a6107d1b7c1a54
[vlc] / modules / misc / sap.c
1 /*****************************************************************************
2  * sap.c :  SAP interface module
3  *****************************************************************************
4  * Copyright (C) 2001 VideoLAN
5  * $Id: sap.c,v 1.7 2003/01/21 18:43:54 lool Exp $
6  *
7  * Authors: Arnaud Schauly <gitan@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>                                      /* malloc(), free() */
28 #include <string.h>
29
30 #include <errno.h>                                                 /* ENOMEM */
31 #include <stdio.h>
32 #include <ctype.h>
33 #include <signal.h>
34
35 #include <vlc/vlc.h>
36 #include <vlc/intf.h>
37 #include <vlc/vout.h>
38
39 #ifdef HAVE_UNISTD_H
40 #    include <unistd.h>
41 #endif
42
43 #ifdef HAVE_SYS_TIME_H
44 #    include <sys/time.h>
45 #endif
46 #include <sys/types.h>
47
48
49 #ifdef WIN32
50 #   include <winsock2.h>
51 #   include <ws2tcpip.h>
52 #   ifndef IN_MULTICAST
53 #       define IN_MULTICAST(a) IN_CLASSD(a)
54 #   endif
55 #else
56 #   include <sys/socket.h>
57 #   include <netinet/in.h>
58 #   if HAVE_ARPA_INET_H
59 #      include <arpa/inet.h>
60 #   elif defined( SYS_BEOS )
61 #      include <net/netdb.h>
62 #   endif
63 #endif
64
65 #include "network.h"
66
67 #define MAX_LINE_LENGTH 256
68
69 /* SAP is always on that port */
70 #define HELLO_PORT 9875
71 #define HELLO_GROUP "224.2.127.254"
72 #define ADD_SESSION 1
73
74 /*****************************************************************************
75  * Local prototypes
76  *****************************************************************************/
77
78 typedef struct media_descr_t media_descr_t;
79 typedef struct sess_descr_t sess_descr_t;
80
81 static int  Activate     ( vlc_object_t * );
82 static void Run          ( intf_thread_t *p_intf );
83 static int Kill          ( intf_thread_t * );
84
85 static ssize_t NetRead    ( intf_thread_t*, int , byte_t *, size_t );
86
87 /* playlist related functions */
88 static int  sess_toitem( intf_thread_t *, sess_descr_t * );
89
90 /* sap/sdp related functions */
91 static int parse_sap ( char ** );
92 static int packet_handle ( intf_thread_t *, char ** );
93 static sess_descr_t *  parse_sdp( char *,intf_thread_t * ) ;
94
95 /* specific sdp fields parsing */
96
97 static void cfield_parse( char *, char ** );
98 static void mfield_parse( char *psz_mfield, char **ppsz_proto,
99                char **ppsz_port );
100
101 static void free_sd( sess_descr_t * );
102 static int  ismult( char * );
103
104 /* The struct that contains sdp informations */
105 struct  sess_descr_t {
106     char *psz_version;
107     char *psz_origin;
108     char *psz_sessionname;
109     char *psz_information;
110     char *psz_uri;
111     char *psz_emails;
112     char *psz_phone;
113     char *psz_time;
114     char *psz_repeat;
115     char *psz_attribute;
116     char *psz_connection;
117     int  i_media;
118     media_descr_t ** pp_media;
119 };
120
121 /* All this informations are not useful yet.  */
122 struct media_descr_t {
123     char *psz_medianame;
124     char *psz_mediaconnection;
125 };
126
127 /*****************************************************************************
128  * Module descriptor
129  *****************************************************************************/
130 vlc_module_begin();
131     add_category_hint( N_("SAP"), NULL );
132         add_string( "sap-addr", NULL, NULL,
133                      "SAP multicast address", "SAP multicast address" );
134     set_description( _("SAP interface module") );
135     set_capability( "interface", 0 );
136     set_callbacks( Activate, NULL);
137 vlc_module_end();
138
139 /*****************************************************************************
140  * Activate: initialize and create stuff
141  *****************************************************************************/
142 static int Activate( vlc_object_t *p_this )
143 {
144     intf_thread_t *p_intf = (intf_thread_t*)p_this;
145
146     p_intf->pf_run = Run;
147
148     return VLC_SUCCESS;
149 }
150
151 /*****************************************************************************
152  * Run: sap thread
153  *****************************************************************************
154  * Listens to SAP packets, and sends them to packet_handle
155  *****************************************************************************/
156
157 static void Run( intf_thread_t *p_intf )
158 {
159     char *psz_addr;
160     char *psz_network = NULL;
161     struct sockaddr_in addr;
162     int fd,addrlen;
163     char *psz_buf;
164
165     module_t            *p_network;
166     network_socket_t    socket_desc;
167
168     psz_buf = NULL;
169
170     if( !(psz_addr = config_GetPsz( p_intf, "sap-addr" ) ) )
171     {
172         psz_addr = strdup( HELLO_GROUP );
173     }
174
175     /* Prepare the network_socket_t structure */
176     socket_desc.i_type = NETWORK_UDP;
177     socket_desc.psz_bind_addr = psz_addr;
178     socket_desc.i_bind_port   = HELLO_PORT;
179     socket_desc.psz_server_addr   = "";
180     socket_desc.i_server_port     = 0;
181     p_intf->p_private = (void*) &socket_desc;
182
183     psz_network = "ipv4"; // FIXME
184
185     /* Create, Bind the socket, ... with the appropriate module  */
186
187     if( !( p_network = module_Need( p_intf, "network", psz_network ) ) )
188     {
189         msg_Warn( p_intf, "failed to open a connection (udp)" );
190         return;
191     }
192     module_Unneed( p_intf, p_network );
193
194     fd = socket_desc.i_handle;
195
196
197     /* read SAP packets */
198
199     psz_buf = malloc( 2000 );
200     if( psz_buf == NULL )
201     {
202         msg_Err( p_intf, "Not enough memory for psz_buf in Run()" );
203         return;
204     }
205
206     while( !p_intf->b_die )
207     {
208         int i_read;
209         addrlen=sizeof(addr);
210
211
212         memset(psz_buf, 0, 2000);
213
214         i_read = NetRead( p_intf, fd, psz_buf, 2000 );
215
216         if( i_read < 0 )
217         {
218             msg_Err( p_intf, "Cannot read in the socket" );
219         }
220         if( i_read == 0 )
221         {
222             continue;
223         }
224
225
226         packet_handle( p_intf,  &psz_buf );
227
228     }
229     free( psz_buf );
230
231     /* Closing socket */
232
233 #ifdef UNDER_CE
234     CloseHandle( socket_desc.i_handle );
235 #elif defined( WIN32 )
236     closesocket( socket_desc.i_handle );
237 #else
238     close( socket_desc.i_handle );
239 #endif
240
241 }
242
243 /********************************************************************
244  * Kill
245  *******************************************************************
246  * Kills the SAP interface.
247  ********************************************************************/
248 static int Kill( intf_thread_t *p_intf )
249 {
250
251     p_intf->b_die = VLC_TRUE;
252
253     return VLC_SUCCESS;
254 }
255
256 /*******************************************************************
257  * sess_toitem : changes a sess_descr_t into a hurd of
258  * playlist_item_t, which are enqueued.
259  *******************************************************************
260  * Note : does not support sessions that take place on consecutive
261  * port or adresses yet.
262  *******************************************************************/
263
264 static int sess_toitem( intf_thread_t * p_intf, sess_descr_t * p_sd )
265 {
266     playlist_item_t * p_item;
267     char *psz_uri, *psz_proto;
268     char *psz_port;
269     char *psz_uri_default;
270     int i_multicast;
271     int i_count;
272     playlist_t *p_playlist;
273
274     psz_uri_default = NULL;
275     cfield_parse( p_sd->psz_connection, &psz_uri_default );
276
277     for( i_count=0 ; i_count <= p_sd->i_media ; i_count ++ )
278     {
279         p_item = malloc( sizeof( playlist_item_t ) );
280         if( p_item == NULL )
281         {
282             msg_Err( p_intf, "Not enough memory for p_item in sesstoitem()" );
283             return 0;
284         }
285         p_item->psz_name = strdup( p_sd->psz_sessionname );
286         p_item->i_type = 0;
287         p_item->i_status = 0;
288         p_item->b_autodeletion = VLC_FALSE;
289         p_item->psz_uri = NULL;
290
291         psz_uri = NULL;
292
293         /* Build what we have to put in p_item->psz_uri, with the m and
294          *  c fields  */
295
296         if( !p_sd->pp_media[i_count] )
297         {
298             return 0;
299         }
300
301         mfield_parse( p_sd->pp_media[i_count]->psz_medianame,
302                         & psz_proto, & psz_port );
303
304         if( !psz_proto || !psz_port )
305         {
306             return 0;
307         }
308
309         if( p_sd->pp_media[i_count]->psz_mediaconnection )
310         {
311             cfield_parse( p_sd->pp_media[i_count]->psz_mediaconnection,
312                             & psz_uri );
313         }
314         else
315         {
316             psz_uri = NULL;
317         }
318
319         if( psz_uri == NULL )
320         {
321             if( psz_uri_default )
322             {
323                 psz_uri = psz_uri_default;
324             }
325             else
326             {
327                 return 0;
328             }
329         }
330
331
332         /* Filling p_item->psz_uri */
333         i_multicast = ismult( psz_uri );
334
335         p_item->psz_uri = malloc( strlen( psz_proto ) + strlen( psz_uri ) +
336                         strlen( psz_port ) + 5 +i_multicast );
337         if( p_item->psz_uri == NULL )
338         {
339             msg_Err( p_intf, "Not enough memory");
340             free( p_item );
341             return 0;
342         }
343
344         if( i_multicast == 1)
345         {
346             sprintf( p_item->psz_uri, "%s://@%s:%s", psz_proto,
347                             psz_uri, psz_port );
348         }
349         else
350         {
351             sprintf( p_item->psz_uri, "%s://%s:%s", psz_proto,
352                             psz_uri, psz_port );
353         }
354
355         /* Enqueueing p_item in the playlist */
356
357         if( p_item )
358         {
359             p_playlist = vlc_object_find( p_intf,
360             VLC_OBJECT_PLAYLIST, FIND_ANYWHERE );
361
362             playlist_AddItem ( p_playlist, p_item,
363             PLAYLIST_CHECK_INSERT, PLAYLIST_END);
364             vlc_object_release( p_playlist );
365         }
366
367
368     }
369
370
371     return 1;
372
373 }
374
375 /**********************************************************************
376  * cfield_parse
377  *********************************************************************
378  * put into *ppsz_uri, the the uri in the cfield, psz_cfield.
379  *********************************************************************/
380
381 static void cfield_parse( char *psz_cfield, char **ppsz_uri )
382 {
383
384     char *psz_pos;
385     if( psz_cfield )
386     {
387         psz_pos = psz_cfield;
388
389         while( *psz_pos != ' ' && *psz_pos !='\0' )
390         {
391             psz_pos++;
392         }
393         psz_pos++;
394         while( *psz_pos != ' ' && *psz_pos !='\0' )
395         {
396             psz_pos++;
397         }
398         psz_pos++;
399         *ppsz_uri = psz_pos;
400         while( *psz_pos != ' ' && *psz_pos !='/'
401                         && *psz_pos != '\0' )
402         {
403             psz_pos++;
404         }
405         *psz_pos = '\0';
406
407     }
408     else
409     {
410         ppsz_uri = NULL;
411     }
412
413     return;
414
415 }
416
417 /**********************************************************************
418  * mfield_parse
419  *********************************************************************
420  * put into *ppsz_proto, and *ppsz_port, the protocol and the port.
421  *********************************************************************/
422
423
424 static void mfield_parse( char *psz_mfield, char **ppsz_proto,
425                char **ppsz_port )
426 {
427     char *psz_pos;
428     if( psz_mfield )
429     {
430         psz_pos = psz_mfield;
431         while( *psz_pos != '\0' && *psz_pos != ' ' )
432         {
433             psz_pos++;
434         }
435         psz_pos++;
436         *ppsz_port = psz_pos;
437         while( *psz_pos != '\0' && *psz_pos && *psz_pos !=' ' && *psz_pos!='/' )
438         {
439             psz_pos++;
440         }
441         if( *psz_pos == '/' )  // FIXME does not support multi-port
442         {
443             *psz_pos = '\0';
444             psz_pos++;
445             while( *psz_pos != '\0' && *psz_pos !=' ' )
446             {
447             psz_pos++;
448             }
449         }
450         *psz_pos = '\0';
451         psz_pos++;
452         *ppsz_proto = psz_pos;
453         while( *psz_pos!='\0' && *psz_pos !=' ' &&
454                         *psz_pos!='/' )
455         {
456             *psz_pos = tolower( *psz_pos );
457             psz_pos++;
458         }
459         *psz_pos = '\0';
460     }
461     else
462     {
463         *ppsz_proto = NULL;
464         *ppsz_port = NULL;
465     }
466     return;
467 }
468
469 /***********************************************************************
470  * parse_sap : Takes care of the SAP headers
471  ***********************************************************************
472  * checks if the packet has the true headers ;
473  ***********************************************************************/
474
475 static int parse_sap( char **  ppsz_sa_packet ) {  /* Dummy Parser : does nothing !*/
476    if( *ppsz_sa_packet )  return ADD_SESSION; //Add this packet
477    return 0; /* FIXME */
478 }
479
480 /*************************************************************************
481  * packet_handle : handle the received packet and enques the
482  * the understated session
483  *************************************************************************/
484
485 static int packet_handle( intf_thread_t * p_intf, char **  ppsz_packet )  {
486     int j=0;
487     sess_descr_t * p_sd;
488
489     j=parse_sap( ppsz_packet );
490
491     if(j != 0) {
492
493         p_sd = parse_sdp( *ppsz_packet, p_intf );
494
495         sess_toitem ( p_intf, p_sd );
496
497         free_sd ( p_sd );
498         return 1;
499     }
500     return 0; // Invalid Packet
501 }
502
503
504
505
506 /***********************************************************************
507  * parse_sdp : SDP parsing
508  * *********************************************************************
509  * Make a sess_descr_t with a psz
510  ***********************************************************************/
511
512 static sess_descr_t *  parse_sdp( char *  psz_pct, intf_thread_t * p_intf )
513 {
514     int j,k;
515     char **  ppsz_fill=NULL;
516     sess_descr_t *  sd;
517
518     sd = malloc( sizeof(sess_descr_t) );
519     if( sd == NULL )
520     {
521         msg_Err( p_intf, "Not enough memory for sd in parse_sdp()" );
522     }
523     else
524     {
525         sd->pp_media = NULL;
526         sd->psz_origin = NULL;
527         sd->psz_sessionname = NULL;
528         sd->psz_information = NULL;
529         sd->psz_uri = NULL;
530         sd->psz_emails = NULL;
531         sd->psz_phone = NULL;
532         sd->psz_time = NULL;
533         sd->psz_repeat = NULL;
534         sd->psz_attribute = NULL;
535         sd->psz_connection = NULL;
536
537         sd->i_media=-1;
538         j=0;
539         while( psz_pct[j]!=EOF && psz_pct[j] != '\0' )
540         {
541            j++;
542            if (psz_pct[j] == '=')
543            {
544                switch(psz_pct[(j-1)]) {
545                case 'v' : {
546                      ppsz_fill = & sd->psz_version;
547                      break;
548                 }
549                 case 'o' : {
550                    ppsz_fill = & sd->psz_origin;
551                    break;
552                 }
553                 case 's' : {
554                    ppsz_fill = & sd->psz_sessionname;
555                    break;
556                 }
557                 case 'i' : {
558                    ppsz_fill = & sd->psz_information;
559                    break;
560                 }
561                 case 'u' : {
562                    ppsz_fill = & sd->psz_uri;
563                    break;
564                 }
565                 case 'e' : {
566                    ppsz_fill = & sd->psz_emails;
567                    break;
568                 }
569                 case 'p' : {
570                    ppsz_fill = & sd->psz_phone;
571                    break;
572                 }
573                 case 't' : {
574                    ppsz_fill = & sd->psz_time;
575                    break;
576                 }
577                 case 'r' : {
578                    ppsz_fill = & sd->psz_repeat;
579                    break;
580                 }
581                 case 'a' : {
582                    ppsz_fill = & sd->psz_attribute;
583                    break;
584                 }
585                 case 'm' : {
586                     sd->i_media++;
587                     if( sd->pp_media ) {
588                         sd->pp_media = realloc( sd->pp_media,
589                                  ( sizeof( void * )  * (sd->i_media + 1)) );
590                     }
591                     else
592                     {
593                         sd->pp_media = malloc( sizeof ( void * ) );
594                         if( sd->pp_media == NULL )
595                         {
596                             msg_Err( p_intf, "Not enough memory for " \
597                                              "sd->pp_media in parse_sdp()" );
598                             free_sd( sd );
599                             return NULL;
600                         }
601                     }
602                     sd->pp_media[sd->i_media] =
603                             malloc( sizeof( media_descr_t ) );
604                     if( sd->pp_media[sd->i_media] == NULL )
605                     {
606                         sd->i_media--;
607                         msg_Err( p_intf, "Not enough memory for "     \
608                                          "sd->pp_media[sd->i_media] " \
609                                          "in parse_sdp()" );
610                         free_sd( sd );
611                         return NULL;
612                     }
613
614                     sd->pp_media[sd->i_media]->psz_medianame = NULL;
615                     sd->pp_media[sd->i_media]->psz_mediaconnection = NULL;
616
617                     ppsz_fill = & sd->pp_media[sd->i_media]->psz_medianame;
618                     break;
619                 }
620                 case ('c') : {
621                    if( sd->i_media == -1 )
622                    {
623                        ppsz_fill = & sd->psz_connection;
624                    }
625                    else
626                    {
627                        ppsz_fill = & sd->pp_media[sd->i_media]->
628                                psz_mediaconnection;
629                    }
630                    break;
631                 }
632
633                 default : {
634                    ppsz_fill = NULL;
635                 }
636
637
638              }
639           k=0;j++;
640
641           while (psz_pct[j] != '\n'&& psz_pct[j] != EOF) {
642              k++; j++;
643           }
644           j--;
645
646           if( ppsz_fill != NULL )
647           {
648               *ppsz_fill = malloc( sizeof(char) * (k + 1) );
649               if( *ppsz_fill == NULL )
650               {
651                   msg_Err( p_intf, "Not enough memory for "     \
652                                    "*ppsz_fill in parse_sdp()" );
653                   free_sd( sd );
654                   return NULL;
655               }
656               memcpy(*ppsz_fill, &(psz_pct[j-k+1]), k );
657               (*ppsz_fill)[k]='\0';
658           }
659           ppsz_fill = NULL;
660           } // if
661        } //for
662     } //if
663
664     return sd;
665 }
666
667 #define FREE( p ) \
668     if( p ) { free( p ); (p) = NULL; }
669 static void free_sd( sess_descr_t * p_sd )
670 {
671     int i;
672     if( p_sd )
673     {
674         FREE( p_sd->psz_origin );
675         FREE( p_sd->psz_sessionname );
676         FREE( p_sd->psz_information );
677         FREE( p_sd->psz_uri );
678         FREE( p_sd->psz_emails );
679         FREE( p_sd->psz_phone );
680         FREE( p_sd->psz_time );
681         FREE( p_sd->psz_repeat );
682         FREE( p_sd->psz_attribute );
683         FREE( p_sd->psz_connection );
684
685         if( p_sd->i_media >= 0 && p_sd->pp_media )
686         {
687             for( i=0; i <= p_sd->i_media ; i++ )
688             {
689                 FREE( p_sd->pp_media[i]->psz_medianame );
690                 FREE( p_sd->pp_media[i]->psz_mediaconnection );
691             }
692             FREE( p_sd->pp_media );
693         }
694         free( p_sd );
695     }
696     else
697     {
698         ;
699     }
700     return;
701 }
702
703 /***********************************************************************
704  * ismult
705  ***********************************************************************/
706
707 static int ismult( char *psz_uri )
708 {
709     char *psz_c;
710     int i;
711
712     psz_c = malloc( 3 );
713     if( psz_c == NULL )
714     {
715 /*        msg_Err( p_intf, "Not enough memory for psz_c in ismult()" ); */
716         return 0;
717     }
718
719     memcpy( psz_c, psz_uri, 3 );
720     if( psz_c[2] == '.' || psz_c[1] == '.' )
721     {
722         free( psz_c );
723         return 0;
724     }
725
726     i = atoi( psz_c );
727     if( i < 224 )
728     {
729         free( psz_c );
730         return 0;
731     }
732
733     free( psz_c );
734     return 1;
735 }
736
737 /*****************************************************************************
738  * Read: read on a file descriptor, checking b_die periodically
739  *****************************************************************************
740  * Taken from udp.c
741  ******************************************************************************/
742 static ssize_t NetRead( intf_thread_t *p_intf,
743                         int i_handle, byte_t *p_buffer, size_t i_len)
744 {
745 #ifdef UNDER_CE
746     return -1;
747
748 #else
749     struct timeval  timeout;
750     fd_set          fds;
751     int             i_ret;
752
753     /* Initialize file descriptor set */
754     FD_ZERO( &fds );
755     FD_SET( i_handle, &fds );
756
757     /* We'll wait 0.5 second if nothing happens */
758     timeout.tv_sec = 0;
759     timeout.tv_usec = 500000;
760
761     /* Find if some data is available */
762     i_ret = select( i_handle + 1, &fds,
763     NULL, NULL, &timeout );
764
765     if( i_ret == -1 && errno != EINTR )
766     {
767         msg_Err( p_intf, "network select error (%s)", strerror(errno) );
768     }
769     else if( i_ret > 0 )
770     {
771         ssize_t i_recv = recv( i_handle, p_buffer, i_len, 0 );
772
773         if( i_recv < 0 )
774         {
775            msg_Err( p_intf, "recv failed (%s)", strerror(errno) );
776         }
777
778         return i_recv;
779     }
780
781     return 0;
782
783 #endif
784 }
785