~matthaeus123/+junk/gupnp-igd

« back to all changes in this revision

Viewing changes to libgupnp-igd/gupnp-simple-igd.c

  • Committer: Matthew Walker
  • Date: 2009-03-23 19:36:45 UTC
  • Revision ID: matthew@matthew-desktop-20090323193645-r4z27ojffzc9fuoy
initial checkin

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*
 
2
 * GUPnP Simple IGD abstraction
 
3
 *
 
4
 * Copyright 2008 Collabora Ltd.
 
5
 *  @author: Olivier Crete <olivier.crete@collabora.co.uk>
 
6
 * Copyright 2008 Nokia Corp.
 
7
 *
 
8
 * This library is free software; you can redistribute it and/or
 
9
 * modify it under the terms of the GNU Lesser General Public
 
10
 * License as published by the Free Software Foundation; either
 
11
 * version 2.1 of the License, or (at your option) any later version.
 
12
 *
 
13
 * This library is distributed in the hope that it will be useful,
 
14
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 
15
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 
16
 * Lesser General Public License for more details.
 
17
 *
 
18
 * You should have received a copy of the GNU Lesser General Public
 
19
 * License along with this library; if not, write to the Free Software
 
20
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301 USA
 
21
 */
 
22
 
 
23
 
 
24
/**
 
25
 * SECTION:gupnp-simple-igd
 
26
 * @short_description: A simple class to map ports on UPnP routers
 
27
 *
 
28
 * This simple class allows applications to map ports on UPnP routers.
 
29
 * It implements the basic functionalities to map ports to external ports.
 
30
 * It also allows implementations to know the external port from the router's
 
31
 * perspective.
 
32
 */
 
33
 
 
34
 
 
35
#include "gupnp-simple-igd.h"
 
36
#include "gupnp-simple-igd-marshal.h"
 
37
 
 
38
#include <string.h>
 
39
 
 
40
#include <libgupnp/gupnp-control-point.h>
 
41
 
 
42
 
 
43
struct _GUPnPSimpleIgdPrivate
 
44
{
 
45
  GMainContext *main_context;
 
46
 
 
47
  GUPnPContext *gupnp_context;
 
48
  GUPnPControlPoint *ip_cp;
 
49
  GUPnPControlPoint *ppp_cp;
 
50
 
 
51
  GPtrArray *service_proxies;
 
52
 
 
53
  GPtrArray *mappings;
 
54
 
 
55
  gulong ip_avail_handler;
 
56
  gulong ip_unavail_handler;
 
57
 
 
58
  gulong ppp_avail_handler;
 
59
  gulong ppp_unavail_handler;
 
60
};
 
61
 
 
62
struct Proxy {
 
63
  GUPnPSimpleIgd *parent;
 
64
  GUPnPServiceProxy *proxy;
 
65
 
 
66
  gchar *external_ip;
 
67
  GUPnPServiceProxyAction *external_ip_action;
 
68
  gboolean external_ip_failed;
 
69
 
 
70
  GPtrArray *proxymappings;
 
71
};
 
72
 
 
73
struct Mapping {
 
74
  gchar *protocol;
 
75
  guint external_port;
 
76
  gchar *local_ip;
 
77
  guint16 local_port;
 
78
  guint32 lease_duration;
 
79
  gchar *description;
 
80
};
 
81
 
 
82
struct ProxyMapping {
 
83
  struct Proxy *proxy;
 
84
  struct Mapping *mapping;
 
85
 
 
86
  GUPnPServiceProxyAction *action;
 
87
 
 
88
  gboolean mapped;
 
89
 
 
90
  GSource *renew_src;
 
91
};
 
92
 
 
93
/* signals */
 
94
enum
 
95
{
 
96
  SIGNAL_MAPPED_EXTERNAL_PORT,
 
97
  SIGNAL_ERROR_MAPPING_PORT,
 
98
  LAST_SIGNAL
 
99
};
 
100
 
 
101
/* props */
 
102
enum
 
103
{
 
104
  PROP_0,
 
105
  PROP_REQUEST_TIMEOUT,
 
106
  PROP_MAIN_CONTEXT
 
107
};
 
108
 
 
109
 
 
110
static guint signals[LAST_SIGNAL] = { 0 };
 
111
 
 
112
 
 
113
#define GUPNP_SIMPLE_IGD_GET_PRIVATE(o)                                 \
 
114
  (G_TYPE_INSTANCE_GET_PRIVATE ((o), GUPNP_TYPE_SIMPLE_IGD,             \
 
115
   GUPnPSimpleIgdPrivate))
 
116
 
 
117
 
 
118
G_DEFINE_TYPE (GUPnPSimpleIgd, gupnp_simple_igd, G_TYPE_OBJECT);
 
119
 
 
120
 
 
121
static void gupnp_simple_igd_constructed (GObject *object);
 
122
static void gupnp_simple_igd_dispose (GObject *object);
 
123
static void gupnp_simple_igd_finalize (GObject *object);
 
124
static void gupnp_simple_igd_get_property (GObject *object, guint prop_id,
 
125
    GValue *value, GParamSpec *pspec);
 
126
static void gupnp_simple_igd_set_property (GObject *object, guint prop_id,
 
127
    const GValue *value, GParamSpec *pspec);
 
128
 
 
129
static void gupnp_simple_igd_gather (GUPnPSimpleIgd *self,
 
130
    struct Proxy *prox);
 
131
static void gupnp_simple_igd_add_proxy_mapping (GUPnPSimpleIgd *self,
 
132
    struct Proxy *prox,
 
133
    struct Mapping *mapping);
 
134
 
 
135
static void free_proxy (struct Proxy *prox);
 
136
static void free_mapping (struct Mapping *mapping);
 
137
 
 
138
static void stop_proxymapping (struct ProxyMapping *pm);
 
139
 
 
140
static void gupnp_simple_igd_add_port_real (GUPnPSimpleIgd *self,
 
141
    const gchar *protocol,
 
142
    guint16 external_port,
 
143
    const gchar *local_ip,
 
144
    guint16 local_port,
 
145
    guint32 lease_duration,
 
146
    const gchar *description);
 
147
static void gupnp_simple_igd_remove_port_real (GUPnPSimpleIgd *self,
 
148
    const gchar *protocol,
 
149
    guint external_port);
 
150
 
 
151
GQuark
 
152
gupnp_simple_igd_get_error_domain (void)
 
153
{
 
154
  return g_quark_from_static_string ("fs-upnp-simple-igd-error");
 
155
}
 
156
 
 
157
 
 
158
static void
 
159
gupnp_simple_igd_class_init (GUPnPSimpleIgdClass *klass)
 
160
{
 
161
  GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
 
162
 
 
163
  g_type_class_add_private (klass, sizeof (GUPnPSimpleIgdPrivate));
 
164
 
 
165
  gobject_class->constructed = gupnp_simple_igd_constructed;
 
166
  gobject_class->dispose = gupnp_simple_igd_dispose;
 
167
  gobject_class->finalize = gupnp_simple_igd_finalize;
 
168
  gobject_class->set_property = gupnp_simple_igd_set_property;
 
169
  gobject_class->get_property = gupnp_simple_igd_get_property;
 
170
 
 
171
  klass->add_port = gupnp_simple_igd_add_port_real;
 
172
  klass->remove_port = gupnp_simple_igd_remove_port_real;
 
173
 
 
174
  g_object_class_install_property (gobject_class,
 
175
      PROP_REQUEST_TIMEOUT,
 
176
      g_param_spec_uint ("request-timeout",
 
177
          "The timeout after which a request is considered to have failed",
 
178
          "After this timeout, the request is considered to have failed and"
 
179
          "is dropped (in seconds).",
 
180
          0, G_MAXUINT, 5,
 
181
          G_PARAM_READWRITE));
 
182
 
 
183
  g_object_class_install_property (gobject_class,
 
184
      PROP_MAIN_CONTEXT,
 
185
      g_param_spec_pointer ("main-context",
 
186
          "The GMainContext to use",
 
187
          "This GMainContext will be used for all async activities",
 
188
          G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
 
189
 
 
190
  /**
 
191
   * GUPnPSimpleIgd::mapped-external-port
 
192
   * @self: #GUPnPSimpleIgd that emitted the signal
 
193
   * @proto: the requested protocol ("UDP" or "TCP")
 
194
   * @external_ip: the external IP
 
195
   * @replaces_external_ip: if this mapping replaces another mapping,
 
196
   *  this is the old external IP
 
197
   * @external_port: the external port
 
198
   * @local_ip: internal ip this is forwarded to
 
199
   * @local_port: the local port
 
200
   * @description: the user's selected description
 
201
   *
 
202
   * This signal means that an IGD has been found that that adding a port
 
203
   * mapping has succeeded.
 
204
   *
 
205
   */
 
206
  signals[SIGNAL_MAPPED_EXTERNAL_PORT] = g_signal_new ("mapped-external-port",
 
207
      G_TYPE_FROM_CLASS (klass),
 
208
      G_SIGNAL_RUN_LAST,
 
209
      0,
 
210
      NULL,
 
211
      NULL,
 
212
      _gupnp_simple_igd_marshal_VOID__STRING_STRING_STRING_UINT_STRING_UINT_STRING,
 
213
      G_TYPE_NONE, 7, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_UINT,
 
214
      G_TYPE_STRING, G_TYPE_UINT, G_TYPE_STRING);
 
215
 
 
216
  /**
 
217
   * GUPnPSimpleIgd::error-mapping-port
 
218
   * @self: #GUPnPSimpleIgd that emitted the signal
 
219
   * @error: a #GError
 
220
   * @proto: The requested protocol
 
221
   * @external_port: the requested external port
 
222
   * @description: the passed description
 
223
   *
 
224
   * This means that mapping a port on a specific IGD has failed (it may still
 
225
   * succeed on other IGDs on the network).
 
226
   */
 
227
  signals[SIGNAL_ERROR_MAPPING_PORT] = g_signal_new ("error-mapping-port",
 
228
      G_TYPE_FROM_CLASS (klass),
 
229
      G_SIGNAL_RUN_LAST | G_SIGNAL_DETAILED,
 
230
      0,
 
231
      NULL,
 
232
      NULL,
 
233
      _gupnp_simple_igd_marshal_VOID__POINTER_STRING_UINT_STRING,
 
234
      G_TYPE_NONE, 4, G_TYPE_POINTER, G_TYPE_STRING, G_TYPE_UINT,
 
235
      G_TYPE_STRING);
 
236
}
 
237
 
 
238
static void
 
239
gupnp_simple_igd_init (GUPnPSimpleIgd *self)
 
240
{
 
241
  self->priv = GUPNP_SIMPLE_IGD_GET_PRIVATE (self);
 
242
 
 
243
  self->priv->service_proxies = g_ptr_array_new ();
 
244
  self->priv->mappings = g_ptr_array_new ();
 
245
}
 
246
 
 
247
static void
 
248
gupnp_simple_igd_dispose (GObject *object)
 
249
{
 
250
  GUPnPSimpleIgd *self = GUPNP_SIMPLE_IGD_CAST (object);
 
251
 
 
252
  if (self->priv->ip_avail_handler)
 
253
    g_signal_handler_disconnect (self->priv->ip_cp,
 
254
        self->priv->ip_avail_handler);
 
255
  self->priv->ip_avail_handler = 0;
 
256
 
 
257
  if (self->priv->ip_unavail_handler)
 
258
    g_signal_handler_disconnect (self->priv->ip_cp,
 
259
        self->priv->ip_unavail_handler);
 
260
  self->priv->ip_unavail_handler = 0;
 
261
 
 
262
  if (self->priv->ppp_avail_handler)
 
263
    g_signal_handler_disconnect (self->priv->ppp_cp,
 
264
        self->priv->ppp_avail_handler);
 
265
  self->priv->ppp_avail_handler = 0;
 
266
 
 
267
  if (self->priv->ppp_unavail_handler)
 
268
    g_signal_handler_disconnect (self->priv->ppp_cp,
 
269
        self->priv->ppp_unavail_handler);
 
270
  self->priv->ppp_unavail_handler = 0;
 
271
 
 
272
  while (self->priv->mappings->len)
 
273
  {
 
274
    free_mapping (
 
275
        g_ptr_array_index (self->priv->mappings, 0));
 
276
    g_ptr_array_remove_index_fast (self->priv->mappings, 0);
 
277
  }
 
278
 
 
279
  while (self->priv->service_proxies->len)
 
280
  {
 
281
    free_proxy (
 
282
        g_ptr_array_index (self->priv->service_proxies, 0));
 
283
    g_ptr_array_remove_index_fast (self->priv->service_proxies, 0);
 
284
  }
 
285
 
 
286
  if (self->priv->ip_cp)
 
287
    g_object_unref (self->priv->ip_cp);
 
288
  self->priv->ip_cp = NULL;
 
289
 
 
290
  if (self->priv->ppp_cp)
 
291
    g_object_unref (self->priv->ppp_cp);
 
292
  self->priv->ppp_cp = NULL;
 
293
 
 
294
  if (self->priv->gupnp_context)
 
295
    g_object_unref (self->priv->gupnp_context);
 
296
  self->priv->gupnp_context = NULL;
 
297
 
 
298
  G_OBJECT_CLASS (gupnp_simple_igd_parent_class)->dispose (object);
 
299
}
 
300
 
 
301
 
 
302
static void
 
303
_external_ip_address_changed (GUPnPServiceProxy *proxy, const gchar *variable,
 
304
    GValue *value, gpointer user_data)
 
305
{
 
306
  struct Proxy *prox = user_data;
 
307
  gchar *new_ip;
 
308
  guint i;
 
309
 
 
310
  g_return_if_fail (G_VALUE_HOLDS_STRING(value));
 
311
 
 
312
  new_ip = g_value_dup_string (value);
 
313
 
 
314
  for (i=0; i < prox->proxymappings->len; i++)
 
315
  {
 
316
    struct ProxyMapping *pm = g_ptr_array_index (prox->proxymappings, i);
 
317
 
 
318
    if (pm->mapped)
 
319
      g_signal_emit (prox->parent, signals[SIGNAL_MAPPED_EXTERNAL_PORT], 0,
 
320
          pm->mapping->protocol, new_ip, prox->external_ip,
 
321
          pm->mapping->external_port, pm->mapping->local_ip,
 
322
          pm->mapping->local_port, pm->mapping->description);
 
323
  }
 
324
 
 
325
  g_free (prox->external_ip);
 
326
  prox->external_ip = new_ip;
 
327
}
 
328
 
 
329
static void
 
330
free_proxymapping (struct ProxyMapping *pm)
 
331
{
 
332
  g_slice_free (struct ProxyMapping, pm);
 
333
}
 
334
 
 
335
static void
 
336
free_proxy (struct Proxy *prox)
 
337
{
 
338
  if (prox->external_ip_action)
 
339
    gupnp_service_proxy_cancel_action (prox->proxy, prox->external_ip_action);
 
340
 
 
341
  gupnp_service_proxy_remove_notify (prox->proxy, "ExternalIPAddress",
 
342
      _external_ip_address_changed, prox);
 
343
 
 
344
  g_object_unref (prox->proxy);
 
345
  g_ptr_array_foreach (prox->proxymappings, (GFunc) stop_proxymapping, NULL);
 
346
  g_ptr_array_foreach (prox->proxymappings, (GFunc) free_proxymapping, NULL);
 
347
  g_ptr_array_free (prox->proxymappings, TRUE);
 
348
  g_free (prox->external_ip);
 
349
  g_slice_free (struct Proxy, prox);
 
350
}
 
351
 
 
352
static void
 
353
free_mapping (struct Mapping *mapping)
 
354
{
 
355
  g_free (mapping->protocol);
 
356
  g_free (mapping->local_ip);
 
357
  g_free (mapping->description);
 
358
  g_slice_free (struct Mapping, mapping);
 
359
}
 
360
 
 
361
static void
 
362
gupnp_simple_igd_finalize (GObject *object)
 
363
{
 
364
  GUPnPSimpleIgd *self = GUPNP_SIMPLE_IGD_CAST (object);
 
365
 
 
366
  g_main_context_unref (self->priv->main_context);
 
367
 
 
368
  g_warn_if_fail (self->priv->service_proxies->len == 0);
 
369
  g_ptr_array_free (self->priv->service_proxies, TRUE);
 
370
 
 
371
  g_warn_if_fail (self->priv->mappings->len == 0);
 
372
  g_ptr_array_free (self->priv->mappings, TRUE);
 
373
 
 
374
  G_OBJECT_CLASS (gupnp_simple_igd_parent_class)->finalize (object);
 
375
}
 
376
 
 
377
static void
 
378
gupnp_simple_igd_get_property (GObject *object, guint prop_id,
 
379
    GValue *value, GParamSpec *pspec)
 
380
{
 
381
  GUPnPSimpleIgd *self = GUPNP_SIMPLE_IGD_CAST (object);
 
382
 
 
383
  switch (prop_id) {
 
384
    case PROP_REQUEST_TIMEOUT:
 
385
      {
 
386
        SoupSession *session;
 
387
        session = gupnp_context_get_session (self->priv->gupnp_context);
 
388
        g_object_get_property (G_OBJECT (session), "timeout", value);
 
389
      }
 
390
      break;
 
391
    case PROP_MAIN_CONTEXT:
 
392
      g_value_set_pointer (value, self->priv->main_context);
 
393
      break;
 
394
    default:
 
395
      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
 
396
      break;
 
397
  }
 
398
 
 
399
}
 
400
 
 
401
static void
 
402
gupnp_simple_igd_set_property (GObject *object, guint prop_id,
 
403
    const GValue *value, GParamSpec *pspec)
 
404
{
 
405
  GUPnPSimpleIgd *self = GUPNP_SIMPLE_IGD_CAST (object);
 
406
 
 
407
  switch (prop_id) {
 
408
    case PROP_REQUEST_TIMEOUT:
 
409
      {
 
410
        SoupSession *session;
 
411
        session = gupnp_context_get_session (self->priv->gupnp_context);
 
412
        g_object_set_property (G_OBJECT (session), "timeout", value);
 
413
      }
 
414
      break;
 
415
    case PROP_MAIN_CONTEXT:
 
416
      if (!self->priv->main_context && g_value_get_pointer (value))
 
417
      {
 
418
        self->priv->main_context = g_value_get_pointer (value);
 
419
        g_main_context_ref (self->priv->main_context);
 
420
      }
 
421
      break;
 
422
    default:
 
423
      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
 
424
      break;
 
425
  }
 
426
}
 
427
 
 
428
static void
 
429
_cp_service_avail (GUPnPControlPoint *cp,
 
430
    GUPnPServiceProxy *proxy,
 
431
    GUPnPSimpleIgd *self)
 
432
{
 
433
  struct Proxy *prox = g_slice_new0 (struct Proxy);
 
434
  guint i;
 
435
 
 
436
  prox->parent = self;
 
437
  prox->proxy = g_object_ref (proxy);
 
438
  prox->proxymappings = g_ptr_array_new ();
 
439
 
 
440
  gupnp_simple_igd_gather (self, prox);
 
441
 
 
442
  for (i = 0; i < self->priv->mappings->len; i++)
 
443
    gupnp_simple_igd_add_proxy_mapping (self, prox,
 
444
        g_ptr_array_index (self->priv->mappings, i));
 
445
 
 
446
  g_ptr_array_add(self->priv->service_proxies, prox);
 
447
}
 
448
 
 
449
 
 
450
static void
 
451
_cp_service_unavail (GUPnPControlPoint *cp,
 
452
    GUPnPServiceProxy *proxy,
 
453
    GUPnPSimpleIgd *self)
 
454
{
 
455
  guint i;
 
456
 
 
457
  for (i=0; i < self->priv->service_proxies->len; i++)
 
458
  {
 
459
    struct Proxy *prox =
 
460
      g_ptr_array_index (self->priv->service_proxies, i);
 
461
 
 
462
    if (!strcmp (gupnp_service_info_get_udn (GUPNP_SERVICE_INFO (prox->proxy)),
 
463
            gupnp_service_info_get_udn (GUPNP_SERVICE_INFO (prox->proxy))))
 
464
    {
 
465
      g_ptr_array_foreach (prox->proxymappings, (GFunc) stop_proxymapping,
 
466
          NULL);
 
467
      g_ptr_array_foreach (prox->proxymappings, (GFunc) free_proxymapping,
 
468
          NULL);
 
469
      free_proxy (prox);
 
470
      g_ptr_array_remove_index_fast (self->priv->service_proxies, i);
 
471
      break;
 
472
    }
 
473
  }
 
474
}
 
475
 
 
476
 
 
477
static void
 
478
gupnp_simple_igd_constructed (GObject *object)
 
479
{
 
480
  GUPnPSimpleIgd *self = GUPNP_SIMPLE_IGD_CAST (object);
 
481
  SoupSession *session;
 
482
 
 
483
  if (!self->priv->main_context)
 
484
    self->priv->main_context = g_main_context_ref (g_main_context_default ());
 
485
 
 
486
  self->priv->gupnp_context = gupnp_context_new (self->priv->main_context,
 
487
      NULL, 0, NULL);
 
488
  g_return_if_fail (self->priv->gupnp_context);
 
489
 
 
490
  session = gupnp_context_get_session (self->priv->gupnp_context);
 
491
  g_object_set (session, "timeout", 5, NULL);
 
492
 
 
493
  self->priv->ip_cp = gupnp_control_point_new (self->priv->gupnp_context,
 
494
      "urn:schemas-upnp-org:service:WANIPConnection:1");
 
495
  g_return_if_fail (self->priv->ip_cp);
 
496
 
 
497
  self->priv->ip_avail_handler = g_signal_connect (self->priv->ip_cp,
 
498
      "service-proxy-available",
 
499
      G_CALLBACK (_cp_service_avail), self);
 
500
  self->priv->ip_unavail_handler = g_signal_connect (self->priv->ip_cp,
 
501
      "service-proxy-unavailable",
 
502
      G_CALLBACK (_cp_service_unavail), self);
 
503
 
 
504
  self->priv->ppp_cp = gupnp_control_point_new (self->priv->gupnp_context,
 
505
      "urn:schemas-upnp-org:service:WANPPPConnection:1");
 
506
  g_return_if_fail (self->priv->ppp_cp);
 
507
 
 
508
  self->priv->ppp_avail_handler = g_signal_connect (self->priv->ppp_cp,
 
509
      "service-proxy-available",
 
510
      G_CALLBACK (_cp_service_avail), self);
 
511
  self->priv->ppp_unavail_handler = g_signal_connect (self->priv->ppp_cp,
 
512
      "service-proxy-unavailable",
 
513
      G_CALLBACK (_cp_service_unavail), self);
 
514
 
 
515
 
 
516
  gssdp_resource_browser_set_active (
 
517
      GSSDP_RESOURCE_BROWSER (self->priv->ip_cp),
 
518
      TRUE);
 
519
  gssdp_resource_browser_set_active (
 
520
      GSSDP_RESOURCE_BROWSER (self->priv->ppp_cp),
 
521
      TRUE);
 
522
 
 
523
  if (G_OBJECT_CLASS (gupnp_simple_igd_parent_class)->constructed)
 
524
    G_OBJECT_CLASS (gupnp_simple_igd_parent_class)->constructed (object);
 
525
}
 
526
 
 
527
/**
 
528
 * gupnp_simple_igd_new:
 
529
 * @main_context: the #GMainContext to use (may be NULL for the default
 
530
 * main context)
 
531
 *
 
532
 * This creates a new #GUPnpSimpleIgd object using the special GMainContext
 
533
 *
 
534
 * Returns: a new #GUPnPSimpleIgd
 
535
 */
 
536
 
 
537
GUPnPSimpleIgd *
 
538
gupnp_simple_igd_new (GMainContext *main_context)
 
539
{
 
540
  return g_object_new (GUPNP_TYPE_SIMPLE_IGD,
 
541
      "main-context", main_context, NULL);
 
542
}
 
543
 
 
544
 
 
545
static void
 
546
_service_proxy_got_external_ip_address (GUPnPServiceProxy *proxy,
 
547
    GUPnPServiceProxyAction *action,
 
548
    gpointer user_data)
 
549
{
 
550
  struct Proxy *prox = user_data;
 
551
  GUPnPSimpleIgd *self = prox->parent;
 
552
  GError *error = NULL;
 
553
  gchar *ip = NULL;
 
554
 
 
555
  g_return_if_fail (prox->external_ip_action == action);
 
556
 
 
557
  prox->external_ip_action = NULL;
 
558
  
 
559
  if (gupnp_service_proxy_end_action (proxy, action, &error,
 
560
          "NewExternalIPAddress", G_TYPE_STRING, &ip,
 
561
          NULL))
 
562
  {
 
563
    guint i;
 
564
 
 
565
    for (i=0; i < prox->proxymappings->len; i++)
 
566
    {
 
567
      struct ProxyMapping *pm = g_ptr_array_index (prox->proxymappings, i);
 
568
 
 
569
      if (pm->mapped)
 
570
        g_signal_emit (self, signals[SIGNAL_MAPPED_EXTERNAL_PORT], 0,
 
571
            pm->mapping->protocol, ip, prox->external_ip,
 
572
            pm->mapping->external_port, pm->mapping->local_ip,
 
573
            pm->mapping->local_port, pm->mapping->description);
 
574
    }
 
575
 
 
576
    g_free (prox->external_ip);
 
577
    prox->external_ip = ip;
 
578
  }
 
579
  else
 
580
  {
 
581
    guint i;
 
582
 
 
583
    prox->external_ip_failed = TRUE;
 
584
    g_return_if_fail (error);
 
585
 
 
586
    for (i=0; i < prox->proxymappings->len; i++)
 
587
    {
 
588
      struct ProxyMapping *pm = g_ptr_array_index (prox->proxymappings, i);
 
589
 
 
590
      g_signal_emit (self, signals[SIGNAL_ERROR_MAPPING_PORT], error->domain,
 
591
          error, pm->mapping->protocol, pm->mapping->external_port,
 
592
          pm->mapping->description);
 
593
    }
 
594
  }
 
595
  g_clear_error (&error);
 
596
}
 
597
 
 
598
static void
 
599
gupnp_simple_igd_gather (GUPnPSimpleIgd *self,
 
600
    struct Proxy *prox)
 
601
{
 
602
  prox->external_ip_action = gupnp_service_proxy_begin_action (prox->proxy,
 
603
      "GetExternalIPAddress",
 
604
      _service_proxy_got_external_ip_address, prox, NULL);
 
605
 
 
606
  gupnp_service_proxy_add_notify (prox->proxy, "ExternalIPAddress",
 
607
      G_TYPE_STRING, _external_ip_address_changed, prox);
 
608
 
 
609
  gupnp_service_proxy_set_subscribed (prox->proxy, TRUE);
 
610
}
 
611
 
 
612
static void
 
613
_service_proxy_renewed_port_mapping (GUPnPServiceProxy *proxy,
 
614
    GUPnPServiceProxyAction *action,
 
615
    gpointer user_data)
 
616
{
 
617
  struct ProxyMapping *pm = user_data;
 
618
  GUPnPSimpleIgd *self = pm->proxy->parent;
 
619
  GError *error = NULL;
 
620
 
 
621
  if (!gupnp_service_proxy_end_action (proxy, action, &error,
 
622
          NULL))
 
623
  {
 
624
    g_return_if_fail (error);
 
625
    g_signal_emit (self, signals[SIGNAL_ERROR_MAPPING_PORT], error->domain,
 
626
        error, pm->mapping->protocol, pm->mapping->external_port,
 
627
        pm->mapping->description);
 
628
  }
 
629
  g_clear_error (&error);
 
630
}
 
631
 
 
632
static gboolean
 
633
_renew_mapping_timeout (gpointer user_data)
 
634
{
 
635
  struct ProxyMapping *pm = user_data;
 
636
 
 
637
  gupnp_service_proxy_begin_action (pm->proxy->proxy,
 
638
      "AddPortMapping",
 
639
      _service_proxy_renewed_port_mapping, pm,
 
640
      "NewRemoteHost", G_TYPE_STRING, "",
 
641
      "NewExternalPort", G_TYPE_UINT, pm->mapping->external_port,
 
642
      "NewProtocol", G_TYPE_STRING, pm->mapping->protocol,
 
643
      "NewInternalPort", G_TYPE_UINT, pm->mapping->local_port,
 
644
      "NewInternalClient", G_TYPE_STRING, pm->mapping->local_ip,
 
645
      "NewEnabled", G_TYPE_BOOLEAN, TRUE,
 
646
      "NewPortMappingDescription", G_TYPE_STRING, pm->mapping->description,
 
647
      "NewLeaseDuration", G_TYPE_UINT, pm->mapping->lease_duration,
 
648
      NULL);
 
649
 
 
650
  return TRUE;
 
651
}
 
652
 
 
653
static void
 
654
_service_proxy_added_port_mapping (GUPnPServiceProxy *proxy,
 
655
    GUPnPServiceProxyAction *action,
 
656
    gpointer user_data)
 
657
{
 
658
  struct ProxyMapping *pm = user_data;
 
659
  GUPnPSimpleIgd *self = pm->proxy->parent;
 
660
  GError *error = NULL;
 
661
 
 
662
  g_return_if_fail (pm->action == action);
 
663
 
 
664
  pm->action = NULL;
 
665
 
 
666
  if (gupnp_service_proxy_end_action (proxy, action, &error,
 
667
          NULL))
 
668
  {
 
669
    pm->mapped = TRUE;
 
670
 
 
671
    if (pm->proxy->external_ip)
 
672
      g_signal_emit (self, signals[SIGNAL_MAPPED_EXTERNAL_PORT], 0,
 
673
          pm->mapping->protocol, pm->proxy->external_ip, NULL,
 
674
          pm->mapping->external_port, pm->mapping->local_ip,
 
675
          pm->mapping->local_port, pm->mapping->description);
 
676
 
 
677
 
 
678
 
 
679
    pm->renew_src =
 
680
      g_timeout_source_new_seconds (pm->mapping->lease_duration / 2);
 
681
    g_source_set_callback (pm->renew_src,
 
682
        _renew_mapping_timeout, pm, NULL);
 
683
    g_source_attach (pm->renew_src, self->priv->main_context);
 
684
 
 
685
  }
 
686
  else
 
687
  {
 
688
    g_return_if_fail (error);
 
689
    g_signal_emit (self, signals[SIGNAL_ERROR_MAPPING_PORT], error->domain,
 
690
        error, pm->mapping->protocol, pm->mapping->external_port,
 
691
        pm->mapping->description);
 
692
  }
 
693
  g_clear_error (&error);
 
694
 
 
695
  stop_proxymapping (pm);
 
696
}
 
697
 
 
698
static void
 
699
gupnp_simple_igd_add_proxy_mapping (GUPnPSimpleIgd *self, struct Proxy *prox,
 
700
    struct Mapping *mapping)
 
701
{
 
702
  struct ProxyMapping *pm = g_slice_new0 (struct ProxyMapping);
 
703
 
 
704
  pm->proxy = prox;
 
705
  pm->mapping = mapping;
 
706
 
 
707
  pm->action = gupnp_service_proxy_begin_action (prox->proxy,
 
708
      "AddPortMapping",
 
709
      _service_proxy_added_port_mapping, pm,
 
710
      "NewRemoteHost", G_TYPE_STRING, "",
 
711
      "NewExternalPort", G_TYPE_UINT, mapping->external_port,
 
712
      "NewProtocol", G_TYPE_STRING, mapping->protocol,
 
713
      "NewInternalPort", G_TYPE_UINT, mapping->local_port,
 
714
      "NewInternalClient", G_TYPE_STRING, mapping->local_ip,
 
715
      "NewEnabled", G_TYPE_BOOLEAN, TRUE,
 
716
      "NewPortMappingDescription", G_TYPE_STRING, mapping->description,
 
717
      "NewLeaseDuration", G_TYPE_UINT, mapping->lease_duration,
 
718
      NULL);
 
719
 
 
720
  g_ptr_array_add (prox->proxymappings, pm);
 
721
}
 
722
 
 
723
static void
 
724
gupnp_simple_igd_add_port_real (GUPnPSimpleIgd *self,
 
725
    const gchar *protocol,
 
726
    guint16 external_port,
 
727
    const gchar *local_ip,
 
728
    guint16 local_port,
 
729
    guint32 lease_duration,
 
730
    const gchar *description)
 
731
{
 
732
  struct Mapping *mapping = g_slice_new0 (struct Mapping);
 
733
  guint i;
 
734
 
 
735
  g_return_if_fail (protocol && local_ip);
 
736
  g_return_if_fail (!strcmp (protocol, "UDP") || !strcmp (protocol, "TCP"));
 
737
 
 
738
  mapping->protocol = g_strdup (protocol);
 
739
  mapping->external_port = external_port;
 
740
  mapping->local_ip = g_strdup (local_ip);
 
741
  mapping->local_port = local_port;
 
742
  mapping->lease_duration = lease_duration;
 
743
  mapping->description = g_strdup (description);
 
744
 
 
745
  if (!mapping->description)
 
746
    mapping->description = g_strdup ("");
 
747
 
 
748
  g_ptr_array_add (self->priv->mappings, mapping);
 
749
 
 
750
  for (i=0; i < self->priv->service_proxies->len; i++)
 
751
  {
 
752
    struct Proxy *prox = g_ptr_array_index (self->priv->service_proxies, i);
 
753
 
 
754
    if (prox->external_ip_failed)
 
755
    {
 
756
      GError error = {GUPNP_SIMPLE_IGD_ERROR,
 
757
                      GUPNP_SIMPLE_IGD_ERROR_EXTERNAL_ADDRESS,
 
758
                      "Could not get external address"};
 
759
      g_signal_emit (self, signals[SIGNAL_ERROR_MAPPING_PORT],
 
760
          GUPNP_SIMPLE_IGD_ERROR,
 
761
          &error, mapping->protocol, mapping->external_port,
 
762
          mapping->description);
 
763
    }
 
764
    else
 
765
    {
 
766
      gupnp_simple_igd_add_proxy_mapping (self, prox, mapping);
 
767
    }
 
768
  }
 
769
}
 
770
 
 
771
/**
 
772
 * gupnp_simple_igd_add_port:
 
773
 * @self: The #GUPnPSimpleIgd object
 
774
 * @protocol: the protocol "UDP" or "TCP"
 
775
 * @external_port: The port to try to open on the external device
 
776
 * @local_ip: The IP address to forward packets to (most likely the local ip address)
 
777
 * @local_port: The local port to forward packets to
 
778
 * @lease_duration: The duration of the lease (it will be auto-renewed before it expires). This is in seconds.
 
779
 * @description: The description that will appear in the router's table
 
780
 *
 
781
 * This adds a port to the router's forwarding table. The mapping will
 
782
 * be automatically refreshed by this object until it is either removed with
 
783
 * gupnp_simple_igd_remove_port() or the object disapears.
 
784
 *
 
785
 * If there is a problem, the #GUPnPSimpleIgd::error-mapping-port signal will
 
786
 * be emitted. If a router is found and a port is mapped correctly,
 
787
 * #GUPnPSimpleIgd::mapped-external-port will be emitted. These signals may
 
788
 * be emitted multiple times if there are multiple routers present.
 
789
 */
 
790
 
 
791
void
 
792
gupnp_simple_igd_add_port (GUPnPSimpleIgd *self,
 
793
    const gchar *protocol,
 
794
    guint16 external_port,
 
795
    const gchar *local_ip,
 
796
    guint16 local_port,
 
797
    guint32 lease_duration,
 
798
    const gchar *description)
 
799
{
 
800
  GUPnPSimpleIgdClass *klass = GUPNP_SIMPLE_IGD_GET_CLASS (self);
 
801
 
 
802
  g_return_if_fail (klass->add_port);
 
803
 
 
804
  klass->add_port (self, protocol, external_port, local_ip, local_port,
 
805
      lease_duration, description);
 
806
}
 
807
 
 
808
 
 
809
static void
 
810
_service_proxy_delete_port_mapping (GUPnPServiceProxy *proxy,
 
811
    GUPnPServiceProxyAction *action,
 
812
    gpointer user_data)
 
813
{
 
814
  GError *error = NULL;
 
815
 
 
816
 
 
817
  if (!gupnp_service_proxy_end_action (proxy, action, &error,
 
818
          NULL))
 
819
  {
 
820
    g_return_if_fail (error);
 
821
    g_warning ("Error deleting port mapping: %s", error->message);
 
822
  }
 
823
  g_clear_error (&error);
 
824
}
 
825
 
 
826
static void
 
827
gupnp_simple_igd_remove_port_real (GUPnPSimpleIgd *self,
 
828
    const gchar *protocol,
 
829
    guint external_port)
 
830
{
 
831
  guint i, j;
 
832
  struct Mapping *mapping = NULL;
 
833
 
 
834
  g_return_if_fail (protocol);
 
835
 
 
836
  for (i = 0; i < self->priv->mappings->len; i++)
 
837
  {
 
838
    struct Mapping *tmpmapping = g_ptr_array_index (self->priv->mappings, i);
 
839
    if (tmpmapping->external_port == external_port &&
 
840
        !strcmp (tmpmapping->protocol, protocol))
 
841
    {
 
842
      mapping = tmpmapping;
 
843
      break;
 
844
    }
 
845
  }
 
846
  if (!mapping)
 
847
    return;
 
848
 
 
849
  g_ptr_array_remove_index_fast (self->priv->mappings, i);
 
850
 
 
851
  for (i=0; i < self->priv->service_proxies->len; i++)
 
852
  {
 
853
    struct Proxy *prox = g_ptr_array_index (self->priv->service_proxies, i);
 
854
 
 
855
    for (j=0; j < prox->proxymappings->len; j++)
 
856
    {
 
857
      struct ProxyMapping *pm = g_ptr_array_index (prox->proxymappings, j);
 
858
      if (pm->mapping == mapping)
 
859
      {
 
860
        stop_proxymapping (pm);
 
861
 
 
862
        if (pm->renew_src)
 
863
        {
 
864
          g_source_destroy (pm->renew_src);
 
865
          g_source_unref (pm->renew_src);
 
866
        }
 
867
        pm->renew_src = NULL;
 
868
 
 
869
        if (pm->mapped)
 
870
          gupnp_service_proxy_begin_action (prox->proxy,
 
871
              "DeletePortMapping",
 
872
              _service_proxy_delete_port_mapping, self,
 
873
              "NewRemoteHost", G_TYPE_STRING, "",
 
874
              "NewExternalPort", G_TYPE_UINT, mapping->external_port,
 
875
              "NewProtocol", G_TYPE_STRING, mapping->protocol,
 
876
              NULL);
 
877
 
 
878
        free_proxymapping (pm);
 
879
        g_ptr_array_remove_index_fast (prox->proxymappings, j);
 
880
        j--;
 
881
      }
 
882
    }
 
883
  }
 
884
 
 
885
  free_mapping (mapping);
 
886
}
 
887
 
 
888
/**
 
889
 * gupnp_simple_igd_remove_port:
 
890
 * @self: The #GUPnPSimpleIgd object
 
891
 * @protocol: the protocol "UDP" or "TCP" as given to
 
892
 *  gupnp_simple_igd_add_port()
 
893
 * @external_port: The port to try to open on the external device as given to
 
894
 *  gupnp_simple_igd_add_port()
 
895
 *
 
896
 * This tries to remove a port entry from the routers that was previously added
 
897
 * with gupnp_simple_igd_add_port(). There is no indicated of success or failure
 
898
 * it is a best effort mechanism. If it fails, the bindings will disapears after
 
899
 * the lease duration set when the port where added.
 
900
 */
 
901
void
 
902
gupnp_simple_igd_remove_port (GUPnPSimpleIgd *self,
 
903
    const gchar *protocol,
 
904
    guint external_port)
 
905
{
 
906
  GUPnPSimpleIgdClass *klass = GUPNP_SIMPLE_IGD_GET_CLASS (self);
 
907
 
 
908
  g_return_if_fail (klass->remove_port);
 
909
 
 
910
  klass->remove_port (self, protocol, external_port);
 
911
}
 
912
 
 
913
static void
 
914
stop_proxymapping (struct ProxyMapping *pm)
 
915
{
 
916
  if (pm->action)
 
917
    gupnp_service_proxy_cancel_action (pm->proxy->proxy,
 
918
        pm->action);
 
919
  pm->action = NULL;
 
920
}