|
|
@@ -0,0 +1,616 @@
|
|
|
+/* iridium-standard-file.c
|
|
|
+ *
|
|
|
+ * Copyright 2018 Matthias Vogelgesang
|
|
|
+ *
|
|
|
+ * 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 3 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, see <http://www.gnu.org/licenses/>.
|
|
|
+ */
|
|
|
+
|
|
|
+#include <string.h>
|
|
|
+#include <stdio.h>
|
|
|
+#include <json-glib/json-glib.h>
|
|
|
+#include <libsoup/soup.h>
|
|
|
+#include <nettle/aes.h>
|
|
|
+#include <nettle/cbc.h>
|
|
|
+#include <nettle/sha2.h>
|
|
|
+#include <nettle/hmac.h>
|
|
|
+#include <nettle/pbkdf2.h>
|
|
|
+#include "iridium-standard-file.h"
|
|
|
+#include "iridium-note.h"
|
|
|
+
|
|
|
+typedef enum {
|
|
|
+ SF_VERSION_001,
|
|
|
+ SF_VERSION_002,
|
|
|
+} StandardFileVersion;
|
|
|
+
|
|
|
+typedef enum {
|
|
|
+ SF_FUNC_PBKDF2,
|
|
|
+} StandardFileFunc;
|
|
|
+
|
|
|
+typedef enum {
|
|
|
+ SF_HASH_SHA512,
|
|
|
+} StandardFileHash;
|
|
|
+
|
|
|
+typedef struct {
|
|
|
+ guint cost;
|
|
|
+ guint key_size;
|
|
|
+ gchar *salt;
|
|
|
+ StandardFileFunc func;
|
|
|
+ StandardFileFunc hash;
|
|
|
+ StandardFileVersion version;
|
|
|
+ struct {
|
|
|
+ guint8 password[32];
|
|
|
+ guint8 master[32];
|
|
|
+ guint8 auth[32];
|
|
|
+ } keys;
|
|
|
+} StandardFileAuthParams;
|
|
|
+
|
|
|
+typedef struct {
|
|
|
+ IridiumStandardFile *client;
|
|
|
+ JsonParser *parser;
|
|
|
+ gchar *password;
|
|
|
+ GCancellable *cancellable;
|
|
|
+} ReadAuthParams;
|
|
|
+
|
|
|
+struct _IridiumStandardFile
|
|
|
+{
|
|
|
+ GObject parent_instance;
|
|
|
+
|
|
|
+ gchar *email;
|
|
|
+ gchar *token;
|
|
|
+ SoupSession *session;
|
|
|
+ SoupURI *base_uri;
|
|
|
+ StandardFileAuthParams auth_params;
|
|
|
+};
|
|
|
+
|
|
|
+G_DEFINE_TYPE (IridiumStandardFile, iridium_standard_file, G_TYPE_OBJECT)
|
|
|
+
|
|
|
+const SecretSchema *
|
|
|
+standard_file_get_schema (void)
|
|
|
+{
|
|
|
+ static const SecretSchema schema = {
|
|
|
+ "net.bloerg.Iridium", SECRET_SCHEMA_NONE,
|
|
|
+ {
|
|
|
+ { "email", SECRET_SCHEMA_ATTRIBUTE_STRING },
|
|
|
+ { "server", SECRET_SCHEMA_ATTRIBUTE_STRING },
|
|
|
+ { NULL, 0 },
|
|
|
+ }
|
|
|
+ };
|
|
|
+ return &schema;
|
|
|
+}
|
|
|
+
|
|
|
+IridiumStandardFile *
|
|
|
+iridium_standard_file_new (void)
|
|
|
+{
|
|
|
+ return g_object_new (IRIDIUM_TYPE_STANDARD_FILE, NULL);
|
|
|
+}
|
|
|
+
|
|
|
+static guint8 *
|
|
|
+unhexlify (const gchar *s, gsize length)
|
|
|
+{
|
|
|
+ guint8 *result;
|
|
|
+
|
|
|
+ result = g_malloc0 (length / 2);
|
|
|
+
|
|
|
+ for (gsize i = 0; i < length / 2; i++)
|
|
|
+ sscanf (&s[i * 2], "%2hhx", &result[i]);
|
|
|
+
|
|
|
+ return result;
|
|
|
+}
|
|
|
+
|
|
|
+static gchar *
|
|
|
+hexlify (const guint8 *s, gsize length)
|
|
|
+{
|
|
|
+ gchar *result;
|
|
|
+
|
|
|
+ result = g_malloc0 (2 * length + 1);
|
|
|
+
|
|
|
+ for (gsize i = 0; i < length; i++)
|
|
|
+ g_snprintf (&result[2 * i], 3, "%02x", s[i]);
|
|
|
+
|
|
|
+ return result;
|
|
|
+}
|
|
|
+
|
|
|
+static void
|
|
|
+derive_keys (const guint8 *password,
|
|
|
+ StandardFileAuthParams *params)
|
|
|
+{
|
|
|
+ struct hmac_sha512_ctx context;
|
|
|
+ guint8 dst[96];
|
|
|
+
|
|
|
+ g_assert_nonnull (params->salt);
|
|
|
+
|
|
|
+ hmac_sha512_set_key (&context, strlen ((gchar *) password), password);
|
|
|
+
|
|
|
+ PBKDF2 (&context, hmac_sha512_update, hmac_sha512_digest, SHA512_DIGEST_SIZE,
|
|
|
+ params->cost, strlen (params->salt), (guint8 *) params->salt, 96, dst);
|
|
|
+
|
|
|
+ memcpy (¶ms->keys, dst, sizeof (dst));
|
|
|
+}
|
|
|
+
|
|
|
+static gchar *
|
|
|
+decrypt (const gchar *s,
|
|
|
+ const gchar *check_uuid,
|
|
|
+ const guint8 *enc_key,
|
|
|
+ const guint8 *auth_key,
|
|
|
+ gsize key_length)
|
|
|
+{
|
|
|
+ gchar **v;
|
|
|
+ gchar *to_auth;
|
|
|
+ gchar *hash;
|
|
|
+ gsize cipher_length;
|
|
|
+ gsize raw_length;
|
|
|
+ const gchar *version;
|
|
|
+ const gchar *auth_hash;
|
|
|
+ const gchar *uuid;
|
|
|
+ const gchar *iv;
|
|
|
+ const gchar *cipher_text;
|
|
|
+ guint8 *iv_bytes;
|
|
|
+ guint8 *dst;
|
|
|
+ guchar *cipher_raw_text;
|
|
|
+ struct hmac_sha256_ctx hmac_contextt;
|
|
|
+ guint8 digest[SHA256_DIGEST_SIZE];
|
|
|
+ struct aes_ctx aes_context;
|
|
|
+
|
|
|
+ v = g_strsplit (s, ":", 0);
|
|
|
+ g_assert_nonnull (v[0]); version = v[0];
|
|
|
+ g_assert_nonnull (v[1]); auth_hash = v[1];
|
|
|
+ g_assert_nonnull (v[2]); uuid = v[2];
|
|
|
+ g_assert_nonnull (v[3]); iv = v[3];
|
|
|
+ g_assert_nonnull (v[4]); cipher_text = v[4];
|
|
|
+
|
|
|
+ g_assert_cmpstr (uuid, ==, check_uuid);
|
|
|
+
|
|
|
+ to_auth = g_strjoin (":", version, uuid, iv, cipher_text, NULL);
|
|
|
+ hmac_sha256_set_key (&hmac_contextt, key_length, auth_key);
|
|
|
+ hmac_sha256_update (&hmac_contextt, strlen (to_auth), (guint8 *) to_auth);
|
|
|
+ hmac_sha256_digest (&hmac_contextt, SHA256_DIGEST_SIZE, digest);
|
|
|
+
|
|
|
+ hash = hexlify (digest, sizeof (digest));
|
|
|
+ g_assert_cmpstr (hash, ==, (gchar *) auth_hash);
|
|
|
+ g_free (hash);
|
|
|
+
|
|
|
+ cipher_length = strlen (cipher_text);
|
|
|
+ cipher_length = AES_BLOCK_SIZE * (cipher_length / AES_BLOCK_SIZE + (cipher_length % AES_BLOCK_SIZE ? 1 : 0));
|
|
|
+ dst = g_malloc0 (cipher_length);
|
|
|
+
|
|
|
+ aes_set_decrypt_key (&aes_context, key_length, enc_key);
|
|
|
+ iv_bytes = unhexlify (iv, strlen (iv));
|
|
|
+ cipher_raw_text = g_base64_decode (cipher_text, &raw_length);
|
|
|
+
|
|
|
+ cbc_decrypt (&aes_context, (nettle_cipher_func *) &aes_decrypt, AES_BLOCK_SIZE, iv_bytes,
|
|
|
+ raw_length, dst, (guint8 *) cipher_raw_text);
|
|
|
+
|
|
|
+ dst[raw_length] = '\0';
|
|
|
+ g_free (cipher_raw_text);
|
|
|
+ g_free (iv_bytes);
|
|
|
+ g_free (to_auth);
|
|
|
+ g_strfreev (v);
|
|
|
+ return (gchar *) dst;
|
|
|
+}
|
|
|
+
|
|
|
+static gchar *
|
|
|
+decrypt_item (JsonObject *item, StandardFileAuthParams *params)
|
|
|
+{
|
|
|
+ const gchar *enc_item_key;
|
|
|
+ const gchar *uuid;
|
|
|
+ gchar *enc_auth_key;
|
|
|
+ const gchar *enc_key;
|
|
|
+ const gchar *auth_key;
|
|
|
+ guint8 *enc_key_bytes;
|
|
|
+ guint8 *auth_key_bytes;
|
|
|
+ gsize enc_key_size;
|
|
|
+ const gchar *enc_content;
|
|
|
+ gchar *content;
|
|
|
+
|
|
|
+ uuid = json_object_get_string_member (item, "uuid");
|
|
|
+ enc_item_key = json_object_get_string_member (item, "enc_item_key");
|
|
|
+ enc_auth_key = decrypt (enc_item_key, uuid, params->keys.master, params->keys.auth, sizeof (params->keys.master));
|
|
|
+
|
|
|
+ enc_key = (gchar *) enc_auth_key;
|
|
|
+ enc_key_size = strlen (enc_auth_key) / 2 - 8;
|
|
|
+ auth_key = &enc_auth_key[enc_key_size];
|
|
|
+
|
|
|
+ enc_key_bytes = unhexlify (enc_key, enc_key_size);
|
|
|
+ auth_key_bytes = unhexlify (auth_key, enc_key_size);
|
|
|
+
|
|
|
+ enc_content = json_object_get_string_member (item, "content");
|
|
|
+ content = decrypt (enc_content, uuid, enc_key_bytes, auth_key_bytes, enc_key_size / 2);
|
|
|
+
|
|
|
+ g_free (enc_key_bytes);
|
|
|
+ g_free (auth_key_bytes);
|
|
|
+ g_free (enc_auth_key);
|
|
|
+ return content;
|
|
|
+}
|
|
|
+
|
|
|
+static IridiumNote *
|
|
|
+deserialize_note (JsonObject *meta, JsonObject *data)
|
|
|
+{
|
|
|
+ IridiumNote *note;
|
|
|
+ GTimeVal time;
|
|
|
+ GDateTime *last_modified;
|
|
|
+
|
|
|
+ if (!g_time_val_from_iso8601 (json_object_get_string_member (meta, "created_at"), &time)) {
|
|
|
+ g_print ("Problem parsing\n");
|
|
|
+ }
|
|
|
+
|
|
|
+ last_modified = g_date_time_new_from_timeval_local (&time);
|
|
|
+
|
|
|
+ note = iridium_note_new (json_object_get_string_member (data, "title"),
|
|
|
+ json_object_get_string_member (data, "text"),
|
|
|
+ last_modified);
|
|
|
+
|
|
|
+ return note;
|
|
|
+}
|
|
|
+
|
|
|
+static GObject *
|
|
|
+deserialize_item (JsonObject *meta, const gchar *data)
|
|
|
+{
|
|
|
+ JsonObject *root;
|
|
|
+ g_autoptr(JsonParser) parser;
|
|
|
+ const gchar *type;
|
|
|
+ GError *error = NULL;
|
|
|
+
|
|
|
+ parser = json_parser_new_immutable ();
|
|
|
+ json_parser_load_from_data (parser, data, -1, &error);
|
|
|
+ type = json_object_get_string_member (meta, "content_type");
|
|
|
+ root = json_node_get_object (json_parser_get_root (parser));
|
|
|
+
|
|
|
+ if (!g_strcmp0 (type, "Note"))
|
|
|
+ return G_OBJECT (deserialize_note (meta, root));
|
|
|
+
|
|
|
+ return NULL;
|
|
|
+}
|
|
|
+
|
|
|
+static gboolean
|
|
|
+get_auth_params (ReadAuthParams *params,
|
|
|
+ GError **error)
|
|
|
+{
|
|
|
+ JsonObject *object;
|
|
|
+ const gchar *s;
|
|
|
+
|
|
|
+ object = json_node_get_object (json_parser_get_root (params->parser));
|
|
|
+
|
|
|
+ params->client->auth_params.func = SF_FUNC_PBKDF2;
|
|
|
+ params->client->auth_params.hash = SF_HASH_SHA512;
|
|
|
+ params->client->auth_params.cost = json_object_get_int_member (object, "pw_cost");
|
|
|
+ params->client->auth_params.key_size = json_object_get_int_member (object, "pw_key_size");
|
|
|
+ params->client->auth_params.salt = g_strdup (json_object_get_string_member (object, "pw_salt"));
|
|
|
+
|
|
|
+ if (g_strcmp0 (json_object_get_string_member (object, "pw_alg"), "sha512")) {
|
|
|
+ g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED,
|
|
|
+ "Hash algorithm other than sha512 is not supported");
|
|
|
+ return FALSE;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (g_strcmp0 (json_object_get_string_member (object, "pw_func"), "pbkdf2")) {
|
|
|
+ g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED,
|
|
|
+ "Password derivative function other than PBKDF2 is not supported");
|
|
|
+ return FALSE;
|
|
|
+ }
|
|
|
+
|
|
|
+ s = json_object_get_string_member (object, "version");
|
|
|
+
|
|
|
+ if (!g_strcmp0 (s, "001"))
|
|
|
+ params->client->auth_params.version = SF_VERSION_001;
|
|
|
+ else if (!g_strcmp0 (s, "002"))
|
|
|
+ params->client->auth_params.version = SF_VERSION_002;
|
|
|
+ else {
|
|
|
+ g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED,
|
|
|
+ "StandardFile protocols other than 001 and 002 are not supported");
|
|
|
+ return FALSE;
|
|
|
+ }
|
|
|
+
|
|
|
+ return TRUE;
|
|
|
+}
|
|
|
+
|
|
|
+GList *
|
|
|
+iridium_standard_file_load (IridiumStandardFile *client,
|
|
|
+ const gchar *filename)
|
|
|
+{
|
|
|
+ JsonObject *root;
|
|
|
+ JsonArray *array;
|
|
|
+ g_autoptr(JsonParser) parser;
|
|
|
+ GList *result = NULL;
|
|
|
+ GError *error = NULL;
|
|
|
+
|
|
|
+ parser = json_parser_new_immutable ();
|
|
|
+ json_parser_load_from_file (parser, filename, &error);
|
|
|
+
|
|
|
+ if (error != NULL) {
|
|
|
+ g_error ("Error: %s\n", error->message);
|
|
|
+ return NULL;
|
|
|
+ }
|
|
|
+
|
|
|
+ root = json_node_get_object (json_parser_get_root (parser));
|
|
|
+ array = json_object_get_array_member (root, "items");
|
|
|
+
|
|
|
+ for (guint i = 0; i < json_array_get_length (array); i++) {
|
|
|
+ JsonObject *data;
|
|
|
+ GObject *item;
|
|
|
+ gchar *content;
|
|
|
+
|
|
|
+ data = json_array_get_object_element (array, i);
|
|
|
+ content = decrypt_item (data, &client->auth_params);
|
|
|
+ item = deserialize_item (data, content);
|
|
|
+
|
|
|
+ if (item)
|
|
|
+ result = g_list_append (result, item);
|
|
|
+
|
|
|
+ g_free (content);
|
|
|
+ }
|
|
|
+
|
|
|
+ return result;
|
|
|
+}
|
|
|
+
|
|
|
+static void
|
|
|
+read_auth_params_data_free (ReadAuthParams *data)
|
|
|
+{
|
|
|
+ secret_password_free (data->password);
|
|
|
+ g_object_unref (data->parser);
|
|
|
+}
|
|
|
+
|
|
|
+static void
|
|
|
+on_signin_response_parsed (GObject *object,
|
|
|
+ GAsyncResult *result,
|
|
|
+ gpointer user_data)
|
|
|
+{
|
|
|
+ GTask *task;
|
|
|
+ ReadAuthParams *data;
|
|
|
+ JsonObject *root_object;
|
|
|
+ GError *error = NULL;
|
|
|
+
|
|
|
+ task = user_data;
|
|
|
+ data = g_task_get_task_data (task);
|
|
|
+
|
|
|
+ if (!json_parser_load_from_stream_finish (data->parser, result, &error)) {
|
|
|
+ g_task_return_error (task, error);
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ root_object = json_node_get_object (json_parser_get_root (data->parser));
|
|
|
+
|
|
|
+ g_free (data->client->token);
|
|
|
+ data->client->token = g_strdup (json_object_get_string_member (root_object, "token"));
|
|
|
+
|
|
|
+ g_task_return_boolean (task, TRUE);
|
|
|
+}
|
|
|
+
|
|
|
+static void
|
|
|
+on_send_signin_message (GObject *object,
|
|
|
+ GAsyncResult *result,
|
|
|
+ gpointer user_data)
|
|
|
+{
|
|
|
+ GInputStream *stream;
|
|
|
+ GTask *task;
|
|
|
+ ReadAuthParams *data;
|
|
|
+ GError *error = NULL;
|
|
|
+
|
|
|
+ task = user_data;
|
|
|
+ data = g_task_get_task_data (task);
|
|
|
+ stream = soup_session_send_finish (SOUP_SESSION (object), result, &error);
|
|
|
+
|
|
|
+ if (stream == NULL) {
|
|
|
+ g_task_return_error (task, error);
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ /* we re-use the old parser */
|
|
|
+ json_parser_load_from_stream_async (data->parser, stream, data->cancellable, on_signin_response_parsed, task);
|
|
|
+}
|
|
|
+
|
|
|
+static void
|
|
|
+on_auth_params_response_parsed (GObject *object,
|
|
|
+ GAsyncResult *result,
|
|
|
+ gpointer user_data)
|
|
|
+{
|
|
|
+ GTask *task;
|
|
|
+ ReadAuthParams *data;
|
|
|
+ SoupURI *uri;
|
|
|
+ SoupMessage *msg;
|
|
|
+ gchar *password;
|
|
|
+ gchar *body;
|
|
|
+ GError *error = NULL;
|
|
|
+
|
|
|
+ task = user_data;
|
|
|
+ data = g_task_get_task_data (task);
|
|
|
+
|
|
|
+ if (!json_parser_load_from_stream_finish (data->parser, result, &error)) {
|
|
|
+ g_task_return_error (task, error);
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (!get_auth_params (data, &error)) {
|
|
|
+ g_task_return_error (task, error);
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ derive_keys ((guint8 *) data->password, &data->client->auth_params);
|
|
|
+
|
|
|
+ g_debug ("StandardFile parameters: version=%i func=%i hash=%i key_size=%u iterations=%u",
|
|
|
+ data->client->auth_params.version,
|
|
|
+ data->client->auth_params.func,
|
|
|
+ data->client->auth_params.hash,
|
|
|
+ data->client->auth_params.key_size,
|
|
|
+ data->client->auth_params.cost);
|
|
|
+
|
|
|
+ uri = soup_uri_new_with_base (data->client->base_uri, "api/auth/sign_in");
|
|
|
+ msg = soup_message_new_from_uri ("POST", uri);
|
|
|
+ password = hexlify (data->client->auth_params.keys.password, sizeof (data->client->auth_params.keys.password));
|
|
|
+ body = g_strdup_printf ("{\"email\": \"%s\", \"password\": \"%s\"}", data->client->email, password);
|
|
|
+ soup_message_set_request (msg, "application/json", SOUP_MEMORY_TAKE, body, strlen (body));
|
|
|
+ soup_session_send_async (data->client->session, msg, NULL, on_send_signin_message, task);
|
|
|
+
|
|
|
+ g_free (password);
|
|
|
+ soup_uri_free (uri);
|
|
|
+ g_object_unref (msg);
|
|
|
+}
|
|
|
+
|
|
|
+static void
|
|
|
+on_send_auth_params_message (GObject *object,
|
|
|
+ GAsyncResult *result,
|
|
|
+ gpointer user_data)
|
|
|
+{
|
|
|
+ GInputStream *stream;
|
|
|
+ GTask *task;
|
|
|
+ ReadAuthParams *data;
|
|
|
+ GError *error = NULL;
|
|
|
+
|
|
|
+ task = user_data;
|
|
|
+ data = g_task_get_task_data (task);
|
|
|
+ stream = soup_session_send_finish (SOUP_SESSION (object), result, &error);
|
|
|
+
|
|
|
+ if (stream == NULL) {
|
|
|
+ g_task_return_error (task, error);
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ data->parser = json_parser_new ();
|
|
|
+ json_parser_load_from_stream_async (data->parser, stream, data->cancellable, on_auth_params_response_parsed, task);
|
|
|
+}
|
|
|
+
|
|
|
+static void
|
|
|
+on_send_sync_request (GObject *object,
|
|
|
+ GAsyncResult *result,
|
|
|
+ gpointer user_data)
|
|
|
+{
|
|
|
+ GInputStream *stream;
|
|
|
+ GTask *task;
|
|
|
+ GError *error = NULL;
|
|
|
+
|
|
|
+ task = user_data;
|
|
|
+ stream = soup_request_send_finish (SOUP_REQUEST (object), result, &error);
|
|
|
+
|
|
|
+ if (stream == NULL) {
|
|
|
+ g_task_return_error (task, error);
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ g_task_return_boolean (task, TRUE);
|
|
|
+}
|
|
|
+
|
|
|
+gboolean
|
|
|
+iridium_standard_file_load_async (IridiumStandardFile *client,
|
|
|
+ GCancellable *cancellable,
|
|
|
+ GAsyncReadyCallback callback,
|
|
|
+ gpointer user_data)
|
|
|
+{
|
|
|
+ SoupURI *uri;
|
|
|
+ SoupMessage *msg;
|
|
|
+ SoupRequestHTTP *request;
|
|
|
+ SoupMessageHeaders *headers;
|
|
|
+ gchar *value;
|
|
|
+ GTask *task;
|
|
|
+ GError *error = NULL;
|
|
|
+
|
|
|
+ task = g_task_new (client, cancellable, callback, user_data);
|
|
|
+
|
|
|
+ uri = soup_uri_new_with_base (client->base_uri, "api/items/sync");
|
|
|
+ request = soup_session_request_http_uri (client->session, "POST", uri, &error);
|
|
|
+
|
|
|
+ if (request == NULL) {
|
|
|
+ g_task_return_error (task, error);
|
|
|
+ return FALSE;
|
|
|
+ }
|
|
|
+
|
|
|
+ msg = soup_request_http_get_message (request);
|
|
|
+ g_object_get (msg, "request-headers", &headers, NULL);
|
|
|
+ value = g_strdup_printf ("Bearer %s", client->token);
|
|
|
+ soup_message_headers_append (headers, "Authorization", value);
|
|
|
+
|
|
|
+ soup_request_send_async (SOUP_REQUEST (request), cancellable, on_send_sync_request, task);
|
|
|
+
|
|
|
+ g_object_unref (msg);
|
|
|
+ soup_uri_free (uri);
|
|
|
+
|
|
|
+ return TRUE;
|
|
|
+}
|
|
|
+
|
|
|
+gboolean
|
|
|
+iridium_standard_file_connect_async (IridiumStandardFile *client,
|
|
|
+ const gchar *server,
|
|
|
+ const gchar *email,
|
|
|
+ const gchar *password,
|
|
|
+ GCancellable *cancellable,
|
|
|
+ GAsyncReadyCallback callback,
|
|
|
+ gpointer user_data)
|
|
|
+{
|
|
|
+ SoupURI *uri;
|
|
|
+ SoupMessage *msg;
|
|
|
+ ReadAuthParams *data;
|
|
|
+ GTask *task;
|
|
|
+
|
|
|
+ if (client->base_uri)
|
|
|
+ soup_uri_free (client->base_uri);
|
|
|
+
|
|
|
+ client->base_uri = soup_uri_new (server);
|
|
|
+ client->email = g_strdup (email);
|
|
|
+
|
|
|
+ uri = soup_uri_new_with_base (client->base_uri, "api/auth/params");
|
|
|
+ soup_uri_set_query_from_fields (uri, "email", email, NULL);
|
|
|
+ msg = soup_message_new_from_uri ("GET", uri);
|
|
|
+
|
|
|
+ data = g_new0 (ReadAuthParams, 1);
|
|
|
+ data->client = client;
|
|
|
+ data->password = g_strdup (password);
|
|
|
+ data->cancellable = cancellable;
|
|
|
+
|
|
|
+ task = g_task_new (client, cancellable, callback, user_data);
|
|
|
+ g_task_set_task_data (task, data, (GDestroyNotify) read_auth_params_data_free);
|
|
|
+ soup_session_send_async (client->session, msg, NULL, on_send_auth_params_message, task);
|
|
|
+
|
|
|
+ g_object_unref (msg);
|
|
|
+ soup_uri_free (uri);
|
|
|
+ return TRUE;
|
|
|
+}
|
|
|
+
|
|
|
+static void
|
|
|
+iridium_standard_file_dispose (GObject *object)
|
|
|
+{
|
|
|
+ IridiumStandardFile *self;
|
|
|
+
|
|
|
+ self = IRIDIUM_STANDARD_FILE (object);
|
|
|
+ g_object_unref (self->session);
|
|
|
+ soup_uri_free (self->base_uri);
|
|
|
+ G_OBJECT_CLASS (iridium_standard_file_parent_class)->dispose (object);
|
|
|
+}
|
|
|
+
|
|
|
+static void
|
|
|
+iridium_standard_file_finalize (GObject *object)
|
|
|
+{
|
|
|
+ IridiumStandardFile *self;
|
|
|
+
|
|
|
+ self = IRIDIUM_STANDARD_FILE (object);
|
|
|
+ g_free (self->email);
|
|
|
+ g_free (self->token);
|
|
|
+ G_OBJECT_CLASS (iridium_standard_file_parent_class)->finalize (object);
|
|
|
+}
|
|
|
+
|
|
|
+static void
|
|
|
+iridium_standard_file_class_init (IridiumStandardFileClass *klass)
|
|
|
+{
|
|
|
+ GObjectClass *oclass;
|
|
|
+
|
|
|
+ oclass = G_OBJECT_CLASS (klass);
|
|
|
+ oclass->dispose = iridium_standard_file_dispose;
|
|
|
+ oclass->finalize = iridium_standard_file_finalize;
|
|
|
+}
|
|
|
+
|
|
|
+static void
|
|
|
+iridium_standard_file_init (IridiumStandardFile *self)
|
|
|
+{
|
|
|
+ self->session = soup_session_new ();
|
|
|
+ self->base_uri = NULL;
|
|
|
+ self->email = NULL;
|
|
|
+ self->token = NULL;
|
|
|
+}
|