Mercurial > grim > purple-plugin-pack
view schedule/schedule.c @ 981:efa713151e2b org.guifications.plugins
Using "-I m4macros" as aclocal arguments is not valid, as we don't have an
m4macros directory.
author | rekkanoryo@guifications.org |
---|---|
date | Thu, 27 Nov 2008 18:22:45 -0500 |
parents | 6dabeef0718f |
children | 5d366994099b |
line wrap: on
line source
/* * 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; gboolean dirty = FALSE; 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; dirty = TRUE; 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); }