gaim/gaim

I feel dumb that it took me so long to get this right.
oldstatus v1_2_1
2005-04-03, Mark Doliner
b59671364e74
I feel dumb that it took me so long to get this right.
I hope it's right. I'm going to do a bit o' testing.
/*
* gaim
*
* Gaim is the legal property of its developers, whose names are too numerous
* to list here. Please refer to the COPYRIGHT file distributed with this
* source distribution.
*
* 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include "status.h"
#include "internal.h"
#include "debug.h"
#include "util.h"
/* XXX CORE/UI */
#include "away.h"
#include "gtkgaim.h"
/* for people like myself who are too lazy to add an away msg :) */
/* I don't know who "myself" is in this context. The exclamation point
* makes it slightly less boring ;) */
#define BORING_DEFAULT_AWAY_MSG _("Sorry, I ran out for a bit!")
/* XML File Saving */
/* All of this code is adapted from Nathan Walp's. It's adapted all over the place
* for accounts, the buddy list, pounces, preferences, and the likes. It would be
* neat if we could somehow make this more generic. */
static gboolean status_loaded = FALSE;
static guint status_save_timer = 0;
typedef enum
{
TAG_NONE = 0,
TAG_STATUS,
TAG_STATE,
TAG_MESSAGE,
} StatusParserTag;
typedef struct
{
StatusParserTag tag;
GString *buffer;
struct away_message *am;
} StatusParserData;
static void
free_parser_data(gpointer user_data)
{
StatusParserData *data = user_data;
if (data->buffer != NULL)
g_string_free(data->buffer, TRUE);
g_free(data);
}
static void gaim_status_write(FILE *fp, struct away_message *am)
{
char *esc = NULL;
esc = g_markup_escape_text(am->name, -1);
fprintf(fp, "\t<status name=\"%s\">\n", esc);
g_free(esc);
fprintf(fp, "\t\t<state>away</state>\n");
esc = g_markup_escape_text(am->message, -1);
fprintf(fp, "\t\t<message>%s</message>\n", esc);
g_free(esc);
fprintf(fp, "\t</status>\n");
}
static gboolean
status_save_cb(gpointer unused)
{
gaim_status_sync();
status_save_timer = 0;
return FALSE;
}
static void
schedule_status_save()
{
if (!status_save_timer)
status_save_timer = gaim_timeout_add(5000, status_save_cb, NULL);
}
static void
start_element_handler(GMarkupParseContext *context,
const gchar *element_name,
const gchar **attribute_names,
const gchar **attribute_values,
gpointer user_data, GError **error)
{
const char *value;
StatusParserData *data = user_data;
GHashTable *atts;
int i;
atts = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, g_free);
for (i = 0; attribute_names[i] != NULL; i++) {
g_hash_table_insert(atts, g_strdup(attribute_names[i]),
g_strdup(attribute_values[i]));
}
if (data->buffer != NULL) {
g_string_free(data->buffer, TRUE);
data->buffer = NULL;
}
if (!strcmp(element_name, "status")) {
data->tag = TAG_STATUS;
if ((value = g_hash_table_lookup(atts, "name")) != NULL) {
data->am = g_new0(struct away_message, 1);
g_snprintf(data->am->name, sizeof(data->am->name), "%s", value);
away_messages = g_slist_append(away_messages, data->am);
}
} else if (!strcmp(element_name, "message")) {
data->tag = TAG_MESSAGE;
}
g_hash_table_destroy(atts);
}
static void
end_element_handler(GMarkupParseContext *context, const gchar *element_name,
gpointer user_data, GError **error)
{
StatusParserData *data = user_data;
gchar *buffer;
if (data->buffer == NULL)
return;
buffer = g_string_free(data->buffer, FALSE);
data->buffer = NULL;
if (data->tag == TAG_MESSAGE) {
if (*buffer != '\0')
g_snprintf(data->am->message, sizeof(data->am->message), "%s", buffer);
}
data->tag = TAG_NONE;
g_free(buffer);
}
static void
text_handler(GMarkupParseContext *context, const gchar *text,
gsize text_len, gpointer user_data, GError **error)
{
StatusParserData *data = user_data;
if (data->buffer == NULL)
data->buffer = g_string_new_len(text, text_len);
else
g_string_append_len(data->buffer, text, text_len);
}
static GMarkupParser status_parser =
{
start_element_handler,
end_element_handler,
text_handler,
NULL,
NULL
};
void gaim_status_sync()
{
FILE *fp;
const char *user_dir = gaim_user_dir();
char *filename, *filename_real;
if (!status_loaded) {
gaim_debug(GAIM_DEBUG_WARNING, "status", "Writing status to disk.\n");
schedule_status_save();
return;
}
if (user_dir == NULL)
return;
gaim_debug(GAIM_DEBUG_INFO, "status", "Saving statuses to disk\n");
fp = g_fopen(user_dir, "r");
if (fp == NULL)
g_mkdir(user_dir, S_IRUSR | S_IWUSR | S_IXUSR);
else
fclose(fp);
filename = g_build_filename(user_dir, "status.xml.save", NULL);
if ((fp = g_fopen(filename, "w")) != NULL) {
GSList *l;
fprintf(fp, "<?xml version='1.0' encoding='UTF-8' ?>\n\n");
fprintf(fp, "<statuses version='1.0'>\n");
for (l = away_messages; l != NULL; l = l->next)
gaim_status_write(fp, l->data);
fprintf(fp, "</statuses>\n");
fclose(fp);
chmod(filename, S_IRUSR | S_IWUSR);
}
else {
gaim_debug(GAIM_DEBUG_ERROR, "status", "Unable to write %s\n",
filename);
g_free(filename);
return;
}
filename_real = g_build_filename(user_dir, "status.xml", NULL);
if (g_rename(filename, filename_real) < 0) {
gaim_debug(GAIM_DEBUG_ERROR, "status", "Error renaming %s to %s\n",
filename, filename_real);
}
g_free(filename);
g_free(filename_real);
}
void gaim_status_load()
{
gchar *filename = g_build_filename(gaim_user_dir(), "status.xml", NULL);
gchar *contents = NULL;
gsize length;
GMarkupParseContext *context;
GError *error = NULL;
StatusParserData *parser_data;
if (filename == NULL) {
status_loaded = TRUE;
return;
}
if (!g_file_get_contents(filename, &contents, &length, &error)) {
gaim_debug(GAIM_DEBUG_ERROR, "status",
"Error reading statuses: %s\n", error->message);
g_error_free(error);
g_free(filename);
status_loaded = TRUE;
if (!away_messages) {
struct away_message *a = g_new0(struct away_message, 1);
g_snprintf(a->name, sizeof(a->name), _("Slightly less boring default"));
g_snprintf(a->message, sizeof(a->message), "%s", _(BORING_DEFAULT_AWAY_MSG));
away_messages = g_slist_append(away_messages, a);
}
return;
}
parser_data = g_new0(StatusParserData, 1);
context = g_markup_parse_context_new(&status_parser, 0,
parser_data, free_parser_data);
if (!g_markup_parse_context_parse(context, contents, length, NULL)) {
g_markup_parse_context_free(context);
g_free(contents);
g_free(filename);
status_loaded = TRUE;
return;
}
if (!g_markup_parse_context_end_parse(context, NULL)) {
gaim_debug(GAIM_DEBUG_ERROR, "status", "Error parsing %s\n",
filename);
g_markup_parse_context_free(context);
g_free(contents);
g_free(filename);
status_loaded = TRUE;
return;
}
g_markup_parse_context_free(context);
g_free(contents);
g_free(filename);
status_loaded = TRUE;
return;
}