28
28
* #GUPnPServiceInfo interface.
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>
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"
47
46
G_DEFINE_TYPE (GUPnPServiceProxy,
229
237
msg = proxy->priv->pending_messages->data;
231
soup_message_set_status (msg, SOUP_STATUS_CANCELLED);
232
soup_session_cancel_message (session, msg);
239
soup_session_cancel_message (session,
241
SOUP_STATUS_CANCELLED);
234
243
proxy->priv->pending_messages =
235
244
g_list_delete_link (proxy->priv->pending_messages,
236
245
proxy->priv->pending_messages);
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;
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);
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
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.
329
* Return value: TRUE if sending the action was succesful.
350
* Return value: %TRUE if sending the action was succesful.
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
367
388
* See gupnp_service_proxy_send_action(); this version takes a va_list for
368
389
* use by language bindings.
370
* Return value: TRUE if sending the action was succesful.
391
* Return value: %TRUE if sending the action was succesful.
373
394
gupnp_service_proxy_send_action_valist (GUPnPServiceProxy *proxy,
481
495
* @callback: The callback to call when sending the action has succeeded
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
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.
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().
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,
504
517
va_list var_args;
505
518
GUPnPServiceProxyAction *ret;
507
va_start (var_args, error);
520
va_start (var_args, user_data);
508
521
ret = gupnp_service_proxy_begin_action_valist (proxy,
514
526
va_end (var_args);
519
531
/* Begins a basic action message */
520
static SoupSoapMessage *
521
begin_action_msg (GUPnPServiceProxy *proxy,
532
static GUPnPServiceProxyAction *
533
begin_action_msg (GUPnPServiceProxy *proxy,
535
GUPnPServiceProxyActionCallback callback,
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;
542
/* Create action structure */
543
ret = g_slice_new (GUPnPServiceProxyAction);
547
ret->callback = callback;
548
ret->user_data = user_data;
554
proxy->priv->pending_actions =
555
g_list_prepend (proxy->priv->pending_actions, ret);
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");
529
568
/* Create message */
530
569
control_url = gupnp_service_info_get_control_url
531
570
(GUPNP_SERVICE_INFO (proxy));
534
572
if (control_url != NULL) {
535
msg = soup_soap_message_new (SOUP_METHOD_POST,
537
FALSE, NULL, NULL, NULL);
573
ret->msg = soup_message_new (SOUP_METHOD_POST, control_url);
539
575
g_free (control_url);
545
GUPNP_SERVER_ERROR_INVALID_URL,
546
"No valid control URL defined");
551
/* Specify language */
552
lang = accept_language_get_header ();
554
soup_message_add_header (SOUP_MESSAGE (msg)->request_headers,
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");
561
586
/* Specify action */
562
service_type = gupnp_service_info_get_service_type
563
(GUPNP_SERVICE_INFO (proxy));
564
if (service_type == NULL) {
567
GUPNP_SERVER_ERROR_OTHER,
568
"No service type defined");
570
g_object_unref (msg);
575
587
full_action = g_strdup_printf ("\"%s#%s\"", service_type, action);
576
soup_message_add_header (SOUP_MESSAGE (msg)->request_headers,
588
soup_message_headers_append (ret->msg->request_headers,
579
591
g_free (full_action);
582
soup_soap_message_start_envelope (msg);
583
soup_soap_message_start_body (msg);
586
soup_soap_message_start_element (msg,
593
/* Specify user agent and language */
594
http_request_set_user_agent (ret->msg);
595
http_request_set_accept_language (ret->msg);
597
/* Set up envelope */
598
ret->msg_str = xml_util_new_string ();
600
g_string_append (ret->msg_str,
601
"<?xml version=\"1.0\"?>"
602
"<s:Envelope xmlns:s="
603
"\"http://schemas.xmlsoap.org/soap/envelope/\" "
605
"\"http://schemas.xmlsoap.org/soap/encoding/\">"
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, "\">");
594
617
/* Received response to action message */
596
action_got_response (SoupMessage *msg,
619
action_got_response (SoupSession *session,
597
621
GUPnPServiceProxyAction *action)
599
623
const char *full_action;
600
GUPnPContext *context;
601
SoupSession *session;
603
625
switch (msg->status_code) {
604
626
case SOUP_STATUS_CANCELLED:
609
631
/* Retry with M-POST */
610
632
msg->method = "M-POST";
612
soup_message_add_header
634
soup_message_headers_append
613
635
(msg->request_headers,
615
637
"\"http://schemas.xmlsoap.org/soap/envelope/\"; ns=s");
617
639
/* Rename "SOAPAction" to "s-SOAPAction" */
618
full_action = soup_message_get_header (msg->request_headers,
620
soup_message_add_header (msg->request_headers,
623
soup_message_remove_header (msg->request_headers,
640
full_action = soup_message_headers_get (msg->request_headers,
642
soup_message_headers_append (msg->request_headers,
645
soup_message_headers_remove (msg->request_headers,
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);
631
649
soup_session_requeue_message (session, msg);
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,
663
finish_action_msg (GUPnPServiceProxyAction *action,
664
const char *action_name)
650
GUPnPServiceProxyAction *ret;
651
666
GUPnPContext *context;
652
667
SoupSession *session;
654
669
/* Finish message */
655
soup_soap_message_end_element (msg);
657
soup_soap_message_end_body (msg);
658
soup_soap_message_end_envelope (msg);
659
soup_soap_message_persist (msg);
661
/* Create action structure */
662
ret = g_slice_new (GUPnPServiceProxyAction);
668
ret->callback = callback;
669
ret->user_data = user_data;
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, '>');
674
g_string_append (action->msg_str,
678
soup_message_set_request (action->msg,
681
action->msg_str->str,
682
action->msg_str->len);
684
g_string_free (action->msg_str, FALSE);
674
686
/* We need to keep our own reference to the message as well,
675
687
* in order for send_action() to work. */
688
g_object_ref (action->msg);
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);
682
695
soup_session_queue_message (session,
684
(SoupMessageCallbackFn) action_got_response,
697
(SoupSessionCallback) action_got_response,
690
701
/* Writes a parameter name and GValue pair to @msg */
692
write_in_parameter (const char *arg_name,
694
SoupSoapMessage *msg)
703
write_in_parameter (const char *arg_name,
698
soup_soap_message_start_element (msg,
703
str = gvalue_util_value_get_string (value);
704
soup_soap_message_write_string (msg, str);
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);
714
717
* @callback: The callback to call when sending the action has succeeded
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
721
723
* See gupnp_service_proxy_begin_action(); this version takes a va_list for
722
724
* use by language bindings.
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().
727
730
GUPnPServiceProxyAction *
728
731
gupnp_service_proxy_begin_action_valist
792
797
* @callback: The callback to call when sending the action has succeeded
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
798
802
* See gupnp_service_proxy_begin_action(); this version takes a #GHashTable
799
803
* for runtime generated parameter lists.
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().
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,
811
815
GHashTable *hash)
813
SoupSoapMessage *msg;
817
GUPnPServiceProxyAction *ret;
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);
819
823
/* Create message */
820
msg = begin_action_msg (proxy, action, error);
824
ret = begin_action_msg (proxy, action, callback, user_data);
827
callback (proxy, ret, user_data);
825
g_hash_table_foreach (hash, (GHFunc) write_in_parameter, msg);
833
g_hash_table_foreach (hash, (GHFunc) write_in_parameter, ret->msg_str);
827
835
/* Finish and send off */
828
return finish_action_msg (proxy, msg, callback, user_data);
836
finish_action_msg (ret, action);
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
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.
844
* Return value: TRUE on success.
854
* Return value: %TRUE on success.
847
857
gupnp_service_proxy_end_action (GUPnPServiceProxy *proxy,
865
875
/* Checks an action response for errors and returns the parsed
866
* SoupSoapResponse object. */
867
static SoupSoapResponse *
868
878
check_action_response (GUPnPServiceProxy *proxy,
869
879
GUPnPServiceProxyAction *action,
872
SoupMessage *soup_msg;
873
SoupSoapResponse *response;
874
SoupSoapParameter *param;
877
soup_msg = SOUP_MESSAGE (action->msg);
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:
885
set_server_error (error, soup_msg);
887
gupnp_service_proxy_action_free (action);
892
set_server_error (error, action->msg);
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);
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,
903
910
GUPNP_SERVER_ERROR,
904
911
GUPNP_SERVER_ERROR_INTERNAL_SERVER_ERROR,
905
soup_msg->reason_phrase);
912
action->msg->reason_phrase);
908
gupnp_service_proxy_action_free (action);
918
/* Get parameter list */
919
*params = xml_util_get_element ((xmlNode *) response,
923
*params = xml_util_real_node ((*params)->children);
925
if (*params != NULL) {
926
if (strcmp ((const char *) (*params)->name, "Header") == 0)
927
*params = xml_util_real_node ((*params)->next);
930
if (strcmp ((const char *) (*params)->name, "Body") != 0)
935
*params = xml_util_real_node ((*params)->children);
937
if (*params == NULL) {
940
GUPNP_SERVER_ERROR_INVALID_RESPONSE,
943
xmlFreeDoc (response);
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) {
918
param = soup_soap_response_get_first_parameter_by_name
919
(response, "detail");
921
param = soup_soap_parameter_get_first_child_by_name
922
(param, "UPnPError");
953
param = xml_util_get_element (*params,
926
959
g_set_error (error,
928
961
GUPNP_SERVER_ERROR_INVALID_RESPONSE,
929
962
"Invalid Fault");
931
gupnp_service_proxy_action_free (action);
933
g_object_unref (response);
964
xmlFreeDoc (response);
939
child = soup_soap_parameter_get_first_child_by_name
940
(param, "errorCode");
942
code = soup_soap_parameter_get_int_value (child);
970
code = xml_util_get_child_element_content_int
971
(param, "errorCode");
944
973
g_set_error (error,
945
974
GUPNP_SERVER_ERROR,
946
975
GUPNP_SERVER_ERROR_INVALID_RESPONSE,
947
976
"Invalid Fault");
949
gupnp_service_proxy_action_free (action);
951
g_object_unref (response);
978
xmlFreeDoc (response);
956
983
/* Description */
957
child = soup_soap_parameter_get_first_child_by_name
958
(param, "errorDescription");
960
desc = soup_soap_parameter_get_string_value (child);
962
desc = g_strdup (soup_msg->reason_phrase);
984
desc = xml_util_get_child_element_content_glib
985
(param, "errorDescription");
987
desc = g_strdup (action->msg->reason_phrase);
964
989
set_error_literal (error,
965
990
GUPNP_CONTROL_ERROR,
1002
/* Parse into @value */
1003
str = soup_soap_parameter_get_string_value (param);
1005
gvalue_util_set_value_from_string (value, str);
1024
gvalue_util_set_value_from_xml_node (value, param);
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
1029
1044
GError **error,
1030
1045
va_list var_args)
1032
SoupSoapResponse *response;
1033
1049
const char *arg_name;
1035
1051
g_return_val_if_fail (GUPNP_IS_SERVICE_PROXY (proxy), FALSE);
1036
1052
g_return_val_if_fail (action, FALSE);
1054
/* Check for saved error from begin_action() */
1055
if (action->error) {
1057
*error = action->error;
1059
g_error_free (action->error);
1061
gupnp_service_proxy_action_free (action);
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, ¶ms, error);
1068
if (response == NULL) {
1069
gupnp_service_proxy_action_free (action);
1043
1074
/* Arguments */
1044
1075
arg_name = va_arg (var_args, const char *);
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
1084
1115
* See gupnp_service_proxy_end_action(); this version takes a #GHashTable
1085
1116
* for runtime generated parameter lists.
1087
* Return value: TRUE on success.
1118
* Return value: %TRUE on success.
1090
1121
gupnp_service_proxy_end_action_hash (GUPnPServiceProxy *proxy,
1092
1123
GError **error,
1093
1124
GHashTable *hash)
1095
SoupSoapResponse *response;
1097
1129
g_return_val_if_fail (GUPNP_IS_SERVICE_PROXY (proxy), FALSE);
1098
1130
g_return_val_if_fail (action, FALSE);
1132
/* Check for saved error from begin_action() */
1133
if (action->error) {
1135
*error = action->error;
1137
g_error_free (action->error);
1139
gupnp_service_proxy_action_free (action);
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, ¶ms, error);
1146
if (response == NULL) {
1147
gupnp_service_proxy_action_free (action);
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);
1109
1156
gupnp_service_proxy_action_free (action);
1111
g_object_unref (response);
1158
xmlFreeDoc (response);
1130
1177
g_return_if_fail (GUPNP_IS_SERVICE_PROXY (proxy));
1131
1178
g_return_if_fail (action);
1133
context = gupnp_service_info_get_context (GUPNP_SERVICE_INFO (proxy));
1134
session = _gupnp_context_get_session (context);
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);
1185
soup_session_cancel_message (session,
1187
SOUP_STATUS_CANCELLED);
1190
if (action->error != NULL)
1191
g_error_free (action->error);
1140
1193
gupnp_service_proxy_action_free (action);
1327
/* Emit pending notifications. See comment below on why we do this. */
1329
emit_notifications (gpointer user_data)
1331
GUPnPServiceProxy *proxy = user_data;
1333
g_assert (user_data);
1335
while (proxy->priv->pending_notifies != NULL) {
1339
doc = proxy->priv->pending_notifies->data;
1340
node = xmlDocGetRootElement (doc);
1342
/* Iterate over all provided properties */
1343
for (node = node->children; node; node = node->next) {
1346
if (strcmp ((char *) node->name, "property") != 0)
1350
for (var_node = node->children; var_node;
1351
var_node = var_node->next) {
1353
GValue value = {0, };
1356
data = g_hash_table_lookup
1357
(proxy->priv->notify_hash,
1362
/* Make a GValue of the desired type */
1363
g_value_init (&value, data->type);
1365
if (!gvalue_util_set_value_from_xml_node
1366
(&value, var_node)) {
1367
g_value_unset (&value);
1372
/* Call callbacks */
1373
for (l = data->callbacks; l; l = l->next) {
1374
CallbackData *callback_data;
1376
callback_data = l->data;
1378
callback_data->callback
1380
(const char *) var_node->name,
1382
callback_data->user_data);
1386
g_value_unset (&value);
1393
proxy->priv->pending_notifies =
1394
g_list_delete_link (proxy->priv->pending_notifies,
1395
proxy->priv->pending_notifies);
1398
proxy->priv->notify_idle_id = 0;
1275
1404
* HTTP server received a message. Handle, if this was a NOTIFY
1276
1405
* message with our SID.
1279
server_handler (SoupServerContext *server_context,
1408
server_handler (SoupServer *soup_server,
1410
const char *server_path,
1412
SoupClientContext *soup_client,
1281
1413
gpointer user_data)
1283
1415
GUPnPServiceProxy *proxy;
1395
/* Iterate over all provided properties */
1396
for (node = node->children; node; node = node->next) {
1399
if (strcmp ((char *) node->name, "property") != 0)
1403
for (var_node = node->children; var_node;
1404
var_node = var_node->next) {
1406
GValue value = {0, };
1409
data = g_hash_table_lookup (proxy->priv->notify_hash,
1414
/* Make a GValue of the desired type */
1415
g_value_init (&value, data->type);
1417
if (!xml_util_node_get_content_value (var_node,
1419
g_value_unset (&value);
1424
/* Call callbacks */
1425
for (l = data->callbacks; l; l = l->next) {
1426
CallbackData *callback_data;
1428
callback_data = l->data;
1430
callback_data->callback
1432
(const char *) var_node->name,
1434
callback_data->user_data);
1438
g_value_unset (&value);
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.
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);
1444
1542
/* Everything went OK */
1445
1543
soup_message_set_status (msg, SOUP_STATUS_OK);
1449
1547
* Generates a timeout header for the subscription timeout specified
1450
1548
* in our GUPnPContext.
1453
1551
make_timeout_header (GUPnPContext *context)
1492
1590
g_assert (msg != NULL);
1494
1592
/* Add headers */
1495
soup_message_add_header (msg->request_headers,
1593
soup_message_headers_append (msg->request_headers,
1499
1597
timeout = make_timeout_header (context);
1500
soup_message_add_header (msg->request_headers,
1598
soup_message_headers_append (msg->request_headers,
1503
1601
g_free (timeout);
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);
1509
1607
session = _gupnp_context_get_session (context);
1511
1609
soup_session_queue_message (session,
1513
(SoupMessageCallbackFn)
1611
(SoupSessionCallback)
1514
1612
subscribe_got_response,
1521
1619
* Received subscription response.
1524
subscribe_got_response (SoupMessage *msg,
1622
subscribe_got_response (SoupSession *session,
1525
1624
GUPnPServiceProxy *proxy)
1672
1776
delivery_url = g_strdup_printf ("<%s%s>",
1674
1778
proxy->priv->path);
1675
soup_message_add_header (msg->request_headers,
1779
soup_message_headers_append (msg->request_headers,
1678
1782
g_free (delivery_url);
1680
soup_message_add_header (msg->request_headers,
1784
soup_message_headers_append (msg->request_headers,
1684
1788
timeout = make_timeout_header (context);
1685
soup_message_add_header (msg->request_headers,
1789
soup_message_headers_append (msg->request_headers,
1688
1792
g_free (timeout);
1690
1794
/* Listen for events */
1691
server = _gupnp_context_get_server (context);
1795
server = gupnp_context_get_server (context);
1693
1797
soup_server_add_handler (server,
1694
1798
proxy->priv->path,
1696
1799
server_handler,
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);
1704
1807
session = _gupnp_context_get_session (context);
1706
1809
soup_session_queue_message (session,
1708
(SoupMessageCallbackFn)
1811
(SoupSessionCallback)
1709
1812
subscribe_got_response,
1714
1817
* Unsubscribe from this service.
1717
1820
unsubscribe (GUPnPServiceProxy *proxy)
1810
1915
return proxy->priv->subscribed;
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
1822
* Return value: A #GUPnPServiceProxy for the service with element @element, as
1823
* read from the service description file specified by @location.
1826
_gupnp_service_proxy_new (GUPnPContext *context,
1830
const char *service_type,
1831
const char *location,
1832
const SoupUri *url_base)
1834
GUPnPServiceProxy *proxy;
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);
1842
proxy = g_object_new (GUPNP_TYPE_SERVICE_PROXY,
1844
"location", location,
1846
"service-type", service_type,
1847
"url-base", url_base,