1007 lines
33 KiB
C
1007 lines
33 KiB
C
/* $Id: discovery.c,v 1.75 2007/01/03 23:35:17 Exp $
|
|
*
|
|
* Lasso - A free implementation of the Liberty Alliance specifications.
|
|
*
|
|
* Copyright (C) 2004-2007 Entr'ouvert
|
|
* http://lasso.entrouvert.org
|
|
*
|
|
* Authors: See AUTHORS file in top-level directory.
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation; either version 2 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program; if not, write to the Free Software
|
|
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|
*/
|
|
|
|
/**
|
|
* SECTION:idwsf2_discovery
|
|
* @short_description: ID-WSF 2.0 Discovery Service profile
|
|
*
|
|
* The Discovery service usually runs on the principal identity provider and
|
|
* knowns about resources and services related to the principal. Attribute
|
|
* providers can register themselves as offering resources for an user while
|
|
* other services can ask where to find a given resource.
|
|
*/
|
|
|
|
#include "../xml/private.h"
|
|
#include <libxml/xpath.h>
|
|
#include <libxml/xpathInternals.h>
|
|
|
|
#include <xmlsec/xmltree.h>
|
|
|
|
#include "../xml/saml_attribute_value.h"
|
|
#include "../xml/xml_enc.h"
|
|
|
|
#include "../xml/saml-2.0/saml2_assertion.h"
|
|
#include "../xml/saml-2.0/samlp2_name_id_policy.h"
|
|
|
|
#include "../xml/id-wsf-2.0/disco_query.h"
|
|
#include "../xml/id-wsf-2.0/disco_requested_service.h"
|
|
#include "../xml/id-wsf-2.0/disco_svc_md_register.h"
|
|
#include "../xml/id-wsf-2.0/disco_svc_md_register_response.h"
|
|
#include "../xml/id-wsf-2.0/disco_svc_md_association_add.h"
|
|
#include "../xml/id-wsf-2.0/disco_svc_md_association_add_response.h"
|
|
#include "../xml/id-wsf-2.0/disco_svc_md_association_add_response.h"
|
|
#include "../xml/id-wsf-2.0/disco_abstract.h"
|
|
#include "../xml/id-wsf-2.0/disco_provider_id.h"
|
|
#include "../xml/id-wsf-2.0/disco_service_type.h"
|
|
#include "../xml/id-wsf-2.0/disco_security_context.h"
|
|
#include "../xml/id-wsf-2.0/disco_service_context.h"
|
|
#include "../xml/id-wsf-2.0/disco_endpoint_context.h"
|
|
#include "../xml/id-wsf-2.0/sec_token.h"
|
|
|
|
#include "../xml/ws/wsa_endpoint_reference.h"
|
|
|
|
#include "../id-ff/server.h"
|
|
#include "../id-ff/provider.h"
|
|
#include "../id-ff/providerprivate.h"
|
|
|
|
#include "discovery.h"
|
|
#include "profile.h"
|
|
#include "identity.h"
|
|
#include "server.h"
|
|
#include "session.h"
|
|
#include "../utils.h"
|
|
|
|
struct _LassoIdWsf2DiscoveryPrivate
|
|
{
|
|
gboolean dispose_has_run;
|
|
GList *new_entry_ids;
|
|
char *security_mech_id;
|
|
};
|
|
|
|
/*****************************************************************************/
|
|
/* public methods */
|
|
/*****************************************************************************/
|
|
|
|
/**
|
|
* lasso_idwsf2_discovery_metadata_register_self:
|
|
* @discovery: a #LassoIdWsf2Discovery
|
|
* @service_type: the service type of the registered metadata, for example LASSO_IDWSF2_DISCO_HREF
|
|
* @abstract: label/title of the service, free form
|
|
* @soap_endpoint: URL of the SoapEndpoint to the service
|
|
* @svcMDID: identifier of the metadatas if caller wants to specify which identifier it wants.
|
|
* If NULL, a random identifier will be generated.
|
|
*
|
|
* Register metadata service on itself as an ID-WSF Provider (WSP).
|
|
* Typically used for an IdP to register itself as Discovery service.
|
|
*
|
|
* Return value: the svcMDID of the new metadata object if the call is successfull, NULL otherwise. These
|
|
* value must be freed using g_free.
|
|
**/
|
|
gchar*
|
|
lasso_idwsf2_discovery_metadata_register_self(LassoIdWsf2Discovery *discovery,
|
|
const gchar *service_type, const gchar *abstract,
|
|
const gchar *soap_endpoint, const gchar *svcMDID)
|
|
{
|
|
LassoIdWsf2Profile *profile;
|
|
LassoProvider *provider;
|
|
gchar *provider_id;
|
|
LassoIdWsf2DiscoSvcMetadata *metadata;
|
|
char *new_svcMDID;
|
|
|
|
g_return_val_if_fail(LASSO_IS_IDWSF2_DISCOVERY(discovery), NULL);
|
|
g_return_val_if_fail(service_type != NULL && service_type[0] != '\0', NULL);
|
|
g_return_val_if_fail(abstract != NULL && abstract[0] != '\0', NULL);
|
|
g_return_val_if_fail(soap_endpoint != NULL && soap_endpoint[0] != '\0', NULL);
|
|
|
|
profile = LASSO_IDWSF2_PROFILE(discovery);
|
|
provider = LASSO_PROVIDER(LASSO_PROFILE(profile)->server);
|
|
provider_id = provider->ProviderID;
|
|
metadata = lasso_idwsf2_disco_svc_metadata_new_full(
|
|
service_type, abstract, provider_id, soap_endpoint);
|
|
if (svcMDID != NULL) {
|
|
metadata->svcMDID = g_strdup(svcMDID);
|
|
} else {
|
|
metadata->svcMDID = lasso_build_unique_id(32);
|
|
}
|
|
lasso_server_add_svc_metadata(LASSO_PROFILE(profile)->server, metadata);
|
|
new_svcMDID = g_strdup(metadata->svcMDID);
|
|
g_object_unref(metadata);
|
|
|
|
return new_svcMDID;
|
|
}
|
|
|
|
/**
|
|
* lasso_idwsf2_discovery_init_metadata_register:
|
|
* @discovery: a #LassoIdWsf2Discovery
|
|
* @service_type: the service type of the registered metadata, for example LASSO_IDWSF2_DISCO_HREF
|
|
* @abstract: label/title of the service, free form
|
|
* @disco_provider_id: provider identifier of the discovery service
|
|
* @soap_endpoint: URL of the SoapEndpoint to the service
|
|
*
|
|
* Initialise a ID-WSF service metadata registration request to a Discovery service
|
|
* specified by disco_provider_id.
|
|
*
|
|
* Return value: 0 on success; an error code otherwise.
|
|
**/
|
|
gint
|
|
lasso_idwsf2_discovery_init_metadata_register(LassoIdWsf2Discovery *discovery,
|
|
const gchar *service_type, const gchar *abstract,
|
|
const gchar *disco_provider_id, const gchar *soap_endpoint)
|
|
{
|
|
LassoIdWsf2Profile *profile = LASSO_IDWSF2_PROFILE(discovery);
|
|
LassoIdWsf2DiscoSvcMDRegister *metadata_register;
|
|
LassoProvider *provider;
|
|
gchar *sp_provider_id;
|
|
|
|
g_return_val_if_fail(LASSO_IS_IDWSF2_DISCOVERY(discovery),
|
|
LASSO_PARAM_ERROR_BAD_TYPE_OR_NULL_OBJ);
|
|
g_return_val_if_fail(service_type != NULL && service_type[0] != '\0',
|
|
LASSO_PARAM_ERROR_BAD_TYPE_OR_NULL_OBJ);
|
|
g_return_val_if_fail(abstract != NULL && abstract[0] != '\0',
|
|
LASSO_PARAM_ERROR_BAD_TYPE_OR_NULL_OBJ);
|
|
g_return_val_if_fail(disco_provider_id != NULL && disco_provider_id[0] != '\0',
|
|
LASSO_PARAM_ERROR_BAD_TYPE_OR_NULL_OBJ);
|
|
g_return_val_if_fail(soap_endpoint != NULL && soap_endpoint[0] != '\0',
|
|
LASSO_PARAM_ERROR_BAD_TYPE_OR_NULL_OBJ);
|
|
|
|
/* Get the providerId of this SP */
|
|
provider = LASSO_PROVIDER(LASSO_PROFILE(profile)->server);
|
|
sp_provider_id = provider->ProviderID;
|
|
|
|
/* Get a MetadataRegister node */
|
|
metadata_register = lasso_idwsf2_disco_svc_md_register_new_full(
|
|
service_type, abstract, sp_provider_id, soap_endpoint);
|
|
|
|
/* Create a request with this xml node */
|
|
lasso_idwsf2_profile_init_soap_request(profile, LASSO_NODE(metadata_register),
|
|
LASSO_IDWSF2_DISCO_HREF);
|
|
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* lasso_idwsf2_discovery_process_metadata_register_msg:
|
|
* @discovery: a #LassoIdWsf2Discovery
|
|
* @message: received metadata register soap request
|
|
*
|
|
* Process received metadata register request.
|
|
* If successful, register the service metadata into the discovery service.
|
|
*
|
|
* Return value: 0 on success; or a negative value otherwise.
|
|
**/
|
|
gint
|
|
lasso_idwsf2_discovery_process_metadata_register_msg(LassoIdWsf2Discovery *discovery,
|
|
const gchar *message)
|
|
{
|
|
LassoIdWsf2Profile *profile;
|
|
LassoIdWsf2DiscoSvcMDRegisterResponse *response;
|
|
LassoSoapEnvelope *envelope;
|
|
int rc = 0;
|
|
|
|
g_return_val_if_fail(LASSO_IS_IDWSF2_DISCOVERY(discovery),
|
|
LASSO_PARAM_ERROR_BAD_TYPE_OR_NULL_OBJ);
|
|
profile = LASSO_IDWSF2_PROFILE(discovery);
|
|
g_return_val_if_fail(message != NULL, LASSO_PARAM_ERROR_INVALID_VALUE);
|
|
|
|
rc = lasso_idwsf2_profile_process_soap_request_msg(profile, message);
|
|
if (! LASSO_IS_IDWSF2_DISCO_SVC_MD_REGISTER(LASSO_PROFILE(profile)->request)) {
|
|
rc = LASSO_PROFILE_ERROR_INVALID_SOAP_MSG;
|
|
}
|
|
|
|
/* OK case */
|
|
if (rc == 0) {
|
|
LassoIdWsf2DiscoSvcMDRegister *request;
|
|
GList *SvcMD;
|
|
|
|
lasso_release_list_of_gobjects(discovery->metadatas);
|
|
|
|
request = LASSO_IDWSF2_DISCO_SVC_MD_REGISTER(LASSO_PROFILE(profile)->request);
|
|
/* Allocate an ID and add the metadatas */
|
|
for (SvcMD = request->SvcMD; SvcMD != NULL; SvcMD = g_list_next(SvcMD)) {
|
|
if (LASSO_IS_IDWSF2_DISCO_SVC_METADATA(SvcMD->data)) {
|
|
lasso_list_add_gobject(discovery->metadatas, SvcMD->data);
|
|
lasso_assign_new_string(
|
|
LASSO_IDWSF2_DISCO_SVC_METADATA(
|
|
SvcMD->data)->svcMDID,
|
|
lasso_build_unique_id(32));
|
|
lasso_server_add_svc_metadata(LASSO_PROFILE(profile)->server,
|
|
LASSO_IDWSF2_DISCO_SVC_METADATA(SvcMD->data));
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Build response */
|
|
response = lasso_idwsf2_disco_svc_md_register_response_new();
|
|
|
|
/* OK case */
|
|
if (rc == 0) {
|
|
GList *SvcMDs;
|
|
/* Return the allocated IDs */
|
|
response->Status =
|
|
lasso_idwsf2_util_status_new_with_code(LASSO_DISCO_STATUS_CODE_OK, NULL);
|
|
for (SvcMDs = discovery->metadatas; SvcMDs != NULL; SvcMDs = g_list_next(SvcMDs)) {
|
|
lasso_list_add_string(response->SvcMDID,
|
|
LASSO_IDWSF2_DISCO_SVC_METADATA(SvcMDs->data)->svcMDID);
|
|
}
|
|
/* KO case */
|
|
} else {
|
|
/* Return a failed status, with a second level code for explanation */
|
|
response->Status =
|
|
lasso_idwsf2_util_status_new_with_code(LASSO_DISCO_STATUS_CODE_FAILED,
|
|
lasso_strerror(rc));
|
|
}
|
|
|
|
lasso_assign_new_gobject(profile->soap_envelope_response,
|
|
lasso_idwsf2_profile_build_soap_envelope(NULL,
|
|
LASSO_PROVIDER(LASSO_PROFILE(profile)->server)->ProviderID));
|
|
|
|
if (LASSO_IS_SOAP_ENVELOPE(profile->soap_envelope_response)) {
|
|
envelope = profile->soap_envelope_response;
|
|
envelope->Body->any = g_list_append(envelope->Body->any, response);
|
|
} else {
|
|
rc = LASSO_SOAP_ERROR_MISSING_ENVELOPE;
|
|
g_critical("soap_envelope_response is missing");
|
|
}
|
|
|
|
return rc;
|
|
}
|
|
|
|
/**
|
|
* lasso_idwsf2_discovery_process_metadata_register_response_msg:
|
|
* @discovery: a #LassoIdWsf2Discovery
|
|
* @message: received metadata register soap response
|
|
*
|
|
* Process received metadata register response.
|
|
* Check response status code.
|
|
* If successful, save into @discovery->svcMDID the service metadata identifier
|
|
* found in the response.
|
|
*
|
|
* Return value: 0 on success; or a negative value otherwise.
|
|
**/
|
|
gint
|
|
lasso_idwsf2_discovery_process_metadata_register_response_msg(LassoIdWsf2Discovery *discovery,
|
|
const gchar *message)
|
|
{
|
|
LassoIdWsf2Profile *profile;
|
|
LassoIdWsf2DiscoSvcMDRegisterResponse *response;
|
|
int rc = 0;
|
|
|
|
g_return_val_if_fail(LASSO_IS_IDWSF2_DISCOVERY(discovery),
|
|
LASSO_PARAM_ERROR_BAD_TYPE_OR_NULL_OBJ);
|
|
profile = LASSO_IDWSF2_PROFILE(discovery);
|
|
g_return_val_if_fail(message != NULL, LASSO_PARAM_ERROR_INVALID_VALUE);
|
|
|
|
/* Process response */
|
|
rc = lasso_idwsf2_profile_process_soap_response_msg(profile, message);
|
|
|
|
if (! LASSO_IS_IDWSF2_DISCO_SVC_MD_REGISTER_RESPONSE(LASSO_PROFILE(profile)->response)) {
|
|
rc = LASSO_PROFILE_ERROR_INVALID_SOAP_MSG;
|
|
}
|
|
|
|
/* If the response has been correctly processed, */
|
|
/* put interesting data into the discovery object */
|
|
if (rc == 0) {
|
|
response = LASSO_IDWSF2_DISCO_SVC_MD_REGISTER_RESPONSE(
|
|
LASSO_PROFILE(profile)->response);
|
|
if (response->SvcMDID != NULL) {
|
|
lasso_assign_list_of_strings(discovery->svcMDIDs, response->SvcMDID);
|
|
} else {
|
|
rc = LASSO_DISCOVERY_ERROR_SVC_METADATA_REGISTER_FAILED;
|
|
}
|
|
}
|
|
|
|
return rc;
|
|
}
|
|
|
|
/**
|
|
* lasso_idwsf2_discovery_init_metadata_association_add:
|
|
* @discovery: a #LassoIdWsf2Discovery
|
|
* @svcMDID: identifier of the service metadata the user wants to associate with
|
|
*
|
|
* Initialise a request to associate a user account to a service metadata, allowing
|
|
* a WSC to request this service for data related to this user account.
|
|
*
|
|
* Return value: 0 on success; or a negative value otherwise.
|
|
**/
|
|
gint
|
|
lasso_idwsf2_discovery_init_metadata_association_add(LassoIdWsf2Discovery *discovery,
|
|
const gchar *svcMDID)
|
|
{
|
|
LassoIdWsf2Profile *profile = LASSO_IDWSF2_PROFILE(discovery);
|
|
LassoSession *session = LASSO_PROFILE(profile)->session;
|
|
LassoIdWsf2DiscoSvcMDAssociationAdd *md_association_add;
|
|
LassoWsAddrEndpointReference *epr;
|
|
|
|
g_return_val_if_fail(LASSO_IS_IDWSF2_DISCOVERY(discovery),
|
|
LASSO_PARAM_ERROR_BAD_TYPE_OR_NULL_OBJ);
|
|
g_return_val_if_fail(svcMDID != NULL, LASSO_PARAM_ERROR_INVALID_VALUE);
|
|
|
|
g_return_val_if_fail(LASSO_IS_SESSION(session), LASSO_PROFILE_ERROR_SESSION_NOT_FOUND);
|
|
|
|
/* Build a MetadataRegister node */
|
|
md_association_add = lasso_idwsf2_disco_svc_md_association_add_new();
|
|
md_association_add->SvcMDID = g_list_append(md_association_add->SvcMDID, g_strdup(svcMDID));
|
|
|
|
/* Create a request with this xml node */
|
|
lasso_idwsf2_profile_init_soap_request(profile, LASSO_NODE(md_association_add),
|
|
LASSO_IDWSF2_DISCO_HREF);
|
|
|
|
epr = lasso_session_get_endpoint_reference(session, LASSO_IDWSF2_DISCO_HREF);
|
|
if (epr != NULL) {
|
|
LASSO_PROFILE(profile)->msg_url = g_strdup(epr->Address->content);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* lasso_idwsf2_discovery_process_metadata_association_add_msg:
|
|
* @discovery: a #LassoIdWsf2Discovery
|
|
* @message: received metadata association add soap request
|
|
*
|
|
* Process received metadata association add request.
|
|
*
|
|
* Return value: 0 on success; or a negative value otherwise.
|
|
**/
|
|
gint
|
|
lasso_idwsf2_discovery_process_metadata_association_add_msg(LassoIdWsf2Discovery *discovery,
|
|
const gchar *message)
|
|
{
|
|
LassoIdWsf2Profile *profile = LASSO_IDWSF2_PROFILE(discovery);
|
|
LassoIdWsf2DiscoSvcMDAssociationAddResponse *response;
|
|
LassoSoapEnvelope *envelope;
|
|
int res = 0;
|
|
|
|
g_return_val_if_fail(LASSO_IS_IDWSF2_DISCOVERY(discovery),
|
|
LASSO_PARAM_ERROR_BAD_TYPE_OR_NULL_OBJ);
|
|
g_return_val_if_fail(message != NULL, LASSO_PARAM_ERROR_INVALID_VALUE);
|
|
|
|
/* Process request */
|
|
res = lasso_idwsf2_profile_process_soap_request_msg(profile, message);
|
|
|
|
if (! LASSO_IS_IDWSF2_DISCO_SVC_MD_ASSOCIATION_ADD(LASSO_PROFILE(profile)->request)) {
|
|
res = LASSO_PROFILE_ERROR_INVALID_SOAP_MSG;
|
|
}
|
|
|
|
/* Build response */
|
|
response = lasso_idwsf2_disco_svc_md_association_add_response_new();
|
|
|
|
envelope = profile->soap_envelope_response;
|
|
envelope->Body->any = g_list_append(envelope->Body->any, response);
|
|
|
|
lasso_assign_gobject(LASSO_PROFILE(profile)->response, LASSO_NODE(response));
|
|
|
|
return res;
|
|
}
|
|
|
|
/**
|
|
* lasso_idwsf2_discovery_register_metadata:
|
|
* @discovery: a #LassoIdWsf2Discovery
|
|
*
|
|
* Add service metadata identifier into user identity object.
|
|
*
|
|
* Return value: 0 on success; or a negative value otherwise.
|
|
**/
|
|
gint
|
|
lasso_idwsf2_discovery_register_metadata(LassoIdWsf2Discovery *discovery)
|
|
{
|
|
LassoIdWsf2Profile *profile = LASSO_IDWSF2_PROFILE(discovery);
|
|
LassoIdWsf2DiscoSvcMDAssociationAdd *request;
|
|
LassoIdWsf2DiscoSvcMDAssociationAddResponse *response;
|
|
LassoIdentity *identity;
|
|
GList *i;
|
|
int res = 0;
|
|
|
|
g_return_val_if_fail(LASSO_IS_IDWSF2_DISCOVERY(discovery),
|
|
LASSO_PARAM_ERROR_BAD_TYPE_OR_NULL_OBJ);
|
|
|
|
/* verify if identity already exists else create it */
|
|
if (LASSO_PROFILE(profile)->identity == NULL) {
|
|
LASSO_PROFILE(profile)->identity = lasso_identity_new();
|
|
}
|
|
identity = LASSO_PROFILE(profile)->identity;
|
|
|
|
if (! LASSO_IS_IDWSF2_DISCO_SVC_MD_ASSOCIATION_ADD(LASSO_PROFILE(profile)->request)) {
|
|
res = LASSO_PROFILE_ERROR_INVALID_SOAP_MSG;
|
|
} else if (! LASSO_IS_IDWSF2_DISCO_SVC_MD_ASSOCIATION_ADD_RESPONSE(
|
|
LASSO_PROFILE(profile)->response)) {
|
|
res = LASSO_PROFILE_ERROR_MISSING_RESPONSE;
|
|
}
|
|
|
|
/* If the request has been correctly processed, */
|
|
/* put interesting data into the discovery object */
|
|
request = LASSO_IDWSF2_DISCO_SVC_MD_ASSOCIATION_ADD(LASSO_PROFILE(profile)->request);
|
|
/* Copy the service metadatas with given svcMDIDs into the identity object */
|
|
for (i = g_list_first(request->SvcMDID); i != NULL; i = g_list_next(i)) {
|
|
lasso_identity_add_svc_md_id(identity, (gchar *)(i->data));
|
|
}
|
|
|
|
/* Set response status code */
|
|
response = LASSO_IDWSF2_DISCO_SVC_MD_ASSOCIATION_ADD_RESPONSE(
|
|
LASSO_PROFILE(profile)->response);
|
|
if (res == 0) {
|
|
response->Status = lasso_idwsf2_util_status_new();
|
|
response->Status->code = g_strdup(LASSO_DISCO_STATUS_CODE_OK);
|
|
} else {
|
|
response->Status = lasso_idwsf2_util_status_new();
|
|
response->Status->code = g_strdup(LASSO_DISCO_STATUS_CODE_FAILED);
|
|
}
|
|
|
|
return res;
|
|
}
|
|
|
|
/**
|
|
* lasso_idwsf2_discovery_process_metadata_association_add_response_msg:
|
|
* @discovery: a #LassoIdWsf2Discovery
|
|
* @message: received metadata association add soap response
|
|
*
|
|
* Process received metadata association add response.
|
|
* Check response status code.
|
|
*
|
|
* Return value: 0 on success; or a negative value otherwise.
|
|
**/
|
|
gint
|
|
lasso_idwsf2_discovery_process_metadata_association_add_response_msg(
|
|
LassoIdWsf2Discovery *discovery, const gchar *message)
|
|
{
|
|
LassoIdWsf2Profile *profile = LASSO_IDWSF2_PROFILE(discovery);
|
|
LassoIdWsf2DiscoSvcMDAssociationAddResponse *response;
|
|
int res = 0;
|
|
|
|
g_return_val_if_fail(LASSO_IS_IDWSF2_DISCOVERY(discovery),
|
|
LASSO_PARAM_ERROR_BAD_TYPE_OR_NULL_OBJ);
|
|
g_return_val_if_fail(message != NULL, LASSO_PARAM_ERROR_INVALID_VALUE);
|
|
|
|
/* Process response */
|
|
res = lasso_idwsf2_profile_process_soap_response_msg(profile, message);
|
|
if (res != 0) {
|
|
return res;
|
|
}
|
|
|
|
if (! LASSO_IS_IDWSF2_DISCO_SVC_MD_ASSOCIATION_ADD_RESPONSE(
|
|
LASSO_PROFILE(profile)->response)) {
|
|
return LASSO_PROFILE_ERROR_INVALID_SOAP_MSG;
|
|
}
|
|
|
|
/* Check response status code */
|
|
response = LASSO_IDWSF2_DISCO_SVC_MD_ASSOCIATION_ADD_RESPONSE(
|
|
LASSO_PROFILE(profile)->response);
|
|
if (response->Status == NULL || response->Status->code == NULL) {
|
|
return LASSO_PROFILE_ERROR_MISSING_STATUS_CODE;
|
|
}
|
|
if (strcmp(response->Status->code, LASSO_DISCO_STATUS_CODE_OK) != 0) {
|
|
return LASSO_DISCOVERY_ERROR_SVC_METADATA_ASSOCIATION_ADD_FAILED;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* lasso_idwsf2_discovery_init_query
|
|
* @discovery: a #LassoIdWsf2Discovery
|
|
* @security_mech_id: obsolete ; can be NULL
|
|
*
|
|
* Initialise a request for ID-WSF discovery Query to a discovery service.
|
|
*
|
|
* Return value: 0 on success; or a negative value otherwise.
|
|
**/
|
|
gint
|
|
lasso_idwsf2_discovery_init_query(LassoIdWsf2Discovery *discovery, G_GNUC_UNUSED const gchar *security_mech_id)
|
|
{
|
|
/* FIXME: add support of security mechanims */
|
|
LassoIdWsf2Profile *profile = LASSO_IDWSF2_PROFILE(discovery);
|
|
LassoSession *session = LASSO_PROFILE(profile)->session;
|
|
LassoWsAddrEndpointReference *epr;
|
|
|
|
g_return_val_if_fail(LASSO_IS_IDWSF2_DISCOVERY(discovery),
|
|
LASSO_PARAM_ERROR_BAD_TYPE_OR_NULL_OBJ);
|
|
|
|
g_return_val_if_fail(LASSO_IS_SESSION(session), LASSO_PROFILE_ERROR_SESSION_NOT_FOUND);
|
|
|
|
lasso_assign_new_gobject(LASSO_PROFILE(profile)->request, (LassoNode*)lasso_idwsf2_disco_query_new());
|
|
|
|
lasso_idwsf2_profile_init_soap_request(profile,
|
|
LASSO_PROFILE(profile)->request, LASSO_IDWSF2_DISCO_HREF);
|
|
|
|
epr = lasso_session_get_endpoint_reference(session, LASSO_IDWSF2_DISCO_HREF);
|
|
if (epr != NULL) {
|
|
LASSO_PROFILE(profile)->msg_url = g_strdup(epr->Address->content);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* lasso_idwsf2_discovery_add_requested_service_type
|
|
* @discovery: a #LassoIdWsf2Discovery
|
|
* @service_type: the service type (or data profile) requested
|
|
*
|
|
* Select the requested service type which will be queried.
|
|
*
|
|
* Return value: 0 on success; or a negative value otherwise.
|
|
**/
|
|
gint
|
|
lasso_idwsf2_discovery_add_requested_service_type(LassoIdWsf2Discovery *discovery,
|
|
const gchar *service_type)
|
|
{
|
|
LassoIdWsf2Profile *profile = LASSO_IDWSF2_PROFILE(discovery);
|
|
LassoIdWsf2DiscoQuery *query;
|
|
LassoIdWsf2DiscoRequestedService *service;
|
|
|
|
g_return_val_if_fail(LASSO_IS_IDWSF2_DISCOVERY(discovery),
|
|
LASSO_PARAM_ERROR_BAD_TYPE_OR_NULL_OBJ);
|
|
|
|
g_return_val_if_fail(LASSO_IS_IDWSF2_DISCO_QUERY(LASSO_PROFILE(profile)->request),
|
|
LASSO_PROFILE_ERROR_MISSING_REQUEST);
|
|
|
|
query = LASSO_IDWSF2_DISCO_QUERY(LASSO_PROFILE(profile)->request);
|
|
service = lasso_idwsf2_disco_requested_service_new();
|
|
service->ServiceType = g_list_append(service->ServiceType, g_strdup(service_type));
|
|
query->RequestedService = g_list_append(query->RequestedService, service);
|
|
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* lasso_idwsf2_discovery_process_query_msg:
|
|
* @discovery: a #LassoIdWsf2Discovery
|
|
* @message: received query soap request
|
|
*
|
|
* Process received query request.
|
|
*
|
|
* Return value: 0 on success; or a negative value otherwise.
|
|
**/
|
|
gint
|
|
lasso_idwsf2_discovery_process_query_msg(LassoIdWsf2Discovery *discovery, const gchar *message)
|
|
{
|
|
LassoIdWsf2Profile *profile = LASSO_IDWSF2_PROFILE(discovery);
|
|
int res = 0;
|
|
|
|
g_return_val_if_fail(LASSO_IS_IDWSF2_DISCOVERY(discovery),
|
|
LASSO_PARAM_ERROR_BAD_TYPE_OR_NULL_OBJ);
|
|
g_return_val_if_fail(message != NULL, LASSO_PARAM_ERROR_INVALID_VALUE);
|
|
|
|
/* Process request */
|
|
res = lasso_idwsf2_profile_process_soap_request_msg(profile, message);
|
|
|
|
if (! LASSO_IS_IDWSF2_DISCO_QUERY(LASSO_PROFILE(profile)->request)) {
|
|
res = LASSO_PROFILE_ERROR_INVALID_SOAP_MSG;
|
|
}
|
|
|
|
return res;
|
|
}
|
|
|
|
/**
|
|
* lasso_idwsf2_discovery_build_epr:
|
|
* @service: a #LassoIdWsf2DiscoRequestedService containing the query criteria
|
|
* @identity: a #LassoIdentity of the current user
|
|
* @server: current #LassoServer
|
|
*
|
|
* Build an EndpointReference (EPR) which contains all the information about a WSP providing
|
|
* the requested service type and matching other query criteria to allow a WSC to request it.
|
|
*
|
|
* Return value: the newly built EPR; or NULL if no WSP match the query criteria.
|
|
**/
|
|
static LassoWsAddrEndpointReference*
|
|
lasso_idwsf2_discovery_build_epr(LassoIdWsf2DiscoRequestedService *service,
|
|
LassoIdentity *identity, LassoServer *server)
|
|
{
|
|
gchar *service_type = NULL;
|
|
GList *svcMDIDs;
|
|
GList *svcMDs;
|
|
LassoIdWsf2DiscoSvcMetadata *svcMD;
|
|
LassoWsAddrEndpointReference *epr;
|
|
LassoWsAddrMetadata *metadata;
|
|
LassoIdWsf2DiscoSecurityContext *security_context;
|
|
LassoIdWsf2SecToken *sec_token;
|
|
LassoSaml2Assertion *assertion;
|
|
LassoSaml2Subject *subject;
|
|
LassoFederation* federation;
|
|
LassoProvider *provider;
|
|
LassoSaml2EncryptedElement *encrypted_element;
|
|
LassoIdWsf2DiscoEndpointContext *endpoint_context;
|
|
LassoIdWsf2DiscoServiceContext *service_context;
|
|
|
|
if (service != NULL && service->ServiceType != NULL && service->ServiceType->data != NULL) {
|
|
service_type = (gchar *)service->ServiceType->data;
|
|
} else {
|
|
/* Can only search for service type at the moment */
|
|
return NULL;
|
|
}
|
|
|
|
svcMDIDs = lasso_identity_get_svc_md_ids(identity);
|
|
svcMDs = lasso_server_get_svc_metadatas_with_id_and_type(server, svcMDIDs, service_type);
|
|
if (svcMDs == NULL) {
|
|
return NULL;
|
|
}
|
|
|
|
/* FIXME : foreach on the whole list and build an epr for each svcMD */
|
|
svcMD = svcMDs->data;
|
|
|
|
if (svcMD == NULL || svcMD->ServiceContext == NULL || svcMD->ServiceContext->data == NULL) {
|
|
g_list_foreach(svcMDs, (GFunc)lasso_node_destroy, NULL);
|
|
g_list_free(svcMDs);
|
|
return NULL;
|
|
}
|
|
|
|
/* Build EndpointReference */
|
|
|
|
epr = lasso_wsa_endpoint_reference_new();
|
|
service_context = LASSO_IDWSF2_DISCO_SERVICE_CONTEXT(svcMD->ServiceContext->data);
|
|
endpoint_context = LASSO_IDWSF2_DISCO_ENDPOINT_CONTEXT(
|
|
service_context->EndpointContext->data);
|
|
|
|
/* XXX: there may be more than one endpoint context */
|
|
epr->Address = lasso_wsa_attributed_uri_new_with_string(
|
|
(gchar*)endpoint_context->Address->data);
|
|
|
|
metadata = lasso_wsa_metadata_new();
|
|
|
|
/* Abstract */
|
|
metadata->any = g_list_append(metadata->any,
|
|
lasso_idwsf2_disco_abstract_new_with_string(svcMD->Abstract));
|
|
/* ProviderID */
|
|
metadata->any = g_list_append(metadata->any,
|
|
lasso_idwsf2_disco_provider_id_new_with_string(svcMD->ProviderID));
|
|
/* ServiceType */
|
|
metadata->any = g_list_append(metadata->any,
|
|
lasso_idwsf2_disco_service_type_new_with_string(
|
|
(char*)service_context->ServiceType->data));
|
|
/* Framework */
|
|
if (endpoint_context->Framework != NULL) {
|
|
metadata->any = g_list_append(metadata->any,
|
|
g_object_ref((GObject*)endpoint_context->Framework->data));
|
|
}
|
|
|
|
/* Identity token */
|
|
federation = lasso_identity_get_federation(identity, svcMD->ProviderID);
|
|
if (federation != NULL) {
|
|
assertion = LASSO_SAML2_ASSERTION(lasso_saml2_assertion_new());
|
|
|
|
/* Identity token Subject */
|
|
subject = LASSO_SAML2_SUBJECT(lasso_saml2_subject_new());
|
|
if (federation->remote_nameIdentifier != NULL) {
|
|
subject->NameID = g_object_ref(federation->remote_nameIdentifier);
|
|
} else {
|
|
subject->NameID = g_object_ref(federation->local_nameIdentifier);
|
|
}
|
|
assertion->Subject = subject;
|
|
|
|
/* Encrypt NameID */
|
|
provider = g_hash_table_lookup(server->providers, svcMD->ProviderID);
|
|
if (provider
|
|
&& provider->private_data->encryption_mode & LASSO_ENCRYPTION_MODE_NAMEID
|
|
&& provider->private_data->encryption_public_key != NULL) {
|
|
|
|
encrypted_element = LASSO_SAML2_ENCRYPTED_ELEMENT(lasso_node_encrypt(
|
|
LASSO_NODE(assertion->Subject->NameID),
|
|
provider->private_data->encryption_public_key,
|
|
provider->private_data->encryption_sym_key_type));
|
|
if (encrypted_element != NULL) {
|
|
assertion->Subject->EncryptedID = encrypted_element;
|
|
lasso_release_gobject(assertion->Subject->NameID);
|
|
} else {
|
|
/** FIXME: find a return value for this case */
|
|
}
|
|
}
|
|
|
|
sec_token = LASSO_IDWSF2_SEC_TOKEN(lasso_idwsf2_sec_token_new());
|
|
sec_token->any = LASSO_NODE(assertion);
|
|
|
|
security_context = LASSO_IDWSF2_DISCO_SECURITY_CONTEXT(
|
|
lasso_idwsf2_disco_security_context_new());
|
|
security_context->SecurityMechID = g_list_append(
|
|
security_context->SecurityMechID, g_strdup(LASSO_SECURITY_MECH_TLS_BEARER));
|
|
security_context->Token = g_list_append(security_context->Token, sec_token);
|
|
|
|
metadata->any = g_list_append(metadata->any, security_context);
|
|
}
|
|
|
|
epr->Metadata = metadata;
|
|
|
|
/* Free resources */
|
|
g_list_foreach(svcMDs, (GFunc)lasso_node_destroy, NULL);
|
|
g_list_free(svcMDs);
|
|
|
|
return epr;
|
|
}
|
|
|
|
/**
|
|
* lasso_idwsf2_discovery_build_query_response_eprs:
|
|
* @discovery: a #LassoIdWsf2Discovery
|
|
*
|
|
* Build a query response containing one or more EndpointReference (EPR) for each WSP providing
|
|
* the requested service type and matching other query criteria to allow a WSC to request them.
|
|
*
|
|
* Return value: 0 on success; or a negative value otherwise.
|
|
**/
|
|
gint
|
|
lasso_idwsf2_discovery_build_query_response_eprs(LassoIdWsf2Discovery *discovery)
|
|
{
|
|
LassoIdWsf2Profile *profile = LASSO_IDWSF2_PROFILE(discovery);
|
|
LassoIdentity *identity = LASSO_PROFILE(profile)->identity;
|
|
LassoServer *server = LASSO_PROFILE(profile)->server;
|
|
LassoIdWsf2DiscoQuery* request;
|
|
LassoIdWsf2DiscoRequestedService *service = NULL;
|
|
LassoIdWsf2DiscoQueryResponse *response;
|
|
LassoWsAddrEndpointReference *epr;
|
|
LassoSoapEnvelope *envelope;
|
|
int res = 0;
|
|
|
|
if (! LASSO_IS_IDWSF2_DISCO_QUERY(LASSO_PROFILE(profile)->request)) {
|
|
res = LASSO_PROFILE_ERROR_MISSING_REQUEST;
|
|
} else if (! LASSO_IS_IDENTITY(identity)) {
|
|
res = LASSO_PROFILE_ERROR_IDENTITY_NOT_FOUND;
|
|
}
|
|
|
|
if (res == 0) {
|
|
request = LASSO_IDWSF2_DISCO_QUERY(LASSO_PROFILE(profile)->request);
|
|
/* FIXME : foreach on the list instead */
|
|
if (request->RequestedService != NULL) {
|
|
service = LASSO_IDWSF2_DISCO_REQUESTED_SERVICE(
|
|
request->RequestedService->data);
|
|
}
|
|
if (service == NULL) {
|
|
res = LASSO_DISCOVERY_ERROR_MISSING_REQUESTED_SERVICE;
|
|
}
|
|
}
|
|
|
|
/* Build response */
|
|
response = lasso_idwsf2_disco_query_response_new();
|
|
|
|
if (res == 0) {
|
|
/* FIXME : foreach here as well */
|
|
epr = lasso_idwsf2_discovery_build_epr(service, identity, server);
|
|
if (epr != NULL) {
|
|
response->EndpointReference =
|
|
g_list_append(response->EndpointReference, epr);
|
|
/* XXX : Should probably check if the epr contains a SecurityContext, */
|
|
/* otherwise return a "federation not found" error code */
|
|
} else {
|
|
res = LASSO_DISCOVERY_ERROR_FAILED_TO_BUILD_ENDPOINT_REFERENCE;
|
|
}
|
|
}
|
|
|
|
/* Set response status code */
|
|
if (res == 0) {
|
|
response->Status = lasso_idwsf2_util_status_new();
|
|
response->Status->code = g_strdup(LASSO_DISCO_STATUS_CODE_OK);
|
|
} else {
|
|
response->Status = lasso_idwsf2_util_status_new();
|
|
response->Status->code = g_strdup(LASSO_DISCO_STATUS_CODE_FAILED);
|
|
/* XXX : May add secondary status codes here */
|
|
}
|
|
|
|
envelope = profile->soap_envelope_response;
|
|
envelope->Body->any = g_list_append(envelope->Body->any, response);
|
|
|
|
return res;
|
|
}
|
|
|
|
/**
|
|
* lasso_idwsf2_discovery_process_query_response_msg:
|
|
* @discovery: a #LassoIdWsf2Discovery
|
|
* @message: received query soap response
|
|
*
|
|
* Process received query response.
|
|
* Copy returned EPRs into session object.
|
|
* Check response status code.
|
|
*
|
|
* Return value: 0 on success; or a negative value otherwise.
|
|
**/
|
|
gint
|
|
lasso_idwsf2_discovery_process_query_response_msg(LassoIdWsf2Discovery *discovery,
|
|
const gchar *message)
|
|
{
|
|
LassoIdWsf2Profile *profile = LASSO_IDWSF2_PROFILE(discovery);
|
|
LassoSession *session = LASSO_PROFILE(profile)->session;
|
|
LassoIdWsf2DiscoQueryResponse *response;
|
|
int res = 0;
|
|
|
|
g_return_val_if_fail(LASSO_IS_IDWSF2_DISCOVERY(discovery),
|
|
LASSO_PARAM_ERROR_BAD_TYPE_OR_NULL_OBJ);
|
|
g_return_val_if_fail(message != NULL, LASSO_PARAM_ERROR_INVALID_VALUE);
|
|
|
|
g_return_val_if_fail(LASSO_IS_SESSION(session), LASSO_PROFILE_ERROR_SESSION_NOT_FOUND);
|
|
|
|
/* Process request */
|
|
res = lasso_idwsf2_profile_process_soap_response_msg(profile, message);
|
|
if (res != 0) {
|
|
return res;
|
|
}
|
|
|
|
if (! LASSO_IS_IDWSF2_DISCO_QUERY_RESPONSE(LASSO_PROFILE(profile)->response)) {
|
|
return LASSO_PROFILE_ERROR_INVALID_SOAP_MSG;
|
|
}
|
|
|
|
/* Check response status code */
|
|
response = LASSO_IDWSF2_DISCO_QUERY_RESPONSE(LASSO_PROFILE(profile)->response);
|
|
if (response->Status == NULL || response->Status->code == NULL) {
|
|
return LASSO_PROFILE_ERROR_MISSING_STATUS_CODE;
|
|
}
|
|
if (strcmp(response->Status->code, LASSO_DISCO_STATUS_CODE_OK) != 0) {
|
|
return LASSO_DISCOVERY_ERROR_SVC_METADATA_ASSOCIATION_ADD_FAILED;
|
|
}
|
|
|
|
/* If the response has been correctly processed, */
|
|
/* put interesting data into the discovery object */
|
|
response = LASSO_IDWSF2_DISCO_QUERY_RESPONSE(LASSO_PROFILE(profile)->response);
|
|
/* FIXME : foreach on the list instead */
|
|
if (response->EndpointReference != NULL
|
|
&& response->EndpointReference->data != NULL) {
|
|
lasso_session_add_endpoint_reference(session,
|
|
response->EndpointReference->data);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* lasso_idwsf2_discovery_get_service:
|
|
* @discovery: a #LassoIdWsf2Discovery
|
|
* @service_type: the requested service type
|
|
*
|
|
* After a discovery query message, create a #LassoIdWsf2DataService instance for the
|
|
* requested @service_type.
|
|
*
|
|
* Return value: a newly created #LassoIdWsf2DataService object; or NULL if an error occured.
|
|
**/
|
|
LassoIdWsf2DataService*
|
|
lasso_idwsf2_discovery_get_service(LassoIdWsf2Discovery *discovery, G_GNUC_UNUSED const gchar *service_type)
|
|
{
|
|
/* FIXME: service_type is unused, should we remove it from public API ? */
|
|
LassoIdWsf2Profile *profile = LASSO_IDWSF2_PROFILE(discovery);
|
|
LassoIdWsf2DiscoQueryResponse *response;
|
|
LassoWsAddrEndpointReference *epr = NULL;
|
|
LassoIdWsf2DataService *service;
|
|
|
|
g_return_val_if_fail(LASSO_IS_IDWSF2_DISCOVERY(discovery), NULL);
|
|
|
|
g_return_val_if_fail(LASSO_IS_IDWSF2_DISCO_QUERY_RESPONSE(
|
|
LASSO_PROFILE(profile)->response), NULL);
|
|
|
|
response = LASSO_IDWSF2_DISCO_QUERY_RESPONSE(LASSO_PROFILE(profile)->response);
|
|
|
|
/* FIXME : foreach on the list instead */
|
|
if (response->EndpointReference != NULL && response->EndpointReference->data != NULL) {
|
|
epr = LASSO_WSA_ENDPOINT_REFERENCE(response->EndpointReference->data);
|
|
} else {
|
|
return NULL;
|
|
}
|
|
|
|
service = lasso_idwsf2_data_service_new_full(LASSO_PROFILE(profile)->server, epr);
|
|
lasso_assign_gobject(LASSO_PROFILE(service)->session, LASSO_PROFILE(profile)->session);
|
|
|
|
return service;
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
/* private methods */
|
|
/*****************************************************************************/
|
|
|
|
static LassoNodeClass *parent_class = NULL;
|
|
|
|
static xmlNode*
|
|
get_xmlNode(LassoNode *node, gboolean lasso_dump)
|
|
{
|
|
xmlNode *xmlnode;
|
|
|
|
xmlnode = parent_class->get_xmlNode(node, lasso_dump);
|
|
xmlNodeSetName(xmlnode, (xmlChar*)"Discovery");
|
|
xmlSetProp(xmlnode, (xmlChar*)"DiscoveryDumpVersion", (xmlChar*)"2");
|
|
|
|
return xmlnode;
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
/* overrided parent class methods */
|
|
/*****************************************************************************/
|
|
|
|
static void
|
|
dispose(GObject *object)
|
|
{
|
|
LassoIdWsf2Discovery *discovery = LASSO_IDWSF2_DISCOVERY(object);
|
|
|
|
if (discovery->private_data->dispose_has_run == TRUE)
|
|
return;
|
|
discovery->private_data->dispose_has_run = TRUE;
|
|
|
|
G_OBJECT_CLASS(parent_class)->dispose(object);
|
|
}
|
|
|
|
static void
|
|
finalize(GObject *object)
|
|
{
|
|
LassoIdWsf2Discovery *discovery = LASSO_IDWSF2_DISCOVERY(object);
|
|
g_free(discovery->private_data);
|
|
discovery->private_data = NULL;
|
|
G_OBJECT_CLASS(parent_class)->finalize(object);
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
/* instance and class init functions */
|
|
/*****************************************************************************/
|
|
|
|
static void
|
|
instance_init(LassoIdWsf2Discovery *discovery)
|
|
{
|
|
discovery->private_data = g_new0(LassoIdWsf2DiscoveryPrivate, 1);
|
|
discovery->private_data->dispose_has_run = FALSE;
|
|
}
|
|
|
|
static void
|
|
class_init(LassoIdWsf2DiscoveryClass *klass)
|
|
{
|
|
parent_class = g_type_class_peek_parent(klass);
|
|
LASSO_NODE_CLASS(klass)->get_xmlNode = get_xmlNode;
|
|
G_OBJECT_CLASS(klass)->dispose = dispose;
|
|
G_OBJECT_CLASS(klass)->finalize = finalize;
|
|
}
|
|
|
|
GType
|
|
lasso_idwsf2_discovery_get_type()
|
|
{
|
|
static GType this_type = 0;
|
|
|
|
if (!this_type) {
|
|
static const GTypeInfo this_info = {
|
|
sizeof(LassoIdWsf2DiscoveryClass),
|
|
NULL,
|
|
NULL,
|
|
(GClassInitFunc) class_init,
|
|
NULL,
|
|
NULL,
|
|
sizeof(LassoIdWsf2Discovery),
|
|
0,
|
|
(GInstanceInitFunc) instance_init,
|
|
NULL
|
|
};
|
|
|
|
this_type = g_type_register_static(LASSO_TYPE_IDWSF2_PROFILE,
|
|
"LassoIdWsf2Discovery", &this_info, 0);
|
|
}
|
|
return this_type;
|
|
}
|
|
|
|
/**
|
|
* lasso_idwsf2_discovery_new:
|
|
* @server: the #LassoServer
|
|
*
|
|
* Create a new #LassoIdWsf2Discovery.
|
|
*
|
|
* Return value: a newly created #LassoIdWsf2Discovery object; or NULL if an error occured.
|
|
**/
|
|
LassoIdWsf2Discovery*
|
|
lasso_idwsf2_discovery_new(LassoServer *server)
|
|
{
|
|
LassoIdWsf2Discovery *discovery = NULL;
|
|
|
|
g_return_val_if_fail(LASSO_IS_SERVER(server), NULL);
|
|
|
|
discovery = g_object_new(LASSO_TYPE_IDWSF2_DISCOVERY, NULL);
|
|
LASSO_PROFILE(discovery)->server = g_object_ref(server);
|
|
|
|
return discovery;
|
|
}
|