2008-12-03 Jorn Baayen <jorn.baayen@collabora.co.uk>
[profile/ivi/GSSDP.git] / libgssdp / gssdp-client.c
1 /* 
2  * Copyright (C) 2006, 2007, 2008 OpenedHand Ltd.
3  *
4  * Author: Jorn Baayen <jorn@openedhand.com>
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Library General Public
8  * License as published by the Free Software Foundation; either
9  * version 2 of the License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Library General Public License for more details.
15  *
16  * You should have received a copy of the GNU Library General Public
17  * License along with this library; if not, write to the
18  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
19  * Boston, MA 02111-1307, USA.
20  */
21
22 /**
23  * SECTION:gssdp-client
24  * @short_description: SSDP "bus" wrapper.
25  *
26  * #GSSDPClient wraps the SSDP "bus" as used by both #GSSDPResourceBrowser
27  * and #GSSDPResourceGroup.
28  */
29
30 #include <config.h>
31 #include <sys/socket.h>
32 #include <sys/types.h>
33 #include <sys/utsname.h>
34 #include <netinet/in.h>
35 #include <arpa/inet.h>
36 #include <string.h>
37 #include <stdio.h>
38 #include <errno.h>
39 #include <unistd.h>
40 #include <libsoup/soup-headers.h>
41
42 #include "gssdp-client.h"
43 #include "gssdp-client-private.h"
44 #include "gssdp-error.h"
45 #include "gssdp-socket-source.h"
46 #include "gssdp-marshal.h"
47 #include "gssdp-protocol.h"
48
49 /* Size of the buffer used for reading from the socket */
50 #define BUF_SIZE 1024
51
52 G_DEFINE_TYPE (GSSDPClient,
53                gssdp_client,
54                G_TYPE_OBJECT);
55
56 struct _GSSDPClientPrivate {
57         GMainContext      *main_context;
58
59         char              *server_id;
60
61         GSSDPSocketSource *request_socket;
62         GSSDPSocketSource *multicast_socket;
63 };
64
65 enum {
66         PROP_0,
67         PROP_MAIN_CONTEXT,
68         PROP_SERVER_ID,
69         PROP_ERROR
70 };
71
72 enum {
73         MESSAGE_RECEIVED,
74         LAST_SIGNAL
75 };
76
77 static guint signals[LAST_SIGNAL];
78
79 /* Function prototypes */
80 static void
81 gssdp_client_set_main_context (GSSDPClient  *client,
82                                GMainContext *context);
83 static char *
84 make_server_id                (void);
85 static gboolean
86 request_socket_source_cb      (gpointer      user_data);
87 static gboolean
88 multicast_socket_source_cb    (gpointer      user_data);
89
90 static void
91 gssdp_client_init (GSSDPClient *client)
92 {
93         client->priv = G_TYPE_INSTANCE_GET_PRIVATE
94                                         (client,
95                                          GSSDP_TYPE_CLIENT,
96                                          GSSDPClientPrivate);
97
98         /* Generate default server ID */
99         client->priv->server_id = make_server_id ();
100
101         /* Set up sockets (Will set errno if it failed) */
102         client->priv->request_socket =
103                 gssdp_socket_source_new (GSSPP_SOCKET_SOURCE_TYPE_REQUEST);
104         if (client->priv->request_socket != NULL) {
105                 g_source_set_callback
106                         ((GSource *) client->priv->request_socket,
107                          request_socket_source_cb,
108                          client,
109                          NULL);
110         }
111
112         client->priv->multicast_socket =
113                 gssdp_socket_source_new (GSSDP_SOCKET_SOURCE_TYPE_MULTICAST);
114         if (client->priv->multicast_socket != NULL) {
115                 g_source_set_callback
116                         ((GSource *) client->priv->multicast_socket,
117                          multicast_socket_source_cb,
118                          client,
119                          NULL);
120         }
121 }
122
123 static void
124 gssdp_client_get_property (GObject    *object,
125                            guint       property_id,
126                            GValue     *value,
127                            GParamSpec *pspec)
128 {
129         GSSDPClient *client;
130
131         client = GSSDP_CLIENT (object);
132
133         switch (property_id) {
134         case PROP_SERVER_ID:
135                 g_value_set_string
136                         (value,
137                          gssdp_client_get_server_id (client));
138                 break;
139         case PROP_MAIN_CONTEXT:
140                 g_value_set_pointer
141                         (value,
142                          (gpointer)
143                           gssdp_client_get_main_context (client));
144                 break;
145         default:
146                 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
147                 break;
148         }
149 }
150
151 static void
152 gssdp_client_set_property (GObject      *object,
153                            guint         property_id,
154                            const GValue *value,
155                            GParamSpec   *pspec)
156 {
157         GSSDPClient *client;
158
159         client = GSSDP_CLIENT (object);
160
161         switch (property_id) {
162         case PROP_SERVER_ID:
163                 gssdp_client_set_server_id (client,
164                                             g_value_get_string (value));
165                 break;
166         case PROP_MAIN_CONTEXT:
167                 gssdp_client_set_main_context (client,
168                                                g_value_get_pointer (value));
169                 break;
170         case PROP_ERROR:
171                 if (!client->priv->request_socket ||
172                     !client->priv->multicast_socket) {
173                         GError **error;
174
175                         error = g_value_get_pointer (value);
176
177                         g_set_error_literal (error,
178                                              GSSDP_ERROR,
179                                              GSSDP_ERROR_FAILED,
180                                              strerror (errno));
181                 }
182
183                 break;
184         default:
185                 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
186                 break;
187         }
188 }
189
190 static void
191 gssdp_client_dispose (GObject *object)
192 {
193         GSSDPClient *client;
194
195         client = GSSDP_CLIENT (object);
196
197         /* Destroy the SocketSources */
198         if (client->priv->request_socket) {
199                 g_source_destroy ((GSource *) client->priv->request_socket);
200                 client->priv->request_socket = NULL;
201         }
202
203         if (client->priv->multicast_socket) {
204                 g_source_destroy ((GSource *) client->priv->multicast_socket);
205                 client->priv->multicast_socket = NULL;
206         }
207
208         /* Unref the context */
209         if (client->priv->main_context) {
210                 g_main_context_unref (client->priv->main_context);
211                 client->priv->main_context = NULL;
212         }
213 }
214
215 static void
216 gssdp_client_finalize (GObject *object)
217 {
218         GSSDPClient *client;
219
220         client = GSSDP_CLIENT (object);
221
222         g_free (client->priv->server_id);
223 }
224
225 static void
226 gssdp_client_class_init (GSSDPClientClass *klass)
227 {
228         GObjectClass *object_class;
229
230         object_class = G_OBJECT_CLASS (klass);
231
232         object_class->set_property = gssdp_client_set_property;
233         object_class->get_property = gssdp_client_get_property;
234         object_class->dispose      = gssdp_client_dispose;
235         object_class->finalize     = gssdp_client_finalize;
236
237         g_type_class_add_private (klass, sizeof (GSSDPClientPrivate));
238
239         /**
240          * GSSDPClient:server-id
241          *
242          * The SSDP server's identifier.
243          **/
244         g_object_class_install_property
245                 (object_class,
246                  PROP_SERVER_ID,
247                  g_param_spec_string
248                          ("server-id",
249                           "Server ID",
250                           "The SSDP server's identifier.",
251                           NULL,
252                           G_PARAM_READWRITE |
253                           G_PARAM_STATIC_NAME | G_PARAM_STATIC_NICK |
254                           G_PARAM_STATIC_BLURB));
255
256         /**
257          * GSSDPClient:main-context
258          *
259          * The #GMainContext to use. Set to NULL to use the default.
260          **/
261         g_object_class_install_property
262                 (object_class,
263                  PROP_MAIN_CONTEXT,
264                  g_param_spec_pointer
265                          ("main-context",
266                           "Main context",
267                           "The associated GMainContext.",
268                           G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY |
269                           G_PARAM_STATIC_NAME | G_PARAM_STATIC_NICK |
270                           G_PARAM_STATIC_BLURB));
271
272         /**
273          * GSSDPClient:error
274          *
275          * Internal property.
276          *
277          * Stability: Private
278          **/
279         g_object_class_install_property
280                 (object_class,
281                  PROP_ERROR,
282                  g_param_spec_pointer
283                          ("error",
284                           "Error",
285                           "Location where to store the constructor GError, "
286                           "if any.",
287                           G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY |
288                           G_PARAM_STATIC_NAME | G_PARAM_STATIC_NICK |
289                           G_PARAM_STATIC_BLURB));
290
291         /**
292          * GSSDPClient::message-received
293          *
294          * Internal signal.
295          *
296          * Stability: Private
297          **/
298         signals[MESSAGE_RECEIVED] =
299                 g_signal_new ("message-received",
300                               GSSDP_TYPE_CLIENT,
301                               G_SIGNAL_RUN_LAST,
302                               0,
303                               NULL, NULL,
304                               gssdp_marshal_VOID__STRING_UINT_INT_POINTER,
305                               G_TYPE_NONE,
306                               4,
307                               G_TYPE_STRING,
308                               G_TYPE_UINT,
309                               G_TYPE_INT,
310                               G_TYPE_POINTER);
311 }
312
313 /**
314  * gssdp_client_new
315  * @main_context: The #GMainContext to associate with, or NULL
316  * @error: Location to store error, or NULL
317  *
318  * Return value: A new #GSSDPClient object.
319  **/
320 GSSDPClient *
321 gssdp_client_new (GMainContext *main_context,
322                   GError      **error)
323 {
324         return g_object_new (GSSDP_TYPE_CLIENT,
325                              "main-context", main_context,
326                              "error", error,
327                              NULL);
328 }
329
330 /**
331  * Sets the GMainContext @client is associated with to @main_context
332  **/
333 static void
334 gssdp_client_set_main_context (GSSDPClient  *client,
335                                GMainContext *main_context)
336 {
337         g_return_if_fail (GSSDP_IS_CLIENT (client));
338
339         if (main_context)
340                 client->priv->main_context = g_main_context_ref (main_context);
341
342         /* A NULL main_context is fine */
343         
344         if (client->priv->request_socket) {
345                 g_source_attach ((GSource *) client->priv->request_socket,
346                                  client->priv->main_context);
347                 g_source_unref ((GSource *) client->priv->request_socket);
348         }
349
350         if (client->priv->multicast_socket) {
351                 g_source_attach ((GSource *) client->priv->multicast_socket,
352                                  client->priv->main_context);
353                 g_source_unref ((GSource *) client->priv->multicast_socket);
354         }
355
356         g_object_notify (G_OBJECT (client), "main-context");
357 }
358
359 /**
360  * gssdp_client_get_main_context
361  * @client: A #GSSDPClient
362  *
363  * Return value: The #GMainContext @client is associated with, or NULL.
364  **/
365 GMainContext *
366 gssdp_client_get_main_context (GSSDPClient *client)
367 {
368         g_return_val_if_fail (GSSDP_IS_CLIENT (client), NULL);
369
370         return client->priv->main_context;
371 }
372
373 /**
374  * gssdp_client_set_server_id
375  * @client: A #GSSDPClient
376  * @server_id: The server ID
377  *
378  * Sets the server ID of @client to @server_id.
379  **/
380 void
381 gssdp_client_set_server_id (GSSDPClient *client,
382                             const char  *server_id)
383 {
384         g_return_if_fail (GSSDP_IS_CLIENT (client));
385
386         if (client->priv->server_id) {
387                 g_free (client->priv->server_id);
388                 client->priv->server_id = NULL;
389         }
390
391         if (server_id)
392                 client->priv->server_id = g_strdup (server_id);
393
394         g_object_notify (G_OBJECT (client), "server-id");
395 }
396
397 /**
398  * gssdp_client_get_server_id
399  * @client: A #GSSDPClient
400  *
401  * Return value: The server ID.
402  **/
403 const char *
404 gssdp_client_get_server_id (GSSDPClient *client)
405 {
406         g_return_val_if_fail (GSSDP_IS_CLIENT (client), NULL);
407
408         return client->priv->server_id;
409 }
410
411 /**
412  * _gssdp_client_send_message
413  * @client: A #GSSDPClient
414  * @dest_ip: The destination IP address, or NULL to broadcast
415  * @dest_port: The destination port, or NULL for default
416  * @message: The message to send
417  *
418  * Sends @message to @dest_ip.
419  **/
420 void
421 _gssdp_client_send_message (GSSDPClient *client,
422                             const char  *dest_ip,
423                             gushort      dest_port,
424                             const char  *message)
425 {
426         struct sockaddr_in addr;
427         int socket_fd, res;
428
429         g_return_if_fail (GSSDP_IS_CLIENT (client));
430         g_return_if_fail (message != NULL);
431
432         /* Broadcast if @dest_ip is NULL */
433         if (dest_ip == NULL)
434                 dest_ip = SSDP_ADDR;
435
436         /* Use default port if no port was explicitly specified */
437         if (dest_port == 0)
438                 dest_port = SSDP_PORT;
439
440         socket_fd = gssdp_socket_source_get_fd (client->priv->request_socket);
441
442         memset (&addr, 0, sizeof (addr));
443
444         addr.sin_family      = AF_INET;
445         addr.sin_port        = htons (dest_port);
446         addr.sin_addr.s_addr = inet_addr (dest_ip);
447
448         res = sendto (socket_fd,
449                       message,
450                       strlen (message),
451                       0,
452                       (struct sockaddr *) &addr,
453                       sizeof (addr));
454
455         if (res == -1) {
456                 g_warning ("sendto: Error %d sending message: %s",
457                            errno, strerror (errno));
458         }
459 }
460
461 /**
462  * Generates the default server ID
463  **/
464 static char *
465 make_server_id (void)
466 {
467         struct utsname sysinfo;
468
469         uname (&sysinfo);
470         
471         return g_strdup_printf ("%s/%s GSSDP/%s",
472                                 sysinfo.sysname,
473                                 sysinfo.version,
474                                 VERSION);
475 }
476
477 static gboolean
478 parse_http_request (char                *buf,
479                     int                  len,
480                     SoupMessageHeaders **headers,
481                     int                 *type)
482 {
483         char *req_method;
484
485         *headers = soup_message_headers_new (SOUP_MESSAGE_HEADERS_REQUEST);
486
487         if (soup_headers_parse_request (buf,
488                                         len,
489                                         *headers,
490                                         &req_method,
491                                         NULL,
492                                         NULL) == SOUP_STATUS_OK) {
493                 if (g_ascii_strncasecmp (req_method,
494                                          SSDP_SEARCH_METHOD,
495                                          strlen (SSDP_SEARCH_METHOD)) == 0)
496                         *type = _GSSDP_DISCOVERY_REQUEST;
497                 else if (g_ascii_strncasecmp (req_method,
498                                               GENA_NOTIFY_METHOD,
499                                               strlen (GENA_NOTIFY_METHOD)) == 0)
500                         *type = _GSSDP_ANNOUNCEMENT;
501                 else
502                         g_warning ("Unhandled method '%s'", req_method);
503
504                 g_free (req_method);
505
506                 return TRUE;
507         } else {
508                 soup_message_headers_free (*headers);
509                 *headers = NULL;
510
511                 return FALSE;
512         }
513 }
514
515 static gboolean
516 parse_http_response (char                *buf,
517                     int                  len,
518                     SoupMessageHeaders **headers,
519                     int                 *type)
520 {
521         guint status_code;
522
523         *headers = soup_message_headers_new (SOUP_MESSAGE_HEADERS_RESPONSE);
524
525         if (soup_headers_parse_response (buf,
526                                          len,
527                                          *headers,
528                                          NULL,
529                                          &status_code,
530                                          NULL)) {
531                 if (status_code == 200)
532                         *type = _GSSDP_DISCOVERY_RESPONSE;
533                 else
534                         g_warning ("Unhandled status code '%d'", status_code);
535
536                 return TRUE;
537         } else {
538                 soup_message_headers_free (*headers);
539                 *headers = NULL;
540
541                 return FALSE;
542         }
543 }
544
545 /**
546  * Called when data can be read from the socket
547  **/
548 static gboolean
549 socket_source_cb (GSSDPSocketSource *socket, GSSDPClient *client)
550 {
551         int fd, type, len;
552         size_t bytes;
553         char buf[BUF_SIZE], *end;
554         struct sockaddr_in addr;
555         socklen_t addr_size;
556         SoupMessageHeaders *headers;
557
558         /* Get FD */
559         fd = gssdp_socket_source_get_fd (socket);
560
561         /* Read data */
562         addr_size = sizeof (addr);
563         
564         bytes = recvfrom (fd,
565                           buf,
566                           BUF_SIZE - 1, /* Leave space for trailing \0 */
567                           MSG_TRUNC,
568                           (struct sockaddr *) &addr,
569                           &addr_size);
570
571         if (bytes >= BUF_SIZE) {
572                 g_warning ("Received packet of %u bytes, but the maximum "
573                            "buffer size is %d. Packed dropped.",
574                            (unsigned int) bytes, BUF_SIZE);
575
576                 return TRUE;
577         }
578
579         /* Add trailing \0 */
580         buf[bytes] = '\0';
581
582         /* Find length */
583         end = strstr (buf, "\r\n\r\n");
584         if (!end) {
585                 g_warning ("Received packet lacks \"\\r\\n\\r\\n\" sequence. "
586                            "Packed dropped.");
587
588                 return TRUE;
589         }
590
591         len = end - buf + 2;
592         
593         /* Parse message */
594         type = -1;
595         headers = NULL;
596
597         if (!parse_http_request (buf,
598                                  len,
599                                  &headers,
600                                  &type)) {
601                 if (!parse_http_response (buf,
602                                           len,
603                                           &headers,
604                                           &type)) {
605                         g_warning ("Unhandled message '%s'", buf);
606                 }
607         }
608         
609         /* Emit signal if parsing succeeded */
610         if (type >= 0) {
611                 g_signal_emit (client,
612                                signals[MESSAGE_RECEIVED],
613                                0,
614                                inet_ntoa (addr.sin_addr),
615                                ntohs (addr.sin_port),
616                                type,
617                                headers);
618         }
619
620         if (headers)
621                 soup_message_headers_free (headers);
622
623         return TRUE;
624 }
625
626 static gboolean
627 request_socket_source_cb (gpointer user_data)
628 {
629         GSSDPClient *client;
630
631         client = GSSDP_CLIENT (user_data);
632
633         return socket_source_cb (client->priv->request_socket, client);
634 }
635
636 static gboolean
637 multicast_socket_source_cb (gpointer user_data)
638 {
639         GSSDPClient *client;
640
641         client = GSSDP_CLIENT (user_data);
642
643         return socket_source_cb (client->priv->multicast_socket, client);
644 }