[evolution-ews/wip/mcrha/office365] Remove e-o365-message for now and introduce e-o365-json-utils and e-o365-soup-logger
- From: Milan Crha <mcrha src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [evolution-ews/wip/mcrha/office365] Remove e-o365-message for now and introduce e-o365-json-utils and e-o365-soup-logger
- Date: Tue, 9 Jun 2020 11:50:43 +0000 (UTC)
commit fbd0ec8c554d97ceaaca67e9534676708a44f994
Author: Milan Crha <mcrha redhat com>
Date: Tue Jun 9 13:53:59 2020 +0200
Remove e-o365-message for now and introduce e-o365-json-utils and e-o365-soup-logger
src/Office365/common/CMakeLists.txt | 6 +-
src/Office365/common/e-o365-connection.c | 453 +++++++++++++++++++++++++++++-
src/Office365/common/e-o365-connection.h | 8 +
src/Office365/common/e-o365-json-utils.c | 60 ++++
src/Office365/common/e-o365-json-utils.h | 35 +++
src/Office365/common/e-o365-message.c | 60 ----
src/Office365/common/e-o365-message.h | 65 -----
src/Office365/common/e-o365-soup-logger.c | 179 ++++++++++++
src/Office365/common/e-o365-soup-logger.h | 30 ++
9 files changed, 764 insertions(+), 132 deletions(-)
---
diff --git a/src/Office365/common/CMakeLists.txt b/src/Office365/common/CMakeLists.txt
index e9a324d2..8fa026c0 100644
--- a/src/Office365/common/CMakeLists.txt
+++ b/src/Office365/common/CMakeLists.txt
@@ -5,8 +5,10 @@ set(SOURCES
camel-o365-settings.h
e-o365-connection.c
e-o365-connection.h
- e-o365-message.c
- e-o365-message.h
+ e-o365-json-utils.c
+ e-o365-json-utils.h
+ e-o365-soup-logger.c
+ e-o365-soup-logger.h
e-oauth2-service-office365.c
e-oauth2-service-office365.h
e-source-o365-folder.c
diff --git a/src/Office365/common/e-o365-connection.c b/src/Office365/common/e-o365-connection.c
index a5c3afc5..4d333125 100644
--- a/src/Office365/common/e-o365-connection.c
+++ b/src/Office365/common/e-o365-connection.c
@@ -19,11 +19,18 @@
#include <glib.h>
#include <glib/gi18n-lib.h>
+#include <json-glib/json-glib.h>
#include "camel-o365-settings.h"
+#include "e-o365-soup-logger.h"
#include "e-o365-connection.h"
+typedef enum {
+ E_O365_API_V1_0,
+ E_O365_API_BETA
+} EO365ApiVersion;
+
#define LOCK(x) g_rec_mutex_lock (&(x->priv->property_lock))
#define UNLOCK(x) g_rec_mutex_unlock (&(x->priv->property_lock))
@@ -57,6 +64,17 @@ G_DEFINE_TYPE_WITH_PRIVATE (EO365Connection, e_o365_connection, G_TYPE_OBJECT)
static GHashTable *opened_connections = NULL;
G_LOCK_DEFINE_STATIC (opened_connections);
+static gboolean
+o365_log_enabled (void)
+{
+ static gint log_enabled = -1;
+
+ if (log_enabled == -1)
+ log_enabled = g_strcmp0 (g_getenv ("O365_DEBUG"), "1") == 0 ? 1 : 0;
+
+ return log_enabled == 1;
+}
+
static SoupSession *
o365_connection_ref_soup_session (EO365Connection *cnc)
{
@@ -393,15 +411,11 @@ static void
o365_connection_constructed (GObject *object)
{
EO365Connection *cnc = E_O365_CONNECTION (object);
- static gint log_enabled = -1;
/* Chain up to parent's method. */
G_OBJECT_CLASS (e_o365_connection_parent_class)->constructed (object);
- if (log_enabled == -1)
- log_enabled = g_strcmp0 (g_getenv ("O365_DEBUG"), "1") == 0 ? 1 : 0;
-
- if (log_enabled) {
+ if (o365_log_enabled ()) {
SoupLogger *logger = soup_logger_new (SOUP_LOGGER_LOG_BODY, -1);
soup_session_add_feature (cnc->priv->soup_session, SOUP_SESSION_FEATURE (logger));
@@ -799,3 +813,432 @@ e_o365_connection_set_bearer_auth (EO365Connection *cnc,
UNLOCK (cnc);
}
+
+static void
+o365_connection_request_cancelled_cb (GCancellable *cancellable,
+ gpointer user_data)
+{
+ EFlag *flag = user_data;
+
+ g_return_if_fail (flag != NULL);
+
+ e_flag_set (flag);
+}
+
+typedef gboolean (* EO365ResponseFunc) (EO365Connection *cnc,
+ SoupMessage *message,
+ GInputStream *input_stream,
+ JsonNode *node,
+ gpointer user_data,
+ gchar **out_next_link,
+ GCancellable *cancellable,
+ GError **error);
+
+static gboolean
+o365_connection_send_request_sync (EO365Connection *cnc,
+ SoupMessage *message,
+ EO365ResponseFunc func,
+ gpointer func_user_data,
+ GCancellable *cancellable,
+ GError **error)
+{
+ SoupSession *soup_session;
+ gint need_retry_seconds = 30;
+ gboolean success = FALSE, need_retry = TRUE;
+
+ g_return_val_if_fail (E_IS_O365_CONNECTION (cnc), FALSE);
+ g_return_val_if_fail (SOUP_IS_MESSAGE (message), FALSE);
+ g_return_val_if_fail (func != NULL, FALSE);
+
+ while (need_retry && !g_cancellable_is_cancelled (cancellable) && message->status_code !=
SOUP_STATUS_CANCELLED) {
+ need_retry = FALSE;
+
+ LOCK (cnc);
+
+ if (cnc->priv->backoff_for_usec) {
+ EFlag *flag;
+ gint64 wait_ms;
+ gulong handler_id = 0;
+
+ wait_ms = cnc->priv->backoff_for_usec / G_TIME_SPAN_MILLISECOND;
+
+ UNLOCK (cnc);
+
+ flag = e_flag_new ();
+
+ if (cancellable) {
+ handler_id = g_cancellable_connect (cancellable, G_CALLBACK
(o365_connection_request_cancelled_cb),
+ flag, NULL);
+ }
+
+ while (wait_ms > 0 && !g_cancellable_is_cancelled (cancellable) &&
message->status_code != SOUP_STATUS_CANCELLED) {
+ gint64 now = g_get_monotonic_time ();
+ gint left_minutes, left_seconds;
+
+ left_minutes = wait_ms / 60000;
+ left_seconds = (wait_ms / 1000) % 60;
+
+ if (left_minutes > 0) {
+ camel_operation_push_message (cancellable,
+ g_dngettext (GETTEXT_PACKAGE,
+ "Office 365 server is busy, waiting to retry (%d:%02d
minute)",
+ "Office 365 server is busy, waiting to retry (%d:%02d
minutes)", left_minutes),
+ left_minutes, left_seconds);
+ } else {
+ camel_operation_push_message (cancellable,
+ g_dngettext (GETTEXT_PACKAGE,
+ "Office 365 server is busy, waiting to retry (%d
second)",
+ "Office 365 server is busy, waiting to retry (%d
seconds)", left_seconds),
+ left_seconds);
+ }
+
+ e_flag_wait_until (flag, now + (G_TIME_SPAN_MILLISECOND * (wait_ms > 1000 ?
1000 : wait_ms)));
+ e_flag_clear (flag);
+
+ now = g_get_monotonic_time () - now;
+ now = now / G_TIME_SPAN_MILLISECOND;
+
+ if (now >= wait_ms)
+ wait_ms = 0;
+ wait_ms -= now;
+
+ camel_operation_pop_message (cancellable);
+ }
+
+ if (handler_id)
+ g_cancellable_disconnect (cancellable, handler_id);
+
+ e_flag_free (flag);
+
+ LOCK (cnc);
+
+ cnc->priv->backoff_for_usec = 0;
+ }
+
+ if (g_cancellable_set_error_if_cancelled (cancellable, error)) {
+ UNLOCK (cnc);
+
+ soup_message_set_status (message, SOUP_STATUS_CANCELLED);
+
+ return FALSE;
+ }
+
+ soup_session = cnc->priv->soup_session ? g_object_ref (cnc->priv->soup_session) : NULL;
+
+ UNLOCK (cnc);
+
+ if (soup_session &&
+ o365_connection_utils_prepare_message (cnc, soup_session, message, cancellable)) {
+ GInputStream *input_stream;
+
+ input_stream = soup_session_send (soup_session, message, cancellable, error);
+
+ success = input_stream != NULL;
+
+ if (success && o365_log_enabled ())
+ input_stream = e_o365_soup_logger_attach (message, input_stream);
+
+ /* Throttling - https://docs.microsoft.com/en-us/graph/throttling */
+ if (message->status_code == 429 ||
+ /*
https://docs.microsoft.com/en-us/graph/best-practices-concept#handling-expected-errors */
+ message->status_code == SOUP_STATUS_SERVICE_UNAVAILABLE) {
+ need_retry = TRUE;
+ }
+
+ if (need_retry) {
+ const gchar *retry_after_str;
+ gint64 retry_after;
+
+ retry_after_str = message->response_headers ? soup_message_headers_get_one
(message->response_headers, "Retry-After") : NULL;
+
+ if (retry_after_str && *retry_after_str)
+ retry_after = g_ascii_strtoll (retry_after_str, NULL, 10);
+ else
+ retry_after = 0;
+
+ if (retry_after > 0)
+ need_retry_seconds = retry_after;
+ else if (need_retry_seconds < 120)
+ need_retry_seconds *= 2;
+
+ LOCK (cnc);
+
+ if (cnc->priv->backoff_for_usec < need_retry_seconds * G_USEC_PER_SEC)
+ cnc->priv->backoff_for_usec = need_retry_seconds * G_USEC_PER_SEC;
+
+ if (message->status_code == SOUP_STATUS_SERVICE_UNAVAILABLE)
+ soup_session_abort (soup_session);
+
+ UNLOCK (cnc);
+
+ success = FALSE;
+ } else if (success) {
+ JsonParser *json_parser = NULL;
+ const gchar *content_type;
+
+ content_type = message->response_headers ?
soup_message_headers_get_content_type (message->response_headers, NULL) : NULL;
+
+ if (content_type && g_ascii_strcasecmp (content_type, "application/json") ==
0) {
+ json_parser = json_parser_new_immutable ();
+
+ success = json_parser_load_from_stream (json_parser, input_stream,
cancellable, error);
+ }
+
+ if (success) {
+ JsonNode *node;
+ gchar *next_link = NULL;
+
+ node = json_parser ? json_parser_get_root (json_parser) : NULL;
+
+ success = func (cnc, message, input_stream, node, func_user_data,
&next_link, cancellable, error);
+
+ if (success && next_link && *next_link) {
+ SoupURI *suri;
+
+ suri = soup_uri_new (next_link);
+
+ /* Check whether the server returned correct nextLink URI */
+ g_warn_if_fail (suri != NULL);
+
+ if (suri) {
+ need_retry = TRUE;
+
+ soup_message_set_uri (message, suri);
+ soup_uri_free (suri);
+ }
+ }
+
+ g_free (next_link);
+ }
+
+ g_clear_object (&json_parser);
+ }
+
+ g_clear_object (&input_stream);
+ } else if (!message->status_code) {
+ soup_message_set_status (message, SOUP_STATUS_CANCELLED);
+ }
+
+ g_clear_object (&soup_session);
+
+ if (need_retry) {
+ success = FALSE;
+ g_clear_error (error);
+ }
+ }
+
+ return success;
+}
+
+/* Expects pair of parameters 'name', 'value'; if value is NULL, the parameter is skipped; the last
parameter name should be NULL */
+static gchar *
+e_o365_construct_uri (EO365Connection *cnc,
+ gboolean include_user,
+ const gchar *user_override,
+ EO365ApiVersion api_version,
+ const gchar *api_part, /* NULL for 'users', empty string to skip */
+ const gchar *resource,
+ const gchar *path,
+ ...) G_GNUC_NULL_TERMINATED;
+
+static gchar *
+e_o365_construct_uri (EO365Connection *cnc,
+ gboolean include_user,
+ const gchar *user_override,
+ EO365ApiVersion api_version,
+ const gchar *api_part,
+ const gchar *resource,
+ const gchar *path,
+ ...)
+{
+ va_list args;
+ const gchar *name, *value;
+ gboolean first_param = TRUE;
+ GString *uri;
+
+ g_return_val_if_fail (E_IS_O365_CONNECTION (cnc), NULL);
+
+ if (!api_part)
+ api_part = "users";
+
+ uri = g_string_sized_new (128);
+
+ /* https://graph.microsoft.com/v1.0/users/XUSERX/mailFolders */
+
+ g_string_append (uri, "https://graph.microsoft.com");
+
+ switch (api_version) {
+ case E_O365_API_V1_0:
+ g_string_append_c (uri, '/');
+ g_string_append (uri, "v1.0");
+ break;
+ case E_O365_API_BETA:
+ g_string_append_c (uri, '/');
+ g_string_append (uri, "beta");
+ break;
+ }
+
+ if (*api_part) {
+ g_string_append_c (uri, '/');
+ g_string_append (uri, api_part);
+ }
+
+ if (include_user) {
+ if (user_override) {
+ gchar *encoded;
+
+ encoded = soup_uri_encode (user_override, NULL);
+
+ g_string_append_c (uri, '/');
+ g_string_append (uri, encoded);
+
+ g_free (encoded);
+ } else {
+ CamelO365Settings *settings;
+ gchar *user;
+
+ settings = e_o365_connection_get_settings (cnc);
+ user = camel_network_settings_dup_user (CAMEL_NETWORK_SETTINGS (settings));
+
+ if (user && *user) {
+ gchar *encoded;
+
+ encoded = soup_uri_encode (user, NULL);
+
+ g_string_append_c (uri, '/');
+ g_string_append (uri, encoded);
+
+ g_free (encoded);
+ }
+
+ g_free (user);
+ }
+ }
+
+ if (resource && *resource) {
+ g_string_append_c (uri, '/');
+ g_string_append (uri, resource);
+ }
+
+ if (path && *path) {
+ g_string_append_c (uri, '/');
+ g_string_append (uri, path);
+ }
+
+ va_start (args, path);
+
+ name = va_arg (args, const gchar *);
+
+ while (name) {
+ value = va_arg (args, const gchar *);
+
+ if (*name && value) {
+ g_string_append_c (uri, first_param ? '?' : '&');
+
+ g_string_append (uri, name);
+ g_string_append_c (uri, '=');
+
+ if (*value) {
+ gchar *encoded;
+
+ encoded = soup_uri_encode (value, NULL);
+
+ g_string_append (uri, encoded);
+
+ g_free (encoded);
+ }
+ }
+
+ name = va_arg (args, const gchar *);
+ }
+
+ va_end (args);
+
+ return g_string_free (uri, FALSE);
+}
+
+static gboolean
+e_o365_list_folders_response_cb (EO365Connection *cnc,
+ SoupMessage *message,
+ GInputStream *input_stream,
+ JsonNode *node,
+ gpointer user_data,
+ gchar **out_next_link,
+ GCancellable *cancellable,
+ GError **error)
+{
+ JsonObject *object;
+ JsonArray *value;
+ GSList **out_folders = user_data;
+ guint ii, len;
+
+ g_return_val_if_fail (G_TYPE_CHECK_INSTANCE_TYPE (node, JSON_TYPE_NODE), FALSE);
+ g_return_val_if_fail (out_folders != NULL, FALSE);
+ g_return_val_if_fail (out_next_link != NULL, FALSE);
+ g_return_val_if_fail (JSON_NODE_HOLDS_OBJECT (node), FALSE);
+
+ object = json_node_get_object (node);
+ g_return_val_if_fail (object != NULL, FALSE);
+
+ *out_next_link = g_strdup (json_object_get_string_member (object, "@odata.nextLink"));
+
+ value = json_object_get_array_member (object, "value");
+ g_return_val_if_fail (value != NULL, FALSE);
+
+ len = json_array_get_length (value);
+
+ for (ii = 0; ii < len; ii++) {
+ JsonNode *elem = json_array_get_element (value, ii);
+
+ g_warn_if_fail (JSON_NODE_HOLDS_OBJECT (elem));
+
+ if (JSON_NODE_HOLDS_OBJECT (elem)) {
+ JsonObject *elem_object = json_node_get_object (elem);
+
+ if (elem_object)
+ *out_folders = g_slist_prepend (*out_folders, json_object_ref (elem_object));
+ }
+ }
+
+ return TRUE;
+}
+
+gboolean
+e_o365_connection_list_folders_sync (EO365Connection *cnc,
+ const gchar *user_override, /* for which user, NULL to use the account
user */
+ const gchar *from_path, /* path for the folder to read, NULL for top
user folder */
+ const gchar *select, /* fields to select, nullable */
+ GSList **out_folders, /* JsonObject * - the returned mailFolder objects
*/
+ GCancellable *cancellable,
+ GError **error)
+{
+ SoupMessage *message;
+ gchar *uri;
+ gboolean success;
+
+ g_return_val_if_fail (E_IS_O365_CONNECTION (cnc), FALSE);
+ g_return_val_if_fail (out_folders != NULL, FALSE);
+
+ uri = e_o365_construct_uri (cnc, TRUE, user_override, E_O365_API_V1_0, NULL,
+ "mailFolders",
+ from_path,
+ "$select", select,
+ NULL);
+
+ message = soup_message_new (SOUP_METHOD_GET, uri);
+
+ if (!message) {
+ g_set_error (error, SOUP_HTTP_ERROR, SOUP_STATUS_MALFORMED, _("Malformed URI: ā%sā"), uri);
+ g_free (uri);
+
+ return FALSE;
+ }
+
+ g_free (uri);
+
+ success = o365_connection_send_request_sync (cnc, message, e_o365_list_folders_response_cb,
out_folders, cancellable, error);
+
+ g_clear_object (&message);
+
+ return success;
+}
diff --git a/src/Office365/common/e-o365-connection.h b/src/Office365/common/e-o365-connection.h
index b174412e..f16145d9 100644
--- a/src/Office365/common/e-o365-connection.h
+++ b/src/Office365/common/e-o365-connection.h
@@ -93,6 +93,14 @@ ESoupAuthBearer *
void e_o365_connection_set_bearer_auth
(EO365Connection *cnc,
ESoupAuthBearer *bearer_auth);
+gboolean e_o365_connection_list_folders_sync
+ (EO365Connection *cnc,
+ const gchar *user_override, /* for which user, NULL to use
the account user */
+ const gchar *from_path, /* path for the folder to read, NULL
for top user folder */
+ const gchar *select, /* fields to select, nullable */
+ GSList **out_folders, /* JsonObject * - the returned
mailFolder objects */
+ GCancellable *cancellable,
+ GError **error);
G_END_DECLS
diff --git a/src/Office365/common/e-o365-json-utils.c b/src/Office365/common/e-o365-json-utils.c
new file mode 100644
index 00000000..9faede3b
--- /dev/null
+++ b/src/Office365/common/e-o365-json-utils.c
@@ -0,0 +1,60 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Copyright (C) 2020 Red Hat (www.redhat.com)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 2 of the GNU Lesser General Public
+ * License as published by the Free Software Foundation.
+ *
+ * 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 Lesser General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "evolution-ews-config.h"
+
+#include <json-glib/json-glib.h>
+
+#include "e-o365-json-utils.h"
+
+/* https://docs.microsoft.com/en-us/graph/api/resources/mailfolder?view=graph-rest-1.0 */
+s
+const gchar *
+e_o365_mail_folder_get_display_name (JsonObject *object)
+{
+ return json_object_get_string_member (object, "displayName");
+}
+
+const gchar *
+e_o365_mail_folder_get_id (JsonObject *object)
+{
+ return json_object_get_string_member (object, "id");
+}
+
+const gchar *
+e_o365_mail_folder_get_parent_folder_id (JsonObject *object)
+{
+ return json_object_get_string_member (object, "parentFolderId");
+}
+
+gint32
+e_o365_mail_folder_get_child_folder_count (JsonObject *object)
+{
+ return (gint32) json_object_get_int_member (object, "childFolderCount");
+}
+
+gint32
+e_o365_mail_folder_get_total_item_count (JsonObject *object)
+{
+ return (gint32) json_object_get_int_member (object, "totalItemCount");
+}
+
+gint32
+e_o365_mail_folder_get_unread_item_count (JsonObject *object)
+{
+ return (gint32) json_object_get_int_member (object, "unreadItemCount");
+}
diff --git a/src/Office365/common/e-o365-json-utils.h b/src/Office365/common/e-o365-json-utils.h
new file mode 100644
index 00000000..dd8bd573
--- /dev/null
+++ b/src/Office365/common/e-o365-json-utils.h
@@ -0,0 +1,35 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Copyright (C) 2020 Red Hat (www.redhat.com)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 2 of the GNU Lesser General Public
+ * License as published by the Free Software Foundation.
+ *
+ * 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 Lesser General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef E_O365_JSON_UTILS_H
+#define E_O365_JSON_UTILS_H
+
+#include <json-glib/json-glib.h>
+
+G_BEGIN_DECLS
+
+const gchar * e_o365_mail_folder_get_display_name (JsonObject *object);
+const gchar * e_o365_mail_folder_get_id (JsonObject *object);
+const gchar * e_o365_mail_folder_get_parent_folder_id (JsonObject *object);
+gint32 e_o365_mail_folder_get_child_folder_count
+ (JsonObject *object);
+gint32 e_o365_mail_folder_get_total_item_count (JsonObject *object);
+gint32 e_o365_mail_folder_get_unread_item_count(JsonObject *object);
+
+G_END_DECLS
+
+#endif /* E_O365_JSON_UTILS_H */
diff --git a/src/Office365/common/e-o365-soup-logger.c b/src/Office365/common/e-o365-soup-logger.c
new file mode 100644
index 00000000..18d8a817
--- /dev/null
+++ b/src/Office365/common/e-o365-soup-logger.c
@@ -0,0 +1,179 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Copyright (C) 2020 Red Hat (www.redhat.com)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 2 of the GNU Lesser General Public
+ * License as published by the Free Software Foundation.
+ *
+ * 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 Lesser General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "evolution-ews-config.h"
+
+#include <glib.h>
+
+#include "e-o365-soup-logger.h"
+
+/* Standard GObject macros */
+#define E_TYPE_O365_SOUP_LOGGER \
+ (e_o365_soup_logger_get_type ())
+#define E_O365_SOUP_LOGGER(obj) \
+ (G_TYPE_CHECK_INSTANCE_CAST \
+ ((obj), E_TYPE_O365_SOUP_LOGGER, EO365SoupLogger))
+#define E_O365_SOUP_LOGGER_CLASS(cls) \
+ (G_TYPE_CHECK_CLASS_CAST \
+ ((cls), E_TYPE_O365_SOUP_LOGGER, EO365SoupLoggerClass))
+#define E_IS_O365_SOUP_LOGGER(obj) \
+ (G_TYPE_CHECK_INSTANCE_TYPE \
+ ((obj), E_TYPE_O365_SOUP_LOGGER))
+#define E_IS_O365_SOUP_LOGGER_CLASS(cls) \
+ (G_TYPE_CHECK_CLASS_TYPE \
+ ((cls), E_TYPE_O365_SOUP_LOGGER))
+#define E_O365_SOUP_LOGGER_GET_CLASS(obj) \
+ (G_TYPE_INSTANCE_GET_CLASS \
+ ((obj), E_TYPE_O365_SOUP_LOGGER))
+
+G_BEGIN_DECLS
+
+typedef struct _EO365SoupLogger EO365SoupLogger;
+typedef struct _EO365SoupLoggerClass EO365SoupLoggerClass;
+
+struct _EO365SoupLogger {
+ GObject parent;
+
+ GString *data;
+};
+
+struct _EO365SoupLoggerClass {
+ GObjectClass parent_class;
+};
+
+GType e_o365_soup_logger_get_type (void) G_GNUC_CONST;
+
+static void e_o365_soup_logger_converter_interface_init
+ (GConverterIface *iface);
+
+G_DEFINE_TYPE_WITH_CODE (EO365SoupLogger, e_o365_soup_logger, G_TYPE_OBJECT,
+ G_IMPLEMENT_INTERFACE (G_TYPE_CONVERTER, e_o365_soup_logger_converter_interface_init))
+
+static GConverterResult
+e_o365_soup_logger_convert (GConverter *converter,
+ gconstpointer inbuf,
+ gsize inbuf_size,
+ gpointer outbuf,
+ gsize outbuf_size,
+ GConverterFlags flags,
+ gsize *bytes_read,
+ gsize *bytes_written,
+ GError **error)
+{
+ EO365SoupLogger *logger = E_O365_SOUP_LOGGER (converter);
+ GConverterResult result;
+ gsize min_size;
+
+ min_size = MIN (inbuf_size, outbuf_size);
+
+ if (inbuf && min_size)
+ memcpy (outbuf, inbuf, min_size);
+ *bytes_read = *bytes_written = min_size;
+
+ if (!logger->data)
+ logger->data = g_string_sized_new (10240);
+
+ g_string_append_len (logger->data, (const gchar *) outbuf, (gssize) min_size);
+
+ if ((flags & G_CONVERTER_INPUT_AT_END) != 0)
+ result = G_CONVERTER_FINISHED;
+ else if ((flags & G_CONVERTER_FLUSH) != 0)
+ result = G_CONVERTER_FLUSHED;
+ else
+ result = G_CONVERTER_CONVERTED;
+
+ return result;
+}
+
+static void
+e_o365_soup_logger_reset (GConverter *converter)
+{
+ /* Nothing to do. */
+}
+
+static void
+e_o365_soup_logger_print_data (EO365SoupLogger *logger)
+{
+ if (logger->data) {
+ g_print ("%s\n", logger->data->str);
+ g_string_free (logger->data, TRUE);
+ logger->data = NULL;
+ }
+}
+
+static void
+e_o365_soup_logger_message_finished_cb (SoupMessage *msg,
+ gpointer user_data)
+{
+ EO365SoupLogger *logger = user_data;
+
+ g_return_if_fail (E_IS_O365_SOUP_LOGGER (logger));
+
+ e_o365_soup_logger_print_data (logger);
+}
+
+static void
+o365_soup_logger_finalize (GObject *object)
+{
+ EO365SoupLogger *logger = E_O365_SOUP_LOGGER (object);
+
+ e_o365_soup_logger_print_data (logger);
+
+ /* Chain up to parent's method. */
+ G_OBJECT_CLASS (e_o365_soup_logger_parent_class)->finalize (object);
+}
+
+static void
+e_o365_soup_logger_class_init (EO365SoupLoggerClass *class)
+{
+ GObjectClass *object_class;
+
+ object_class = G_OBJECT_CLASS (class);
+ object_class->finalize = o365_soup_logger_finalize;
+}
+
+static void
+e_o365_soup_logger_converter_interface_init (GConverterIface *iface)
+{
+ iface->convert = e_o365_soup_logger_convert;
+ iface->reset = e_o365_soup_logger_reset;
+}
+
+static void
+e_o365_soup_logger_init (EO365SoupLogger *logger)
+{
+}
+
+GInputStream *
+e_o365_soup_logger_attach (SoupMessage *message,
+ GInputStream *input_stream)
+{
+ GConverter *logger;
+
+ g_return_val_if_fail (SOUP_IS_MESSAGE (message), input_stream);
+ g_return_val_if_fail (G_IS_INPUT_STREAM (input_stream), input_stream);
+
+ logger = g_object_new (E_TYPE_O365_SOUP_LOGGER, NULL);
+
+ input_stream = g_converter_input_stream_new (input_stream, logger);
+ g_object_set_data_full (G_OBJECT (message), "EO365SoupLogger", logger, g_object_unref);
+
+ g_signal_connect_object (message, "finished",
+ G_CALLBACK (e_o365_soup_logger_message_finished_cb), logger, G_CONNECT_AFTER);
+
+ return input_stream;
+}
diff --git a/src/Office365/common/e-o365-soup-logger.h b/src/Office365/common/e-o365-soup-logger.h
new file mode 100644
index 00000000..6166c642
--- /dev/null
+++ b/src/Office365/common/e-o365-soup-logger.h
@@ -0,0 +1,30 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Copyright (C) 2020 Red Hat (www.redhat.com)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 2 of the GNU Lesser General Public
+ * License as published by the Free Software Foundation.
+ *
+ * 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 Lesser General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef E_O365_SOUP_LOGGER_H
+#define E_O365_SOUP_LOGGER_H
+
+#include <glib-object.h>
+
+#include <libsoup/soup.h>
+
+GInputStream * e_o365_soup_logger_attach (SoupMessage *message,
+ GInputStream *input_stream);
+
+G_END_DECLS
+
+#endif /* E_O365_SOUP_LOGGER_H */
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]