6065
|
1 /*
|
|
2 * gaim
|
|
3 *
|
|
4 * Copyright (C) 2003 Jason Priestly
|
|
5 * Copyright (C) 2003 Luke Perry
|
|
6 *
|
|
7 * This program is free software; you can redistribute it and/or modify
|
|
8 * it under the terms of the GNU General Public License as published by
|
|
9 * the Free Software Foundation; either version 2 of the License, or
|
|
10 * (at your option) any later version.
|
|
11 *
|
|
12 * This program is distributed in the hope that it will be useful,
|
|
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
15 * GNU General Public License for more details.
|
|
16 *
|
|
17 * You should have received a copy of the GNU General Public License
|
|
18 * along with this program; if not, write to the Free Software
|
|
19 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|
20 */
|
|
21
|
|
22 #include "status.h"
|
|
23 #include "internal.h"
|
|
24 #include "ui.h"
|
|
25 #include "debug.h"
|
|
26 #include "util.h"
|
|
27
|
|
28 /* for people like myself who are too lazy to add an away msg :) */
|
|
29 /* I don't know who "myself" is in this context. The exclamation point
|
|
30 * makes it slightly less boring ;) */
|
|
31 #define BORING_DEFAULT_AWAY_MSG _("Sorry, I ran out for a bit!")
|
|
32
|
|
33 /* XML File Saving */
|
|
34
|
|
35 /* All of this code is adapted from Nathan Walp's. It's adapted all over the place
|
|
36 * for accounts, the buddy list, pounces, preferences, and the likes. It would be
|
|
37 * neat if we could somehow make this more generic. */
|
|
38 static gboolean status_loaded = FALSE;
|
|
39 static guint status_save_timer = 0;
|
|
40
|
|
41
|
|
42 typedef enum
|
|
43 {
|
|
44 TAG_NONE = 0,
|
|
45 TAG_STATUS,
|
|
46 TAG_STATE,
|
|
47 TAG_MESSAGE,
|
|
48
|
|
49 } StatusParserTag;
|
|
50
|
|
51
|
|
52 typedef struct
|
|
53 {
|
|
54 StatusParserTag tag;
|
|
55 GString *buffer;
|
|
56 struct away_message *am;
|
|
57
|
|
58 } StatusParserData;
|
|
59
|
|
60 static void
|
|
61 free_parser_data(gpointer user_data)
|
|
62 {
|
|
63 StatusParserData *data = user_data;
|
|
64
|
|
65 if (data->buffer != NULL)
|
|
66 g_string_free(data->buffer, TRUE);
|
|
67
|
|
68 g_free(data);
|
|
69 }
|
|
70
|
|
71 static void gaim_status_write(FILE *fp, struct away_message *am)
|
|
72 {
|
|
73 char *esc = NULL;
|
|
74
|
|
75 esc = g_markup_escape_text(am->name, -1);
|
|
76 fprintf(fp, " <status name=\"%s\">\n", esc);
|
|
77 g_free(esc);
|
|
78
|
|
79 fprintf(fp, " <state>away</state>\n");
|
|
80
|
|
81 esc = g_markup_escape_text(am->message, -1);
|
|
82 fprintf(fp, " <message>%s</message>\n", esc);
|
|
83 g_free(esc);
|
|
84
|
|
85 fprintf(fp, " </status>\n");
|
|
86 }
|
|
87
|
|
88 static gboolean
|
|
89 status_save_cb(gpointer unused)
|
|
90 {
|
|
91 gaim_status_sync();
|
|
92 status_save_timer = 0;
|
|
93
|
|
94 return FALSE;
|
|
95 }
|
|
96
|
|
97 static void
|
|
98 schedule_status_save()
|
|
99 {
|
|
100 if (!status_save_timer)
|
|
101 status_save_timer = g_timeout_add(5000, status_save_cb, NULL);
|
|
102 }
|
|
103
|
|
104 static void
|
|
105 start_element_handler(GMarkupParseContext *context,
|
|
106 const gchar *element_name,
|
|
107 const gchar **attribute_names,
|
|
108 const gchar **attribute_values,
|
|
109 gpointer user_data, GError **error)
|
|
110 {
|
|
111 const char *value;
|
|
112 StatusParserData *data = user_data;
|
|
113 GHashTable *atts;
|
|
114 int i;
|
|
115
|
|
116 atts = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, g_free);
|
|
117
|
|
118 for (i = 0; attribute_names[i] != NULL; i++) {
|
|
119 g_hash_table_insert(atts, g_strdup(attribute_names[i]),
|
|
120 g_strdup(attribute_values[i]));
|
|
121 }
|
|
122
|
|
123 if (data->buffer != NULL) {
|
|
124 g_string_free(data->buffer, TRUE);
|
|
125 data->buffer = NULL;
|
|
126 }
|
|
127
|
|
128 if (!strcmp(element_name, "status")) {
|
|
129 data->tag = TAG_STATUS;
|
|
130 if ((value = g_hash_table_lookup(atts, "name")) != NULL) {
|
|
131 data->am = g_new0(struct away_message, 1);
|
|
132 g_snprintf(data->am->name, sizeof(data->am->name), "%s", value);
|
|
133 away_messages = g_slist_append(away_messages, data->am);
|
|
134 }
|
|
135 } else if (!strcmp(element_name, "message")) {
|
|
136 data->tag = TAG_MESSAGE;
|
|
137
|
|
138 }
|
|
139 }
|
|
140
|
|
141 static void
|
|
142 end_element_handler(GMarkupParseContext *context, const gchar *element_name,
|
|
143 gpointer user_data, GError **error)
|
|
144 {
|
|
145 StatusParserData *data = user_data;
|
|
146 gchar *buffer;
|
|
147
|
|
148 if (data->buffer == NULL)
|
|
149 return;
|
|
150
|
|
151 buffer = g_string_free(data->buffer, FALSE);
|
|
152 data->buffer = NULL;
|
|
153
|
|
154 if (data->tag == TAG_MESSAGE) {
|
|
155 if (*buffer != '\0')
|
|
156 g_snprintf(data->am->message, sizeof(data->am->message), "%s", buffer);
|
|
157 }
|
|
158
|
|
159 data->tag = TAG_NONE;
|
|
160
|
|
161 g_free(buffer);
|
|
162 }
|
|
163
|
|
164 static void
|
|
165 text_handler(GMarkupParseContext *context, const gchar *text,
|
|
166 gsize text_len, gpointer user_data, GError **error)
|
|
167 {
|
|
168 StatusParserData *data = user_data;
|
|
169
|
|
170 if (data->buffer == NULL)
|
|
171 data->buffer = g_string_new_len(text, text_len);
|
|
172 else
|
|
173 g_string_append_len(data->buffer, text, text_len);
|
|
174 }
|
|
175
|
|
176 static GMarkupParser status_parser =
|
|
177 {
|
|
178 start_element_handler,
|
|
179 end_element_handler,
|
|
180 text_handler,
|
|
181 NULL,
|
|
182 NULL
|
|
183 };
|
|
184
|
|
185 void gaim_status_sync()
|
|
186 {
|
|
187 FILE *fp;
|
|
188 const char *user_dir = gaim_user_dir();
|
|
189 char *filename, *filename_real;
|
|
190
|
|
191 if (!status_loaded) {
|
|
192 gaim_debug(GAIM_DEBUG_WARNING, "status", "Writing status to disk.\n");
|
|
193 schedule_status_save();
|
|
194 return;
|
|
195 }
|
|
196
|
|
197 if (user_dir == NULL)
|
|
198 return;
|
|
199
|
|
200 gaim_debug(GAIM_DEBUG_INFO, "status", "Saving statuses to disk\n");
|
|
201
|
|
202 fp = fopen(user_dir, "r");
|
|
203
|
|
204 if (fp == NULL)
|
|
205 mkdir(user_dir, S_IRUSR | S_IWUSR | S_IXUSR);
|
|
206 else
|
|
207 fclose(fp);
|
|
208
|
|
209 filename = g_build_filename(user_dir, "status.xml.save", NULL);
|
|
210
|
|
211 if ((fp = fopen(filename, "w")) != NULL) {
|
|
212 GSList *l;
|
|
213
|
|
214 fprintf(fp, "<?xml version='1.0' encoding='UTF-8' ?>\n\n");
|
|
215 fprintf(fp, "<statuses>\n");
|
|
216
|
|
217 for (l = away_messages; l != NULL; l = l->next)
|
|
218 gaim_status_write(fp, l->data);
|
|
219
|
|
220 fprintf(fp, "</statuses>\n");
|
|
221
|
|
222 fclose(fp);
|
|
223 chmod(filename, S_IRUSR | S_IWUSR);
|
|
224 }
|
|
225 else {
|
|
226 gaim_debug(GAIM_DEBUG_ERROR, "status", "Unable to write %s\n",
|
|
227 filename);
|
|
228 }
|
|
229
|
|
230 filename_real = g_build_filename(user_dir, "status.xml", NULL);
|
|
231
|
|
232 if (rename(filename, filename_real) < 0) {
|
|
233 gaim_debug(GAIM_DEBUG_ERROR, "status", "Error renaming %s to %s\n",
|
|
234 filename, filename_real);
|
|
235 }
|
|
236
|
|
237 g_free(filename);
|
|
238 g_free(filename_real);
|
|
239
|
|
240 }
|
|
241
|
|
242 void gaim_status_load()
|
|
243 {
|
|
244 gchar *filename = g_build_filename(gaim_user_dir(), "status.xml", NULL);
|
|
245 gchar *contents = NULL;
|
|
246 gsize length;
|
|
247 GMarkupParseContext *context;
|
|
248 GError *error = NULL;
|
|
249 StatusParserData *parser_data;
|
|
250
|
|
251 if (filename == NULL) {
|
|
252 status_loaded = TRUE;
|
|
253 return;
|
|
254 }
|
|
255
|
|
256 if (!g_file_get_contents(filename, &contents, &length, &error)) {
|
|
257 gaim_debug(GAIM_DEBUG_ERROR, "status",
|
|
258 "Error reading statuses: %s\n", error->message);
|
|
259 g_error_free(error);
|
|
260 g_free(filename);
|
|
261 status_loaded = TRUE;
|
|
262 if (!away_messages) {
|
|
263 struct away_message *a = g_new0(struct away_message, 1);
|
|
264 g_snprintf(a->name, sizeof(a->name), _("Slightly less boring default"));
|
|
265 g_snprintf(a->message, sizeof(a->message), "%s", BORING_DEFAULT_AWAY_MSG);
|
|
266 away_messages = g_slist_append(away_messages, a);
|
|
267 }
|
|
268 return;
|
|
269 }
|
|
270
|
|
271 parser_data = g_new0(StatusParserData, 1);
|
|
272
|
|
273 context = g_markup_parse_context_new(&status_parser, 0,
|
|
274 parser_data, free_parser_data);
|
|
275
|
|
276 if (!g_markup_parse_context_parse(context, contents, length, NULL)) {
|
|
277 g_markup_parse_context_free(context);
|
|
278 g_free(contents);
|
|
279 g_free(filename);
|
|
280 status_loaded = TRUE;
|
|
281 return;
|
|
282 }
|
|
283
|
|
284 if (!g_markup_parse_context_end_parse(context, NULL)) {
|
|
285 gaim_debug(GAIM_DEBUG_ERROR, "status", "Error parsing %s\n",
|
|
286 filename);
|
|
287 g_markup_parse_context_free(context);
|
|
288 g_free(contents);
|
|
289 g_free(filename);
|
|
290 status_loaded = TRUE;
|
|
291 return;
|
|
292 }
|
|
293
|
|
294 g_markup_parse_context_free(context);
|
|
295 g_free(contents);
|
|
296 g_free(filename);
|
|
297 status_loaded = TRUE;
|
|
298 return;
|
|
299 }
|