gupnp-context.c
来自「另一 UPNP SDK 支持在UNIX/LINUX上运行。 UPnP是一种网络协」· C语言 代码 · 共 949 行 · 第 1/2 页
C
949 行
/* * Copyright (C) 2006, 2007, 2008 OpenedHand Ltd. * * Author: Jorn Baayen <jorn@openedhand.com> * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public * License along with this library; if not, write to the * Free Software Foundation, Inc., 59 Temple Place - Suite 330, * Boston, MA 02111-1307, USA. *//** * SECTION:gupnp-context * @short_description: Context object wrapping shared networking bits. * * #GUPnPContext wraps the networking bits that are used by the various * GUPnP classes. It automatically starts a web server on demand. * * For debugging, it is possible to see the messages being sent and received by * exporting #GUPNP_DEBUG. */#include <config.h>#include <errno.h>#include <string.h>#include <unistd.h>#include <fcntl.h>#include <stdlib.h>#include <stdio.h>#include <sys/utsname.h>#include <sys/ioctl.h>#include <sys/socket.h>#include <sys/types.h>#include <sys/stat.h>#include <arpa/inet.h>#include <net/if.h>#include <ifaddrs.h>#include <libsoup/soup-address.h>#include <glib/gstdio.h>#include "gupnp-context.h"#include "gupnp-context-private.h"#include "gupnp-marshal.h"#include "gena-protocol.h"#include "http-headers.h"G_DEFINE_TYPE (GUPnPContext, gupnp_context, GSSDP_TYPE_CLIENT);struct _GUPnPContextPrivate { char *host_ip; guint port; guint subscription_timeout; SoupSession *session; SoupServer *server; /* Started on demand */ char *server_url;};enum { PROP_0, PROP_HOST_IP, PROP_PORT, PROP_SERVER, PROP_SUBSCRIPTION_TIMEOUT};#define LOOPBACK_IP "127.0.0.1"typedef struct { char *local_path; char *server_path;} HostPathData;/* * Generates the default server ID. **/static char *make_server_id (void){ struct utsname sysinfo; uname (&sysinfo); return g_strdup_printf ("%s/%s UPnP/1.0 GUPnP/%s", sysinfo.sysname, sysinfo.version, VERSION);}/* * Get the host IP for the specified interface, or the first up and non-loopback * interface if no name is specified. */static char *get_host_ip (const char *name){ struct ifaddrs *ifa_list, *ifa; char *ret; ret = NULL; if (getifaddrs (&ifa_list) != 0) { g_error ("Failed to retrieve list of network interfaces:\n%s\n", strerror (errno)); return NULL; } for (ifa = ifa_list; ifa != NULL; ifa = ifa->ifa_next) { char ip[INET6_ADDRSTRLEN]; const char *p; struct sockaddr_in *s4; struct sockaddr_in6 *s6; if (ifa->ifa_addr == NULL) continue; if ((ifa->ifa_flags & IFF_LOOPBACK) || !(ifa->ifa_flags & IFF_UP)) continue; /* If a name was specified, check it */ if (name && strcmp (name, ifa->ifa_name) != 0) continue; p = NULL; switch (ifa->ifa_addr->sa_family) { case AF_INET: s4 = (struct sockaddr_in *) ifa->ifa_addr; p = inet_ntop (AF_INET, &s4->sin_addr, ip, sizeof (ip)); break; case AF_INET6: s6 = (struct sockaddr_in6 *) ifa->ifa_addr; p = inet_ntop (AF_INET6, &s6->sin6_addr, ip, sizeof (ip)); break; default: continue; /* Unknown: ignore */ } if (p != NULL) { ret = g_strdup (p); break; } } freeifaddrs (ifa_list); if (!ret) { /* Didn't find anything. Let's take the loopback IP. */ ret = g_strdup (LOOPBACK_IP); } return ret;}/* * Get the host IP of the interface used for the default route. On any error, * the first up and non-loopback interface is used. */static char *get_default_host_ip (void){ FILE *fp; int ret; char dev[32]; unsigned long dest; gboolean found = FALSE; /* TODO: error checking */ fp = fopen ("/proc/net/route", "r"); /* Skip the header */ fscanf (fp, "%*[^\n]\n"); while ((ret = fscanf (fp, "%31s %lx %*x %*X %*d %*d %*d %*x %*d %*d %*d", dev, &dest)) != EOF) { /* If we got a device name and destination, and the destination is 0, then we have the default route */ if (ret == 2 && dest == 0) { found = TRUE; break; } } fclose (fp); return get_host_ip (found ? dev : NULL);}static voidgupnp_context_init (GUPnPContext *context){ char *server_id; context->priv = G_TYPE_INSTANCE_GET_PRIVATE (context, GUPNP_TYPE_CONTEXT, GUPnPContextPrivate); context->priv->session = soup_session_async_new (); g_object_set (context->priv->session, SOUP_SESSION_IDLE_TIMEOUT, 60, NULL); server_id = make_server_id (); gssdp_client_set_server_id (GSSDP_CLIENT (context), server_id); g_free (server_id); if (g_getenv ("GUPNP_DEBUG")) { SoupLogger *logger; logger = soup_logger_new (SOUP_LOGGER_LOG_BODY, -1); soup_logger_attach (logger, context->priv->session); }}static voidgupnp_context_set_property (GObject *object, guint property_id, const GValue *value, GParamSpec *pspec){ GUPnPContext *context; context = GUPNP_CONTEXT (object); switch (property_id) { case PROP_HOST_IP: context->priv->host_ip = g_value_dup_string (value); break; case PROP_PORT: context->priv->port = g_value_get_uint (value); break; case PROP_SUBSCRIPTION_TIMEOUT: context->priv->subscription_timeout = g_value_get_uint (value); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); break; }}static voidgupnp_context_get_property (GObject *object, guint property_id, GValue *value, GParamSpec *pspec){ GUPnPContext *context; context = GUPNP_CONTEXT (object); switch (property_id) { case PROP_HOST_IP: g_value_set_string (value, gupnp_context_get_host_ip (context)); break; case PROP_PORT: g_value_set_uint (value, gupnp_context_get_port (context)); break; case PROP_SERVER: g_value_set_object (value, gupnp_context_get_server (context)); break; case PROP_SUBSCRIPTION_TIMEOUT: g_value_set_uint (value, gupnp_context_get_subscription_timeout (context)); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); break; }}static voidgupnp_context_dispose (GObject *object){ GUPnPContext *context; GObjectClass *object_class; context = GUPNP_CONTEXT (object); if (context->priv->session) { g_object_unref (context->priv->session); context->priv->session = NULL; } if (context->priv->server) { g_object_unref (context->priv->server); context->priv->server = NULL; } /* Call super */ object_class = G_OBJECT_CLASS (gupnp_context_parent_class); object_class->dispose (object);}static voidgupnp_context_finalize (GObject *object){ GUPnPContext *context; GObjectClass *object_class; context = GUPNP_CONTEXT (object); g_free (context->priv->host_ip); g_free (context->priv->server_url); /* Call super */ object_class = G_OBJECT_CLASS (gupnp_context_parent_class); object_class->finalize (object);}static voidgupnp_context_class_init (GUPnPContextClass *klass){ GObjectClass *object_class; object_class = G_OBJECT_CLASS (klass); object_class->set_property = gupnp_context_set_property; object_class->get_property = gupnp_context_get_property; object_class->dispose = gupnp_context_dispose; object_class->finalize = gupnp_context_finalize; g_type_class_add_private (klass, sizeof (GUPnPContextPrivate)); /** * GUPnPContext:host-ip * * The local host's IP address. Set to NULL to autodetect. **/ g_object_class_install_property (object_class, PROP_HOST_IP, g_param_spec_string ("host-ip", "Host IP", "The local host's IP address", NULL, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_NAME | G_PARAM_STATIC_NICK | G_PARAM_STATIC_BLURB)); /** * GUPnPContext:port * * The port to run on. Set to 0 if you don't care what port to run on. **/ g_object_class_install_property (object_class, PROP_PORT, g_param_spec_uint ("port", "Port", "Port to run on", 0, G_MAXUINT, SOUP_ADDRESS_ANY_PORT, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_NAME | G_PARAM_STATIC_NICK | G_PARAM_STATIC_BLURB)); /** * GUPnPContext:server * * The #SoupServer HTTP server used by GUPnP. **/ g_object_class_install_property (object_class, PROP_SERVER, g_param_spec_object ("server", "SoupServer", "SoupServer HTTP server", SOUP_TYPE_SERVER, G_PARAM_READABLE | G_PARAM_STATIC_NAME | G_PARAM_STATIC_NICK | G_PARAM_STATIC_BLURB)); /** * GUPnPContext:subscription-timeout * * The preferred subscription timeout: the number of seconds after * which subscriptions are renewed. Set to '0' if subscriptions * are never to time out. **/ g_object_class_install_property (object_class, PROP_SUBSCRIPTION_TIMEOUT, g_param_spec_uint ("subscription-timeout", "Subscription timeout", "Subscription timeout", 0, GENA_MAX_TIMEOUT, GENA_DEFAULT_TIMEOUT, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_NAME | G_PARAM_STATIC_NICK | G_PARAM_STATIC_BLURB));}SoupSession *_gupnp_context_get_session (GUPnPContext *context){ return context->priv->session;}/* * Default server handler: Return 404 not found. **/static voiddefault_server_handler (SoupServer *server, SoupMessage *msg, const char *path, GHashTable *query, SoupClientContext *client, gpointer user_data){ soup_message_set_status (msg, SOUP_STATUS_NOT_FOUND);}/** * gupnp_context_get_server * @context: A #GUPnPContext * * Get the #SoupServer HTTP server that GUPnP is using. * * Return value: The #SoupServer used by GUPnP. Do not unref this when finished. **/SoupServer *gupnp_context_get_server (GUPnPContext *context){ g_return_val_if_fail (GUPNP_IS_CONTEXT (context), NULL); if (context->priv->server == NULL) { context->priv->server = soup_server_new (SOUP_SERVER_PORT, context->priv->port, NULL); soup_server_add_handler (context->priv->server, NULL, default_server_handler, context, NULL); soup_server_run_async (context->priv->server); } return context->priv->server;}/*
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?