|
Packit |
79f644 |
/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
|
|
Packit |
79f644 |
/*
|
|
Packit |
79f644 |
* Copyright © 2012 – 2017 Red Hat, Inc.
|
|
Packit |
79f644 |
*
|
|
Packit |
79f644 |
* This library is free software; you can redistribute it and/or
|
|
Packit |
79f644 |
* modify it under the terms of the GNU Lesser General Public
|
|
Packit |
79f644 |
* License as published by the Free Software Foundation; either
|
|
Packit |
79f644 |
* version 2 of the License, or (at your option) any later version.
|
|
Packit |
79f644 |
*
|
|
Packit |
79f644 |
* This library is distributed in the hope that it will be useful,
|
|
Packit |
79f644 |
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
Packit |
79f644 |
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
Packit |
79f644 |
* Lesser General Public License for more details.
|
|
Packit |
79f644 |
*
|
|
Packit |
79f644 |
* You should have received a copy of the GNU Lesser General
|
|
Packit |
79f644 |
* Public License along with this library; if not, see <http://www.gnu.org/licenses/>.
|
|
Packit |
79f644 |
*/
|
|
Packit |
79f644 |
|
|
Packit |
79f644 |
/* Based on code by the Evolution team.
|
|
Packit |
79f644 |
*
|
|
Packit |
79f644 |
* This was originally written as a part of evolution-ews:
|
|
Packit |
79f644 |
* evolution-ews/src/server/e-ews-connection.c
|
|
Packit |
79f644 |
*/
|
|
Packit |
79f644 |
|
|
Packit |
79f644 |
#include "config.h"
|
|
Packit |
79f644 |
|
|
Packit |
79f644 |
#include <glib/gi18n-lib.h>
|
|
Packit |
79f644 |
|
|
Packit |
79f644 |
#include <libsoup/soup.h>
|
|
Packit |
79f644 |
#include <libxml/xmlIO.h>
|
|
Packit |
79f644 |
|
|
Packit |
79f644 |
#include "goaewsclient.h"
|
|
Packit |
79f644 |
#include "goautils.h"
|
|
Packit |
79f644 |
|
|
Packit |
79f644 |
struct _GoaEwsClient
|
|
Packit |
79f644 |
{
|
|
Packit |
79f644 |
GObject parent_instance;
|
|
Packit |
79f644 |
};
|
|
Packit |
79f644 |
|
|
Packit |
79f644 |
G_DEFINE_TYPE (GoaEwsClient, goa_ews_client, G_TYPE_OBJECT);
|
|
Packit |
79f644 |
|
|
Packit |
79f644 |
/* ---------------------------------------------------------------------------------------------------- */
|
|
Packit |
79f644 |
|
|
Packit |
79f644 |
static void
|
|
Packit |
79f644 |
goa_ews_client_init (GoaEwsClient *self)
|
|
Packit |
79f644 |
{
|
|
Packit |
79f644 |
}
|
|
Packit |
79f644 |
|
|
Packit |
79f644 |
static void
|
|
Packit |
79f644 |
goa_ews_client_class_init (GoaEwsClientClass *self)
|
|
Packit |
79f644 |
{
|
|
Packit |
79f644 |
}
|
|
Packit |
79f644 |
|
|
Packit |
79f644 |
/* ---------------------------------------------------------------------------------------------------- */
|
|
Packit |
79f644 |
|
|
Packit |
79f644 |
GoaEwsClient *
|
|
Packit |
79f644 |
goa_ews_client_new (void)
|
|
Packit |
79f644 |
{
|
|
Packit |
79f644 |
return GOA_EWS_CLIENT (g_object_new (GOA_TYPE_EWS_CLIENT, NULL));
|
|
Packit |
79f644 |
}
|
|
Packit |
79f644 |
|
|
Packit |
79f644 |
/* ---------------------------------------------------------------------------------------------------- */
|
|
Packit |
79f644 |
|
|
Packit |
79f644 |
typedef struct
|
|
Packit |
79f644 |
{
|
|
Packit |
79f644 |
GCancellable *cancellable;
|
|
Packit |
79f644 |
GSimpleAsyncResult *res;
|
|
Packit |
79f644 |
SoupMessage *msgs[2];
|
|
Packit |
79f644 |
SoupSession *session;
|
|
Packit |
79f644 |
gboolean accept_ssl_errors;
|
|
Packit |
79f644 |
guint pending;
|
|
Packit |
79f644 |
gulong cancellable_id;
|
|
Packit |
79f644 |
xmlOutputBuffer *buf;
|
|
Packit |
79f644 |
} AutodiscoverData;
|
|
Packit |
79f644 |
|
|
Packit |
79f644 |
typedef struct
|
|
Packit |
79f644 |
{
|
|
Packit |
79f644 |
gchar *password;
|
|
Packit |
79f644 |
gchar *username;
|
|
Packit |
79f644 |
} AutodiscoverAuthData;
|
|
Packit |
79f644 |
|
|
Packit |
79f644 |
static gboolean
|
|
Packit |
79f644 |
ews_client_autodiscover_data_free (gpointer user_data)
|
|
Packit |
79f644 |
{
|
|
Packit |
79f644 |
AutodiscoverData *data = user_data;
|
|
Packit |
79f644 |
|
|
Packit |
79f644 |
g_simple_async_result_complete_in_idle (data->res);
|
|
Packit |
79f644 |
|
|
Packit |
79f644 |
if (data->cancellable_id > 0)
|
|
Packit |
79f644 |
{
|
|
Packit |
79f644 |
g_cancellable_disconnect (data->cancellable, data->cancellable_id);
|
|
Packit |
79f644 |
g_object_unref (data->cancellable);
|
|
Packit |
79f644 |
}
|
|
Packit |
79f644 |
|
|
Packit |
79f644 |
/* soup_session_queue_message stole the references to data->msgs */
|
|
Packit |
79f644 |
xmlOutputBufferClose (data->buf);
|
|
Packit |
79f644 |
g_object_unref (data->res);
|
|
Packit |
79f644 |
g_object_unref (data->session);
|
|
Packit |
79f644 |
g_slice_free (AutodiscoverData, data);
|
|
Packit |
79f644 |
|
|
Packit |
79f644 |
return G_SOURCE_REMOVE;
|
|
Packit |
79f644 |
}
|
|
Packit |
79f644 |
|
|
Packit |
79f644 |
static void
|
|
Packit |
79f644 |
ews_client_autodiscover_auth_data_free (gpointer data, GClosure *closure)
|
|
Packit |
79f644 |
{
|
|
Packit |
79f644 |
AutodiscoverAuthData *auth = data;
|
|
Packit |
79f644 |
|
|
Packit |
79f644 |
g_free (auth->password);
|
|
Packit |
79f644 |
g_free (auth->username);
|
|
Packit |
79f644 |
g_slice_free (AutodiscoverAuthData, auth);
|
|
Packit |
79f644 |
}
|
|
Packit |
79f644 |
|
|
Packit |
79f644 |
static gboolean
|
|
Packit |
79f644 |
ews_client_check_node (const xmlNode *node, const gchar *name)
|
|
Packit |
79f644 |
{
|
|
Packit |
79f644 |
g_return_val_if_fail (node != NULL, FALSE);
|
|
Packit |
79f644 |
return node->type == XML_ELEMENT_NODE && !g_strcmp0 ((gchar *) node->name, name);
|
|
Packit |
79f644 |
}
|
|
Packit |
79f644 |
|
|
Packit |
79f644 |
static void
|
|
Packit |
79f644 |
ews_client_authenticate (SoupSession *session,
|
|
Packit |
79f644 |
SoupMessage *msg,
|
|
Packit |
79f644 |
SoupAuth *auth,
|
|
Packit |
79f644 |
gboolean retrying,
|
|
Packit |
79f644 |
gpointer user_data)
|
|
Packit |
79f644 |
{
|
|
Packit |
79f644 |
AutodiscoverAuthData *data = user_data;
|
|
Packit |
79f644 |
|
|
Packit |
79f644 |
if (retrying)
|
|
Packit |
79f644 |
return;
|
|
Packit |
79f644 |
|
|
Packit |
79f644 |
soup_auth_authenticate (auth, data->username, data->password);
|
|
Packit |
79f644 |
}
|
|
Packit |
79f644 |
|
|
Packit |
79f644 |
static void
|
|
Packit |
79f644 |
ews_client_request_started (SoupSession *session, SoupMessage *msg, SoupSocket *socket, gpointer user_data)
|
|
Packit |
79f644 |
{
|
|
Packit |
79f644 |
AutodiscoverData *data = user_data;
|
|
Packit |
79f644 |
GError *error;
|
|
Packit |
79f644 |
GTlsCertificateFlags cert_flags;
|
|
Packit |
79f644 |
|
|
Packit |
79f644 |
error = NULL;
|
|
Packit |
79f644 |
|
|
Packit |
79f644 |
if (!data->accept_ssl_errors
|
|
Packit |
79f644 |
&& soup_message_get_https_status (msg, NULL, &cert_flags)
|
|
Packit |
79f644 |
&& cert_flags != 0)
|
|
Packit |
79f644 |
{
|
|
Packit |
79f644 |
goa_utils_set_error_ssl (&error, cert_flags);
|
|
Packit |
79f644 |
g_simple_async_result_take_error (data->res, error);
|
|
Packit |
79f644 |
soup_session_abort (data->session);
|
|
Packit |
79f644 |
}
|
|
Packit |
79f644 |
}
|
|
Packit |
79f644 |
|
|
Packit |
79f644 |
static void
|
|
Packit |
79f644 |
ews_client_autodiscover_cancelled_cb (GCancellable *cancellable, gpointer user_data)
|
|
Packit |
79f644 |
{
|
|
Packit |
79f644 |
AutodiscoverData *data = user_data;
|
|
Packit |
79f644 |
soup_session_abort (data->session);
|
|
Packit |
79f644 |
}
|
|
Packit |
79f644 |
|
|
Packit |
79f644 |
static gboolean
|
|
Packit |
79f644 |
ews_client_autodiscover_parse_protocol (xmlNode *node)
|
|
Packit |
79f644 |
{
|
|
Packit |
79f644 |
gboolean as_url = FALSE;
|
|
Packit |
79f644 |
gboolean oab_url = FALSE;
|
|
Packit |
79f644 |
|
|
Packit |
79f644 |
for (node = node->children; node; node = node->next)
|
|
Packit |
79f644 |
{
|
|
Packit |
79f644 |
if (ews_client_check_node (node, "ASUrl"))
|
|
Packit |
79f644 |
as_url = TRUE;
|
|
Packit |
79f644 |
else if (ews_client_check_node (node, "OABUrl"))
|
|
Packit |
79f644 |
oab_url = TRUE;
|
|
Packit |
79f644 |
|
|
Packit |
79f644 |
if (as_url && oab_url)
|
|
Packit |
79f644 |
break;
|
|
Packit |
79f644 |
}
|
|
Packit |
79f644 |
|
|
Packit |
79f644 |
return as_url && oab_url;
|
|
Packit |
79f644 |
}
|
|
Packit |
79f644 |
|
|
Packit |
79f644 |
static void
|
|
Packit |
79f644 |
ews_client_autodiscover_response_cb (SoupSession *session, SoupMessage *msg, gpointer user_data)
|
|
Packit |
79f644 |
{
|
|
Packit |
79f644 |
GError *error = NULL;
|
|
Packit |
79f644 |
AutodiscoverData *data = user_data;
|
|
Packit |
79f644 |
gboolean op_res = FALSE;
|
|
Packit |
79f644 |
guint idx;
|
|
Packit |
79f644 |
guint status;
|
|
Packit |
79f644 |
gsize size;
|
|
Packit |
79f644 |
xmlDoc *doc;
|
|
Packit |
79f644 |
xmlNode *node;
|
|
Packit |
79f644 |
|
|
Packit |
79f644 |
size = sizeof (data->msgs) / sizeof (data->msgs[0]);
|
|
Packit |
79f644 |
|
|
Packit |
79f644 |
for (idx = 0; idx < size; idx++)
|
|
Packit |
79f644 |
{
|
|
Packit |
79f644 |
if (data->msgs[idx] == msg)
|
|
Packit |
79f644 |
break;
|
|
Packit |
79f644 |
}
|
|
Packit |
79f644 |
if (idx == size || data->pending == 0)
|
|
Packit |
79f644 |
return;
|
|
Packit |
79f644 |
|
|
Packit |
79f644 |
data->msgs[idx] = NULL;
|
|
Packit |
79f644 |
status = msg->status_code;
|
|
Packit |
79f644 |
|
|
Packit |
79f644 |
/* status == SOUP_STATUS_CANCELLED, if we are being aborted by the
|
|
Packit |
79f644 |
* GCancellable, an SSL error or another message that was
|
|
Packit |
79f644 |
* successful.
|
|
Packit |
79f644 |
*/
|
|
Packit |
79f644 |
if (status == SOUP_STATUS_CANCELLED)
|
|
Packit |
79f644 |
{
|
|
Packit |
79f644 |
/* If a previous autodiscover attempt for the same GAsyncResult
|
|
Packit |
79f644 |
* was successful then no additional attempts are required and
|
|
Packit |
79f644 |
* we should use the result from the earlier attempt.
|
|
Packit |
79f644 |
*/
|
|
Packit |
79f644 |
op_res = g_simple_async_result_get_op_res_gboolean (data->res);
|
|
Packit |
79f644 |
goto out;
|
|
Packit |
79f644 |
}
|
|
Packit |
79f644 |
else if (status != SOUP_STATUS_OK)
|
|
Packit |
79f644 |
{
|
|
Packit |
79f644 |
g_warning ("goa_ews_client_autodiscover() failed: %u — %s", msg->status_code, msg->reason_phrase);
|
|
Packit |
79f644 |
goa_utils_set_error_soup (&error, msg);
|
|
Packit |
79f644 |
goto out;
|
|
Packit |
79f644 |
}
|
|
Packit |
79f644 |
|
|
Packit |
79f644 |
soup_buffer_free (soup_message_body_flatten (SOUP_MESSAGE (msg)->response_body));
|
|
Packit |
79f644 |
g_debug ("The response headers");
|
|
Packit |
79f644 |
g_debug ("===================");
|
|
Packit |
79f644 |
g_debug ("%s", SOUP_MESSAGE (msg)->response_body->data);
|
|
Packit |
79f644 |
|
|
Packit |
79f644 |
doc = xmlReadMemory (msg->response_body->data, msg->response_body->length, "autodiscover.xml", NULL, 0);
|
|
Packit |
79f644 |
if (doc == NULL)
|
|
Packit |
79f644 |
{
|
|
Packit |
79f644 |
g_set_error (&error,
|
|
Packit |
79f644 |
GOA_ERROR,
|
|
Packit |
79f644 |
GOA_ERROR_FAILED, /* TODO: more specific */
|
|
Packit |
79f644 |
_("Failed to parse autodiscover response XML"));
|
|
Packit |
79f644 |
goto out;
|
|
Packit |
79f644 |
}
|
|
Packit |
79f644 |
|
|
Packit |
79f644 |
node = xmlDocGetRootElement (doc);
|
|
Packit |
79f644 |
if (g_strcmp0 ((gchar *) node->name, "Autodiscover"))
|
|
Packit |
79f644 |
{
|
|
Packit |
79f644 |
g_set_error (&error,
|
|
Packit |
79f644 |
GOA_ERROR,
|
|
Packit |
79f644 |
GOA_ERROR_FAILED, /* TODO: more specific */
|
|
Packit |
79f644 |
/* Translators: the parameter is an XML element name. */
|
|
Packit |
79f644 |
_("Failed to find “%s” element"), "Autodiscover");
|
|
Packit |
79f644 |
goto out;
|
|
Packit |
79f644 |
}
|
|
Packit |
79f644 |
|
|
Packit |
79f644 |
for (node = node->children; node; node = node->next)
|
|
Packit |
79f644 |
{
|
|
Packit |
79f644 |
if (ews_client_check_node (node, "Response"))
|
|
Packit |
79f644 |
break;
|
|
Packit |
79f644 |
}
|
|
Packit |
79f644 |
if (node == NULL)
|
|
Packit |
79f644 |
{
|
|
Packit |
79f644 |
g_set_error (&error,
|
|
Packit |
79f644 |
GOA_ERROR,
|
|
Packit |
79f644 |
GOA_ERROR_FAILED, /* TODO: more specific */
|
|
Packit |
79f644 |
/* Translators: the parameter is an XML element name. */
|
|
Packit |
79f644 |
_("Failed to find “%s” element"), "Response");
|
|
Packit |
79f644 |
goto out;
|
|
Packit |
79f644 |
}
|
|
Packit |
79f644 |
|
|
Packit |
79f644 |
for (node = node->children; node; node = node->next)
|
|
Packit |
79f644 |
{
|
|
Packit |
79f644 |
if (ews_client_check_node (node, "Account"))
|
|
Packit |
79f644 |
break;
|
|
Packit |
79f644 |
}
|
|
Packit |
79f644 |
if (node == NULL)
|
|
Packit |
79f644 |
{
|
|
Packit |
79f644 |
g_set_error (&error,
|
|
Packit |
79f644 |
GOA_ERROR,
|
|
Packit |
79f644 |
GOA_ERROR_FAILED, /* TODO: more specific */
|
|
Packit |
79f644 |
/* Translators: the parameter is an XML element name. */
|
|
Packit |
79f644 |
_("Failed to find “%s” element"), "Account");
|
|
Packit |
79f644 |
goto out;
|
|
Packit |
79f644 |
}
|
|
Packit |
79f644 |
|
|
Packit |
79f644 |
for (node = node->children; node; node = node->next)
|
|
Packit |
79f644 |
{
|
|
Packit |
79f644 |
if (ews_client_check_node (node, "Protocol"))
|
|
Packit |
79f644 |
{
|
|
Packit |
79f644 |
op_res = ews_client_autodiscover_parse_protocol (node);
|
|
Packit |
79f644 |
/* Since the server may send back multiple <Protocol> nodes
|
|
Packit |
79f644 |
* don't break unless we found the one we want.
|
|
Packit |
79f644 |
*/
|
|
Packit |
79f644 |
if (op_res)
|
|
Packit |
79f644 |
break;
|
|
Packit |
79f644 |
}
|
|
Packit |
79f644 |
}
|
|
Packit |
79f644 |
if (!op_res)
|
|
Packit |
79f644 |
{
|
|
Packit |
79f644 |
g_set_error (&error,
|
|
Packit |
79f644 |
GOA_ERROR,
|
|
Packit |
79f644 |
GOA_ERROR_FAILED, /* TODO: more specific*/
|
|
Packit |
79f644 |
_("Failed to find ASUrl and OABUrl in autodiscover response"));
|
|
Packit |
79f644 |
goto out;
|
|
Packit |
79f644 |
}
|
|
Packit |
79f644 |
|
|
Packit |
79f644 |
/* This autodiscover attempt was successful. Save the result now so
|
|
Packit |
79f644 |
* that it won't get lost when we hear from another autodiscover
|
|
Packit |
79f644 |
* attempt for the same GAsyncResult.
|
|
Packit |
79f644 |
*/
|
|
Packit |
79f644 |
g_simple_async_result_set_op_res_gboolean (data->res, op_res);
|
|
Packit |
79f644 |
|
|
Packit |
79f644 |
for (idx = 0; idx < size; idx++)
|
|
Packit |
79f644 |
{
|
|
Packit |
79f644 |
if (data->msgs[idx] != NULL)
|
|
Packit |
79f644 |
{
|
|
Packit |
79f644 |
/* The callback (ie. this function) will be invoked after we
|
|
Packit |
79f644 |
* have returned to the main loop.
|
|
Packit |
79f644 |
*/
|
|
Packit |
79f644 |
soup_session_cancel_message (data->session, data->msgs[idx], SOUP_STATUS_CANCELLED);
|
|
Packit |
79f644 |
}
|
|
Packit |
79f644 |
}
|
|
Packit |
79f644 |
|
|
Packit |
79f644 |
out:
|
|
Packit |
79f644 |
/* error == NULL, if we are being aborted by the GCancellable, an
|
|
Packit |
79f644 |
* SSL error or another message that was successful.
|
|
Packit |
79f644 |
*/
|
|
Packit |
79f644 |
if (!op_res)
|
|
Packit |
79f644 |
{
|
|
Packit |
79f644 |
/* There's another request outstanding.
|
|
Packit |
79f644 |
* Hope that it has better luck.
|
|
Packit |
79f644 |
*/
|
|
Packit |
79f644 |
if (data->pending > 1)
|
|
Packit |
79f644 |
g_clear_error (&error);
|
|
Packit |
79f644 |
|
|
Packit |
79f644 |
if (error != NULL)
|
|
Packit |
79f644 |
g_simple_async_result_take_error (data->res, error);
|
|
Packit |
79f644 |
}
|
|
Packit |
79f644 |
|
|
Packit |
79f644 |
data->pending--;
|
|
Packit |
79f644 |
if (data->pending == 0)
|
|
Packit |
79f644 |
{
|
|
Packit |
79f644 |
GMainContext *context;
|
|
Packit |
79f644 |
GSource *source;
|
|
Packit |
79f644 |
|
|
Packit |
79f644 |
/* The result of the GAsyncResult should already be set when we
|
|
Packit |
79f644 |
* get here. If it wasn't explicitly set to TRUE then
|
|
Packit |
79f644 |
* autodiscovery has failed and the default value of the
|
|
Packit |
79f644 |
* GAsyncResult (which is FALSE) should be returned to the
|
|
Packit |
79f644 |
* original caller.
|
|
Packit |
79f644 |
*/
|
|
Packit |
79f644 |
|
|
Packit |
79f644 |
source = g_idle_source_new ();
|
|
Packit |
79f644 |
g_source_set_priority (source, G_PRIORITY_DEFAULT_IDLE);
|
|
Packit |
79f644 |
g_source_set_callback (source, ews_client_autodiscover_data_free, data, NULL);
|
|
Packit |
79f644 |
g_source_set_name (source, "[goa] ews_client_autodiscover_data_free");
|
|
Packit |
79f644 |
|
|
Packit |
79f644 |
context = g_main_context_get_thread_default ();
|
|
Packit |
79f644 |
g_source_attach (source, context);
|
|
Packit |
79f644 |
g_source_unref (source);
|
|
Packit |
79f644 |
}
|
|
Packit |
79f644 |
}
|
|
Packit |
79f644 |
|
|
Packit |
79f644 |
static xmlDoc *
|
|
Packit |
79f644 |
ews_client_create_autodiscover_xml (const gchar *email)
|
|
Packit |
79f644 |
{
|
|
Packit |
79f644 |
xmlDoc *doc;
|
|
Packit |
79f644 |
xmlNode *node;
|
|
Packit |
79f644 |
xmlNs *ns;
|
|
Packit |
79f644 |
|
|
Packit |
79f644 |
doc = xmlNewDoc ((xmlChar *) "1.0");
|
|
Packit |
79f644 |
|
|
Packit |
79f644 |
node = xmlNewDocNode (doc, NULL, (xmlChar *) "Autodiscover", NULL);
|
|
Packit |
79f644 |
xmlDocSetRootElement (doc, node);
|
|
Packit |
79f644 |
ns = xmlNewNs (node,
|
|
Packit |
79f644 |
(xmlChar *) "http://schemas.microsoft.com/exchange/autodiscover/outlook/requestschema/2006",
|
|
Packit |
79f644 |
NULL);
|
|
Packit |
79f644 |
|
|
Packit |
79f644 |
node = xmlNewChild (node, ns, (xmlChar *) "Request", NULL);
|
|
Packit |
79f644 |
xmlNewChild (node, ns, (xmlChar *) "EMailAddress", (xmlChar *) email);
|
|
Packit |
79f644 |
xmlNewChild (node,
|
|
Packit |
79f644 |
ns,
|
|
Packit |
79f644 |
(xmlChar *) "AcceptableResponseSchema",
|
|
Packit |
79f644 |
(xmlChar *) "http://schemas.microsoft.com/exchange/autodiscover/outlook/responseschema/2006a");
|
|
Packit |
79f644 |
|
|
Packit |
79f644 |
return doc;
|
|
Packit |
79f644 |
}
|
|
Packit |
79f644 |
|
|
Packit |
79f644 |
static void
|
|
Packit |
79f644 |
ews_client_post_restarted_cb (SoupMessage *msg, gpointer data)
|
|
Packit |
79f644 |
{
|
|
Packit |
79f644 |
xmlOutputBuffer *buf = data;
|
|
Packit |
79f644 |
|
|
Packit |
79f644 |
/* In violation of RFC2616, libsoup will change a POST request to
|
|
Packit |
79f644 |
* a GET on receiving a 302 redirect.
|
|
Packit |
79f644 |
*/
|
|
Packit |
79f644 |
g_debug ("Working around libsoup bug with redirect");
|
|
Packit |
79f644 |
g_object_set (msg, SOUP_MESSAGE_METHOD, "POST", NULL);
|
|
Packit |
79f644 |
|
|
Packit |
79f644 |
soup_message_set_request(msg,
|
|
Packit |
79f644 |
"text/xml; charset=utf-8",
|
|
Packit |
79f644 |
SOUP_MEMORY_COPY,
|
|
Packit |
79f644 |
#ifdef LIBXML2_NEW_BUFFER
|
|
Packit |
79f644 |
(gchar *) xmlOutputBufferGetContent(buf),
|
|
Packit |
79f644 |
xmlOutputBufferGetSize(buf));
|
|
Packit |
79f644 |
#else
|
|
Packit |
79f644 |
(gchar *) buf->buffer->content,
|
|
Packit |
79f644 |
buf->buffer->use);
|
|
Packit |
79f644 |
#endif
|
|
Packit |
79f644 |
}
|
|
Packit |
79f644 |
|
|
Packit |
79f644 |
static SoupMessage *
|
|
Packit |
79f644 |
ews_client_create_msg_for_url (const gchar *url, xmlOutputBuffer *buf)
|
|
Packit |
79f644 |
{
|
|
Packit |
79f644 |
SoupMessage *msg;
|
|
Packit |
79f644 |
|
|
Packit |
79f644 |
msg = soup_message_new (buf != NULL ? "POST" : "GET", url);
|
|
Packit |
79f644 |
soup_message_headers_append (msg->request_headers, "User-Agent", "libews/0.1");
|
|
Packit |
79f644 |
|
|
Packit |
79f644 |
if (buf != NULL)
|
|
Packit |
79f644 |
{
|
|
Packit |
79f644 |
soup_message_set_request (msg,
|
|
Packit |
79f644 |
"text/xml; charset=utf-8",
|
|
Packit |
79f644 |
SOUP_MEMORY_COPY,
|
|
Packit |
79f644 |
#ifdef LIBXML2_NEW_BUFFER
|
|
Packit |
79f644 |
(gchar *) xmlOutputBufferGetContent(buf),
|
|
Packit |
79f644 |
xmlOutputBufferGetSize(buf));
|
|
Packit |
79f644 |
#else
|
|
Packit |
79f644 |
(gchar *) buf->buffer->content,
|
|
Packit |
79f644 |
buf->buffer->use);
|
|
Packit |
79f644 |
#endif
|
|
Packit |
79f644 |
g_signal_connect (msg, "restarted", G_CALLBACK (ews_client_post_restarted_cb), buf);
|
|
Packit |
79f644 |
}
|
|
Packit |
79f644 |
|
|
Packit |
79f644 |
soup_buffer_free (soup_message_body_flatten (SOUP_MESSAGE (msg)->request_body));
|
|
Packit |
79f644 |
g_debug ("The request headers");
|
|
Packit |
79f644 |
g_debug ("===================");
|
|
Packit |
79f644 |
g_debug ("%s", SOUP_MESSAGE (msg)->request_body->data);
|
|
Packit |
79f644 |
|
|
Packit |
79f644 |
return msg;
|
|
Packit |
79f644 |
}
|
|
Packit |
79f644 |
|
|
Packit |
79f644 |
void
|
|
Packit |
79f644 |
goa_ews_client_autodiscover (GoaEwsClient *self,
|
|
Packit |
79f644 |
const gchar *email,
|
|
Packit |
79f644 |
const gchar *password,
|
|
Packit |
79f644 |
const gchar *username,
|
|
Packit |
79f644 |
const gchar *server,
|
|
Packit |
79f644 |
gboolean accept_ssl_errors,
|
|
Packit |
79f644 |
GCancellable *cancellable,
|
|
Packit |
79f644 |
GAsyncReadyCallback callback,
|
|
Packit |
79f644 |
gpointer user_data)
|
|
Packit |
79f644 |
{
|
|
Packit |
79f644 |
AutodiscoverData *data;
|
|
Packit |
79f644 |
AutodiscoverAuthData *auth;
|
|
Packit |
79f644 |
gchar *url1;
|
|
Packit |
79f644 |
gchar *url2;
|
|
Packit |
79f644 |
xmlDoc *doc;
|
|
Packit |
79f644 |
xmlOutputBuffer *buf;
|
|
Packit |
79f644 |
|
|
Packit |
79f644 |
g_return_if_fail (GOA_IS_EWS_CLIENT (self));
|
|
Packit |
79f644 |
g_return_if_fail (email != NULL && email[0] != '\0');
|
|
Packit |
79f644 |
g_return_if_fail (password != NULL && password[0] != '\0');
|
|
Packit |
79f644 |
g_return_if_fail (username != NULL && username[0] != '\0');
|
|
Packit |
79f644 |
g_return_if_fail (server != NULL && server[0] != '\0');
|
|
Packit |
79f644 |
g_return_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable));
|
|
Packit |
79f644 |
|
|
Packit |
79f644 |
doc = ews_client_create_autodiscover_xml (email);
|
|
Packit |
79f644 |
buf = xmlAllocOutputBuffer (NULL);
|
|
Packit |
79f644 |
xmlNodeDumpOutput (buf, doc, xmlDocGetRootElement (doc), 0, 1, NULL);
|
|
Packit |
79f644 |
xmlOutputBufferFlush (buf);
|
|
Packit |
79f644 |
|
|
Packit |
79f644 |
url1 = g_strdup_printf ("https://%s/autodiscover/autodiscover.xml", server);
|
|
Packit |
79f644 |
url2 = g_strdup_printf ("https://autodiscover.%s/autodiscover/autodiscover.xml", server);
|
|
Packit |
79f644 |
|
|
Packit |
79f644 |
/* http://msdn.microsoft.com/en-us/library/ee332364.aspx says we are
|
|
Packit |
79f644 |
* supposed to try $domain and then autodiscover.$domain. But some
|
|
Packit |
79f644 |
* people have broken firewalls on the former which drop packets
|
|
Packit |
79f644 |
* instead of rejecting connections, and make the request take ages
|
|
Packit |
79f644 |
* to time out. So run both queries in parallel and let the fastest
|
|
Packit |
79f644 |
* (successful) one win.
|
|
Packit |
79f644 |
*/
|
|
Packit |
79f644 |
data = g_slice_new0 (AutodiscoverData);
|
|
Packit |
79f644 |
data->buf = buf;
|
|
Packit |
79f644 |
data->res = g_simple_async_result_new (G_OBJECT (self), callback, user_data, goa_ews_client_autodiscover);
|
|
Packit |
79f644 |
data->msgs[0] = ews_client_create_msg_for_url (url1, buf);
|
|
Packit |
79f644 |
data->msgs[1] = ews_client_create_msg_for_url (url2, buf);
|
|
Packit |
79f644 |
data->pending = sizeof (data->msgs) / sizeof (data->msgs[0]);
|
|
Packit |
79f644 |
data->session = soup_session_new_with_options (SOUP_SESSION_SSL_STRICT, FALSE,
|
|
Packit |
79f644 |
NULL);
|
|
Packit |
79f644 |
soup_session_add_feature_by_type (data->session, SOUP_TYPE_AUTH_NTLM);
|
|
Packit |
79f644 |
data->accept_ssl_errors = accept_ssl_errors;
|
|
Packit |
79f644 |
|
|
Packit |
79f644 |
if (cancellable != NULL)
|
|
Packit |
79f644 |
{
|
|
Packit |
79f644 |
data->cancellable = g_object_ref (cancellable);
|
|
Packit |
79f644 |
data->cancellable_id = g_cancellable_connect (data->cancellable,
|
|
Packit |
79f644 |
G_CALLBACK (ews_client_autodiscover_cancelled_cb),
|
|
Packit |
79f644 |
data,
|
|
Packit |
79f644 |
NULL);
|
|
Packit |
79f644 |
g_simple_async_result_set_check_cancellable (data->res, data->cancellable);
|
|
Packit |
79f644 |
}
|
|
Packit |
79f644 |
|
|
Packit |
79f644 |
auth = g_slice_new0 (AutodiscoverAuthData);
|
|
Packit |
79f644 |
auth->username = g_strdup (username);
|
|
Packit |
79f644 |
auth->password = g_strdup (password);
|
|
Packit |
79f644 |
g_signal_connect_data (data->session,
|
|
Packit |
79f644 |
"authenticate",
|
|
Packit |
79f644 |
G_CALLBACK (ews_client_authenticate),
|
|
Packit |
79f644 |
auth,
|
|
Packit |
79f644 |
ews_client_autodiscover_auth_data_free,
|
|
Packit |
79f644 |
0);
|
|
Packit |
79f644 |
|
|
Packit |
79f644 |
g_signal_connect (data->session, "request-started", G_CALLBACK (ews_client_request_started), data);
|
|
Packit |
79f644 |
|
|
Packit |
79f644 |
soup_session_queue_message (data->session, data->msgs[0], ews_client_autodiscover_response_cb, data);
|
|
Packit |
79f644 |
soup_session_queue_message (data->session, data->msgs[1], ews_client_autodiscover_response_cb, data);
|
|
Packit |
79f644 |
|
|
Packit |
79f644 |
g_free (url2);
|
|
Packit |
79f644 |
g_free (url1);
|
|
Packit |
79f644 |
xmlFreeDoc (doc);
|
|
Packit |
79f644 |
}
|
|
Packit |
79f644 |
|
|
Packit |
79f644 |
gboolean
|
|
Packit |
79f644 |
goa_ews_client_autodiscover_finish (GoaEwsClient *self, GAsyncResult *res, GError **error)
|
|
Packit |
79f644 |
{
|
|
Packit |
79f644 |
GSimpleAsyncResult *simple;
|
|
Packit |
79f644 |
|
|
Packit |
79f644 |
g_return_val_if_fail (g_simple_async_result_is_valid (res, G_OBJECT (self), goa_ews_client_autodiscover),
|
|
Packit |
79f644 |
FALSE);
|
|
Packit |
79f644 |
g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
|
|
Packit |
79f644 |
|
|
Packit |
79f644 |
simple = G_SIMPLE_ASYNC_RESULT (res);
|
|
Packit |
79f644 |
|
|
Packit |
79f644 |
if (g_simple_async_result_propagate_error (simple, error))
|
|
Packit |
79f644 |
return FALSE;
|
|
Packit |
79f644 |
|
|
Packit |
79f644 |
return g_simple_async_result_get_op_res_gboolean (simple);
|
|
Packit |
79f644 |
}
|
|
Packit |
79f644 |
|
|
Packit |
79f644 |
/* ---------------------------------------------------------------------------------------------------- */
|
|
Packit |
79f644 |
|
|
Packit |
79f644 |
typedef struct
|
|
Packit |
79f644 |
{
|
|
Packit |
79f644 |
GError **error;
|
|
Packit |
79f644 |
GMainLoop *loop;
|
|
Packit |
79f644 |
gboolean op_res;
|
|
Packit |
79f644 |
} AutodiscoverSyncData;
|
|
Packit |
79f644 |
|
|
Packit |
79f644 |
static void
|
|
Packit |
79f644 |
ews_client_autodiscover_sync_cb (GObject *source_object, GAsyncResult *res, gpointer user_data)
|
|
Packit |
79f644 |
{
|
|
Packit |
79f644 |
AutodiscoverSyncData *data = user_data;
|
|
Packit |
79f644 |
|
|
Packit |
79f644 |
data->op_res = goa_ews_client_autodiscover_finish (GOA_EWS_CLIENT (source_object), res, data->error);
|
|
Packit |
79f644 |
g_main_loop_quit (data->loop);
|
|
Packit |
79f644 |
}
|
|
Packit |
79f644 |
|
|
Packit |
79f644 |
gboolean
|
|
Packit |
79f644 |
goa_ews_client_autodiscover_sync (GoaEwsClient *self,
|
|
Packit |
79f644 |
const gchar *email,
|
|
Packit |
79f644 |
const gchar *password,
|
|
Packit |
79f644 |
const gchar *username,
|
|
Packit |
79f644 |
const gchar *server,
|
|
Packit |
79f644 |
gboolean accept_ssl_errors,
|
|
Packit |
79f644 |
GCancellable *cancellable,
|
|
Packit |
79f644 |
GError **error)
|
|
Packit |
79f644 |
{
|
|
Packit |
79f644 |
AutodiscoverSyncData data;
|
|
Packit |
79f644 |
GMainContext *context = NULL;
|
|
Packit |
79f644 |
|
|
Packit |
79f644 |
data.error = error;
|
|
Packit |
79f644 |
|
|
Packit |
79f644 |
context = g_main_context_new ();
|
|
Packit |
79f644 |
g_main_context_push_thread_default (context);
|
|
Packit |
79f644 |
data.loop = g_main_loop_new (context, FALSE);
|
|
Packit |
79f644 |
|
|
Packit |
79f644 |
goa_ews_client_autodiscover (self,
|
|
Packit |
79f644 |
email,
|
|
Packit |
79f644 |
password,
|
|
Packit |
79f644 |
username,
|
|
Packit |
79f644 |
server,
|
|
Packit |
79f644 |
accept_ssl_errors,
|
|
Packit |
79f644 |
cancellable,
|
|
Packit |
79f644 |
ews_client_autodiscover_sync_cb,
|
|
Packit |
79f644 |
&data);
|
|
Packit |
79f644 |
g_main_loop_run (data.loop);
|
|
Packit |
79f644 |
g_main_loop_unref (data.loop);
|
|
Packit |
79f644 |
|
|
Packit |
79f644 |
g_main_context_pop_thread_default (context);
|
|
Packit |
79f644 |
g_main_context_unref (context);
|
|
Packit |
79f644 |
|
|
Packit |
79f644 |
return data.op_res;
|
|
Packit |
79f644 |
}
|