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;
|
6216
|
74
|
6065
|
75 esc = g_markup_escape_text(am->name, -1);
|
6216
|
76 fprintf(fp, "\t<status name=\"%s\">\n", esc);
|
6065
|
77 g_free(esc);
|
|
78
|
6216
|
79 fprintf(fp, "\t\t<state>away</state>\n");
|
|
80
|
|
81 esc = g_markup_escape_text(am->message, -1);
|
|
82 fprintf(fp, "\t\t<message>%s</message>\n", esc);
|
|
83 g_free(esc);
|
|
84
|
|
85 fprintf(fp, "\t</status>\n");
|
6065
|
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;
|
6216
|
137
|
6065
|
138 }
|
6113
|
139
|
|
140 g_hash_table_destroy(atts);
|
6065
|
141 }
|
|
142
|
|
143 static void
|
|
144 end_element_handler(GMarkupParseContext *context, const gchar *element_name,
|
|
145 gpointer user_data, GError **error)
|
|
146 {
|
|
147 StatusParserData *data = user_data;
|
|
148 gchar *buffer;
|
|
149
|
|
150 if (data->buffer == NULL)
|
|
151 return;
|
|
152
|
|
153 buffer = g_string_free(data->buffer, FALSE);
|
|
154 data->buffer = NULL;
|
|
155
|
|
156 if (data->tag == TAG_MESSAGE) {
|
|
157 if (*buffer != '\0')
|
|
158 g_snprintf(data->am->message, sizeof(data->am->message), "%s", buffer);
|
|
159 }
|
|
160
|
|
161 data->tag = TAG_NONE;
|
|
162
|
|
163 g_free(buffer);
|
|
164 }
|
|
165
|
|
166 static void
|
|
167 text_handler(GMarkupParseContext *context, const gchar *text,
|
|
168 gsize text_len, gpointer user_data, GError **error)
|
|
169 {
|
|
170 StatusParserData *data = user_data;
|
|
171
|
|
172 if (data->buffer == NULL)
|
|
173 data->buffer = g_string_new_len(text, text_len);
|
|
174 else
|
|
175 g_string_append_len(data->buffer, text, text_len);
|
|
176 }
|
|
177
|
|
178 static GMarkupParser status_parser =
|
|
179 {
|
|
180 start_element_handler,
|
|
181 end_element_handler,
|
|
182 text_handler,
|
|
183 NULL,
|
|
184 NULL
|
|
185 };
|
|
186
|
|
187 void gaim_status_sync()
|
|
188 {
|
|
189 FILE *fp;
|
|
190 const char *user_dir = gaim_user_dir();
|
|
191 char *filename, *filename_real;
|
|
192
|
|
193 if (!status_loaded) {
|
|
194 gaim_debug(GAIM_DEBUG_WARNING, "status", "Writing status to disk.\n");
|
|
195 schedule_status_save();
|
|
196 return;
|
|
197 }
|
|
198
|
|
199 if (user_dir == NULL)
|
|
200 return;
|
|
201
|
|
202 gaim_debug(GAIM_DEBUG_INFO, "status", "Saving statuses to disk\n");
|
|
203
|
|
204 fp = fopen(user_dir, "r");
|
|
205
|
|
206 if (fp == NULL)
|
|
207 mkdir(user_dir, S_IRUSR | S_IWUSR | S_IXUSR);
|
|
208 else
|
|
209 fclose(fp);
|
|
210
|
|
211 filename = g_build_filename(user_dir, "status.xml.save", NULL);
|
|
212
|
|
213 if ((fp = fopen(filename, "w")) != NULL) {
|
|
214 GSList *l;
|
|
215
|
|
216 fprintf(fp, "<?xml version='1.0' encoding='UTF-8' ?>\n\n");
|
|
217 fprintf(fp, "<statuses>\n");
|
|
218
|
|
219 for (l = away_messages; l != NULL; l = l->next)
|
|
220 gaim_status_write(fp, l->data);
|
|
221
|
|
222 fprintf(fp, "</statuses>\n");
|
|
223
|
|
224 fclose(fp);
|
|
225 chmod(filename, S_IRUSR | S_IWUSR);
|
|
226 }
|
|
227 else {
|
|
228 gaim_debug(GAIM_DEBUG_ERROR, "status", "Unable to write %s\n",
|
|
229 filename);
|
|
230 }
|
|
231
|
|
232 filename_real = g_build_filename(user_dir, "status.xml", NULL);
|
|
233
|
|
234 if (rename(filename, filename_real) < 0) {
|
|
235 gaim_debug(GAIM_DEBUG_ERROR, "status", "Error renaming %s to %s\n",
|
|
236 filename, filename_real);
|
|
237 }
|
|
238
|
|
239 g_free(filename);
|
|
240 g_free(filename_real);
|
|
241
|
|
242 }
|
|
243
|
|
244 void gaim_status_load()
|
|
245 {
|
|
246 gchar *filename = g_build_filename(gaim_user_dir(), "status.xml", NULL);
|
|
247 gchar *contents = NULL;
|
|
248 gsize length;
|
|
249 GMarkupParseContext *context;
|
|
250 GError *error = NULL;
|
|
251 StatusParserData *parser_data;
|
|
252
|
|
253 if (filename == NULL) {
|
|
254 status_loaded = TRUE;
|
|
255 return;
|
|
256 }
|
|
257
|
|
258 if (!g_file_get_contents(filename, &contents, &length, &error)) {
|
|
259 gaim_debug(GAIM_DEBUG_ERROR, "status",
|
|
260 "Error reading statuses: %s\n", error->message);
|
|
261 g_error_free(error);
|
|
262 g_free(filename);
|
|
263 status_loaded = TRUE;
|
|
264 if (!away_messages) {
|
|
265 struct away_message *a = g_new0(struct away_message, 1);
|
|
266 g_snprintf(a->name, sizeof(a->name), _("Slightly less boring default"));
|
|
267 g_snprintf(a->message, sizeof(a->message), "%s", BORING_DEFAULT_AWAY_MSG);
|
|
268 away_messages = g_slist_append(away_messages, a);
|
|
269 }
|
|
270 return;
|
|
271 }
|
|
272
|
|
273 parser_data = g_new0(StatusParserData, 1);
|
|
274
|
|
275 context = g_markup_parse_context_new(&status_parser, 0,
|
|
276 parser_data, free_parser_data);
|
|
277
|
|
278 if (!g_markup_parse_context_parse(context, contents, length, NULL)) {
|
|
279 g_markup_parse_context_free(context);
|
|
280 g_free(contents);
|
|
281 g_free(filename);
|
|
282 status_loaded = TRUE;
|
|
283 return;
|
|
284 }
|
|
285
|
|
286 if (!g_markup_parse_context_end_parse(context, NULL)) {
|
|
287 gaim_debug(GAIM_DEBUG_ERROR, "status", "Error parsing %s\n",
|
|
288 filename);
|
|
289 g_markup_parse_context_free(context);
|
|
290 g_free(contents);
|
|
291 g_free(filename);
|
|
292 status_loaded = TRUE;
|
|
293 return;
|
|
294 }
|
|
295
|
|
296 g_markup_parse_context_free(context);
|
|
297 g_free(contents);
|
|
298 g_free(filename);
|
|
299 status_loaded = TRUE;
|
|
300 return;
|
|
301 }
|