2 * Copyright (C) 2006, 2007, 2008 OpenedHand Ltd.
4 * Author: Jorn Baayen <jorn@openedhand.com>
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.
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.
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.
23 * SECTION:gssdp-client
24 * @short_description: SSDP "bus" wrapper.
26 * #GSSDPClient wraps the SSDP "bus" as used by both #GSSDPResourceBrowser
27 * and #GSSDPResourceGroup.
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>
40 #include <libsoup/soup-headers.h>
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"
49 /* Size of the buffer used for reading from the socket */
52 G_DEFINE_TYPE (GSSDPClient,
56 struct _GSSDPClientPrivate {
57 GMainContext *main_context;
61 GSSDPSocketSource *request_socket;
62 GSSDPSocketSource *multicast_socket;
77 static guint signals[LAST_SIGNAL];
79 /* Function prototypes */
81 gssdp_client_set_main_context (GSSDPClient *client,
82 GMainContext *context);
84 make_server_id (void);
86 request_socket_source_cb (gpointer user_data);
88 multicast_socket_source_cb (gpointer user_data);
91 gssdp_client_init (GSSDPClient *client)
93 client->priv = G_TYPE_INSTANCE_GET_PRIVATE
98 /* Generate default server ID */
99 client->priv->server_id = make_server_id ();
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,
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,
124 gssdp_client_get_property (GObject *object,
131 client = GSSDP_CLIENT (object);
133 switch (property_id) {
137 gssdp_client_get_server_id (client));
139 case PROP_MAIN_CONTEXT:
143 gssdp_client_get_main_context (client));
146 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
152 gssdp_client_set_property (GObject *object,
159 client = GSSDP_CLIENT (object);
161 switch (property_id) {
163 gssdp_client_set_server_id (client,
164 g_value_get_string (value));
166 case PROP_MAIN_CONTEXT:
167 gssdp_client_set_main_context (client,
168 g_value_get_pointer (value));
171 if (!client->priv->request_socket ||
172 !client->priv->multicast_socket) {
175 error = g_value_get_pointer (value);
177 g_set_error_literal (error,
185 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
191 gssdp_client_dispose (GObject *object)
195 client = GSSDP_CLIENT (object);
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;
203 if (client->priv->multicast_socket) {
204 g_source_destroy ((GSource *) client->priv->multicast_socket);
205 client->priv->multicast_socket = NULL;
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;
216 gssdp_client_finalize (GObject *object)
220 client = GSSDP_CLIENT (object);
222 g_free (client->priv->server_id);
226 gssdp_client_class_init (GSSDPClientClass *klass)
228 GObjectClass *object_class;
230 object_class = G_OBJECT_CLASS (klass);
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;
237 g_type_class_add_private (klass, sizeof (GSSDPClientPrivate));
240 * GSSDPClient:server-id
242 * The SSDP server's identifier.
244 g_object_class_install_property
250 "The SSDP server's identifier.",
253 G_PARAM_STATIC_NAME | G_PARAM_STATIC_NICK |
254 G_PARAM_STATIC_BLURB));
257 * GSSDPClient:main-context
259 * The #GMainContext to use. Set to NULL to use the default.
261 g_object_class_install_property
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));
279 g_object_class_install_property
285 "Location where to store the constructor GError, "
287 G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY |
288 G_PARAM_STATIC_NAME | G_PARAM_STATIC_NICK |
289 G_PARAM_STATIC_BLURB));
292 * GSSDPClient::message-received
298 signals[MESSAGE_RECEIVED] =
299 g_signal_new ("message-received",
304 gssdp_marshal_VOID__STRING_UINT_INT_POINTER,
315 * @main_context: The #GMainContext to associate with, or NULL
316 * @error: Location to store error, or NULL
318 * Return value: A new #GSSDPClient object.
321 gssdp_client_new (GMainContext *main_context,
324 return g_object_new (GSSDP_TYPE_CLIENT,
325 "main-context", main_context,
331 * Sets the GMainContext @client is associated with to @main_context
334 gssdp_client_set_main_context (GSSDPClient *client,
335 GMainContext *main_context)
337 g_return_if_fail (GSSDP_IS_CLIENT (client));
340 client->priv->main_context = g_main_context_ref (main_context);
342 /* A NULL main_context is fine */
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);
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);
356 g_object_notify (G_OBJECT (client), "main-context");
360 * gssdp_client_get_main_context
361 * @client: A #GSSDPClient
363 * Return value: The #GMainContext @client is associated with, or NULL.
366 gssdp_client_get_main_context (GSSDPClient *client)
368 g_return_val_if_fail (GSSDP_IS_CLIENT (client), NULL);
370 return client->priv->main_context;
374 * gssdp_client_set_server_id
375 * @client: A #GSSDPClient
376 * @server_id: The server ID
378 * Sets the server ID of @client to @server_id.
381 gssdp_client_set_server_id (GSSDPClient *client,
382 const char *server_id)
384 g_return_if_fail (GSSDP_IS_CLIENT (client));
386 if (client->priv->server_id) {
387 g_free (client->priv->server_id);
388 client->priv->server_id = NULL;
392 client->priv->server_id = g_strdup (server_id);
394 g_object_notify (G_OBJECT (client), "server-id");
398 * gssdp_client_get_server_id
399 * @client: A #GSSDPClient
401 * Return value: The server ID.
404 gssdp_client_get_server_id (GSSDPClient *client)
406 g_return_val_if_fail (GSSDP_IS_CLIENT (client), NULL);
408 return client->priv->server_id;
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
418 * Sends @message to @dest_ip.
421 _gssdp_client_send_message (GSSDPClient *client,
426 struct sockaddr_in addr;
429 g_return_if_fail (GSSDP_IS_CLIENT (client));
430 g_return_if_fail (message != NULL);
432 /* Broadcast if @dest_ip is NULL */
436 /* Use default port if no port was explicitly specified */
438 dest_port = SSDP_PORT;
440 socket_fd = gssdp_socket_source_get_fd (client->priv->request_socket);
442 memset (&addr, 0, sizeof (addr));
444 addr.sin_family = AF_INET;
445 addr.sin_port = htons (dest_port);
446 addr.sin_addr.s_addr = inet_addr (dest_ip);
448 res = sendto (socket_fd,
452 (struct sockaddr *) &addr,
456 g_warning ("sendto: Error %d sending message: %s",
457 errno, strerror (errno));
462 * Generates the default server ID
465 make_server_id (void)
467 struct utsname sysinfo;
471 return g_strdup_printf ("%s/%s GSSDP/%s",
478 parse_http_request (char *buf,
480 SoupMessageHeaders **headers,
485 *headers = soup_message_headers_new (SOUP_MESSAGE_HEADERS_REQUEST);
487 if (soup_headers_parse_request (buf,
492 NULL) == SOUP_STATUS_OK) {
493 if (g_ascii_strncasecmp (req_method,
495 strlen (SSDP_SEARCH_METHOD)) == 0)
496 *type = _GSSDP_DISCOVERY_REQUEST;
497 else if (g_ascii_strncasecmp (req_method,
499 strlen (GENA_NOTIFY_METHOD)) == 0)
500 *type = _GSSDP_ANNOUNCEMENT;
502 g_warning ("Unhandled method '%s'", req_method);
508 soup_message_headers_free (*headers);
516 parse_http_response (char *buf,
518 SoupMessageHeaders **headers,
523 *headers = soup_message_headers_new (SOUP_MESSAGE_HEADERS_RESPONSE);
525 if (soup_headers_parse_response (buf,
531 if (status_code == 200)
532 *type = _GSSDP_DISCOVERY_RESPONSE;
534 g_warning ("Unhandled status code '%d'", status_code);
538 soup_message_headers_free (*headers);
546 * Called when data can be read from the socket
549 socket_source_cb (GSSDPSocketSource *socket, GSSDPClient *client)
553 char buf[BUF_SIZE], *end;
554 struct sockaddr_in addr;
556 SoupMessageHeaders *headers;
559 fd = gssdp_socket_source_get_fd (socket);
562 addr_size = sizeof (addr);
564 bytes = recvfrom (fd,
566 BUF_SIZE - 1, /* Leave space for trailing \0 */
568 (struct sockaddr *) &addr,
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);
579 /* Add trailing \0 */
583 end = strstr (buf, "\r\n\r\n");
585 g_warning ("Received packet lacks \"\\r\\n\\r\\n\" sequence. "
597 if (!parse_http_request (buf,
601 if (!parse_http_response (buf,
605 g_warning ("Unhandled message '%s'", buf);
609 /* Emit signal if parsing succeeded */
611 g_signal_emit (client,
612 signals[MESSAGE_RECEIVED],
614 inet_ntoa (addr.sin_addr),
615 ntohs (addr.sin_port),
621 soup_message_headers_free (headers);
627 request_socket_source_cb (gpointer user_data)
631 client = GSSDP_CLIENT (user_data);
633 return socket_source_cb (client->priv->request_socket, client);
637 multicast_socket_source_cb (gpointer user_data)
641 client = GSSDP_CLIENT (user_data);
643 return socket_source_cb (client->priv->multicast_socket, client);