pidgin/purple-plugin-pack

Add the turtles target to keep the foot clan at bay
default tip
13 months ago, Gary Kramlich
63ad7e4f10b4
Add the turtles target to keep the foot clan at bay

Testing Done:
Ran `ninja turtles` and verified it worked correctly.

Reviewed at https://reviews.imfreedom.org/r/2409/
/*
* enhanced_hist.c - Enhanced History Plugin for libpurple
* Copyright (C) 2004-2008 Andrew Pangborn <gaim@andrewpangborn.com>
* Copyright (C) 2007-2008 John Bailey <rekkanoryo@rekkanoryo.org>
* Copyright (C) 2007 Ankit Singla
*
* 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 2 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.
*/
/* SUMMARY: Puts log of past conversations in the user's IM window and
* allows the user to select the number of conversations to display from
* purple plugin preferences.
*
* Author:
* Andrew Pangborn - gaim@andrewpangborn.com
*
* Contributors:
* Ankit Singla
*/
/* If you can't figure out what this line is for, DON'T TOUCH IT. */
#include "../common/pp_internal.h"
/* Purple headers */
#include <conversation.h>
#include <debug.h>
#include <log.h>
#include <plugin.h>
#include <pluginpref.h>
#include <prefs.h>
#include <signals.h>
#include <util.h>
/* Pidgin headers */
#include <gtkconv.h>
#include <gtkimhtml.h>
#include <gtkplugin.h>
#include <gtkprefs.h>
#include <gtkutils.h>
#include <pidgin.h>
/* libc headers */
#include <time.h>
#define ENHANCED_HISTORY_ID "gtk-plugin_pack-enhanced_history"
#define PREF_ROOT_GPPATH "/plugins/gtk"
#define PREF_ROOT_PPATH "/plugins/gtk/plugin_pack"
#define PREF_ROOT_PATH "/plugins/gtk/plugin_pack/enhanced_history"
#define PREF_NUMBER_PATH "/plugins/gtk/plugin_pack/enhanced_history/number"
#define PREF_BYTES_PATH "/plugins/gtk/plugin_pack/enhanced_history/bytes"
#define PREF_MINS_PATH "/plugins/gtk/plugin_pack/enhanced_history/minutes"
#define PREF_HOURS_PATH "/plugins/gtk/plugin_pack/enhanced_history/hours"
#define PREF_DAYS_PATH "/plugins/gtk/plugin_pack/enhanced_history/days"
#define PREF_DATES_PATH "/plugins/gtk/plugin_pack/enhanced_history/dates"
#define PREF_IM_PATH "/plugins/gtk/plugin_pack/enhanced_history/im"
#define PREF_CHAT_PATH "/plugins/gtk/plugin_pack/enhanced_history/chat"
#define PREF_NUMBER_VAL purple_prefs_get_int(PREF_NUMBER_PATH)
#define PREF_BYTES_VAL purple_prefs_get_int(PREF_BYTES_PATH)
#define PREF_MINS_VAL purple_prefs_get_int(PREF_MINS_PATH)
#define PREF_HOURS_VAL purple_prefs_get_int(PREF_HOURS_PATH)
#define PREF_DAYS_VAL purple_prefs_get_int(PREF_DAYS_PATH)
#define PREF_DATES_VAL purple_prefs_get_bool(PREF_DATES_PATH)
#define PREF_IM_VAL purple_prefs_get_bool(PREF_IM_PATH)
#define PREF_CHAT_VAL purple_prefs_get_bool(PREF_CHAT_PATH)
static gboolean _scroll_imhtml_to_end(gpointer data)
{
GtkIMHtml *imhtml = data;
gtk_imhtml_scroll_to_end(GTK_IMHTML(imhtml), FALSE);
g_object_unref(G_OBJECT(imhtml));
return FALSE;
}
static void historize(PurpleConversation *c)
{
PurpleAccount *account = NULL;
PurpleConversationType convtype;
PidginConversation *gtkconv = NULL;
GtkTextIter start;
GtkIMHtmlOptions options;
GList *logs = NULL, *logs_head = NULL;
struct tm *log_tm = NULL, *local_tm = NULL;
time_t t, log_time;
double limit_time = 0.0;
const char *name = NULL, *alias = NULL, *LOG_MODE = NULL;
char *header = NULL, *protocol = NULL, *history = NULL;
int conv_counter = 0;
int limit_offset = 0;
int byte_counter = 0;
int check_time = 0;
int log_size, overshoot;
account = purple_conversation_get_account(c);
name = purple_conversation_get_name(c);
alias = name;
LOG_MODE = purple_prefs_get_string("/purple/logging/format");
/* If logging isn't enabled, don't show any history */
if(!purple_prefs_get_bool("/purple/logging/log_ims") &&
!purple_prefs_get_bool("/purple/logging/log_chats"))
return;
/* If the user wants to show 0 logs, stop now */
if(PREF_NUMBER_VAL == 0) {
return;
}
/* If the logging mode is html, set the output options to include no newline.
* Otherwise, it's normal text, so we don't need extra lines */
if(strcasecmp(LOG_MODE, "html")==0) {
options = GTK_IMHTML_NO_NEWLINE;
} else {
options = GTK_IMHTML_NO_COLOURS;
}
/* Determine whether this is an IM or a chat. In either case, if the user has that
* particular log type disabled, the logs file doesnt not get specified */
convtype = purple_conversation_get_type(c);
if (convtype == PURPLE_CONV_TYPE_IM && PREF_IM_VAL) {
GSList *cur;
GSList *buddies = NULL;
/* Find buddies for this conversation. */
buddies = purple_find_buddies(account, name);
/* If we found at least one buddy, save the first buddy's alias. */
if (buddies != NULL)
alias = purple_buddy_get_contact_alias((PurpleBuddy *)buddies->data);
for(cur = buddies; cur; cur = cur->next) {
PurpleBlistNode *node = cur->data;
if(node && (node->prev || node->next)) {
PurpleBlistNode *node2;
for(node2 = node->parent->child; node2; node2 = node2->next) {
logs = g_list_concat(purple_log_get_logs(PURPLE_LOG_IM,
purple_buddy_get_name((PurpleBuddy *)node2),
purple_buddy_get_account((PurpleBuddy *)node2)), logs);
}
break;
}
}
g_slist_free(buddies);
if (logs)
logs = g_list_sort(logs, purple_log_compare);
else
logs = purple_log_get_logs(PURPLE_LOG_IM, name, account);
} else if (convtype == PURPLE_CONV_TYPE_CHAT && PREF_CHAT_VAL) {
logs = purple_log_get_logs(PURPLE_LOG_CHAT,
purple_conversation_get_name(c), purple_conversation_get_account(c));
}
gtkconv = PIDGIN_CONVERSATION(c);
/* The logs are non-existant or the user has disabled this type for log displaying. */
if (!logs) {
return;
}
/* Keep a pointer to the head of the logs for freeing later */
logs_head = logs;
/* If all time prefs are not 0, prepare to check times */
if (!(PREF_MINS_VAL == 0 && PREF_HOURS_VAL == 0 && PREF_DAYS_VAL == 0)) {
check_time = 1;
/* Grab current time and normalize it to UTC */
t = time(NULL);
local_tm = gmtime(&t);
t = mktime(local_tm);
limit_time = (PREF_MINS_VAL * 60.0) + (PREF_HOURS_VAL * 60.0 * 60.0) +
(PREF_DAYS_VAL * 60.0 * 60.0 * 24.0);
}
/* The protocol will need to be adjusted for each log for correct display,
so save the current imhtml protocol_name to restore it later */
protocol = g_strdup(gtk_imhtml_get_protocol_name(GTK_IMHTML(gtkconv->imhtml)));
/* Calculate time for the first log */
log_tm = gmtime(&((PurpleLog*)logs->data)->time);
log_time = mktime(log_tm);
/* Continue to add older logs until they run out or the conditions are no
longer met */
while (logs && conv_counter < PREF_NUMBER_VAL
&& byte_counter < PREF_BYTES_VAL
&& (!check_time || difftime(t, log_time) < limit_time)) {
guint flags;
/* Get the current log's contents as a char* */
history = purple_log_read((PurpleLog*)logs->data, &flags);
log_size = strlen(history);
if (flags & PURPLE_LOG_READ_NO_NEWLINE)
options |= GTK_IMHTML_NO_NEWLINE;
else
options &= ~GTK_IMHTML_NO_NEWLINE;
/* Update the overall byte count and determine if this log exceeds the limit */
byte_counter += log_size;
overshoot = byte_counter - PREF_BYTES_VAL;
if (overshoot > 0) {
/* Start looking at the maximum log size for a newline to break at */
limit_offset = overshoot;
/* Find the next \n, or stop if the end of the log is reached */
while (history[limit_offset] && history[limit_offset] != '\n') {
limit_offset++;
}
/* If we're at or very close to the end of the log, forget this log */
if (!history[limit_offset] || (log_size - limit_offset < 3)) {
limit_offset = -1;
}
else {
/* Start at the first character after the newline */
limit_offset++;
}
}
conv_counter++;
/* If this log won't fit at all, don't display it in the conversation */
if (limit_offset != -1) {
/* Set the correct protocol_name for this log */
gtk_imhtml_set_protocol_name(GTK_IMHTML(gtkconv->imhtml),
purple_account_get_protocol_name(((PurpleLog*)logs->data)->account));
/* Prepend the contents of the log starting at the calculated offset */
gtk_text_buffer_get_iter_at_offset(GTK_IMHTML(gtkconv->imhtml)->text_buffer,
&start, 0);
gtk_imhtml_insert_html_at_iter(GTK_IMHTML(gtkconv->imhtml),
history + limit_offset, options, &start);
/* Prepend the conversation header */
if (PREF_DATES_VAL) {
header = g_strdup_printf(_("<b>Conversation with %s on %s:</b><br>"), alias,
purple_date_format_full(localtime(&((PurpleLog *)logs->data)->time)));
gtk_text_buffer_get_iter_at_offset(GTK_IMHTML(gtkconv->imhtml)->text_buffer,
&start, 0);
gtk_imhtml_insert_html_at_iter(GTK_IMHTML(gtkconv->imhtml),
header, options, &start);
g_free(header);
}
}
g_free(history);
if (limit_offset > 0) {
/* This log had to be chopped to fit, so stop after this one */
break;
}
logs = logs->next;
/* Recalculate log time if we haven't run out of logs */
if (logs) {
log_tm = gmtime(&((PurpleLog*)logs->data)->time);
log_time = mktime(log_tm);
}
}
gtk_imhtml_append_text(GTK_IMHTML(gtkconv->imhtml), "<hr>", options);
/* Restore the original protocol_name */
gtk_imhtml_set_protocol_name(GTK_IMHTML(gtkconv->imhtml), protocol);
g_free(protocol);
g_object_ref(G_OBJECT(gtkconv->imhtml));
g_idle_add(_scroll_imhtml_to_end, gtkconv->imhtml);
/* Clear the allocated memory that the logs are using */
g_list_foreach(logs_head, (GFunc)purple_log_free, NULL);
g_list_free(logs_head);
}
static gboolean
plugin_load(PurplePlugin *plugin)
{
purple_signal_connect(purple_conversations_get_handle(),
"conversation-created", plugin, PURPLE_CALLBACK(historize), NULL);
return TRUE;
}
static GtkWidget *
eh_prefs_get_frame(PurplePlugin *plugin)
{
GtkSizeGroup *sg = NULL;
GtkWidget *vbox = NULL, *frame = NULL;
sg = gtk_size_group_new(GTK_SIZE_GROUP_HORIZONTAL);
vbox = gtk_vbox_new(TRUE, PIDGIN_HIG_BOX_SPACE);
/* heading for the more general options */
frame = pidgin_make_frame(vbox, _("Display Options"));
/* the integer pref for the number of logs to display */
pidgin_prefs_labeled_spin_button(frame, _("Maximum number of conversations:"),
PREF_NUMBER_PATH, 0, 255, NULL);
/* the integer pref for maximum number of bytes to read back */
pidgin_prefs_labeled_spin_button(frame, _("Maximum number of bytes:"),
PREF_BYTES_PATH, 0, 1024*1024, NULL);
/* the boolean preferences */
pidgin_prefs_checkbox(_("Show dates with text"), PREF_DATES_PATH, frame);
pidgin_prefs_checkbox(_("Show logs for IMs"), PREF_IM_PATH, frame);
pidgin_prefs_checkbox(_("Show logs for chats"), PREF_CHAT_PATH, frame);
/* heading for the age limit options */
frame = pidgin_make_frame(vbox, _("Age Limit for Logs (0 to disable):"));
/* the integer preferences for time limiting */
pidgin_prefs_labeled_spin_button(frame, "Days:", PREF_DAYS_PATH, 0, 255, sg);
pidgin_prefs_labeled_spin_button(frame, "Hours:", PREF_HOURS_PATH, 0, 255, sg);
pidgin_prefs_labeled_spin_button(frame, "Minutes:", PREF_MINS_PATH, 0, 255, sg);
gtk_widget_show_all(vbox);
return vbox;
}
static PidginPluginUiInfo ui_info = {
eh_prefs_get_frame, /* get prefs frame */
0, /* page number - reserved */
/* reserved pointers */
NULL,
NULL,
NULL,
NULL
};
static PurplePluginInfo info =
{
PURPLE_PLUGIN_MAGIC,
PURPLE_MAJOR_VERSION,
PURPLE_MINOR_VERSION,
PURPLE_PLUGIN_STANDARD,
PIDGIN_PLUGIN_TYPE,
0,
NULL,
PURPLE_PRIORITY_DEFAULT,
ENHANCED_HISTORY_ID,
NULL,
PP_VERSION,
NULL,
NULL,
"Andrew Pangborn <gaim@andrewpangborn.com>",
PP_WEBSITE,
plugin_load,
NULL,
NULL,
&ui_info,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL
};
static void
init_plugin(PurplePlugin *plugin)
{
gboolean dates = FALSE, ims = FALSE, chats = FALSE;
purple_prefs_add_none(PREF_ROOT_GPPATH);
purple_prefs_add_none(PREF_ROOT_PPATH);
purple_prefs_add_none(PREF_ROOT_PATH);
if(purple_prefs_exists("/plugins/core/enhanced_history/int")) {
if(strcmp(purple_prefs_get_string("/plugins/core/enhanced_history/string_date"), "no"))
dates = TRUE;
if(strcmp(purple_prefs_get_string("/plugins/core/enhanced_history/string_im"), "no"))
ims = TRUE;
if(strcmp(purple_prefs_get_string("/plugins/core/enhanced_history/string_chat"), "no"))
chats = TRUE;
purple_prefs_add_int(PREF_NUMBER_PATH, purple_prefs_get_int("/plugins/core/enhanced_history/int"));
purple_prefs_add_int(PREF_BYTES_PATH, purple_prefs_get_int("/plugins/core/enhanced_history/bytes"));
purple_prefs_add_int(PREF_MINS_PATH, purple_prefs_get_int("/plugins/core/enhanced_history/mins"));
purple_prefs_add_int(PREF_HOURS_PATH, purple_prefs_get_int("/plugins/core/enhanced_history/hours"));
purple_prefs_add_int(PREF_DAYS_PATH, purple_prefs_get_int("/plugins/core/enhanced_history/days"));
purple_prefs_add_bool(PREF_DATES_PATH, dates);
purple_prefs_add_bool(PREF_IM_PATH, ims);
purple_prefs_add_bool(PREF_CHAT_PATH, chats);
purple_prefs_remove("/plugins/core/enhanced_history/int");
purple_prefs_remove("/plugins/core/enhanced_history/bytes");
purple_prefs_remove("/plugins/core/enhanced_history/mins");
purple_prefs_remove("/plugins/core/enhanced_history/hours");
purple_prefs_remove("/plugins/core/enhanced_history/days");
purple_prefs_remove("/plugins/core/enhanced_history/string_date");
purple_prefs_remove("/plugins/core/enhanced_history/string_im");
purple_prefs_remove("/plugins/core/enhanced_history/string_chat");
purple_prefs_remove("/plugins/core/enhanced_history");
} else {
/* Create these prefs with sensible defaults */
purple_prefs_add_int(PREF_NUMBER_PATH, 10);
purple_prefs_add_int(PREF_BYTES_PATH, 4096);
purple_prefs_add_int(PREF_MINS_PATH, 0);
purple_prefs_add_int(PREF_HOURS_PATH, 0);
purple_prefs_add_int(PREF_DAYS_PATH, 0);
purple_prefs_add_bool(PREF_DATES_PATH, TRUE);
purple_prefs_add_bool(PREF_IM_PATH, TRUE);
purple_prefs_add_bool(PREF_CHAT_PATH, FALSE);
}
info.name = _("Enhanced History");
info.summary = _("An enhanced version of the history plugin.");
info.description = _("An enhanced versoin of the history plugin. Grants ability to "
"select the number of previous conversations to show instead of just one.");
}
PURPLE_INIT_PLUGIN(enhanced_history, init_plugin, info)