~ubuntu-branches/debian/experimental/gupnp/experimental

« back to all changes in this revision

Viewing changes to libgupnp/gupnp-service-proxy.c

  • Committer: Bazaar Package Importer
  • Author(s): Ross Burton
  • Date: 2008-06-30 09:32:18 UTC
  • mfrom: (1.2.1 upstream) (2.1.5 intrepid)
  • Revision ID: james.westby@ubuntu.com-20080630093218-vmb4elru8cn18579
Tags: 0.12.1-1
New upstream release.

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
1
/*
2
 
 * Copyright (C) 2006, 2007 OpenedHand Ltd.
 
2
 * Copyright (C) 2006, 2007, 2008 OpenedHand Ltd.
3
3
 *
4
4
 * Author: Jorn Baayen <jorn@openedhand.com>
5
5
 *
28
28
 * #GUPnPServiceInfo interface.
29
29
 */
30
30
 
31
 
#include <libsoup/soup-soap-message.h>
 
31
#include <libsoup/soup.h>
32
32
#include <gobject/gvaluecollector.h>
33
33
#include <string.h>
34
34
#include <locale.h>
35
35
 
36
36
#include "gupnp-service-proxy.h"
37
 
#include "gupnp-service-proxy-private.h"
38
 
#include "gupnp-device-proxy-private.h"
39
37
#include "gupnp-context-private.h"
40
38
#include "gupnp-error.h"
41
39
#include "gupnp-error-private.h"
 
40
#include "gupnp-types.h"
42
41
#include "xml-util.h"
43
42
#include "gena-protocol.h"
44
 
#include "accept-language.h"
 
43
#include "http-headers.h"
45
44
#include "gvalue-util.h"
46
45
 
47
46
G_DEFINE_TYPE (GUPnPServiceProxy,
63
62
        GHashTable *notify_hash;
64
63
 
65
64
        GList *pending_messages; /* Pending SoupMessages from this proxy */
 
65
 
 
66
        GList *pending_notifies; /* Pending notifications to be sent (xmlDoc*) */
 
67
        guint notify_idle_id; /* Idle handler ID of notification emiter */
66
68
};
67
69
 
68
70
enum {
80
82
struct _GUPnPServiceProxyAction {
81
83
        GUPnPServiceProxy *proxy;
82
84
 
83
 
        SoupSoapMessage *msg;
 
85
        SoupMessage *msg;
 
86
        GString *msg_str;
84
87
 
85
88
        GUPnPServiceProxyActionCallback callback;
86
89
        gpointer user_data;
87
90
 
 
91
        GError *error;    /* If non-NULL, description of error that
 
92
                             occurred when preparing message */
 
93
 
88
94
        va_list var_args; /* The va_list after begin_action_valist has
89
95
                             gone through it. Used by send_action_valist(). */
90
96
};
101
107
} CallbackData;
102
108
 
103
109
static void
104
 
subscribe_got_response (SoupMessage       *msg,
 
110
subscribe_got_response (SoupSession       *session,
 
111
                        SoupMessage       *msg,
105
112
                        GUPnPServiceProxy *proxy);
106
113
static void
107
114
subscribe (GUPnPServiceProxy *proxy);
114
121
        action->proxy->priv->pending_actions =
115
122
                g_list_remove (action->proxy->priv->pending_actions, action);
116
123
 
117
 
        g_object_unref (action->msg);
 
124
        if (action->msg != NULL)
 
125
                g_object_unref (action->msg);
118
126
 
119
127
        g_slice_free (GUPnPServiceProxyAction, action);
120
128
}
228
236
 
229
237
                msg = proxy->priv->pending_messages->data;
230
238
 
231
 
                soup_message_set_status (msg, SOUP_STATUS_CANCELLED);
232
 
                soup_session_cancel_message (session, msg);
 
239
                soup_session_cancel_message (session,
 
240
                                             msg,
 
241
                                             SOUP_STATUS_CANCELLED);
233
242
 
234
243
                proxy->priv->pending_messages =
235
244
                        g_list_delete_link (proxy->priv->pending_messages,
236
245
                                            proxy->priv->pending_messages);
237
246
        }
238
247
 
 
248
        /* Cancel pending notifications */
 
249
        if (proxy->priv->notify_idle_id) {
 
250
                g_source_remove (proxy->priv->notify_idle_id);
 
251
                proxy->priv->notify_idle_id = 0;
 
252
        }
 
253
        
 
254
        while (proxy->priv->pending_notifies) {
 
255
                xmlFreeDoc (proxy->priv->pending_notifies->data);
 
256
                proxy->priv->pending_notifies = g_list_delete_link (proxy->priv->pending_notifies,
 
257
                                                                   proxy->priv->pending_notifies);
 
258
        }
 
259
        
239
260
        /* Call super */
240
261
        object_class = G_OBJECT_CLASS (gupnp_service_proxy_parent_class);
241
262
        object_class->dispose (object);
317
338
 * gupnp_service_proxy_send_action
318
339
 * @proxy: A #GUPnPServiceProxy
319
340
 * @action: An action
320
 
 * @error: The location where to store any error, or NULL
 
341
 * @error: The location where to store any error, or %NULL
321
342
 * @Varargs: tuples of in parameter name, in paramater type, and in parameter
322
 
 * value, followed by NULL, and then tuples of out paramater name,
323
 
 * out parameter type, and out parameter value location, terminated with NULL
 
343
 * value, followed by %NULL, and then tuples of out paramater name,
 
344
 * out parameter type, and out parameter value location, terminated with %NULL
324
345
 *
325
346
 * Sends action @action with parameters @Varargs to the service exposed by
326
347
 * @proxy synchronously. If an error occurred, @error will be set. In case of
327
348
 * a UPnPError the error code will be the same in @error.
328
349
 *
329
 
 * Return value: TRUE if sending the action was succesful.
 
350
 * Return value: %TRUE if sending the action was succesful.
330
351
 **/
331
352
gboolean
332
353
gupnp_service_proxy_send_action (GUPnPServiceProxy *proxy,
359
380
 * gupnp_service_proxy_send_action_valist
360
381
 * @proxy: A #GUPnPServiceProxy
361
382
 * @action: An action
362
 
 * @error: The location where to store any error, or NULL
 
383
 * @error: The location where to store any error, or %NULL
363
384
 * @var_args: va_list of tuples of in parameter name, in paramater type, and in
364
 
 * parameter value, followed by NULL, and then tuples of out paramater name,
 
385
 * parameter value, followed by %NULL, and then tuples of out paramater name,
365
386
 * out parameter type, and out parameter value location
366
387
 *
367
388
 * See gupnp_service_proxy_send_action(); this version takes a va_list for
368
389
 * use by language bindings.
369
390
 *
370
 
 * Return value: TRUE if sending the action was succesful.
 
391
 * Return value: %TRUE if sending the action was succesful.
371
392
 **/
372
393
gboolean
373
394
gupnp_service_proxy_send_action_valist (GUPnPServiceProxy *proxy,
391
412
                                                          action,
392
413
                                                          stop_main_loop,
393
414
                                                          main_loop,
394
 
                                                          error,
395
415
                                                          var_args);
396
 
        if (!handle) {
397
 
                g_main_loop_unref (main_loop);
398
 
 
399
 
                return FALSE;
400
 
        }
401
416
 
402
417
        /* Loop till we get a reply (or time out) */
403
418
        if (g_main_loop_is_running (main_loop))
418
433
 * gupnp_service_proxy_send_action_hash
419
434
 * @proxy: A #GUPnPServiceProxy
420
435
 * @action: An action
421
 
 * @error: The location where to store any error, or NULL
 
436
 * @error: The location where to store any error, or %NULL
422
437
 * @in_hash: A #GHashTable of in parameter name and #GValue pairs
423
438
 * @out_hash: A #GHashTable of out parameter name and initialized
424
439
 * #GValue pairs
426
441
 * See gupnp_service_proxy_send_action(); this version takes a pair of
427
442
 * #GHashTable<!-- -->s for runtime determined parameter lists.
428
443
 *
429
 
 * Return value: TRUE if sending the action was succesful.
 
444
 * Return value: %TRUE if sending the action was succesful.
430
445
 **/
431
446
gboolean
432
447
gupnp_service_proxy_send_action_hash (GUPnPServiceProxy *proxy,
451
466
                                                        action,
452
467
                                                        stop_main_loop,
453
468
                                                        main_loop,
454
 
                                                        error,
455
469
                                                        in_hash);
456
470
        if (!handle) {
457
471
                g_main_loop_unref (main_loop);
481
495
 * @callback: The callback to call when sending the action has succeeded
482
496
 * or failed
483
497
 * @user_data: User data for @callback
484
 
 * @error: The location where to store any error, or NULL
485
498
 * @Varargs: tuples of in parameter name, in paramater type, and in parameter
486
 
 * value, terminated with NULL
 
499
 * value, terminated with %NULL
487
500
 *
488
501
 * Sends action @action with parameters @Varargs to the service exposed by
489
502
 * @proxy asynchronously, calling @callback on completion. From @callback, call
490
503
 * gupnp_service_proxy_end_action() to check for errors, to retrieve return
491
504
 * values, and to free the #GUPnPServiceProxyAction.
492
505
 *
493
 
 * Return value: A #GUPnPServiceProxyAction handle. This is freed when
494
 
 * calling gupnp_service_proxy_end_action().
 
506
 * Return value: A #GUPnPServiceProxyAction handle. This will
 
507
 * be freed when calling gupnp_service_proxy_cancel_action() or
 
508
 * gupnp_service_proxy_end_action_valist().
495
509
 **/
496
510
GUPnPServiceProxyAction *
497
511
gupnp_service_proxy_begin_action (GUPnPServiceProxy              *proxy,
498
512
                                  const char                     *action,
499
513
                                  GUPnPServiceProxyActionCallback callback,
500
514
                                  gpointer                        user_data,
501
 
                                  GError                        **error,
502
515
                                  ...)
503
516
{
504
517
        va_list var_args;
505
518
        GUPnPServiceProxyAction *ret;
506
519
 
507
 
        va_start (var_args, error);
 
520
        va_start (var_args, user_data);
508
521
        ret = gupnp_service_proxy_begin_action_valist (proxy,
509
522
                                                       action,
510
523
                                                       callback,
511
524
                                                       user_data,
512
 
                                                       error,
513
525
                                                       var_args);
514
526
        va_end (var_args);
515
527
 
517
529
}
518
530
 
519
531
/* Begins a basic action message */
520
 
static SoupSoapMessage *
521
 
begin_action_msg (GUPnPServiceProxy *proxy,
522
 
                  const char        *action,
523
 
                  GError           **error)
 
532
static GUPnPServiceProxyAction *
 
533
begin_action_msg (GUPnPServiceProxy              *proxy,
 
534
                  const char                     *action,
 
535
                  GUPnPServiceProxyActionCallback callback,
 
536
                  gpointer                        user_data)
524
537
{
525
 
        SoupSoapMessage *msg;
526
 
        char *control_url, *lang, *full_action;
 
538
        GUPnPServiceProxyAction *ret;
 
539
        char *control_url, *full_action;
527
540
        const char *service_type;
528
541
 
 
542
        /* Create action structure */
 
543
        ret = g_slice_new (GUPnPServiceProxyAction);
 
544
 
 
545
        ret->proxy = proxy;
 
546
 
 
547
        ret->callback  = callback;
 
548
        ret->user_data = user_data;
 
549
 
 
550
        ret->msg = NULL;
 
551
 
 
552
        ret->error = NULL;
 
553
 
 
554
        proxy->priv->pending_actions =
 
555
                g_list_prepend (proxy->priv->pending_actions, ret);
 
556
 
 
557
        /* Make sure we have a service type */
 
558
        service_type = gupnp_service_info_get_service_type
 
559
                                        (GUPNP_SERVICE_INFO (proxy));
 
560
        if (service_type == NULL) {
 
561
                ret->error = g_error_new (GUPNP_SERVER_ERROR,
 
562
                                          GUPNP_SERVER_ERROR_OTHER,
 
563
                                          "No service type defined");
 
564
 
 
565
                return ret;
 
566
        }
 
567
 
529
568
        /* Create message */
530
569
        control_url = gupnp_service_info_get_control_url
531
570
                                        (GUPNP_SERVICE_INFO (proxy));
532
571
 
533
 
        msg = NULL;
534
572
        if (control_url != NULL) {
535
 
                msg = soup_soap_message_new (SOUP_METHOD_POST,
536
 
                                             control_url,
537
 
                                             FALSE, NULL, NULL, NULL);
 
573
                ret->msg = soup_message_new (SOUP_METHOD_POST, control_url);
538
574
 
539
575
                g_free (control_url);
540
576
        }
541
577
 
542
 
        if (msg == NULL) {
543
 
                g_set_error (error,
544
 
                             GUPNP_SERVER_ERROR,
545
 
                             GUPNP_SERVER_ERROR_INVALID_URL,
546
 
                             "No valid control URL defined");
547
 
 
548
 
                return NULL;
549
 
        }
550
 
 
551
 
        /* Specify language */
552
 
        lang = accept_language_get_header ();
553
 
        if (lang) {
554
 
                soup_message_add_header (SOUP_MESSAGE (msg)->request_headers,
555
 
                                         "Accept-Language",
556
 
                                         lang);
557
 
 
558
 
                g_free (lang);
 
578
        if (ret->msg == NULL) {
 
579
                ret->error = g_error_new (GUPNP_SERVER_ERROR,
 
580
                                          GUPNP_SERVER_ERROR_INVALID_URL,
 
581
                                          "No valid control URL defined");
 
582
 
 
583
                return ret;
559
584
        }
560
585
 
561
586
        /* Specify action */
562
 
        service_type = gupnp_service_info_get_service_type
563
 
                                        (GUPNP_SERVICE_INFO (proxy));
564
 
        if (service_type == NULL) {
565
 
                g_set_error (error,
566
 
                             GUPNP_SERVER_ERROR,
567
 
                             GUPNP_SERVER_ERROR_OTHER,
568
 
                             "No service type defined");
569
 
 
570
 
                g_object_unref (msg);
571
 
 
572
 
                return NULL;
573
 
        }
574
 
 
575
587
        full_action = g_strdup_printf ("\"%s#%s\"", service_type, action);
576
 
        soup_message_add_header (SOUP_MESSAGE (msg)->request_headers,
577
 
                                 "SOAPAction",
578
 
                                 full_action);
 
588
        soup_message_headers_append (ret->msg->request_headers,
 
589
                                     "SOAPAction",
 
590
                                     full_action);
579
591
        g_free (full_action);
580
592
 
581
 
        /* Fill envelope */
582
 
        soup_soap_message_start_envelope (msg);
583
 
        soup_soap_message_start_body (msg);
584
 
 
585
 
        /* Action element */
586
 
        soup_soap_message_start_element (msg,
587
 
                                         action,
588
 
                                         "u",
589
 
                                         service_type);
590
 
 
591
 
        return msg;
 
593
        /* Specify user agent and language */
 
594
        http_request_set_user_agent (ret->msg);
 
595
        http_request_set_accept_language (ret->msg);
 
596
 
 
597
        /* Set up envelope */
 
598
        ret->msg_str = xml_util_new_string ();
 
599
 
 
600
        g_string_append (ret->msg_str,
 
601
                         "<?xml version=\"1.0\"?>"
 
602
                         "<s:Envelope xmlns:s="
 
603
                                "\"http://schemas.xmlsoap.org/soap/envelope/\" "
 
604
                          "s:encodingStyle="
 
605
                                "\"http://schemas.xmlsoap.org/soap/encoding/\">"
 
606
                         "<s:Body>");
 
607
 
 
608
        g_string_append (ret->msg_str, "<u:");
 
609
        g_string_append (ret->msg_str, action);
 
610
        g_string_append (ret->msg_str, " xmlns:u=\"");
 
611
        g_string_append (ret->msg_str, service_type);
 
612
        g_string_append (ret->msg_str, "\">");
 
613
 
 
614
        return ret;
592
615
}
593
616
 
594
617
/* Received response to action message */
595
618
static void
596
 
action_got_response (SoupMessage             *msg,
 
619
action_got_response (SoupSession             *session,
 
620
                     SoupMessage             *msg,
597
621
                     GUPnPServiceProxyAction *action)
598
622
{
599
623
        const char *full_action;
600
 
        GUPnPContext *context;
601
 
        SoupSession *session;
602
624
 
603
625
        switch (msg->status_code) {
604
626
        case SOUP_STATUS_CANCELLED:
609
631
                /* Retry with M-POST */
610
632
                msg->method = "M-POST";
611
633
 
612
 
                soup_message_add_header
 
634
                soup_message_headers_append
613
635
                        (msg->request_headers,
614
636
                         "Man",
615
637
                         "\"http://schemas.xmlsoap.org/soap/envelope/\"; ns=s");
616
638
 
617
639
                /* Rename "SOAPAction" to "s-SOAPAction" */
618
 
                full_action = soup_message_get_header (msg->request_headers,
619
 
                                                       "SOAPAction");
620
 
                soup_message_add_header (msg->request_headers,
621
 
                                         "s-SOAPAction",
622
 
                                         full_action);
623
 
                soup_message_remove_header (msg->request_headers,
 
640
                full_action = soup_message_headers_get (msg->request_headers,
 
641
                                                        "SOAPAction");
 
642
                soup_message_headers_append (msg->request_headers,
 
643
                                             "s-SOAPAction",
 
644
                                             full_action);
 
645
                soup_message_headers_remove (msg->request_headers,
624
646
                                            "SOAPAction");
625
647
 
626
648
                /* And re-queue */
627
 
                context = gupnp_service_info_get_context
628
 
                                (GUPNP_SERVICE_INFO (action->proxy));
629
 
                session = _gupnp_context_get_session (context);
630
 
 
631
649
                soup_session_requeue_message (session, msg);
632
650
 
633
651
                break;
641
659
}
642
660
 
643
661
/* Finishes an action message and sends it off */
644
 
static GUPnPServiceProxyAction *
645
 
finish_action_msg (GUPnPServiceProxy              *proxy,
646
 
                   SoupSoapMessage                *msg,
647
 
                   GUPnPServiceProxyActionCallback callback,
648
 
                   gpointer                        user_data)
 
662
static void
 
663
finish_action_msg (GUPnPServiceProxyAction *action,
 
664
                   const char              *action_name)
649
665
{
650
 
        GUPnPServiceProxyAction *ret;
651
666
        GUPnPContext *context;
652
667
        SoupSession *session;
653
668
 
654
669
        /* Finish message */
655
 
        soup_soap_message_end_element (msg);
656
 
 
657
 
        soup_soap_message_end_body (msg);
658
 
        soup_soap_message_end_envelope (msg);
659
 
        soup_soap_message_persist (msg);
660
 
 
661
 
        /* Create action structure */
662
 
        ret = g_slice_new (GUPnPServiceProxyAction);
663
 
 
664
 
        ret->proxy = proxy;
665
 
 
666
 
        ret->msg   = msg;
667
 
 
668
 
        ret->callback  = callback;
669
 
        ret->user_data = user_data;
670
 
 
671
 
        proxy->priv->pending_actions =
672
 
                g_list_prepend (proxy->priv->pending_actions, ret);
 
670
        g_string_append (action->msg_str, "</u:");
 
671
        g_string_append (action->msg_str, action_name);
 
672
        g_string_append_c (action->msg_str, '>');
 
673
 
 
674
        g_string_append (action->msg_str,
 
675
                         "</s:Body>"
 
676
                         "</s:Envelope>");
 
677
 
 
678
        soup_message_set_request (action->msg,
 
679
                                  "text/xml",
 
680
                                  SOUP_MEMORY_TAKE,
 
681
                                  action->msg_str->str,
 
682
                                  action->msg_str->len);
 
683
 
 
684
        g_string_free (action->msg_str, FALSE);
673
685
 
674
686
        /* We need to keep our own reference to the message as well,
675
687
         * in order for send_action() to work. */
676
 
        g_object_ref (msg);
 
688
        g_object_ref (action->msg);
677
689
 
678
690
        /* Send the message */
679
 
        context = gupnp_service_info_get_context (GUPNP_SERVICE_INFO (proxy));
 
691
        context = gupnp_service_info_get_context
 
692
                                (GUPNP_SERVICE_INFO (action->proxy));
680
693
        session = _gupnp_context_get_session (context);
681
694
 
682
695
        soup_session_queue_message (session,
683
 
                                    SOUP_MESSAGE (msg),
684
 
                                    (SoupMessageCallbackFn) action_got_response,
685
 
                                    ret);
686
 
 
687
 
        return ret;
 
696
                                    action->msg,
 
697
                                    (SoupSessionCallback) action_got_response,
 
698
                                    action);
688
699
}
689
700
 
690
701
/* Writes a parameter name and GValue pair to @msg */
691
702
static void
692
 
write_in_parameter (const char      *arg_name,
693
 
                    GValue          *value,
694
 
                    SoupSoapMessage *msg)
 
703
write_in_parameter (const char *arg_name,
 
704
                    GValue     *value,
 
705
                    GString    *msg_str)
695
706
{
696
 
        char *str;
697
 
 
698
 
        soup_soap_message_start_element (msg,
699
 
                                         arg_name,
700
 
                                         NULL,
701
 
                                         NULL);
702
 
 
703
 
        str = gvalue_util_value_get_string (value);
704
 
        soup_soap_message_write_string (msg, str);
705
 
        g_free (str);
706
 
 
707
 
        soup_soap_message_end_element (msg);
 
707
        /* Write parameter pair */
 
708
        xml_util_start_element (msg_str, arg_name);
 
709
        gvalue_util_value_append_to_xml_string (value, msg_str);
 
710
        xml_util_end_element (msg_str, arg_name);
708
711
}
709
712
 
710
713
/**
714
717
 * @callback: The callback to call when sending the action has succeeded
715
718
 * or failed
716
719
 * @user_data: User data for @callback
717
 
 * @error: The location where to store any error, or NULL
718
720
 * @var_args: A va_list of tuples of in parameter name, in paramater type, and
719
721
 * in parameter value
720
722
 *
721
723
 * See gupnp_service_proxy_begin_action(); this version takes a va_list for
722
724
 * use by language bindings.
723
725
 *
724
 
 * Return value: A #GUPnPServiceProxyAction handle, or NULL on error. This will
725
 
 * be freed automatically on @callback calling.
 
726
 * Return value: A #GUPnPServiceProxyAction handle. This will
 
727
 * be freed when calling gupnp_service_proxy_cancel_action() or
 
728
 * gupnp_service_proxy_end_action_valist().
726
729
 **/
727
730
GUPnPServiceProxyAction *
728
731
gupnp_service_proxy_begin_action_valist
730
733
                                    const char                     *action,
731
734
                                    GUPnPServiceProxyActionCallback callback,
732
735
                                    gpointer                        user_data,
733
 
                                    GError                        **error,
734
736
                                    va_list                         var_args)
735
737
{
736
738
        const char *arg_name;
737
 
        SoupSoapMessage *msg;
738
739
        GUPnPServiceProxyAction *ret;
739
740
 
740
741
        g_return_val_if_fail (GUPNP_IS_SERVICE_PROXY (proxy), NULL);
742
743
        g_return_val_if_fail (callback, NULL);
743
744
 
744
745
        /* Create message */
745
 
        msg = begin_action_msg (proxy, action, error);
746
 
        if (msg == NULL)
747
 
                return NULL;
 
746
        ret = begin_action_msg (proxy, action, callback, user_data);
 
747
 
 
748
        if (ret->error) {
 
749
                callback (proxy, ret, user_data);
 
750
 
 
751
                return ret;
 
752
        }
748
753
 
749
754
        /* Arguments */
750
755
        arg_name = va_arg (var_args, const char *);
758
763
 
759
764
                G_VALUE_COLLECT (&value, var_args, 0, &collect_error);
760
765
                if (!collect_error) {
761
 
                        write_in_parameter (arg_name, &value, msg);
 
766
                        write_in_parameter (arg_name, &value, ret->msg_str);
762
767
 
763
768
                        g_value_unset (&value);
764
769
 
777
782
        }
778
783
 
779
784
        /* Finish and send off */
780
 
        ret = finish_action_msg (proxy, msg, callback, user_data);
 
785
        finish_action_msg (ret, action);
781
786
 
782
787
        /* Save the current position in the va_list for send_action_valist() */
783
788
        G_VA_COPY (ret->var_args, var_args);
792
797
 * @callback: The callback to call when sending the action has succeeded
793
798
 * or failed
794
799
 * @user_data: User data for @callback
795
 
 * @error: The location where to store any error, or NULL
796
800
 * @hash: A #GHashTable of in parameter name and #GValue pairs
797
801
 *
798
802
 * See gupnp_service_proxy_begin_action(); this version takes a #GHashTable
799
803
 * for runtime generated parameter lists.
800
804
 *
801
 
 * Return value: A #GUPnPServiceProxyAction handle, or NULL on error. This will
802
 
 * be freed automatically on @callback calling.
 
805
 * Return value: A #GUPnPServiceProxyAction handle. This will
 
806
 * be freed when calling gupnp_service_proxy_cancel_action() or
 
807
 * gupnp_service_proxy_end_action_hash().
803
808
 **/
804
809
GUPnPServiceProxyAction *
805
810
gupnp_service_proxy_begin_action_hash
807
812
                                    const char                     *action,
808
813
                                    GUPnPServiceProxyActionCallback callback,
809
814
                                    gpointer                        user_data,
810
 
                                    GError                        **error,
811
815
                                    GHashTable                     *hash)
812
816
{
813
 
        SoupSoapMessage *msg;
 
817
        GUPnPServiceProxyAction *ret;
814
818
 
815
819
        g_return_val_if_fail (GUPNP_IS_SERVICE_PROXY (proxy), NULL);
816
820
        g_return_val_if_fail (action, NULL);
817
821
        g_return_val_if_fail (callback, NULL);
818
822
 
819
823
        /* Create message */
820
 
        msg = begin_action_msg (proxy, action, error);
821
 
        if (msg == NULL)
822
 
                return NULL;
 
824
        ret = begin_action_msg (proxy, action, callback, user_data);
 
825
 
 
826
        if (ret->error) {
 
827
                callback (proxy, ret, user_data);
 
828
 
 
829
                return ret;
 
830
        }
823
831
 
824
832
        /* Arguments */
825
 
        g_hash_table_foreach (hash, (GHFunc) write_in_parameter, msg);
 
833
        g_hash_table_foreach (hash, (GHFunc) write_in_parameter, ret->msg_str);
826
834
 
827
835
        /* Finish and send off */
828
 
        return finish_action_msg (proxy, msg, callback, user_data);
 
836
        finish_action_msg (ret, action);
 
837
 
 
838
        return ret;
829
839
}
830
840
 
831
841
/**
832
842
 * gupnp_service_proxy_end_action
833
843
 * @proxy: A #GUPnPServiceProxy
834
844
 * @action: A #GUPnPServiceProxyAction handle
835
 
 * @error: The location where to store any error, or NULL
 
845
 * @error: The location where to store any error, or %NULL
836
846
 * @Varargs: tuples of out parameter name, out paramater type, and out parameter
837
 
 * value location, terminated with NULL. The out parameter values should be
 
847
 * value location, terminated with %NULL. The out parameter values should be
838
848
 * freed after use
839
849
 *
840
850
 * Retrieves the result of @action. The out parameters in @Varargs will be
841
851
 * filled in, and if an error occurred, @error will be set. In case of
842
852
 * a UPnPError the error code will be the same in @error.
843
853
 *
844
 
 * Return value: TRUE on success.
 
854
 * Return value: %TRUE on success.
845
855
 **/
846
856
gboolean
847
857
gupnp_service_proxy_end_action (GUPnPServiceProxy       *proxy,
863
873
}
864
874
 
865
875
/* Checks an action response for errors and returns the parsed
866
 
 * SoupSoapResponse object. */
867
 
static SoupSoapResponse *
 
876
 * xmlDoc object. */
 
877
static xmlDoc *
868
878
check_action_response (GUPnPServiceProxy       *proxy,
869
879
                       GUPnPServiceProxyAction *action,
 
880
                       xmlNode                **params,
870
881
                       GError                 **error)
871
882
{
872
 
        SoupMessage *soup_msg;
873
 
        SoupSoapResponse *response;
874
 
        SoupSoapParameter *param;
 
883
        xmlDoc *response;
875
884
        int code;
876
885
 
877
 
        soup_msg = SOUP_MESSAGE (action->msg);
878
 
 
879
886
        /* Check for errors */
880
 
        switch (soup_msg->status_code) {
 
887
        switch (action->msg->status_code) {
881
888
        case SOUP_STATUS_OK:
882
889
        case SOUP_STATUS_INTERNAL_SERVER_ERROR:
883
890
                break;
884
891
        default:
885
 
                set_server_error (error, soup_msg);
886
 
 
887
 
                gupnp_service_proxy_action_free (action);
 
892
                set_server_error (error, action->msg);
888
893
 
889
894
                return NULL;
890
895
        }
891
896
 
892
897
        /* Parse response */
893
 
        response = soup_soap_message_parse_response (action->msg);
 
898
        response = xmlParseMemory (action->msg->response_body->data,
 
899
                                   action->msg->response_body->length);
 
900
 
894
901
        if (!response) {
895
 
                if (soup_msg->status_code == SOUP_STATUS_OK) {
 
902
                if (action->msg->status_code == SOUP_STATUS_OK) {
896
903
                        g_set_error (error,
897
904
                                     GUPNP_SERVER_ERROR,
898
905
                                     GUPNP_SERVER_ERROR_INVALID_RESPONSE,
902
909
                                    (error,
903
910
                                     GUPNP_SERVER_ERROR,
904
911
                                     GUPNP_SERVER_ERROR_INTERNAL_SERVER_ERROR,
905
 
                                     soup_msg->reason_phrase);
 
912
                                     action->msg->reason_phrase);
906
913
                }
907
914
 
908
 
                gupnp_service_proxy_action_free (action);
909
 
 
910
915
                return NULL;
911
916
        }
912
917
 
 
918
        /* Get parameter list */
 
919
        *params = xml_util_get_element ((xmlNode *) response,
 
920
                                        "Envelope",
 
921
                                        NULL);
 
922
        if (*params != NULL)
 
923
                *params = xml_util_real_node ((*params)->children);
 
924
 
 
925
        if (*params != NULL) {
 
926
                if (strcmp ((const char *) (*params)->name, "Header") == 0)
 
927
                        *params = xml_util_real_node ((*params)->next);
 
928
 
 
929
                if (*params != NULL)
 
930
                        if (strcmp ((const char *) (*params)->name, "Body") != 0)
 
931
                                *params = NULL;
 
932
        }
 
933
 
 
934
        if (*params != NULL)
 
935
                *params = xml_util_real_node ((*params)->children);
 
936
 
 
937
        if (*params == NULL) {
 
938
                g_set_error (error,
 
939
                             GUPNP_SERVER_ERROR,
 
940
                             GUPNP_SERVER_ERROR_INVALID_RESPONSE,
 
941
                             "Invalid Envelope");
 
942
 
 
943
                xmlFreeDoc (response);
 
944
 
 
945
                return NULL;
 
946
        }
 
947
 
913
948
        /* Check whether we have a Fault */
914
 
        if (soup_msg->status_code == SOUP_STATUS_INTERNAL_SERVER_ERROR) {
915
 
                SoupSoapParameter *child;
 
949
        if (action->msg->status_code == SOUP_STATUS_INTERNAL_SERVER_ERROR) {
 
950
                xmlNode *param;
916
951
                char *desc;
917
952
 
918
 
                param = soup_soap_response_get_first_parameter_by_name
919
 
                                                        (response, "detail");
920
 
                if (param) {
921
 
                        param = soup_soap_parameter_get_first_child_by_name
922
 
                                                        (param, "UPnPError");
923
 
                }
 
953
                param = xml_util_get_element (*params,
 
954
                                              "detail",
 
955
                                              "UPnPError",
 
956
                                              NULL);
924
957
 
925
958
                if (!param) {
926
959
                        g_set_error (error,
928
961
                                     GUPNP_SERVER_ERROR_INVALID_RESPONSE,
929
962
                                     "Invalid Fault");
930
963
 
931
 
                        gupnp_service_proxy_action_free (action);
932
 
 
933
 
                        g_object_unref (response);
 
964
                        xmlFreeDoc (response);
934
965
 
935
966
                        return NULL;
936
967
                }
937
968
 
938
969
                /* Code */
939
 
                child = soup_soap_parameter_get_first_child_by_name
940
 
                                                (param, "errorCode");
941
 
                if (child)
942
 
                        code = soup_soap_parameter_get_int_value (child);
943
 
                else {
 
970
                code = xml_util_get_child_element_content_int
 
971
                                        (param, "errorCode");
 
972
                if (code == -1) {
944
973
                        g_set_error (error,
945
974
                                     GUPNP_SERVER_ERROR,
946
975
                                     GUPNP_SERVER_ERROR_INVALID_RESPONSE,
947
976
                                     "Invalid Fault");
948
977
 
949
 
                        gupnp_service_proxy_action_free (action);
950
 
 
951
 
                        g_object_unref (response);
 
978
                        xmlFreeDoc (response);
952
979
 
953
980
                        return NULL;
954
981
                }
955
982
 
956
983
                /* Description */
957
 
                child = soup_soap_parameter_get_first_child_by_name
958
 
                                                (param, "errorDescription");
959
 
                if (child)
960
 
                        desc = soup_soap_parameter_get_string_value (child);
961
 
                else
962
 
                        desc = g_strdup (soup_msg->reason_phrase);
 
984
                desc = xml_util_get_child_element_content_glib
 
985
                                        (param, "errorDescription");
 
986
                if (desc == NULL)
 
987
                        desc = g_strdup (action->msg->reason_phrase);
963
988
 
964
989
                set_error_literal (error,
965
990
                                   GUPNP_CONTROL_ERROR,
968
993
 
969
994
                g_free (desc);
970
995
 
971
 
                /* Cleanup */
972
 
                gupnp_service_proxy_action_free (action);
973
 
 
974
 
                g_object_unref (response);
 
996
                xmlFreeDoc (response);
975
997
 
976
998
                return NULL;
977
999
        }
982
1004
/* Reads a value into the parameter name and initialised GValue pair
983
1005
 * from @response */
984
1006
static void
985
 
read_out_parameter (const char       *arg_name,
986
 
                    GValue           *value,
987
 
                    SoupSoapResponse *response)
 
1007
read_out_parameter (const char *arg_name,
 
1008
                    GValue     *value,
 
1009
                    xmlNode    *params)
988
1010
{
989
 
        SoupSoapParameter *param;
990
 
        char *str;
 
1011
        xmlNode *param;
991
1012
 
992
 
        /* Try to find a matching paramater in the response */
993
 
        param = soup_soap_response_get_first_parameter_by_name
994
 
                                                (response, arg_name);
 
1013
        /* Try to find a matching paramater in the response*/
 
1014
        param = xml_util_get_element (params,
 
1015
                                      arg_name,
 
1016
                                      NULL);
995
1017
        if (!param) {
996
1018
                g_warning ("Could not find variable \"%s\" in response",
997
1019
                           arg_name);
999
1021
                return;
1000
1022
        }
1001
1023
 
1002
 
        /* Parse into @value */
1003
 
        str = soup_soap_parameter_get_string_value (param);
1004
 
 
1005
 
        gvalue_util_set_value_from_string (value, str);
1006
 
 
1007
 
        g_free (str);
1008
 
 
1009
 
        return;
 
1024
        gvalue_util_set_value_from_xml_node (value, param);
1010
1025
}
1011
1026
 
1012
1027
/**
1013
1028
 * gupnp_service_proxy_end_action_valist
1014
1029
 * @proxy: A #GUPnPServiceProxy
1015
1030
 * @action: A #GUPnPServiceProxyAction handle
1016
 
 * @error: The location where to store any error, or NULL
 
1031
 * @error: The location where to store any error, or %NULL
1017
1032
 * @var_args: A va_list of tuples of out parameter name, out paramater type,
1018
1033
 * and out parameter value location. The out parameter values should be
1019
1034
 * freed after use
1021
1036
 * See gupnp_service_proxy_end_action(); this version takes a va_list for
1022
1037
 * use by language bindings.
1023
1038
 *
1024
 
 * Return value: TRUE on success.
 
1039
 * Return value: %TRUE on success.
1025
1040
 **/
1026
1041
gboolean
1027
1042
gupnp_service_proxy_end_action_valist (GUPnPServiceProxy       *proxy,
1029
1044
                                       GError                 **error,
1030
1045
                                       va_list                  var_args)
1031
1046
{
1032
 
        SoupSoapResponse *response;
 
1047
        xmlDoc *response;
 
1048
        xmlNode *params;
1033
1049
        const char *arg_name;
1034
1050
 
1035
1051
        g_return_val_if_fail (GUPNP_IS_SERVICE_PROXY (proxy), FALSE);
1036
1052
        g_return_val_if_fail (action, FALSE);
1037
1053
 
 
1054
        /* Check for saved error from begin_action() */
 
1055
        if (action->error) {
 
1056
                if (error)
 
1057
                        *error = action->error;
 
1058
                else
 
1059
                        g_error_free (action->error);
 
1060
 
 
1061
                gupnp_service_proxy_action_free (action);
 
1062
 
 
1063
                return FALSE;
 
1064
        }
 
1065
 
1038
1066
        /* Check response for errors and do initial parsing */
1039
 
        response = check_action_response (proxy, action, error);
1040
 
        if (response == NULL)
 
1067
        response = check_action_response (proxy, action, &params, error);
 
1068
        if (response == NULL) {
 
1069
                gupnp_service_proxy_action_free (action);
 
1070
 
1041
1071
                return FALSE;
 
1072
        }
1042
1073
 
1043
1074
        /* Arguments */
1044
1075
        arg_name = va_arg (var_args, const char *);
1051
1082
 
1052
1083
                g_value_init (&value, arg_type);
1053
1084
 
1054
 
                read_out_parameter (arg_name, &value, response);
 
1085
                read_out_parameter (arg_name, &value, params);
1055
1086
 
1056
1087
                G_VALUE_LCOPY (&value, var_args, 0, &copy_error);
1057
1088
 
1069
1100
        /* Cleanup */
1070
1101
        gupnp_service_proxy_action_free (action);
1071
1102
 
1072
 
        g_object_unref (response);
 
1103
        xmlFreeDoc (response);
1073
1104
 
1074
1105
        return TRUE;
1075
1106
}
1078
1109
 * gupnp_service_proxy_end_action_hash
1079
1110
 * @proxy: A #GUPnPServiceProxy
1080
1111
 * @action: A #GUPnPServiceProxyAction handle
1081
 
 * @error: The location where to store any error, or NULL
 
1112
 * @error: The location where to store any error, or %NULL
1082
1113
 * @hash: A #GHashTable of out parameter name and initialised #GValue pairs
1083
1114
 *
1084
1115
 * See gupnp_service_proxy_end_action(); this version takes a #GHashTable
1085
1116
 * for runtime generated parameter lists.
1086
1117
 *
1087
 
 * Return value: TRUE on success.
 
1118
 * Return value: %TRUE on success.
1088
1119
 **/
1089
1120
gboolean
1090
1121
gupnp_service_proxy_end_action_hash (GUPnPServiceProxy       *proxy,
1092
1123
                                     GError                 **error,
1093
1124
                                     GHashTable              *hash)
1094
1125
{
1095
 
        SoupSoapResponse *response;
 
1126
        xmlDoc *response;
 
1127
        xmlNode *params;
1096
1128
 
1097
1129
        g_return_val_if_fail (GUPNP_IS_SERVICE_PROXY (proxy), FALSE);
1098
1130
        g_return_val_if_fail (action, FALSE);
1099
1131
 
 
1132
        /* Check for saved error from begin_action() */
 
1133
        if (action->error) {
 
1134
                if (error)
 
1135
                        *error = action->error;
 
1136
                else
 
1137
                        g_error_free (action->error);
 
1138
 
 
1139
                gupnp_service_proxy_action_free (action);
 
1140
 
 
1141
                return FALSE;
 
1142
        }
 
1143
 
1100
1144
        /* Check response for errors and do initial parsing */
1101
 
        response = check_action_response (proxy, action, error);
1102
 
        if (response == NULL)
 
1145
        response = check_action_response (proxy, action, &params, error);
 
1146
        if (response == NULL) {
 
1147
                gupnp_service_proxy_action_free (action);
 
1148
 
1103
1149
                return FALSE;
 
1150
        }
1104
1151
 
1105
1152
        /* Read arguments */
1106
 
        g_hash_table_foreach (hash, (GHFunc) read_out_parameter, response);
 
1153
        g_hash_table_foreach (hash, (GHFunc) read_out_parameter, params);
1107
1154
 
1108
1155
        /* Cleanup */
1109
1156
        gupnp_service_proxy_action_free (action);
1110
1157
 
1111
 
        g_object_unref (response);
 
1158
        xmlFreeDoc (response);
1112
1159
 
1113
1160
        return TRUE;
1114
1161
}
1130
1177
        g_return_if_fail (GUPNP_IS_SERVICE_PROXY (proxy));
1131
1178
        g_return_if_fail (action);
1132
1179
 
1133
 
        context = gupnp_service_info_get_context (GUPNP_SERVICE_INFO (proxy));
1134
 
        session = _gupnp_context_get_session (context);
1135
 
 
1136
 
        soup_message_set_status (SOUP_MESSAGE (action->msg),
1137
 
                                 SOUP_STATUS_CANCELLED);
1138
 
        soup_session_cancel_message (session, SOUP_MESSAGE (action->msg));
 
1180
        if (action->msg != NULL) {
 
1181
                context = gupnp_service_info_get_context
 
1182
                                        (GUPNP_SERVICE_INFO (proxy));
 
1183
                session = _gupnp_context_get_session (context);
 
1184
 
 
1185
                soup_session_cancel_message (session,
 
1186
                                             action->msg,
 
1187
                                             SOUP_STATUS_CANCELLED);
 
1188
        }
 
1189
 
 
1190
        if (action->error != NULL)
 
1191
                g_error_free (action->error);
1139
1192
 
1140
1193
        gupnp_service_proxy_action_free (action);
1141
1194
}
1151
1204
 * Sets up @callback to be called whenever a change notification for
1152
1205
 * @variable is recieved.
1153
1206
 *
1154
 
 * Return value: TRUE on success.
 
1207
 * Return value: %TRUE on success.
1155
1208
 **/
1156
1209
gboolean
1157
1210
gupnp_service_proxy_add_notify (GUPnPServiceProxy              *proxy,
1213
1266
 *
1214
1267
 * Cancels the variable change notification for @callback and @user_data.
1215
1268
 *
1216
 
 * Return value: TRUE on success.
 
1269
 * Return value: %TRUE on success.
1217
1270
 **/
1218
1271
gboolean
1219
1272
gupnp_service_proxy_remove_notify (GUPnPServiceProxy              *proxy,
1271
1324
        return found;
1272
1325
}
1273
1326
 
1274
 
/**
 
1327
/* Emit pending notifications. See comment below on why we do this. */
 
1328
static gboolean
 
1329
emit_notifications (gpointer user_data)
 
1330
{
 
1331
        GUPnPServiceProxy *proxy = user_data;
 
1332
 
 
1333
        g_assert (user_data);
 
1334
        
 
1335
        while (proxy->priv->pending_notifies != NULL) {
 
1336
                xmlDoc *doc;
 
1337
                xmlNode *node;
 
1338
 
 
1339
                doc = proxy->priv->pending_notifies->data;
 
1340
                node = xmlDocGetRootElement (doc);
 
1341
                
 
1342
                /* Iterate over all provided properties */
 
1343
                for (node = node->children; node; node = node->next) {
 
1344
                        xmlNode *var_node;
 
1345
                        
 
1346
                        if (strcmp ((char *) node->name, "property") != 0)
 
1347
                                continue;
 
1348
                        
 
1349
                        /* property */
 
1350
                        for (var_node = node->children; var_node;
 
1351
                             var_node = var_node->next) {
 
1352
                                NotifyData *data;
 
1353
                                GValue value = {0, };
 
1354
                                GList *l;
 
1355
                                
 
1356
                                data = g_hash_table_lookup
 
1357
                                        (proxy->priv->notify_hash,
 
1358
                                         var_node->name);
 
1359
                                if (data == NULL)
 
1360
                                        continue;
 
1361
                                
 
1362
                                /* Make a GValue of the desired type */
 
1363
                                g_value_init (&value, data->type);
 
1364
                                
 
1365
                                if (!gvalue_util_set_value_from_xml_node
 
1366
                                                        (&value, var_node)) {
 
1367
                                        g_value_unset (&value);
 
1368
                                        
 
1369
                                        continue;
 
1370
                                }
 
1371
                                
 
1372
                                /* Call callbacks */
 
1373
                                for (l = data->callbacks; l; l = l->next) {
 
1374
                                        CallbackData *callback_data;
 
1375
                                        
 
1376
                                        callback_data = l->data;
 
1377
                                        
 
1378
                                        callback_data->callback
 
1379
                                                (proxy,
 
1380
                                                 (const char *) var_node->name,
 
1381
                                                 &value,
 
1382
                                                 callback_data->user_data);
 
1383
                                }
 
1384
                                
 
1385
                                /* Cleanup */
 
1386
                                g_value_unset (&value);
 
1387
                        }
 
1388
                }
 
1389
                
 
1390
                /* Cleanup */
 
1391
                xmlFreeDoc (doc);
 
1392
 
 
1393
                proxy->priv->pending_notifies =
 
1394
                        g_list_delete_link (proxy->priv->pending_notifies,
 
1395
                                            proxy->priv->pending_notifies);
 
1396
        }
 
1397
        
 
1398
        proxy->priv->notify_idle_id = 0;
 
1399
 
 
1400
        return FALSE;
 
1401
}
 
1402
 
 
1403
/*
1275
1404
 * HTTP server received a message. Handle, if this was a NOTIFY
1276
1405
 * message with our SID.
1277
 
 **/
 
1406
 */
1278
1407
static void
1279
 
server_handler (SoupServerContext *server_context,
1280
 
                SoupMessage       *msg,
 
1408
server_handler (SoupServer        *soup_server,
 
1409
                SoupMessage       *msg, 
 
1410
                const char        *server_path,
 
1411
                GHashTable        *query,
 
1412
                SoupClientContext *soup_client,
1281
1413
                gpointer           user_data)
1282
1414
{
1283
1415
        GUPnPServiceProxy *proxy;
1295
1427
                return;
1296
1428
        }
1297
1429
 
1298
 
        hdr = soup_message_get_header (msg->request_headers, "NT");
 
1430
        hdr = soup_message_headers_get (msg->request_headers, "NT");
1299
1431
        if (hdr == NULL || strcmp (hdr, "upnp:event") != 0) {
1300
1432
                /* Proper NT header lacking */
1301
1433
                soup_message_set_status (msg, SOUP_STATUS_PRECONDITION_FAILED);
1303
1435
                return;
1304
1436
        }
1305
1437
 
1306
 
        hdr = soup_message_get_header (msg->request_headers, "NTS");
 
1438
        hdr = soup_message_headers_get (msg->request_headers, "NTS");
1307
1439
        if (hdr == NULL || strcmp (hdr, "upnp:propchange") != 0) {
1308
1440
                /* Proper NTS header lacking */
1309
1441
                soup_message_set_status (msg, SOUP_STATUS_PRECONDITION_FAILED);
1311
1443
                return;
1312
1444
        }
1313
1445
 
1314
 
        hdr = soup_message_get_header (msg->request_headers, "SID");
 
1446
        hdr = soup_message_headers_get (msg->request_headers, "SID");
1315
1447
        if (hdr == NULL ||
1316
1448
            (proxy->priv->sid && (strcmp (hdr, proxy->priv->sid) != 0))) {
1317
1449
                /* No SID or not ours */
1320
1452
                return;
1321
1453
        }
1322
1454
 
 
1455
        /* We do not error out if proxy->priv->sid is NIL as the subscription
 
1456
         * response may not have been processed yet. */
 
1457
#if 0
1323
1458
        if (!proxy->priv->sid) {
1324
1459
                GUPnPContext *context;
1325
1460
                GMainContext *main_context;
1341
1476
                        return;
1342
1477
                }
1343
1478
        }
 
1479
#endif
1344
1480
 
1345
 
        hdr = soup_message_get_header (msg->request_headers, "SEQ");
 
1481
        hdr = soup_message_headers_get (msg->request_headers, "SEQ");
1346
1482
        if (hdr == NULL) {
1347
1483
                /* No SEQ header */
1348
1484
                soup_message_set_status (msg, SOUP_STATUS_PRECONDITION_FAILED);
1369
1505
                proxy->priv->seq = 1;
1370
1506
 
1371
1507
        /* Parse the actual XML message content */
1372
 
        doc = xmlParseMemory (msg->request.body,
1373
 
                              msg->request.length);
 
1508
        doc = xmlParseMemory (msg->request_body->data,
 
1509
                              msg->request_body->length);
1374
1510
        if (doc == NULL) {
1375
1511
                /* Failed */
1376
1512
                g_warning ("Failed to parse NOTIFY message body");
1391
1527
 
1392
1528
                return;
1393
1529
        }
1394
 
 
1395
 
        /* Iterate over all provided properties */
1396
 
        for (node = node->children; node; node = node->next) {
1397
 
                xmlNode *var_node;
1398
 
 
1399
 
                if (strcmp ((char *) node->name, "property") != 0)
1400
 
                        continue;
1401
 
 
1402
 
                /* property */
1403
 
                for (var_node = node->children; var_node;
1404
 
                     var_node = var_node->next) {
1405
 
                        NotifyData *data;
1406
 
                        GValue value = {0, };
1407
 
                        GList *l;
1408
 
 
1409
 
                        data = g_hash_table_lookup (proxy->priv->notify_hash,
1410
 
                                                    var_node->name);
1411
 
                        if (data == NULL)
1412
 
                                continue;
1413
 
 
1414
 
                        /* Make a GValue of the desired type */
1415
 
                        g_value_init (&value, data->type);
1416
 
 
1417
 
                        if (!xml_util_node_get_content_value (var_node,
1418
 
                                                              &value)) {
1419
 
                                g_value_unset (&value);
1420
 
 
1421
 
                                continue;
1422
 
                        }
1423
 
 
1424
 
                        /* Call callbacks */
1425
 
                        for (l = data->callbacks; l; l = l->next) {
1426
 
                                CallbackData *callback_data;
1427
 
 
1428
 
                                callback_data = l->data;
1429
 
 
1430
 
                                callback_data->callback
1431
 
                                        (proxy,
1432
 
                                         (const char *) var_node->name,
1433
 
                                         &value,
1434
 
                                         callback_data->user_data);
1435
 
                        }
1436
 
 
1437
 
                        /* Cleanup */
1438
 
                        g_value_unset (&value);
1439
 
                }
1440
 
        }
1441
 
 
1442
 
        xmlFreeDoc (doc);
1443
 
 
 
1530
        
 
1531
        /*
 
1532
         * Some UPnP stacks (hello, myigd/1.0) block when sending a NOTIFY, so
 
1533
         * call the callbacks in an idle handler so that if the client calls the
 
1534
         * device in the notify callback the server can actually respond.
 
1535
         */
 
1536
        proxy->priv->pending_notifies =
 
1537
                g_list_append (proxy->priv->pending_notifies, doc);
 
1538
        if (!proxy->priv->notify_idle_id)
 
1539
                proxy->priv->notify_idle_id =
 
1540
                        g_idle_add (emit_notifications, proxy);
 
1541
        
1444
1542
        /* Everything went OK */
1445
1543
        soup_message_set_status (msg, SOUP_STATUS_OK);
1446
1544
}
1447
1545
 
1448
 
/**
 
1546
/*
1449
1547
 * Generates a timeout header for the subscription timeout specified
1450
1548
 * in our GUPnPContext.
1451
 
 **/
 
1549
 */
1452
1550
static char *
1453
1551
make_timeout_header (GUPnPContext *context)
1454
1552
{
1461
1559
                return g_strdup ("infinite");
1462
1560
}
1463
1561
 
1464
 
/**
 
1562
/*
1465
1563
 * Subscription expired.
1466
 
 **/
 
1564
 */
1467
1565
static gboolean
1468
1566
subscription_expire (gpointer user_data)
1469
1567
{
1492
1590
        g_assert (msg != NULL);
1493
1591
 
1494
1592
        /* Add headers */
1495
 
        soup_message_add_header (msg->request_headers,
1496
 
                                 "SID",
1497
 
                                 proxy->priv->sid);
 
1593
        soup_message_headers_append (msg->request_headers,
 
1594
                                    "SID",
 
1595
                                    proxy->priv->sid);
1498
1596
 
1499
1597
        timeout = make_timeout_header (context);
1500
 
        soup_message_add_header (msg->request_headers,
1501
 
                                 "Timeout",
1502
 
                                 timeout);
 
1598
        soup_message_headers_append (msg->request_headers,
 
1599
                                     "Timeout",
 
1600
                                     timeout);
1503
1601
        g_free (timeout);
1504
1602
 
1505
1603
        /* And send it off */
1506
 
        proxy->priv->pending_messages = 
 
1604
        proxy->priv->pending_messages =
1507
1605
                g_list_prepend (proxy->priv->pending_messages, msg);
1508
1606
 
1509
1607
        session = _gupnp_context_get_session (context);
1510
1608
 
1511
1609
        soup_session_queue_message (session,
1512
1610
                                    msg,
1513
 
                                    (SoupMessageCallbackFn)
 
1611
                                    (SoupSessionCallback)
1514
1612
                                        subscribe_got_response,
1515
1613
                                    proxy);
1516
1614
 
1517
1615
        return FALSE;
1518
1616
}
1519
1617
 
1520
 
/**
 
1618
/*
1521
1619
 * Received subscription response.
1522
 
 **/
 
1620
 */
1523
1621
static void
1524
 
subscribe_got_response (SoupMessage       *msg,
 
1622
subscribe_got_response (SoupSession       *session,
 
1623
                        SoupMessage       *msg,
1525
1624
                        GUPnPServiceProxy *proxy)
1526
1625
{
1527
1626
        GError *error;
1531
1630
                return;
1532
1631
 
1533
1632
        /* Remove from pending messages list */
1534
 
        proxy->priv->pending_messages = 
 
1633
        proxy->priv->pending_messages =
1535
1634
                g_list_remove (proxy->priv->pending_messages, msg);
1536
1635
 
1537
1636
        /* Check whether the subscription is still wanted */
1549
1648
                int timeout;
1550
1649
 
1551
1650
                /* Save SID. */
1552
 
                hdr = soup_message_get_header (msg->response_headers, "SID");
 
1651
                hdr = soup_message_headers_get (msg->response_headers, "SID");
1553
1652
                if (hdr == NULL) {
1554
1653
                        error = g_error_new
1555
1654
                                        (GUPNP_EVENTING_ERROR,
1562
1661
                proxy->priv->sid = g_strdup (hdr);
1563
1662
 
1564
1663
                /* Figure out when the subscription times out */
1565
 
                hdr = soup_message_get_header (msg->response_headers,
1566
 
                                               "Timeout");
 
1664
                hdr = soup_message_headers_get (msg->response_headers,
 
1665
                                                "Timeout");
1567
1666
                if (hdr == NULL) {
1568
1667
                        g_warning ("No Timeout in SUBSCRIBE response.");
1569
1668
 
1573
1672
                if (strncmp (hdr, "Second-", strlen ("Second-")) == 0) {
1574
1673
                        /* We have a finite timeout */
1575
1674
                        timeout = atoi (hdr + strlen ("Second-"));
 
1675
 
 
1676
                        /* We want to resubscribe before the subscription
 
1677
                         * expires. */
 
1678
                        timeout -= GENA_TIMEOUT_DELTA;
 
1679
 
1576
1680
                        if (timeout < 0) {
1577
1681
                                g_warning ("Invalid time-out specified. "
1578
1682
                                           "Assuming default value of %d.",
1583
1687
 
1584
1688
                        /* Add actual timeout */
1585
1689
                        proxy->priv->subscription_timeout_id =
1586
 
                                g_timeout_add (timeout * 1000,
1587
 
                                               subscription_expire,
1588
 
                                               proxy);
 
1690
                                g_timeout_add_seconds (timeout,
 
1691
                                                       subscription_expire,
 
1692
                                                       proxy);
1589
1693
                }
1590
1694
        } else {
1591
1695
                GUPnPContext *context;
1613
1717
                context = gupnp_service_info_get_context
1614
1718
                                        (GUPNP_SERVICE_INFO (proxy));
1615
1719
 
1616
 
                server = _gupnp_context_get_server (context);
 
1720
                server = gupnp_context_get_server (context);
1617
1721
                soup_server_remove_handler (server, proxy->priv->path);
1618
1722
        }
1619
1723
}
1620
1724
 
1621
 
/**
 
1725
/*
1622
1726
 * Subscribe to this service.
1623
 
 **/
 
1727
 */
1624
1728
static void
1625
1729
subscribe (GUPnPServiceProxy *proxy)
1626
1730
{
1672
1776
        delivery_url = g_strdup_printf ("<%s%s>",
1673
1777
                                        server_url,
1674
1778
                                        proxy->priv->path);
1675
 
        soup_message_add_header (msg->request_headers,
1676
 
                                 "Callback",
1677
 
                                 delivery_url);
 
1779
        soup_message_headers_append (msg->request_headers,
 
1780
                                     "Callback",
 
1781
                                     delivery_url);
1678
1782
        g_free (delivery_url);
1679
1783
 
1680
 
        soup_message_add_header (msg->request_headers,
1681
 
                                 "NT",
1682
 
                                 "upnp:event");
 
1784
        soup_message_headers_append (msg->request_headers,
 
1785
                                     "NT",
 
1786
                                     "upnp:event");
1683
1787
 
1684
1788
        timeout = make_timeout_header (context);
1685
 
        soup_message_add_header (msg->request_headers,
1686
 
                                 "Timeout",
1687
 
                                 timeout);
 
1789
        soup_message_headers_append (msg->request_headers,
 
1790
                                     "Timeout",
 
1791
                                     timeout);
1688
1792
        g_free (timeout);
1689
1793
 
1690
1794
        /* Listen for events */
1691
 
        server = _gupnp_context_get_server (context);
 
1795
        server = gupnp_context_get_server (context);
1692
1796
 
1693
1797
        soup_server_add_handler (server,
1694
1798
                                 proxy->priv->path,
1695
 
                                 NULL,
1696
1799
                                 server_handler,
1697
 
                                 NULL,
1698
 
                                 proxy);
 
1800
                                 proxy,
 
1801
                                 NULL);
1699
1802
 
1700
1803
        /* And send our subscription message off */
1701
 
        proxy->priv->pending_messages = 
 
1804
        proxy->priv->pending_messages =
1702
1805
                g_list_prepend (proxy->priv->pending_messages, msg);
1703
1806
 
1704
1807
        session = _gupnp_context_get_session (context);
1705
1808
 
1706
1809
        soup_session_queue_message (session,
1707
1810
                                    msg,
1708
 
                                    (SoupMessageCallbackFn)
 
1811
                                    (SoupSessionCallback)
1709
1812
                                        subscribe_got_response,
1710
1813
                                    proxy);
1711
1814
}
1712
1815
 
1713
 
/**
 
1816
/*
1714
1817
 * Unsubscribe from this service.
1715
 
 **/
 
1818
 */
1716
1819
static void
1717
1820
unsubscribe (GUPnPServiceProxy *proxy)
1718
1821
{
1738
1841
        g_assert (msg != NULL);
1739
1842
 
1740
1843
        /* Add headers */
1741
 
        soup_message_add_header (msg->request_headers,
1742
 
                                 "SID",
1743
 
                                 proxy->priv->sid);
 
1844
        soup_message_headers_append (msg->request_headers,
 
1845
                                     "SID",
 
1846
                                     proxy->priv->sid);
1744
1847
 
1745
1848
        /* And queue it */
1746
1849
        session = _gupnp_context_get_session (context);
1761
1864
        }
1762
1865
 
1763
1866
        /* Remove server handler */
1764
 
        server = _gupnp_context_get_server (context);
 
1867
        server = gupnp_context_get_server (context);
1765
1868
        soup_server_remove_handler (server, proxy->priv->path);
1766
1869
}
1767
1870
 
1768
1871
/**
1769
1872
 * gupnp_service_proxy_set_subscribed
1770
1873
 * @proxy: A #GUPnPServiceProxy
1771
 
 * @subscribed: TRUE to subscribe to this service
 
1874
 * @subscribed: %TRUE to subscribe to this service
1772
1875
 *
1773
1876
 * (Un)subscribes to this service.
1774
1877
 *
1800
1903
 * gupnp_service_proxy_get_subscribed
1801
1904
 * @proxy: A #GUPnPServiceProxy
1802
1905
 *
1803
 
 * Return value: TRUE if we are subscribed to this service.
 
1906
 * Returns if we are subscribed to this service.
 
1907
 *
 
1908
 * Return value: %TRUE if we are subscribed to this service, otherwise %FALSE.
1804
1909
 **/
1805
1910
gboolean
1806
1911
gupnp_service_proxy_get_subscribed (GUPnPServiceProxy *proxy)
1809
1914
 
1810
1915
        return proxy->priv->subscribed;
1811
1916
}
1812
 
 
1813
 
/**
1814
 
 * _gupnp_service_proxy_new
1815
 
 * @context: A #GUPnPContext
1816
 
 * @element: The #xmlNode ponting to the right service element
1817
 
 * @location: The location of the service description file
1818
 
 * @udn: The UDN of the device the service is contained in
1819
 
 * @service_type: The service type
1820
 
 * @url_base: The URL base for this service, or NULL if none
1821
 
 *
1822
 
 * Return value: A #GUPnPServiceProxy for the service with element @element, as
1823
 
 * read from the service description file specified by @location.
1824
 
 **/
1825
 
GUPnPServiceProxy *
1826
 
_gupnp_service_proxy_new (GUPnPContext  *context,
1827
 
                          XmlDocWrapper *doc,
1828
 
                          xmlNode       *element,
1829
 
                          const char    *udn,
1830
 
                          const char    *service_type,
1831
 
                          const char    *location,
1832
 
                          const SoupUri *url_base)
1833
 
{
1834
 
        GUPnPServiceProxy *proxy;
1835
 
 
1836
 
        g_return_val_if_fail (GUPNP_IS_CONTEXT (context), NULL);
1837
 
        g_return_val_if_fail (IS_XML_DOC_WRAPPER (doc), NULL);
1838
 
        g_return_val_if_fail (element != NULL, NULL);
1839
 
        g_return_val_if_fail (location != NULL, NULL);
1840
 
        g_return_val_if_fail (url_base != NULL, NULL);
1841
 
 
1842
 
        proxy = g_object_new (GUPNP_TYPE_SERVICE_PROXY,
1843
 
                              "context", context,
1844
 
                              "location", location,
1845
 
                              "udn", udn,
1846
 
                              "service-type", service_type,
1847
 
                              "url-base", url_base,
1848
 
                              "document", doc,
1849
 
                              "element", element,
1850
 
                              NULL);
1851
 
 
1852
 
        return proxy;
1853
 
}