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/
/*
* Purple-Schedule - Schedule reminders/pounces at specified times.
* Copyright (C) 2006-2008
*
* 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.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02111-1301, USA.
*/
#include "schedule.h"
#include <notify.h>
#include <xmlnode.h>
#include <util.h>
#include <stdarg.h>
#include <stdlib.h>
#include <string.h>
static GList *schedules;
static int timeout;
static void save_cb();
static void parse_schedule(xmlnode *node);
static void parse_when(PurpleSchedule *schedule, xmlnode *when);
static void parse_action(PurpleSchedule *schedule, xmlnode *action);
static int sort_schedules(gconstpointer a, gconstpointer b);
static void purple_schedules_load();
#define TIMEOUT 60 /* update every 60 seconds */
#define MINUTE 60
#define HOUR (MINUTE * 60)
#define DAY (HOUR * 24)
#define YEAR (DAY * 365)
#define WEEK (DAY * 7)
/*static int months[] = {-1, */
/* I think this is going to be rather ugly */
static int
days_in_month(int month, int year)
{
int days[] = {31, -1, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
if (month == 1)
{
if (year %400 == 0)
return 29;
if (year % 100 == 0)
return 28;
if (year % 4 == 0)
return 29;
return 28;
}
else
return days[month];
}
static time_t
get_next(PurpleSchedule *s)
{
struct
{
int mins[61];
int hrs[25];
int dates[32];
int months[13];
int years[3];
} p;
int i;
int y, mo, d, h, mi;
struct tm *tm, test;
time_t tme;
int first = 0;
memset(&p, -1, sizeof(p));
time(&tme);
tm = localtime(&tme);
#define DECIDE(prop, st, count, offset) \
do { \
if (prop == -1) { \
first = 1; \
for (i = 0; i < count; i++) { \
st[i] = ((first ? 0 : offset) + i) % count; \
} \
} else \
st[0] = prop; \
} while (0)
DECIDE(s->minute, p.mins, 60, tm->tm_min); /* Minute */
DECIDE(s->hour, p.hrs, 24, tm->tm_hour); /* Hour */
DECIDE(s->d.date, p.dates, 31, tm->tm_mday); /* Date */
DECIDE(s->month, p.months, 12, tm->tm_mon); /* Month */
if (s->year == -1) {
p.years[0] = tm->tm_year;
p.years[1] = p.years[0] + 1;
} else
p.years[0] = s->year;
test = *tm;
for (y = 0; p.years[y] != -1; y++) {
test.tm_year = p.years[y];
for (mo = 0; p.months[mo] != -1; mo++) {
test.tm_mon = p.months[mo];
for (d = 0; p.dates[d] != -1; d++) {
test.tm_mday = p.dates[d] + 1; /* XXX: +1 is necessary */
if (test.tm_mday > days_in_month(test.tm_mon, test.tm_year + 1900))
continue;
for (h = 0; p.hrs[h] != -1; h++) {
test.tm_hour = p.hrs[h];
for (mi = 0; p.mins[mi] != -1; mi++) {
time_t then;
test.tm_min = p.mins[mi];
then = mktime(&test);
if (then > tme)
return then;
}
}
}
}
}
return -1;
}
static void
calculate_timestamp_for_schedule(PurpleSchedule *schedule)
{
schedule->timestamp = get_next(schedule);
}
void
purple_schedule_reschedule(PurpleSchedule *schedule)
{
calculate_timestamp_for_schedule(schedule);
if (schedule->timestamp < time(NULL))
{
purple_debug_warning("purple-schedule", "schedule \"%s\" will not be executed (%s)\n", schedule->name,
purple_date_format_full(localtime(&schedule->timestamp)));
schedule->timestamp = 0;
}
else
{
purple_debug_info("purple-schedule", "schedule \"%s\" will be executed at: %s\n", schedule->name,
purple_date_format_full(localtime(&schedule->timestamp)));
}
}
static int
sort_schedules(gconstpointer a, gconstpointer b)
{
const PurpleSchedule *sa = a, *sb = b;
if (sa->timestamp < sb->timestamp)
return -1;
else if (sa->timestamp == sb->timestamp)
return 0;
else
return 1;
}
static void
recalculate_next_slots()
{
GList *iter;
for (iter = schedules; iter; iter = iter->next)
{
purple_schedule_reschedule(iter->data);
}
schedules = g_list_sort(schedules, sort_schedules);
}
PurpleSchedule *purple_schedule_new()
{
PurpleSchedule *sch = g_new0(PurpleSchedule, 1);
return sch;
}
void purple_schedule_add_action(PurpleSchedule *schedule, ScheduleActionType type, ...)
{
ScheduleAction *action;
va_list vargs;
action = g_new0(ScheduleAction, 1);
action->type = type;
va_start(vargs, type);
switch (type)
{
case SCHEDULE_ACTION_POPUP:
action->d.popup_message = g_strdup(va_arg(vargs, char *));
break;
case SCHEDULE_ACTION_CONV:
action->d.send.message = g_strdup(va_arg(vargs, char*));
action->d.send.who = g_strdup(va_arg(vargs, char *));
action->d.send.account = va_arg(vargs, PurpleAccount*);
break;
case SCHEDULE_ACTION_STATUS:
action->d.status_title = g_strdup(va_arg(vargs, char *));
break;
default:
g_free(action);
g_return_if_reached();
}
va_end(vargs);
schedule->actions = g_list_append(schedule->actions, action);
save_cb();
}
void purple_schedule_remove_action(PurpleSchedule *schedule, ScheduleActionType type)
{
GList *iter;
for (iter = schedule->actions; iter; iter = iter->next)
{
ScheduleAction *action = iter->data;
if (action->type == type)
{
purple_schedule_action_destroy(action);
schedule->actions = g_list_delete_link(schedule->actions, iter);
break;
}
}
}
void purple_schedule_action_activate(ScheduleAction *action)
{
PurpleConversation *conv;
switch (action->type)
{
case SCHEDULE_ACTION_POPUP:
purple_notify_message(action, PURPLE_NOTIFY_MSG_INFO, _("Schedule"), action->d.popup_message,
NULL, NULL, NULL);
break;
case SCHEDULE_ACTION_CONV:
conv = purple_conversation_new(PURPLE_CONV_TYPE_IM, action->d.send.account, action->d.send.who);
purple_conv_im_send_with_flags(PURPLE_CONV_IM(conv), action->d.send.message, 0);
break;
default:
purple_debug_warning("purple-schedule", "unimplemented action\n");
break;
}
}
void purple_schedule_activate_actions(PurpleSchedule *sch)
{
GList *iter;
for (iter = sch->actions; iter; iter = iter->next)
{
purple_schedule_action_activate(iter->data);
}
}
void purple_schedule_action_destroy(ScheduleAction *action)
{
switch (action->type)
{
case SCHEDULE_ACTION_POPUP:
g_free(action->d.popup_message);
break;
case SCHEDULE_ACTION_CONV:
g_free(action->d.send.message);
g_free(action->d.send.who);
break;
case SCHEDULE_ACTION_STATUS:
g_free(action->d.status_title);
break;
default:
purple_debug_warning("purple-schedule", "unknown action type\n");
break;
}
g_free(action);
purple_notify_close_with_handle(action);
}
void purple_schedule_destroy(PurpleSchedule *schedule)
{
while (schedule->actions)
{
ScheduleAction *action = schedule->actions->data;
purple_schedule_action_destroy(action);
schedule->actions = g_list_delete_link(schedule->actions, schedule->actions);
}
g_free(schedule);
schedules = g_list_remove(schedules, schedule);
}
GList *purple_schedules_get_all()
{
return schedules;
}
void purple_schedules_add(PurpleSchedule *schedule)
{
schedules = g_list_append(schedules, schedule);
}
static gboolean
check_and_execute(gpointer null)
{
PurpleSchedule *schedule;
GList *iter = schedules;
if (iter == NULL)
return TRUE;
schedule = iter->data;
while (schedule->timestamp && schedule->timestamp < time(NULL))
{
purple_schedule_activate_actions(schedule);
purple_schedule_reschedule(schedule);
iter = iter->next;
if (iter == NULL)
break;
schedule = iter->data;
}
schedules = g_list_sort(schedules, sort_schedules);
return TRUE;
}
void purple_schedule_init()
{
purple_schedules_load();
recalculate_next_slots();
timeout = g_timeout_add(10000, (GSourceFunc)check_and_execute, NULL);
}
void purple_schedule_uninit()
{
g_source_remove(timeout);
save_cb();
while (schedules)
{
purple_schedule_destroy(schedules->data);
}
}
void purple_schedules_sync()
{
save_cb();
}
/**
* Read from XML
*/
static void
purple_schedules_load()
{
xmlnode *purple, *schedule;
purple = purple_util_read_xml_from_file("schedules.xml", _("list of schedules"));
if (purple == NULL)
return;
schedule = xmlnode_get_child(purple, "schedules");
if (schedule)
{
xmlnode *sch;
for (sch = xmlnode_get_child(schedule, "schedule"); sch;
sch = xmlnode_get_next_twin(sch))
{
parse_schedule(sch);
}
}
xmlnode_free(purple);
}
static void
parse_schedule(xmlnode *node)
{
PurpleSchedule *schedule;
xmlnode *child;
const char *name;
child = xmlnode_get_child(node, "when");
name = xmlnode_get_attrib(node, "name");
if (name == NULL || child == NULL)
{
return;
}
schedule = purple_schedule_new();
schedule->name = g_strdup(name);
schedules = g_list_append(schedules, schedule);
parse_when(schedule, child);
for (child = xmlnode_get_child(node, "action"); child; child = xmlnode_get_next_twin(child))
{
parse_action(schedule, child);
}
}
static void
parse_when(PurpleSchedule *schedule, xmlnode *when)
{
int type = atoi(xmlnode_get_attrib(when, "type"));
schedule->type = type;
if (type == PURPLE_SCHEDULE_TYPE_DATE)
schedule->d.date = atoi(xmlnode_get_attrib(when, "date"));
else
schedule->d.day = atoi(xmlnode_get_attrib(when, "day"));
schedule->month = atoi(xmlnode_get_attrib(when, "month"));
schedule->year = atoi(xmlnode_get_attrib(when, "year"));
schedule->hour = atoi(xmlnode_get_attrib(when, "hour"));
schedule->minute = atoi(xmlnode_get_attrib(when, "minute"));
}
static void
parse_action(PurpleSchedule *schedule, xmlnode *action)
{
int type = atoi(xmlnode_get_attrib(action, "type"));
xmlnode *data = xmlnode_get_child(action, "data"), *account, *message;
char *tmp;
switch (type)
{
case SCHEDULE_ACTION_POPUP:
tmp = xmlnode_get_data(data);
purple_schedule_add_action(schedule, type, tmp);
g_free(tmp);
break;
case SCHEDULE_ACTION_CONV:
account = xmlnode_get_child(data, "account");
message = xmlnode_get_child(data, "message");
tmp = xmlnode_get_data(message);
purple_schedule_add_action(schedule, type, tmp,
xmlnode_get_attrib(account, "who"),
purple_accounts_find(xmlnode_get_attrib(account, "name"),
xmlnode_get_attrib(account, "prpl")));
g_free(tmp);
break;
case SCHEDULE_ACTION_STATUS:
tmp = xmlnode_get_data(action);
purple_schedule_add_action(schedule, type, tmp);
g_free(tmp);
break;
default:
g_return_if_reached();
}
}
/**
* Write into XML
*/
static void
xmlnode_set_attrib_int(xmlnode *node, const char *name, int value)
{
char *v = g_strdup_printf("%d", value);
xmlnode_set_attrib(node, name, v);
g_free(v);
}
static xmlnode*
action_to_xmlnode(ScheduleAction *action)
{
xmlnode *node, *child, *gchild;
node = xmlnode_new("action");
xmlnode_set_attrib_int(node, "type", action->type);
child = xmlnode_new_child(node, "data");
switch (action->type)
{
case SCHEDULE_ACTION_POPUP:
/* XXX: need to do funky string-operations? */
xmlnode_insert_data(child, action->d.popup_message, -1);
break;
case SCHEDULE_ACTION_CONV:
gchild = xmlnode_new_child(child, "account");
xmlnode_set_attrib(gchild, "prpl", purple_account_get_protocol_id(action->d.send.account));
xmlnode_set_attrib(gchild, "name", purple_account_get_username(action->d.send.account));
xmlnode_set_attrib(gchild, "who", action->d.send.who);
gchild = xmlnode_new_child(child, "message");
xmlnode_insert_data(gchild, action->d.send.message, -1);
break;
default:
purple_debug_warning("purple-schedule", "unknown action type\n");
break;
}
return node;
}
static xmlnode*
when_to_xmlnode(PurpleSchedule *schedule)
{
xmlnode *node;
node = xmlnode_new("when");
xmlnode_set_attrib_int(node, "type", schedule->type);
switch (schedule->type)
{
case PURPLE_SCHEDULE_TYPE_DATE:
xmlnode_set_attrib_int(node, "date", schedule->d.date);
break;
case PURPLE_SCHEDULE_TYPE_DAY:
xmlnode_set_attrib_int(node, "day", schedule->d.day);
break;
}
xmlnode_set_attrib_int(node, "month", schedule->month);
xmlnode_set_attrib_int(node, "year", schedule->year);
xmlnode_set_attrib_int(node, "hour", schedule->hour);
xmlnode_set_attrib_int(node, "minute", schedule->minute);
return node;
}
static xmlnode*
schedule_to_xmlnode(PurpleSchedule *schedule)
{
xmlnode *node, *child;
GList *iter;
node = xmlnode_new("schedule");
xmlnode_set_attrib(node, "name", schedule->name);
child = when_to_xmlnode(schedule);
xmlnode_insert_child(node, child);
for (iter = schedule->actions; iter; iter = iter->next)
{
xmlnode_insert_child(node, action_to_xmlnode(iter->data));
}
return node;
}
static xmlnode*
schedules_to_xmlnode()
{
GList *iter;
xmlnode *node, *child;
node = xmlnode_new("purple-schedule");
xmlnode_set_attrib(node, "version", PP_VERSION);
child = xmlnode_new_child(node, "schedules");
for (iter = schedules; iter; iter = iter->next)
{
PurpleSchedule *schedule = iter->data;
xmlnode_insert_child(child, schedule_to_xmlnode(schedule));
}
return node;
}
static void
save_cb()
{
xmlnode *node;
char *data;
node = schedules_to_xmlnode();
data = xmlnode_to_formatted_str(node, NULL);
purple_util_write_data_to_file("schedules.xml", data, -1);
g_free(data);
xmlnode_free(node);
}