/soc/2015/igor.gajowiak/chatlog: 32ede87a799d: Implemented expor...

Igor Gajowiak igor.gajowiak at gmail.com
Wed Aug 5 18:15:26 EDT 2015


Changeset: 32ede87a799d466b41be373199942310e9520ae3
Author:	 Igor Gajowiak <igor.gajowiak at gmail.com>
Date:	 2015-08-06 00:14 +0200
Branch:	 default
URL: https://hg.pidgin.im/soc/2015/igor.gajowiak/chatlog/rev/32ede87a799d

Description:

Implemented exporting messages from legacy log.

diffstat:

 libpurple/genericlog.c            |  140 ++++++++++
 libpurple/genericlog.h            |   30 +-
 libpurple/plugins/log/legacylog.c |  496 ++++++++++++++++++++++++++++++++++++++
 3 files changed, 661 insertions(+), 5 deletions(-)

diffs (truncated from 764 to 300 lines):

diff --git a/libpurple/genericlog.c b/libpurple/genericlog.c
--- a/libpurple/genericlog.c
+++ b/libpurple/genericlog.c
@@ -26,6 +26,8 @@
 #define PURPLE_GENERICLOG_GET_PRIVATE(obj) \
 	(G_TYPE_INSTANCE_GET_PRIVATE((obj), PURPLE_TYPE_GENERICLOG, PurpleGenericLogPrivate))
 
+#define PURPLE_GENERICLOG_IMPORT_CHUNK_SIZE 500
+
 typedef struct
 {
 	gchar *id;
@@ -47,6 +49,17 @@ static GList *purple_genericlogs = NULL;
 static PurpleGenericLog *purple_genericlog_inuse = NULL;
 static GList *purple_genericlog_loaded_plugins = NULL;
 
+typedef struct
+{
+	PurpleGenericLog *src_log;
+	PurpleGenericLog_OnImportFinished on_finished;
+	void *export_state;
+	guint timer;
+	guint imported_messages_count;
+} PurpleGenericLogImportState;
+
+PurpleGenericLogImportState *import_state = NULL;
+
 /**************************************************************************/
 /* PurpleGenericLog intance methods                                       */
 /**************************************************************************/
@@ -293,6 +306,133 @@ purple_genericlog_wipe_log_for_buddy(Pur
 
 #undef DEFINE_GENERICLOG_FUNC_WITH_RETURN
 
+static void
+uninit_import_state(PurpleGenericLogImportState *import_state)
+{
+	g_assert(import_state);
+
+	PurpleGenericLogClass *klass = PURPLE_GENERICLOG_GET_CLASS(
+		import_state->src_log);
+
+	klass->finalize_export(import_state->export_state);
+	klass->deactivate();
+	purple_timeout_remove(import_state->timer);
+}
+
+static void
+unref_g_object_pointee(gpointer data)
+{
+	g_object_unref(G_OBJECT(*(GObject**) data));
+}
+
+static gboolean
+run_import_iteration(gpointer unused)
+{
+	PurpleGenericLogClass *klass = PURPLE_GENERICLOG_GET_CLASS(
+		import_state->src_log);
+
+	GArray *accounts = g_array_new(FALSE, FALSE, sizeof(PurpleAccount*));
+	g_array_set_clear_func(accounts, unref_g_object_pointee);
+
+	GArray *messages = g_array_new(FALSE, FALSE, sizeof(PurpleMessage*));
+	g_array_set_clear_func(messages, unref_g_object_pointee);
+
+	gboolean status = klass->read_chunk(import_state->export_state,
+		PURPLE_GENERICLOG_IMPORT_CHUNK_SIZE, accounts, messages);
+
+	if (!status || messages->len == 0) {
+		g_array_unref(accounts);
+		g_array_unref(messages);
+
+		uninit_import_state(import_state);
+
+		import_state->on_finished(status,
+			import_state->imported_messages_count);
+
+		g_free(import_state);
+		import_state = NULL;
+
+		return FALSE;
+	}
+
+	g_assert(accounts->len == messages->len);
+
+	for (guint i = 0; i < messages->len; ++i) {
+		PurpleAccount *account = g_array_index(accounts, PurpleAccount*, i);
+		PurpleMessage *message = g_array_index(messages, PurpleMessage*, i);
+
+		/* Should we stop if logging fails? */
+		if (purple_genericlog_logim(account, message, NULL))
+			++(import_state->imported_messages_count);
+	}
+
+	g_array_free(accounts, TRUE);
+	g_array_free(messages, TRUE);
+
+	return TRUE;
+}
+
+gboolean
+purple_genericlog_import_msgs(PurpleGenericLog *src_log,
+	PurpleGenericLog_OnImportFinished on_finished, GError **error)
+{
+	if (!purple_genericlog_inuse) {
+		if (!error) return FALSE;
+
+		*error = g_error_new(purple_genericlog_error_domain(),
+			PURPLE_GENERICLOG_ERROR_NOLOG, "No active log");
+		return FALSE;
+	}
+
+	if (src_log == purple_genericlog_inuse) {
+		if (!error) return FALSE;
+
+		*error = g_error_new(purple_genericlog_error_domain(),
+			PURPLE_GENERICLOG_ERROR_INTERNAL, "Self import is not allowed");
+		return FALSE;
+	}
+
+	if (import_state) {
+		if (!error) return FALSE;
+
+		*error = g_error_new(purple_genericlog_error_domain(),
+			PURPLE_GENERICLOG_ERROR_INTERNAL, "Import is already running");
+		return FALSE;
+	}
+
+	PurpleGenericLogClass *klass = PURPLE_GENERICLOG_GET_CLASS(src_log);
+	if (!klass->activate()) {
+		if (!error) return FALSE;
+
+		*error = g_error_new(purple_genericlog_error_domain(),
+			PURPLE_GENERICLOG_ERROR_BACKENDFAIL, "Failed to activate %s",
+			purple_genericlog_get_id(src_log));
+		return FALSE;
+	}
+
+	void *export_state;
+	if (!klass->init_export(&export_state)) {
+		klass->deactivate();
+
+		if (!error) return FALSE;
+
+		*error = g_error_new(purple_genericlog_error_domain(),
+			PURPLE_GENERICLOG_ERROR_BACKENDFAIL,
+			"Failed to init export from %s", purple_genericlog_get_id(src_log));
+		return FALSE;
+	}
+
+	import_state = g_new0(PurpleGenericLogImportState, 1);
+	import_state->src_log = src_log;
+	import_state->on_finished = on_finished;
+	import_state->export_state = export_state;
+
+	import_state->timer = purple_timeout_add(1, run_import_iteration,
+		import_state);
+
+	return TRUE;
+}
+
 /**************************************************************************/
 /* Error Codes                                                            */
 /**************************************************************************/
diff --git a/libpurple/genericlog.h b/libpurple/genericlog.h
--- a/libpurple/genericlog.h
+++ b/libpurple/genericlog.h
@@ -95,8 +95,7 @@ struct _PurpleGenericLog
  *                   UI responsiveness. Returning no messages means end of
  *                   export. Returns %TRUE if operation succeeds,
  *                   %FALSE otherwise.
- * @finalize_export: Finalizes an export procedure. Returns %TRUE if operation
- *                   succeeds, %FALSE otherwise.
+ * @finalize_export: Finalizes an export procedure.
  *
  * The abstract base class for all log implementations. A conforming plugin
  * must implement at least activate and deactivate methods. Not supported
@@ -133,7 +132,7 @@ struct _PurpleGenericLogClass
 	gboolean (*read_chunk) (void *export_state, guint chunk_size,
 		GArray *accounts, GArray *messages);
 
-	gboolean (*finalize_export) (void *export_state);
+	void (*finalize_export) (void *export_state);
 
 	void (*reserved1)(void);
 	void (*reserved2)(void);
@@ -141,6 +140,8 @@ struct _PurpleGenericLogClass
 	void (*reserved4)(void);
 };
 
+typedef void (*PurpleGenericLog_OnImportFinished)(gboolean, guint);
+
 #define PURPLE_GENERICLOG_DEFAULT "genericlog-sqlitelog"
 
 #define PURPLE_GENERICLOG_PROPERTY_ID   "id"
@@ -374,11 +375,27 @@ purple_genericlog_get_all_days(PurpleBud
  * Deletes all messages sent to or received from a given buddy from the
  * active log.
  *
- * Returns: %TRUE if succeeds. %FALSE otherwise.
+ * Returns: %TRUE if succeeds, %FALSE otherwise.
  */
 gboolean
 purple_genericlog_wipe_log_for_buddy(PurpleBuddy *buddy, GError **error);
 
+/**
+ * purple_genericlog_import_msgs:
+ * @src_log:     The log from which to import messages.
+ * @on_finished: The callback to call when import is done. Parameters passed
+ *               to it are a success status and the number of imported messages.
+ * @error:       Return location for a #GError or %NULL. If provided, this
+ *               will be set to the reason if getting days fails.
+ *
+ * Asynchronously imports messages from src_log to the active log.
+ *
+ * Returns: %TRUE if starting import succeeded, %FALSE otherwise.
+ */
+gboolean
+purple_genericlog_import_msgs(PurpleGenericLog *src_log,
+	PurpleGenericLog_OnImportFinished on_finished, GError **error);
+
 /**************************************************************************/
 /* Error Codes                                                            */
 /**************************************************************************/
@@ -397,6 +414,8 @@ GQuark purple_genericlog_error_domain(vo
  *
  * @PURPLE_GENERICLOG_ERROR_UNKNOWN:               Unknown error.
  * @PURPLE_GENERICLOG_ERROR_NOLOG:                 No log configured.
+ * @PURPLE_GENERICLOG_ERROR_INTERNAL:              Internal generic log system
+ *                                                 error.
  * @PURPLE_GENERICLOG_ERROR_BACKENDFAIL:           Failed to communicate with
  *                                                 the backend or internal
  *                                                 backend error.
@@ -408,8 +427,9 @@ enum PurpleGenericLogError
 	PURPLE_GENERICLOG_ERROR_UNKNOWN = 0,
 
 	PURPLE_GENERICLOG_ERROR_NOLOG = 10,
+	PURPLE_GENERICLOG_ERROR_INTERNAL,
+	PURPLE_GENERICLOG_ERROR_BACKENDFAIL,
 	PURPLE_GENERICLOG_ERROR_OPERATIONNOTSUPPORTED,
-	PURPLE_GENERICLOG_ERROR_BACKENDFAIL,
 };
 
 /**************************************************************************/
diff --git a/libpurple/plugins/log/legacylog.c b/libpurple/plugins/log/legacylog.c
--- a/libpurple/plugins/log/legacylog.c
+++ b/libpurple/plugins/log/legacylog.c
@@ -31,6 +31,438 @@
 #define LEGACYLOG_AUTHORS \
 	{ "Igor Gajowiak <igor.gajowiak at gmail.com>", NULL }
 
+#define LEGACYLOG_DEBUG_CATEGORY "legacy-log"
+
+/**************************************************************************/
+/* PurpleGenericLog API implementation                                    */
+/**************************************************************************/
+
+typedef enum
+{
+	DIR_DEPTH_PROTOCOL  = 1,
+	DIR_DEPTH_ACCOUNT   = 2,
+	DIR_DEPTH_BUDDYNAME = 3,
+	DIR_DEPTH_LAST      = 3,
+	DIR_DEPTH_MAX       = 4
+} DirDepth;
+
+typedef struct _Iter
+{
+	const gchar *dir_names[DIR_DEPTH_MAX];
+	gchar *dir_paths[DIR_DEPTH_MAX];
+	GDir *dirs[DIR_DEPTH_MAX];
+
+	const gchar* log_file_name;
+	FILE* log_file;
+} Iter;
+
+static void
+open_next_subfile(GDir *dir, const gchar *dir_path, FILE **file_res,
+	const gchar **filename_res)
+{
+	g_assert(dir);
+	g_assert(dir_path);
+	g_assert(file_res);
+
+	const gchar *filename;
+	while ((filename = g_dir_read_name(dir)) != NULL) {
+		gchar *filepath  = g_strdup_printf(
+			"%s/%s", dir_path, filename);
+
+		FILE *file = fopen(filepath, "rb");
+		g_free(filepath);
+
+		if (file) {
+			*file_res = file;
+			*filename_res = filename;
+			return;
+		}
+	}
+
+	*file_res = NULL;



More information about the Commits mailing list