changeset 15540:16845e318249

merge of 'da0538e2883464dc803b72262e19be90b9612288' and 'e99f3a76f0c205fa22237b3bc8c73947a5d69493'
author Sean Egan <seanegan@gmail.com>
date Sun, 04 Feb 2007 08:25:43 +0000
parents b258250b72c9 (current diff) d41c1006e670 (diff)
children d74985add99f 5ec72809228b
files
diffstat 5 files changed, 374 insertions(+), 38 deletions(-) [+]
line wrap: on
line diff
--- a/gaim.spec.in	Sun Feb 04 08:10:50 2007 +0000
+++ b/gaim.spec.in	Sun Feb 04 08:25:43 2007 +0000
@@ -6,7 +6,7 @@
 # When not doing betas comment this out
 # NOTE: %defines in spec files are evaluated in comments so the correct
 #       way to comment it out is to replace the % with #
-%define beta 6
+#define beta 7
 
 %if 0%{?beta}
 %define gaimver %(echo "@VERSION@"|sed -e 's/dev//; s/beta.*//')
--- a/libpurple/log.c	Sun Feb 04 08:10:50 2007 +0000
+++ b/libpurple/log.c	Sun Feb 04 08:25:43 2007 +0000
@@ -248,6 +248,31 @@
 	return size;
 }
 
+gboolean gaim_log_is_deletable(GaimLog *log)
+{
+	g_return_val_if_fail(log != NULL, FALSE);
+	g_return_val_if_fail(log->logger != NULL, FALSE);
+
+	if (log->logger->delete == NULL)
+		return FALSE;
+
+	if (log->logger->is_deletable != NULL)
+		return log->logger->is_deletable(log);
+
+	return TRUE;
+}
+
+gboolean gaim_log_delete(GaimLog *log)
+{
+	g_return_val_if_fail(log != NULL, FALSE);
+	g_return_val_if_fail(log->logger != NULL, FALSE);
+
+	if (log->logger->delete != NULL)
+		return log->logger->delete(log);
+
+	return FALSE;
+}
+
 char *
 gaim_log_get_log_dir(GaimLogType type, const char *name, GaimAccount *account)
 {
@@ -318,7 +343,9 @@
 				int(*size)(GaimLog*),
 				int(*total_size)(GaimLogType type, const char *name, GaimAccount *account),
 				GList*(*list_syslog)(GaimAccount *account),
-				void(*get_log_sets)(GaimLogSetCallback cb, GHashTable *sets))
+				void(*get_log_sets)(GaimLogSetCallback cb, GHashTable *sets),
+				gboolean(*delete)(GaimLog *log),
+				gboolean(*is_deletable)(GaimLog *log))
 {
 #endif
 	GaimLogLogger *logger;
@@ -352,8 +379,12 @@
 		logger->list_syslog = va_arg(args, void *);
 	if (functions >= 9)
 		logger->get_log_sets = va_arg(args, void *);
+	if (functions >= 10)
+		logger->delete = va_arg(args, void *);
+	if (functions >= 11)
+		logger->is_deletable = va_arg(args, void *);
 
-	if (functions > 9)
+	if (functions >= 12)
 		gaim_debug_info("log", "Dropping new functions for logger: %s (%s)\n", name, id);
 
 	va_end(args);
@@ -559,7 +590,7 @@
 
 	gaim_prefs_add_string("/core/logging/format", "txt");
 
-	html_logger = gaim_log_logger_new("html", _("HTML"), 8,
+	html_logger = gaim_log_logger_new("html", _("HTML"), 11,
 									  NULL,
 									  html_logger_write,
 									  html_logger_finalize,
@@ -567,10 +598,13 @@
 									  html_logger_read,
 									  gaim_log_common_sizer,
 									  html_logger_total_size,
-									  html_logger_list_syslog);
+									  html_logger_list_syslog,
+									  NULL,
+									  gaim_log_common_deleter,
+									  gaim_log_common_is_deletable);
 	gaim_log_logger_add(html_logger);
 
-	txt_logger = gaim_log_logger_new("txt", _("Plain text"), 8,
+	txt_logger = gaim_log_logger_new("txt", _("Plain text"), 11,
 									 NULL,
 									 txt_logger_write,
 									 txt_logger_finalize,
@@ -578,7 +612,10 @@
 									 txt_logger_read,
 									 gaim_log_common_sizer,
 									 txt_logger_total_size,
-									 txt_logger_list_syslog);
+									 txt_logger_list_syslog,
+									 NULL,
+									 gaim_log_common_deleter,
+									 gaim_log_common_is_deletable);
 	gaim_log_logger_add(txt_logger);
 
 	old_logger = gaim_log_logger_new("old", _("Old flat format"), 9,
@@ -601,7 +638,7 @@
 #else
 #error Unknown size of time_t
 #endif
-	                     gaim_value_new(GAIM_TYPE_POINTER), 2,
+	                     gaim_value_new(GAIM_TYPE_STRING), 2,
 	                     gaim_value_new(GAIM_TYPE_SUBTYPE,
 	                                    GAIM_SUBTYPE_LOG),
 #if SIZEOF_TIME_T == 4
@@ -817,6 +854,8 @@
 	struct stat st;
 	GaimLogCommonLoggerData *data = log->logger_data;
 
+	g_return_val_if_fail(data != NULL, 0);
+
 	if (!data->path || g_stat(data->path, &st))
 		st.st_size = 0;
 
@@ -943,6 +982,95 @@
 	g_dir_close(log_dir);
 }
 
+gboolean gaim_log_common_deleter(GaimLog *log)
+{
+	GaimLogCommonLoggerData *data;
+	int ret;
+
+	g_return_val_if_fail(log != NULL, FALSE);
+
+	data = log->logger_data;
+	if (data == NULL)
+		return FALSE;
+
+	if (data->path == NULL)
+		return FALSE;
+
+	ret = g_unlink(data->path);
+	if (ret == 0)
+		return TRUE;
+	else if (ret == -1)
+	{
+		gaim_debug_error("log", "Failed to delete: %s - %s\n", data->path, strerror(errno));
+	}
+	else
+	{
+		/* I'm not sure that g_unlink() will ever return
+		 * something other than 0 or -1. -- rlaager */
+		gaim_debug_error("log", "Failed to delete: %s\n", data->path);
+	}
+
+	return FALSE;
+}
+
+gboolean gaim_log_common_is_deletable(GaimLog *log)
+{
+	GaimLogCommonLoggerData *data;
+	gchar *dirname;
+
+	g_return_val_if_fail(log != NULL, FALSE);
+
+	data = log->logger_data;
+	if (data == NULL)
+		return FALSE;
+
+	if (data->path == NULL)
+		return FALSE;
+
+#ifndef _WIN32
+	dirname = g_path_get_dirname(data->path);
+	if (g_access(dirname, W_OK) == 0)
+	{
+		g_free(dirname);
+		return TRUE;
+	}
+	gaim_debug_info("log", "access(%s) failed: %s\n", dirname, strerror(errno));
+	g_free(dirname);
+#else
+	/* Unless and until someone writes equivalent win32 code,
+	 * we'll assume the file is deletable. */
+	return TRUE;
+#endif
+
+	return FALSE;
+}
+
+static char *process_txt_log(char *txt, char *to_free)
+{
+	char *tmp;
+
+	/* The to_free argument allows us to save a
+	 * g_strdup() in some cases. */
+
+	if (to_free == NULL)
+		to_free = txt;
+
+	/* g_markup_escape_text requires valid UTF-8 */
+	if (!g_utf8_validate(txt, -1, NULL))
+	{
+		tmp = gaim_utf8_salvage(txt);
+		g_free(to_free);
+		to_free = txt = tmp;
+	}
+
+	tmp = g_markup_escape_text(txt, -1);
+	g_free(to_free);
+	txt = gaim_markup_linkify(tmp);
+	g_free(tmp);
+
+	return txt;
+}
+
 #if 0 /* Maybe some other time. */
 /****************
  ** XML LOGGER **
@@ -1314,16 +1442,11 @@
 		return g_strdup(_("<font color=\"red\"><b>Unable to find log path!</b></font>"));
 	if (g_file_get_contents(data->path, &read, NULL, NULL)) {
 		minus_header = strchr(read, '\n');
-		if (!minus_header)
-			minus_header = g_strdup(read);
+
+		if (minus_header)
+			return process_txt_log(minus_header + 1, read);
 		else
-			minus_header = g_strdup(minus_header + 1);
-		g_free(read);
-		minus_header2 = g_markup_escape_text(minus_header, -1);
-		g_free(minus_header);
-		read = gaim_markup_linkify(minus_header2);
-		g_free(minus_header2);
-		return read;
+			return process_txt_log(read, NULL);
 	}
 	return g_strdup_printf(_("<font color=\"red\"><b>Could not read file: %s</b></font>"), data->path);
 }
@@ -1628,15 +1751,13 @@
 	fclose(file);
 	read[data->length] = '\0';
 	*flags = 0;
-	if(strstr(read, "<BR>"))
+	if (strstr(read, "<BR>"))
+	{
 		*flags |= GAIM_LOG_READ_NO_NEWLINE;
-	else {
-		tmp = g_markup_escape_text(read, -1);
-		g_free(read);
-		read = gaim_markup_linkify(tmp);
-		g_free(tmp);
+		return read;
 	}
-	return read;
+
+	return process_txt_log(read, NULL);
 }
 
 static int old_logger_size (GaimLog *log)
--- a/libpurple/log.h	Sun Feb 04 08:10:50 2007 +0000
+++ b/libpurple/log.h	Sun Feb 04 08:25:43 2007 +0000
@@ -80,15 +80,15 @@
 	GList *(*list)(GaimLogType type, const char *name, GaimAccount *account);
 
 	/** Given one of the logs returned by the logger's list function,
-	 * this returns the contents of the log in GtkIMHtml markup */
+	 *  this returns the contents of the log in GtkIMHtml markup */
 	char *(*read)(GaimLog *log, GaimLogReadFlags *flags);
 
 	/** Given one of the logs returned by the logger's list function,
-	 * this returns the size of the log in bytes */
+	 *  this returns the size of the log in bytes */
 	int (*size)(GaimLog *log);
 
 	/** Returns the total size of all the logs. If this is undefined a default
-	 * implementation is used */
+	 *  implementation is used */
 	int (*total_size)(GaimLogType type, const char *name, GaimAccount *account);
 
 	/** This function returns a sorted GList of available system GaimLogs */
@@ -103,6 +103,12 @@
 	 *  Loggers which implement this function must create a GaimLogSet,
 	 *  then call @a cb with @a sets and the newly created GaimLogSet. */
 	void (*get_log_sets)(GaimLogSetCallback cb, GHashTable *sets);
+
+	/* Attempts to delete the specified log, indicating success or failure */
+	gboolean (*delete)(GaimLog *log);
+
+	/* Tests whether a log is deletable */
+	gboolean (*is_deletable)(GaimLog *log);
 };
 
 /**
@@ -281,6 +287,26 @@
 int gaim_log_get_total_size(GaimLogType type, const char *name, GaimAccount *account);
 
 /**
+ * Tests whether a log is deletable
+ *
+ * A return value of @c FALSE indicates that gaim_log_delete() will fail on this
+ * log, unless something changes between the two calls.  A return value of @c TRUE,
+ * however, does not guarantee the log can be deleted.
+ *
+ * @param log                 The log
+ * @return                    A boolean indicating if the log is deletable
+ */
+gboolean gaim_log_is_deletable(GaimLog *log);
+
+/**
+ * Deletes a log
+ *
+ * @param log                 The log
+ * @return                    A boolean indicating success or failure
+ */
+gboolean gaim_log_delete(GaimLog *log);
+
+/**
  * Returns the default logger directory Gaim uses for a given account
  * and username.  This would be where Gaim stores logs created by
  * the built-in text or HTML loggers.
@@ -332,6 +358,11 @@
  * set to a GaimLogCommonLoggerData struct containing the log
  * file handle and log path.
  *
+ * This function is intended to be used as a "common"
+ * implementation of a logger's @c write function.
+ * It should only be passed to gaim_log_logger_new() and never
+ * called directly.
+ *
  * @param log   The log to write to.
  * @param ext   The file extension to give to this log file.
  */
@@ -339,8 +370,12 @@
 
 /**
  * Returns a sorted GList of GaimLogs of the requested type.
+ *
  * This function should only be used with logs that are written
- * with gaim_log_common_writer().
+ * with gaim_log_common_writer().  It's intended to be used as
+ * a "common" implementation of a logger's @c list function.
+ * It should only be passed to gaim_log_logger_new() and never
+ * called directly.
  *
  * @param type     The type of the logs being listed.
  * @param name     The name of the log.
@@ -356,10 +391,13 @@
 
 /**
  * Returns the total size of all the logs for a given user, with
- * a given extension.  This is the "common" implemention of a
- * logger's total_size function.
+ * a given extension.
+ *
  * This function should only be used with logs that are written
- * with gaim_log_common_writer().
+ * with gaim_log_common_writer().  It's intended to be used as
+ * a "common" implementation of a logger's @c total_size function.
+ * It should only be passed to gaim_log_logger_new() and never
+ * called directly.
  *
  * @param type     The type of the logs being sized.
  * @param name     The name of the logs to size
@@ -375,14 +413,49 @@
 
 /**
  * Returns the size of a given GaimLog.
+ *
  * This function should only be used with logs that are written
- * with gaim_log_common_writer().
+ * with gaim_log_common_writer().  It's intended to be used as
+ * a "common" implementation of a logger's @c size function.
+ * It should only be passed to gaim_log_logger_new() and never
+ * called directly.
  *
  * @param log      The GaimLog to size.
  *
  * @return An integer indicating the size of the log in bytes.
  */
 int gaim_log_common_sizer(GaimLog *log);
+
+/**
+ * Deletes a log
+ *
+ * This function should only be used with logs that are written
+ * with gaim_log_common_writer().  It's intended to be used as
+ * a "common" implementation of a logger's @c delete function.
+ * It should only be passed to gaim_log_logger_new() and never
+ * called directly.
+ *
+ * @param log      The GaimLog to delete.
+ *
+ * @return A boolean indicating success or failure.
+ */
+gboolean gaim_log_common_deleter(GaimLog *log);
+
+/**
+ * Checks to see if a log is deletable
+ *
+ * This function should only be used with logs that are written
+ * with gaim_log_common_writer().  It's intended to be used as
+ * a "common" implementation of a logger's @c is_deletable function.
+ * It should only be passed to gaim_log_logger_new() and never
+ * called directly.
+ *
+ * @param log      The GaimLog to check.
+ *
+ * @return A boolean indicating if the log is deletable.
+ */
+gboolean gaim_log_common_is_deletable(GaimLog *log);
+
 /*@}*/
 
 /******************************************/
@@ -398,8 +471,9 @@
  * @param functions    The number of functions being passed. The following
  *                     functions are currently available (in order): @c create,
  *                     @c write, @c finalize, @c list, @c read, @c size,
- *                     @c total_size, @c list_syslog, @c get_log_sets. For
- *                     details on these functions, see GaimLogLogger.
+ *                     @c total_size, @c list_syslog, @c get_log_sets,
+ *                     @c delete, @c is_deletable.
+ *                     For details on these functions, see GaimLogLogger.
  *                     Functions may not be skipped. For example, passing
  *                     @c create and @c write is acceptable (for a total of
  *                     two functions). Passing @c create and @c finalize,
--- a/pidgin/gtkconv.c	Sun Feb 04 08:10:50 2007 +0000
+++ b/pidgin/gtkconv.c	Sun Feb 04 08:25:43 2007 +0000
@@ -6807,7 +6807,7 @@
 #else
 #error Unkown size of time_t
 #endif
-	                     gaim_value_new(GAIM_TYPE_POINTER), 3,
+	                     gaim_value_new(GAIM_TYPE_STRING), 3,
 	                     gaim_value_new(GAIM_TYPE_SUBTYPE,
 	                                    GAIM_SUBTYPE_CONVERSATION),
 #if SIZEOF_TIME_T == 4
--- a/pidgin/gtklog.c	Sun Feb 04 08:10:50 2007 +0000
+++ b/pidgin/gtklog.c	Sun Feb 04 08:25:43 2007 +0000
@@ -26,13 +26,16 @@
 #include "pidgin.h"
 
 #include "account.h"
+#include "debug.h"
+#include "log.h"
+#include "notify.h"
+#include "request.h"
+#include "util.h"
+
 #include "gtkblist.h"
 #include "gtkimhtml.h"
 #include "gtklog.h"
 #include "gtkutils.h"
-#include "log.h"
-#include "notify.h"
-#include "util.h"
 
 static GHashTable *log_viewers = NULL;
 static void populate_log_tree(PidginLogViewer *lv);
@@ -184,6 +187,8 @@
 	} else
 		syslog_viewer = NULL;
 
+	gaim_request_close_with_handle(lv);
+
 	g_list_foreach(lv->logs, (GFunc)gaim_log_free, NULL);
 	g_list_free(lv->logs);
 
@@ -200,6 +205,139 @@
 		gtk_tree_view_expand_row(tv, path, FALSE);
 }
 
+static void delete_log_cb(GaimLog *log)
+{
+	if (!gaim_log_delete(log))
+	{
+		gaim_notify_error(NULL, NULL, "Log Deletion Failed",
+		                  "Check permissions and try again.");
+	}
+}
+
+static void log_delete_log_cb(GtkWidget *menuitem, gpointer *data)
+{
+	PidginLogViewer *lv = data[0];
+	GaimLog *log = data[1];
+	const char *time = log_get_date(log);
+	const char *name;
+	char *tmp;
+
+	if (log->type == GAIM_LOG_IM)
+	{
+		GaimBuddy *buddy = gaim_find_buddy(log->account, log->name);
+		if (buddy != NULL)
+			name = gaim_buddy_get_contact_alias(buddy);
+		else
+			name = log->name;
+
+		tmp = g_strdup_printf(_("Are you sure you want to permanently delete the log of the "
+		                        "conversation with %s which started at %s?"), name, time);
+	}
+	else if (log->type == GAIM_LOG_CHAT)
+	{
+		GaimChat *chat = gaim_blist_find_chat(log->account, log->name);
+		if (chat != NULL)
+			name = gaim_chat_get_name(chat);
+		else
+			name = log->name;
+
+		tmp = g_strdup_printf(_("Are you sure you want to permanently delete the log of the "
+		                        "conversation in %s which started at %s?"), name, time);
+	}
+	else if (log->type == GAIM_LOG_SYSTEM)
+	{
+		tmp = g_strdup_printf(_("Are you sure you want to permanently delete the system log "
+		                        "which started at %s?"), time);
+	}
+	else
+		g_return_if_reached();
+
+	gaim_request_action(lv, NULL, "Delete Log?", tmp,
+	                    0, log, 2, _("Delete"), delete_log_cb, _("Cancel"), NULL);
+	g_free(tmp);
+}
+
+static void log_show_popup_menu(GtkWidget *treeview, GdkEventButton *event, gpointer *data)
+{
+	GtkWidget *menu = gtk_menu_new();
+	GtkWidget *menuitem = gtk_menu_item_new_with_label("Delete Log...");
+
+	if (!gaim_log_is_deletable((GaimLog *)data[1]))
+		gtk_widget_set_sensitive(menuitem, FALSE);
+
+	g_signal_connect(menuitem, "activate", G_CALLBACK(log_delete_log_cb), data);
+	g_object_set_data_full(menuitem, "log-viewer-data", data, g_free);
+	gtk_menu_shell_append(GTK_MENU_SHELL(menu), menuitem);
+	gtk_widget_show_all(menu);
+
+	gtk_menu_popup(GTK_MENU(menu), NULL, (GtkMenuPositionFunc)data[2], NULL, NULL,
+	               (event != NULL) ? event->button : 0,
+	               gdk_event_get_time((GdkEvent *)event));
+}
+
+static gboolean log_button_press_cb(GtkWidget *treeview, GdkEventButton *event, PidginLogViewer *lv)
+{
+	if (event->type == GDK_BUTTON_PRESS && event->button == 3)
+	{
+		GtkTreePath *path;
+		GtkTreeIter iter;
+		GValue val;
+		GaimLog *log;
+		gpointer *data;
+
+		if (!gtk_tree_view_get_path_at_pos(GTK_TREE_VIEW(treeview), event->x, event->y, &path, NULL, NULL, NULL))
+			return FALSE;
+		gtk_tree_model_get_iter(GTK_TREE_MODEL(lv->treestore), &iter, path);
+		val.g_type = 0;
+		gtk_tree_model_get_value(GTK_TREE_MODEL(lv->treestore), &iter, 1, &val);
+
+		log = g_value_get_pointer(&val);
+
+		if (log == NULL)
+			return FALSE;
+
+		data = g_new(gpointer, 3);
+		data[0] = lv;
+		data[1] = log;
+		data[2] = NULL;
+
+		log_show_popup_menu(treeview, event, data);
+		return TRUE;
+	}
+
+	return FALSE;
+}
+
+static gboolean log_popup_menu_cb(GtkWidget *treeview, PidginLogViewer *lv)
+{
+	GtkTreeSelection *sel;
+	GtkTreeIter iter;
+	GValue val;
+	GaimLog *log;
+	gpointer *data;
+
+	sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(lv));
+	if (!gtk_tree_selection_get_selected(sel, NULL, &iter))
+		return FALSE;
+
+	val.g_type = 0;
+	gtk_tree_model_get_value(GTK_TREE_MODEL(lv->treestore),
+	                         &iter, NODE_COLUMN, &val);
+
+	log = g_value_get_pointer(&val);
+
+	if (log == NULL)
+		return FALSE;
+
+	data = g_new(gpointer, 3);
+	data[0] = lv;
+	data[1] = log;
+	data[2] = pidgin_treeview_popup_menu_position_func;
+
+	log_show_popup_menu(treeview, NULL, data);
+	return TRUE;
+}
+
 static gboolean search_find_cb(gpointer data)
 {
 	PidginLogViewer *viewer = data;
@@ -416,6 +554,9 @@
 			lv);
 	pidgin_set_accessible_label(lv->treeview, lv->label);
 
+	g_signal_connect(lv->treeview, "button-press-event", G_CALLBACK(log_button_press_cb), lv);
+	g_signal_connect(lv->treeview, "popup-menu", G_CALLBACK(log_popup_menu_cb), lv);
+
 	/* Log size ************/
 	if(log_size) {
 		char *sz_txt = gaim_str_size_to_units(log_size);