[evolution-ews/wip/mcrha/office365] Remove e-o365-message for now and introduce e-o365-json-utils and e-o365-soup-logger



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]