Mercurial > pidgin
annotate src/log.c @ 9924:872c4d8c1192
[gaim-migrate @ 10816]
Sam S. added back support for rendering <strike>
Before anyone gets too excited and decides to add back sup, sub, and pre,
I'd like to recode some stuff in imhtml and make it easier and cleaner to
add tags, especially simple boolean tags like strike.
committer: Tailor Script <tailor@pidgin.im>
author | Tim Ringenbach <marv@pidgin.im> |
---|---|
date | Tue, 31 Aug 2004 20:10:21 +0000 |
parents | 248c3f88ce99 |
children | b23e70bd1215 |
rev | line source |
---|---|
7431 | 1 /** |
2 * @file log.c Logging API | |
3 * @ingroup core | |
4 * | |
5 * gaim | |
6 * | |
8046 | 7 * Gaim is the legal property of its developers, whose names are too numerous |
8 * to list here. Please refer to the COPYRIGHT file distributed with this | |
9 * source distribution. | |
7436 | 10 * |
7431 | 11 * This program is free software; you can redistribute it and/or modify |
12 * it under the terms of the GNU General Public License as published by | |
13 * the Free Software Foundation; either version 2 of the License, or | |
14 * (at your option) any later version. | |
15 * | |
16 * This program is distributed in the hope that it will be useful, | |
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
19 * GNU General Public License for more details. | |
20 * | |
21 * You should have received a copy of the GNU General Public License | |
22 * along with this program; if not, write to the Free Software | |
23 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | |
4184 | 24 */ |
4195 | 25 |
7431 | 26 #include "account.h" |
5872
059d95c67cda
[gaim-migrate @ 6304]
Christian Hammond <chipx86@chipx86.com>
parents:
5839
diff
changeset
|
27 #include "debug.h" |
7431 | 28 #include "internal.h" |
5872
059d95c67cda
[gaim-migrate @ 6304]
Christian Hammond <chipx86@chipx86.com>
parents:
5839
diff
changeset
|
29 #include "log.h" |
5548 | 30 #include "prefs.h" |
5872
059d95c67cda
[gaim-migrate @ 6304]
Christian Hammond <chipx86@chipx86.com>
parents:
5839
diff
changeset
|
31 #include "util.h" |
7764 | 32 #include "stringref.h" |
5872
059d95c67cda
[gaim-migrate @ 6304]
Christian Hammond <chipx86@chipx86.com>
parents:
5839
diff
changeset
|
33 |
8096 | 34 static GSList *loggers = NULL; |
35 | |
7457 | 36 static GaimLogLogger html_logger; |
7431 | 37 static GaimLogLogger txt_logger; |
38 static GaimLogLogger old_logger; | |
5872
059d95c67cda
[gaim-migrate @ 6304]
Christian Hammond <chipx86@chipx86.com>
parents:
5839
diff
changeset
|
39 |
8635 | 40 struct _gaim_logsize_user { |
41 char *name; | |
42 GaimAccount *account; | |
43 }; | |
44 static GHashTable *logsize_users = NULL; | |
45 | |
46 | |
7431 | 47 /************************************************************************** |
48 * PUBLIC LOGGING FUNCTIONS *********************************************** | |
49 **************************************************************************/ | |
4184 | 50 |
7431 | 51 GaimLog *gaim_log_new(GaimLogType type, const char *name, GaimAccount *account, time_t time) |
52 { | |
53 GaimLog *log = g_new0(GaimLog, 1); | |
8635 | 54 log->name = g_strdup(gaim_normalize(account, name)); |
7431 | 55 log->account = account; |
56 log->time = time; | |
8573 | 57 log->type = type; |
8096 | 58 log->logger_data = NULL; |
7431 | 59 log->logger = gaim_log_logger_get(); |
7440 | 60 if (log->logger && log->logger->create) |
61 log->logger->create(log); | |
7431 | 62 return log; |
4184 | 63 } |
64 | |
7431 | 65 void gaim_log_free(GaimLog *log) |
4184 | 66 { |
7431 | 67 g_return_if_fail(log); |
68 if (log->logger && log->logger->finalize) | |
69 log->logger->finalize(log); | |
70 g_free(log->name); | |
71 g_free(log); | |
72 } | |
7436 | 73 |
74 void gaim_log_write(GaimLog *log, GaimMessageFlags type, | |
7431 | 75 const char *from, time_t time, const char *message) |
76 { | |
77 g_return_if_fail(log); | |
78 g_return_if_fail(log->logger); | |
7442 | 79 g_return_if_fail(log->logger->write); |
7431 | 80 |
8635 | 81 if ((log->type == GAIM_LOG_IM && |
82 gaim_prefs_get_bool("/core/logging/log_ims")) || | |
83 (log->type == GAIM_LOG_CHAT && | |
84 gaim_prefs_get_bool("/core/logging/log_chats")) || | |
85 (log->type == GAIM_LOG_SYSTEM && | |
86 gaim_prefs_get_bool("/core/logging/log_system"))) { | |
9892 | 87 struct _gaim_logsize_user *lu; |
7553 | 88 (log->logger->write)(log, type, from, time, message); |
9892 | 89 |
90 lu = g_new(struct _gaim_logsize_user, 1); | |
91 | |
92 lu->name = g_strdup(gaim_normalize(log->account, log->name)); | |
93 lu->account = log->account; | |
94 g_hash_table_remove(logsize_users, lu); | |
95 g_free(lu->name); | |
96 g_free(lu); | |
8635 | 97 } |
4184 | 98 } |
99 | |
7431 | 100 char *gaim_log_read(GaimLog *log, GaimLogReadFlags *flags) |
4184 | 101 { |
7542 | 102 GaimLogReadFlags mflags; |
7431 | 103 g_return_val_if_fail(log && log->logger, NULL); |
7462 | 104 if (log->logger->read) { |
7535 | 105 char *ret = (log->logger->read)(log, flags ? flags : &mflags); |
7478
3c21f3084ff0
[gaim-migrate @ 8091]
Herman Bloggs <hermanator12002@yahoo.com>
parents:
7473
diff
changeset
|
106 gaim_str_strip_cr(ret); |
7462 | 107 return ret; |
108 } | |
7470 | 109 return (_("<b><font color=\"red\">The logger has no read function</font></b>")); |
4184 | 110 } |
7616 | 111 |
7556 | 112 int gaim_log_get_size(GaimLog *log) |
113 { | |
114 g_return_val_if_fail(log && log->logger, 0); | |
8096 | 115 |
7556 | 116 if (log->logger->size) |
117 return log->logger->size(log); | |
118 return 0; | |
119 } | |
120 | |
8635 | 121 static guint _gaim_logsize_user_hash(struct _gaim_logsize_user *lu) |
122 { | |
123 return g_str_hash(lu->name); | |
124 } | |
125 | |
126 static guint _gaim_logsize_user_equal(struct _gaim_logsize_user *lu1, | |
127 struct _gaim_logsize_user *lu2) | |
128 { | |
129 return ((!strcmp(lu1->name, lu2->name)) && lu1->account == lu2->account); | |
130 } | |
131 | |
132 static void _gaim_logsize_user_free_key(struct _gaim_logsize_user *lu) | |
133 { | |
134 g_free(lu->name); | |
135 g_free(lu); | |
136 } | |
137 | |
8898 | 138 int gaim_log_get_total_size(GaimLogType type, const char *name, GaimAccount *account) |
7556 | 139 { |
9677 | 140 gpointer ptrsize; |
141 int size = 0; | |
8096 | 142 GSList *n; |
8635 | 143 struct _gaim_logsize_user *lu; |
8096 | 144 |
8635 | 145 lu = g_new(struct _gaim_logsize_user, 1); |
146 lu->name = g_strdup(gaim_normalize(account, name)); | |
147 lu->account = account; | |
148 | |
9677 | 149 if(g_hash_table_lookup_extended(logsize_users, lu, NULL, &ptrsize)) { |
150 size = GPOINTER_TO_INT(ptrsize); | |
8635 | 151 g_free(lu->name); |
152 g_free(lu); | |
153 } else { | |
154 for (n = loggers; n; n = n->next) { | |
155 GaimLogLogger *logger = n->data; | |
7616 | 156 |
8635 | 157 if(logger->total_size){ |
8898 | 158 size += (logger->total_size)(type, name, account); |
8635 | 159 } else if(logger->list) { |
8898 | 160 GList *logs = (logger->list)(type, name, account); |
8635 | 161 int this_size = 0; |
162 | |
163 while (logs) { | |
164 GList *logs2 = logs->next; | |
165 GaimLog *log = (GaimLog*)(logs->data); | |
166 this_size += gaim_log_get_size(log); | |
167 gaim_log_free(log); | |
168 g_list_free_1(logs); | |
169 logs = logs2; | |
170 } | |
171 | |
172 size += this_size; | |
8096 | 173 } |
8635 | 174 } |
8096 | 175 |
8635 | 176 g_hash_table_replace(logsize_users, lu, GINT_TO_POINTER(size)); |
7556 | 177 } |
178 return size; | |
179 } | |
4184 | 180 |
9923 | 181 #if 0 |
182 static char* unescape_filename(const char *escaped) { | |
183 const char *c = escaped; | |
184 GString *ret; | |
185 | |
186 if (escaped == NULL) | |
187 return NULL; | |
188 | |
189 ret = g_string_new(""); | |
190 | |
191 /** | |
192 * <>:"/\ |?*'&$ | |
193 * The above chars are "taboo" for gaim log names and are URL escaped | |
194 * % is also escaped so we can convert back easily | |
195 */ | |
196 | |
197 while (*c) { | |
198 if (*c == '%') { | |
199 if (*(c + 1) && *(c + 2)) { | |
200 char hex[2]; | |
201 hex[0] = *(c + 1); | |
202 hex[1] = *(c + 2); | |
203 unsigned char *nonhex; | |
204 gaim_base16_decode(hex, &nonhex); | |
205 ret = g_string_append_c(ret, *nonhex); | |
206 g_free(nonhex); | |
207 c += 2; | |
208 } | |
209 } else { | |
210 ret = g_string_append_c(ret, *c); | |
211 } | |
212 c++; | |
213 } | |
214 | |
215 return g_string_free(ret, FALSE); | |
216 } | |
217 #endif | |
218 | |
219 static char* escape_filename(const char *unescaped) { | |
220 const char *c = unescaped; | |
221 char *hex; | |
222 GString *ret; | |
223 | |
224 if (unescaped == NULL) | |
225 return NULL; | |
226 | |
227 ret = g_string_new(""); | |
228 | |
229 /** | |
230 * <>:"/\ |?*'&$ | |
231 * The above chars are "taboo" for gaim log names and are URL escaped | |
232 * % is also escaped so we can convert back easily | |
233 */ | |
234 | |
235 while (*c) { | |
236 switch (*c) { | |
237 case '<': | |
238 case '>': | |
239 case ':': | |
240 case '"': | |
241 case '/': | |
242 case '\\': | |
243 case ' ': | |
244 case '|': | |
245 case '?': | |
246 case '*': | |
247 case '\'': | |
248 case '&': | |
249 case '$': | |
250 case '%': | |
251 hex = g_strdup_printf ("%%%X", (int) *c); | |
252 ret = g_string_append(ret, hex); | |
253 g_free(hex); | |
254 break; | |
255 default: | |
256 ret = g_string_append_c(ret, *c); | |
257 } | |
258 c++; | |
259 } | |
260 | |
261 return g_string_free(ret, FALSE); | |
262 } | |
263 | |
264 static char* gaim_log_get_log_dir(GaimLogType type, const char *name, GaimAccount *account) { | |
265 char *acct_name = escape_filename(gaim_normalize(account, | |
266 gaim_account_get_username(account))); | |
267 char *target; | |
268 /* does this seem like a bad way to get this component of the path to anyone else? --Nathan */ | |
269 const char *prpl = GAIM_PLUGIN_PROTOCOL_INFO( | |
270 gaim_find_prpl(gaim_account_get_protocol_id(account)) | |
271 )->list_icon(account, NULL); | |
272 | |
273 char *dir; | |
274 | |
275 if (type == GAIM_LOG_CHAT) { | |
276 char *temp = g_strdup_printf("%s.chat", gaim_normalize(account, name)); | |
277 target = escape_filename(temp); | |
278 g_free(temp); | |
279 } else if(type == GAIM_LOG_SYSTEM) { | |
280 target = g_strdup(".system"); | |
281 } else { | |
282 target = escape_filename(gaim_normalize(account, name)); | |
283 } | |
284 | |
285 | |
286 dir = g_build_filename(gaim_user_dir(), "logs", prpl, acct_name, target, NULL); | |
287 g_free(target); | |
288 g_free(acct_name); | |
289 | |
290 return dir; | |
291 } | |
292 | |
7431 | 293 /**************************************************************************** |
294 * LOGGER FUNCTIONS ********************************************************* | |
295 ****************************************************************************/ | |
4184 | 296 |
7431 | 297 static GaimLogLogger *current_logger = NULL; |
7436 | 298 |
7431 | 299 static void logger_pref_cb(const char *name, GaimPrefType type, |
300 gpointer value, gpointer data) | |
301 { | |
302 GaimLogLogger *logger; | |
303 GSList *l = loggers; | |
304 while (l) { | |
305 logger = l->data; | |
306 if (!strcmp(logger->id, value)) { | |
307 gaim_log_logger_set(logger); | |
308 return; | |
4184 | 309 } |
7431 | 310 l = l->next; |
311 } | |
312 gaim_log_logger_set(&txt_logger); | |
313 } | |
4184 | 314 |
315 | |
8898 | 316 GaimLogLogger *gaim_log_logger_new( |
317 void(*create)(GaimLog *), | |
318 void(*write)(GaimLog *, GaimMessageFlags, const char *, time_t, const char *), | |
319 void(*finalize)(GaimLog *), | |
320 GList*(*list)(GaimLogType type, const char*, GaimAccount*), | |
321 char*(*read)(GaimLog*, GaimLogReadFlags*), | |
322 int(*size)(GaimLog*)) | |
7431 | 323 { |
324 GaimLogLogger *logger = g_new0(GaimLogLogger, 1); | |
7440 | 325 logger->create = create; |
7431 | 326 logger->write = write; |
327 logger->finalize = finalize; | |
328 logger->list = list; | |
329 logger->read = read; | |
7556 | 330 logger->size = size; |
7431 | 331 return logger; |
4184 | 332 } |
333 | |
7431 | 334 void gaim_log_logger_free(GaimLogLogger *logger) |
4184 | 335 { |
7431 | 336 g_free(logger); |
337 } | |
4184 | 338 |
7431 | 339 void gaim_log_logger_add (GaimLogLogger *logger) |
340 { | |
341 g_return_if_fail(logger); | |
342 if (g_slist_find(loggers, logger)) | |
343 return; | |
344 loggers = g_slist_append(loggers, logger); | |
345 } | |
346 | |
347 void gaim_log_logger_remove (GaimLogLogger *logger) | |
348 { | |
349 g_return_if_fail(logger); | |
350 g_slist_remove(loggers, logger); | |
4184 | 351 } |
352 | |
7431 | 353 void gaim_log_logger_set (GaimLogLogger *logger) |
4184 | 354 { |
7431 | 355 g_return_if_fail(logger); |
356 current_logger = logger; | |
7436 | 357 } |
4184 | 358 |
7431 | 359 GaimLogLogger *gaim_log_logger_get() |
360 { | |
361 return current_logger; | |
362 } | |
4184 | 363 |
7431 | 364 GList *gaim_log_logger_get_options(void) |
365 { | |
366 GSList *n; | |
367 GList *list = NULL; | |
368 GaimLogLogger *data; | |
4184 | 369 |
7431 | 370 for (n = loggers; n; n = n->next) { |
371 data = n->data; | |
372 if (!data->write) | |
373 continue; | |
7494 | 374 list = g_list_append(list, _(data->name)); |
7431 | 375 list = g_list_append(list, data->id); |
4184 | 376 } |
377 | |
7431 | 378 return list; |
379 } | |
380 | |
8573 | 381 gint gaim_log_compare(gconstpointer y, gconstpointer z) |
7431 | 382 { |
7436 | 383 const GaimLog *a = y; |
384 const GaimLog *b = z; | |
385 | |
7431 | 386 return b->time - a->time; |
387 } | |
388 | |
8898 | 389 GList *gaim_log_get_logs(GaimLogType type, const char *name, GaimAccount *account) |
7431 | 390 { |
391 GList *logs = NULL; | |
392 GSList *n; | |
393 for (n = loggers; n; n = n->next) { | |
394 GaimLogLogger *logger = n->data; | |
395 if (!logger->list) | |
396 continue; | |
8898 | 397 logs = g_list_concat(logs, logger->list(type, name, account)); |
7431 | 398 } |
7436 | 399 |
8573 | 400 return g_list_sort(logs, gaim_log_compare); |
401 } | |
402 | |
403 GList *gaim_log_get_system_logs(GaimAccount *account) | |
404 { | |
405 GList *logs = NULL; | |
406 GSList *n; | |
407 for (n = loggers; n; n = n->next) { | |
408 GaimLogLogger *logger = n->data; | |
409 if (!logger->list_syslog) | |
410 continue; | |
411 logs = g_list_concat(logs, logger->list_syslog(account)); | |
412 } | |
413 | |
414 return g_list_sort(logs, gaim_log_compare); | |
7431 | 415 } |
416 | |
417 void gaim_log_init(void) | |
7436 | 418 { |
7431 | 419 gaim_prefs_add_none("/core/logging"); |
7555 | 420 gaim_prefs_add_bool("/core/logging/log_ims", FALSE); |
421 gaim_prefs_add_bool("/core/logging/log_chats", FALSE); | |
8573 | 422 gaim_prefs_add_bool("/core/logging/log_system", FALSE); |
423 gaim_prefs_add_bool("/core/logging/log_signon_signoff", FALSE); | |
424 gaim_prefs_add_bool("/core/logging/log_idle_state", FALSE); | |
425 gaim_prefs_add_bool("/core/logging/log_away_state", FALSE); | |
426 gaim_prefs_add_bool("/core/logging/log_own_states", FALSE); | |
427 | |
7431 | 428 gaim_prefs_add_string("/core/logging/format", "txt"); |
7457 | 429 gaim_log_logger_add(&html_logger); |
7431 | 430 gaim_log_logger_add(&txt_logger); |
431 gaim_log_logger_add(&old_logger); | |
432 gaim_prefs_connect_callback("/core/logging/format", | |
433 logger_pref_cb, NULL); | |
434 gaim_prefs_trigger_callback("/core/logging/format"); | |
8635 | 435 |
436 logsize_users = g_hash_table_new_full((GHashFunc)_gaim_logsize_user_hash, | |
437 (GEqualFunc)_gaim_logsize_user_equal, | |
438 (GDestroyNotify)_gaim_logsize_user_free_key, NULL); | |
7431 | 439 } |
440 | |
441 /**************************************************************************** | |
442 * LOGGERS ****************************************************************** | |
443 ****************************************************************************/ | |
444 | |
7616 | 445 struct generic_logger_data { |
446 char *path; | |
447 FILE *file; | |
448 }; | |
449 | |
9763 | 450 static void log_writer_common(GaimLog *log, GaimMessageFlags type, |
9923 | 451 time_t time, const char *ext) |
9763 | 452 { |
453 char date[64]; | |
454 struct generic_logger_data *data = log->logger_data; | |
455 | |
456 if(!data) { | |
457 /* This log is new */ | |
9923 | 458 char *dir, *filename, *path; |
459 | |
460 dir = gaim_log_get_log_dir(log->type, log->name, log->account); | |
461 gaim_build_dir (dir, S_IRUSR | S_IWUSR | S_IXUSR); | |
9763 | 462 |
463 strftime(date, sizeof(date), "%Y-%m-%d.%H%M%S", localtime(&log->time)); | |
464 | |
465 filename = g_strdup_printf("%s%s", date, ext ? ext : ""); | |
466 | |
467 path = g_build_filename(dir, filename, NULL); | |
468 g_free(dir); | |
469 g_free(filename); | |
470 | |
471 log->logger_data = data = g_new0(struct generic_logger_data, 1); | |
472 | |
473 data->file = fopen(path, "a"); | |
474 if (!data->file) { | |
475 gaim_debug(GAIM_DEBUG_ERROR, "log", | |
9892 | 476 "Could not create log file %s\n", path); |
9763 | 477 g_free(path); |
478 return; | |
479 } | |
480 g_free(path); | |
481 } | |
482 } | |
483 | |
8898 | 484 static GList *log_lister_common(GaimLogType type, const char *name, GaimAccount *account, const char *ext, GaimLogLogger *logger) |
7431 | 485 { |
486 GDir *dir; | |
487 GList *list = NULL; | |
7628 | 488 const char *filename; |
8111 | 489 char *path; |
490 | |
491 if(!account) | |
492 return NULL; | |
493 | |
9923 | 494 path = gaim_log_get_log_dir(type, name, account); |
7447 | 495 |
7431 | 496 if (!(dir = g_dir_open(path, 0, NULL))) { |
497 g_free(path); | |
498 return NULL; | |
499 } | |
8898 | 500 |
7431 | 501 while ((filename = g_dir_read_name(dir))) { |
8577 | 502 if (gaim_str_has_suffix(filename, ext) && |
503 strlen(filename) == 17 + strlen(ext)) { | |
7431 | 504 GaimLog *log; |
7616 | 505 struct generic_logger_data *data; |
8577 | 506 time_t stamp = gaim_str_to_time(filename, FALSE); |
7431 | 507 |
8898 | 508 log = gaim_log_new(type, name, account, stamp); |
7431 | 509 log->logger = logger; |
7616 | 510 log->logger_data = data = g_new0(struct generic_logger_data, 1); |
511 data->path = g_build_filename(path, filename, NULL); | |
7431 | 512 list = g_list_append(list, log); |
4184 | 513 } |
514 } | |
7431 | 515 g_dir_close(dir); |
7447 | 516 g_free(path); |
7431 | 517 return list; |
518 } | |
4184 | 519 |
7556 | 520 /* Only to be used with logs listed from log_lister_common */ |
7616 | 521 int log_sizer_common(GaimLog *log) |
7556 | 522 { |
523 struct stat st; | |
7616 | 524 struct generic_logger_data *data = log->logger_data; |
7556 | 525 |
7616 | 526 if (!data->path || stat(data->path, &st)) |
7556 | 527 st.st_size = 0; |
528 | |
529 return st.st_size; | |
530 } | |
531 | |
7431 | 532 #if 0 /* Maybe some other time. */ |
7443 | 533 /**************** |
7431 | 534 ** XML LOGGER ** |
535 ****************/ | |
536 | |
537 static const char *str_from_msg_type (GaimMessageFlags type) | |
538 { | |
7443 | 539 |
7431 | 540 return ""; |
7443 | 541 |
7431 | 542 } |
543 | |
7443 | 544 static void xml_logger_write(GaimLog *log, |
545 GaimMessageFlags type, | |
7431 | 546 const char *from, time_t time, const char *message) |
547 { | |
548 char date[64]; | |
549 char *xhtml = NULL; | |
550 if (!log->logger_data) { | |
551 /* This log is new. We could use the loggers 'new' function, but | |
552 * creating a new file there would result in empty files in the case | |
553 * that you open a convo with someone, but don't say anything. | |
554 */ | |
9923 | 555 char *dir = gaim_log_get_log_dir(log->type, log->name, log->account); |
7431 | 556 FILE *file; |
7453 | 557 strftime(date, sizeof(date), "%Y-%m-%d.%H%M%S.xml", localtime(&log->time)); |
7443 | 558 |
7612 | 559 gaim_build_dir (dir, S_IRUSR | S_IWUSR | S_IXUSR); |
7443 | 560 |
7431 | 561 char *filename = g_build_filename(dir, date, NULL); |
562 g_free(dir); | |
7443 | 563 |
7431 | 564 log->logger_data = fopen(filename, "a"); |
565 if (!log->logger_data) { | |
566 gaim_debug(GAIM_DEBUG_ERROR, "log", "Could not create log file %s\n", filename); | |
7564 | 567 g_free(filename); |
7431 | 568 return; |
569 } | |
7564 | 570 g_free(filename); |
7431 | 571 fprintf(log->logger_data, "<?xml version='1.0' encoding='UTF-8' ?>\n" |
572 "<?xml-stylesheet href='file:///usr/src/web/htdocs/log-stylesheet.xsl' type='text/xml' ?>\n"); | |
7443 | 573 |
7453 | 574 strftime(date, sizeof(date), "%Y-%m-%d %H:%M:%S", localtime(&log->time)); |
7431 | 575 fprintf(log->logger_data, "<conversation time='%s' screenname='%s' protocol='%s'>\n", |
576 date, log->name, prpl); | |
577 } | |
7443 | 578 |
9923 | 579 /* if we can't write to the file, give up before we hurt ourselves */ |
580 if(!data->file) | |
581 return; | |
582 | |
7453 | 583 strftime(date, sizeof(date), "%H:%M:%S", localtime(&time)); |
7431 | 584 gaim_markup_html_to_xhtml(message, &xhtml, NULL); |
585 if (from) | |
7443 | 586 fprintf(log->logger_data, "<message %s %s from='%s' time='%s'>%s</message>\n", |
587 str_from_msg_type(type), | |
7431 | 588 type & GAIM_MESSAGE_SEND ? "direction='sent'" : |
589 type & GAIM_MESSAGE_RECV ? "direction='received'" : "", | |
590 from, date, xhtml); | |
591 else | |
7443 | 592 fprintf(log->logger_data, "<message %s %s time='%s'>%s</message>\n", |
593 str_from_msg_type(type), | |
7431 | 594 type & GAIM_MESSAGE_SEND ? "direction='sent'" : |
595 type & GAIM_MESSAGE_RECV ? "direction='received'" : "", | |
7443 | 596 date, xhtml): |
7431 | 597 fflush(log->logger_data); |
598 g_free(xhtml); | |
7443 | 599 } |
600 | |
7431 | 601 static void xml_logger_finalize(GaimLog *log) |
602 { | |
603 if (log->logger_data) { | |
604 fprintf(log->logger_data, "</conversation>\n"); | |
605 fclose(log->logger_data); | |
606 log->logger_data = NULL; | |
607 } | |
608 } | |
7443 | 609 |
8898 | 610 static GList *xml_logger_list(GaimLogType type, const char *sn, GaimAccount *account) |
7431 | 611 { |
8898 | 612 return log_lister_common(type, sn, account, ".xml", &xml_logger); |
4184 | 613 } |
614 | |
7431 | 615 static GaimLogLogger xml_logger = { |
616 N_("XML"), "xml", | |
617 NULL, | |
618 xml_logger_write, | |
619 xml_logger_finalize, | |
620 xml_logger_list, | |
8096 | 621 NULL, |
7431 | 622 NULL |
623 }; | |
624 #endif | |
5563
9eb5b13fd412
[gaim-migrate @ 5965]
Christian Hammond <chipx86@chipx86.com>
parents:
5560
diff
changeset
|
625 |
7431 | 626 /**************************** |
7457 | 627 ** HTML LOGGER ************* |
628 ****************************/ | |
629 | |
630 static void html_logger_write(GaimLog *log, GaimMessageFlags type, | |
631 const char *from, time_t time, const char *message) | |
632 { | |
9763 | 633 char *msg_fixed; |
7457 | 634 char date[64]; |
9613 | 635 GaimPlugin *plugin = gaim_find_prpl(gaim_account_get_protocol_id(log->account)); |
636 const char *prpl_name = plugin->info->name; | |
9763 | 637 struct generic_logger_data *data = log->logger_data; |
9613 | 638 |
7618 | 639 if(!data) { |
9763 | 640 const char *prpl = |
641 GAIM_PLUGIN_PROTOCOL_INFO(plugin)->list_icon(log->account, NULL); | |
9923 | 642 log_writer_common(log, type, time, ".html"); |
7457 | 643 |
9763 | 644 data = log->logger_data; |
7457 | 645 |
9763 | 646 /* if we can't write to the file, give up before we hurt ourselves */ |
647 if(!data->file) | |
648 return; | |
7616 | 649 |
7457 | 650 strftime(date, sizeof(date), "%Y-%m-%d %H:%M:%S", localtime(&log->time)); |
7616 | 651 fprintf(data->file, "<html><head><title>"); |
652 fprintf(data->file, "Conversation with %s at %s on %s (%s)", | |
7457 | 653 log->name, date, gaim_account_get_username(log->account), prpl); |
7616 | 654 fprintf(data->file, "</title></head><body>"); |
655 fprintf(data->file, | |
7457 | 656 "<h3>Conversation with %s at %s on %s (%s)</h3>\n", |
657 log->name, date, gaim_account_get_username(log->account), prpl); | |
9763 | 658 |
7457 | 659 } |
7623 | 660 |
9892 | 661 /* if we can't write to the file, give up before we hurt ourselves */ |
662 if(!data->file) | |
663 return; | |
664 | |
7882 | 665 gaim_markup_html_to_xhtml(message, &msg_fixed, NULL); |
666 | |
8577 | 667 if(log->type == GAIM_LOG_SYSTEM){ |
9592 | 668 strftime(date, sizeof(date), "%Y-%m-%d %H:%M:%S", localtime(&time)); |
8577 | 669 fprintf(data->file, "---- %s @ %s ----<br/>\n", msg_fixed, date); |
670 } else { | |
671 strftime(date, sizeof(date), "%H:%M:%S", localtime(&time)); | |
672 if (type & GAIM_MESSAGE_SYSTEM) | |
673 fprintf(data->file, "<font size=\"2\">(%s)</font><b> %s</b><br/>\n", date, msg_fixed); | |
674 else if (type & GAIM_MESSAGE_WHISPER) | |
675 fprintf(data->file, "<font color=\"#6C2585\"><font size=\"2\">(%s)</font><b> %s:</b></font> %s<br/>\n", | |
676 date, from, msg_fixed); | |
677 else if (type & GAIM_MESSAGE_AUTO_RESP) { | |
678 if (type & GAIM_MESSAGE_SEND) | |
679 fprintf(data->file, _("<font color=\"#16569E\"><font size=\"2\">(%s)</font> <b>%s <AUTO-REPLY>:</b></font> %s<br/>\n"), date, from, msg_fixed); | |
680 else if (type & GAIM_MESSAGE_RECV) | |
681 fprintf(data->file, _("<font color=\"#A82F2F\"><font size=\"2\">(%s)</font> <b>%s <AUTO-REPLY>:</b></font> %s<br/>\n"), date, from, msg_fixed); | |
682 } else if (type & GAIM_MESSAGE_RECV) { | |
683 if(gaim_message_meify(msg_fixed, -1)) | |
684 fprintf(data->file, "<font color=\"#6C2585\"><font size=\"2\">(%s)</font> <b>***%s</b></font> <font sml=\"%s\">%s</font><br/>\n", | |
9613 | 685 date, from, prpl_name, msg_fixed); |
8577 | 686 else |
687 fprintf(data->file, "<font color=\"#A82F2F\"><font size=\"2\">(%s)</font> <b>%s:</b></font> <font sml=\"%s\">%s</font><br/>\n", | |
9613 | 688 date, from, prpl_name, msg_fixed); |
8577 | 689 } else if (type & GAIM_MESSAGE_SEND) { |
690 if(gaim_message_meify(msg_fixed, -1)) | |
691 fprintf(data->file, "<font color=\"#6C2585\"><font size=\"2\">(%s)</font> <b>***%s</b></font> <font sml=\"%s\">%s</font><br/>\n", | |
9613 | 692 date, from, prpl_name, msg_fixed); |
8577 | 693 else |
694 fprintf(data->file, "<font color=\"#16569E\"><font size=\"2\">(%s)</font> <b>%s:</b></font> <font sml=\"%s\">%s</font><br/>\n", | |
9613 | 695 date, from, prpl_name, msg_fixed); |
8577 | 696 } |
7564 | 697 } |
8573 | 698 |
7882 | 699 g_free(msg_fixed); |
7616 | 700 fflush(data->file); |
7457 | 701 } |
702 | |
703 static void html_logger_finalize(GaimLog *log) | |
704 { | |
7616 | 705 struct generic_logger_data *data = log->logger_data; |
706 if (data) { | |
707 if(data->file) { | |
708 fprintf(data->file, "</body></html>"); | |
709 fclose(data->file); | |
710 } | |
711 g_free(data->path); | |
7752 | 712 g_free(data); |
7463 | 713 } |
7457 | 714 } |
715 | |
8898 | 716 static GList *html_logger_list(GaimLogType type, const char *sn, GaimAccount *account) |
7457 | 717 { |
8898 | 718 return log_lister_common(type, sn, account, ".html", &html_logger); |
7457 | 719 } |
720 | |
8573 | 721 static GList *html_logger_list_syslog(GaimAccount *account) |
722 { | |
8898 | 723 return log_lister_common(GAIM_LOG_SYSTEM, ".system", account, ".html", &html_logger); |
8573 | 724 } |
725 | |
7457 | 726 static char *html_logger_read(GaimLog *log, GaimLogReadFlags *flags) |
727 { | |
728 char *read, *minus_header; | |
7616 | 729 struct generic_logger_data *data = log->logger_data; |
7457 | 730 *flags = GAIM_LOG_READ_NO_NEWLINE; |
7616 | 731 if (!data || !data->path) |
732 return g_strdup(_("<font color=\"red\"><b>Unable to find log path!</b></font>")); | |
733 if (g_file_get_contents(data->path, &read, NULL, NULL)) { | |
7457 | 734 minus_header = strchr(read, '\n'); |
735 if (!minus_header) | |
736 minus_header = g_strdup(read); | |
737 else | |
738 minus_header = g_strdup(minus_header + 1); | |
739 g_free(read); | |
740 return minus_header; | |
741 } | |
8578 | 742 return g_strdup_printf(_("<font color=\"red\"><b>Could not read file: %s</b></font>"), data->path); |
7457 | 743 } |
744 | |
745 static GaimLogLogger html_logger = { | |
746 N_("HTML"), "html", | |
9819 | 747 NULL, |
7457 | 748 html_logger_write, |
749 html_logger_finalize, | |
750 html_logger_list, | |
7556 | 751 html_logger_read, |
8096 | 752 log_sizer_common, |
8573 | 753 NULL, |
754 html_logger_list_syslog | |
7457 | 755 }; |
756 | |
757 | |
758 | |
759 | |
760 /**************************** | |
7431 | 761 ** PLAIN TEXT LOGGER ******* |
762 ****************************/ | |
4184 | 763 |
7436 | 764 static void txt_logger_write(GaimLog *log, |
765 GaimMessageFlags type, | |
7431 | 766 const char *from, time_t time, const char *message) |
767 { | |
768 char date[64]; | |
9763 | 769 GaimPlugin *plugin = gaim_find_prpl(gaim_account_get_protocol_id(log->account)); |
7616 | 770 struct generic_logger_data *data = log->logger_data; |
9763 | 771 char *stripped = NULL; |
772 | |
773 if(!data) { | |
7431 | 774 /* This log is new. We could use the loggers 'new' function, but |
775 * creating a new file there would result in empty files in the case | |
776 * that you open a convo with someone, but don't say anything. | |
777 */ | |
9763 | 778 const char *prpl = |
779 GAIM_PLUGIN_PROTOCOL_INFO(plugin)->list_icon(log->account, NULL); | |
9923 | 780 log_writer_common(log, type, time, ".txt"); |
8898 | 781 |
9763 | 782 data = log->logger_data; |
7436 | 783 |
9763 | 784 /* if we can't write to the file, give up before we hurt ourselves */ |
785 if(!data->file) | |
786 return; | |
7616 | 787 |
7453 | 788 strftime(date, sizeof(date), "%Y-%m-%d %H:%M:%S", localtime(&log->time)); |
7616 | 789 fprintf(data->file, "Conversation with %s at %s on %s (%s)\n", |
7431 | 790 log->name, date, gaim_account_get_username(log->account), prpl); |
791 } | |
7436 | 792 |
7623 | 793 /* if we can't write to the file, give up before we hurt ourselves */ |
794 if(!data->file) | |
795 return; | |
796 | |
8573 | 797 stripped = gaim_markup_strip_html(message); |
798 | |
799 if(log->type == GAIM_LOG_SYSTEM){ | |
9592 | 800 strftime(date, sizeof(date), "%Y-%m-%d %H:%M:%S", localtime(&time)); |
8573 | 801 fprintf(data->file, "---- %s @ %s ----\n", stripped, date); |
802 } else { | |
803 strftime(date, sizeof(date), "%H:%M:%S", localtime(&time)); | |
804 if (type & GAIM_MESSAGE_SEND || | |
805 type & GAIM_MESSAGE_RECV) { | |
806 if (type & GAIM_MESSAGE_AUTO_RESP) { | |
807 fprintf(data->file, _("(%s) %s <AUTO-REPLY>: %s\n"), date, | |
808 from, stripped); | |
809 } else { | |
810 if(gaim_message_meify(stripped, -1)) | |
811 fprintf(data->file, "(%s) ***%s %s\n", date, from, | |
812 stripped); | |
813 else | |
814 fprintf(data->file, "(%s) %s: %s\n", date, from, | |
815 stripped); | |
816 } | |
817 } else if (type & GAIM_MESSAGE_SYSTEM) | |
818 fprintf(data->file, "(%s) %s\n", date, stripped); | |
819 else if (type & GAIM_MESSAGE_NO_LOG) { | |
820 /* This shouldn't happen */ | |
821 g_free(stripped); | |
822 return; | |
823 } else if (type & GAIM_MESSAGE_WHISPER) | |
824 fprintf(data->file, "(%s) *%s* %s", date, from, stripped); | |
825 else | |
826 fprintf(data->file, "(%s) %s%s %s\n", date, from ? from : "", | |
827 from ? ":" : "", stripped); | |
828 } | |
829 | |
830 fflush(data->file); | |
831 g_free(stripped); | |
7431 | 832 } |
833 | |
834 static void txt_logger_finalize(GaimLog *log) | |
835 { | |
7616 | 836 struct generic_logger_data *data = log->logger_data; |
837 if (data) { | |
838 if(data->file) | |
839 fclose(data->file); | |
840 if(data->path) | |
841 g_free(data->path); | |
7752 | 842 g_free(data); |
7616 | 843 } |
7431 | 844 } |
845 | |
8898 | 846 static GList *txt_logger_list(GaimLogType type, const char *sn, GaimAccount *account) |
7431 | 847 { |
8898 | 848 return log_lister_common(type, sn, account, ".txt", &txt_logger); |
7431 | 849 } |
850 | |
8573 | 851 static GList *txt_logger_list_syslog(GaimAccount *account) |
852 { | |
8898 | 853 return log_lister_common(GAIM_LOG_SYSTEM, ".system", account, ".txt", &txt_logger); |
8573 | 854 } |
855 | |
7431 | 856 static char *txt_logger_read(GaimLog *log, GaimLogReadFlags *flags) |
857 { | |
8517 | 858 char *read, *minus_header, *minus_header2; |
7616 | 859 struct generic_logger_data *data = log->logger_data; |
7457 | 860 *flags = 0; |
7616 | 861 if (!data || !data->path) |
862 return g_strdup(_("<font color=\"red\"><b>Unable to find log path!</b></font>")); | |
863 if (g_file_get_contents(data->path, &read, NULL, NULL)) { | |
7431 | 864 minus_header = strchr(read, '\n'); |
865 if (!minus_header) | |
866 minus_header = g_strdup(read); | |
7436 | 867 else |
7431 | 868 minus_header = g_strdup(minus_header + 1); |
869 g_free(read); | |
8517 | 870 minus_header2 = gaim_escape_html(minus_header); |
871 g_free(minus_header); | |
872 return minus_header2; | |
7431 | 873 } |
8578 | 874 return g_strdup_printf(_("<font color=\"red\"><b>Could not read file: %s</b></font>"), data->path); |
7436 | 875 } |
7431 | 876 |
877 static GaimLogLogger txt_logger = { | |
878 N_("Plain text"), "txt", | |
9819 | 879 NULL, |
7431 | 880 txt_logger_write, |
881 txt_logger_finalize, | |
882 txt_logger_list, | |
7556 | 883 txt_logger_read, |
8096 | 884 log_sizer_common, |
8573 | 885 NULL, |
886 txt_logger_list_syslog | |
7431 | 887 }; |
888 | |
889 /**************** | |
890 * OLD LOGGER *** | |
891 ****************/ | |
892 | |
893 /* The old logger doesn't write logs, only reads them. This is to include | |
894 * old logs in the log viewer transparently. | |
895 */ | |
896 | |
897 struct old_logger_data { | |
7764 | 898 GaimStringref *pathref; |
7431 | 899 int offset; |
900 int length; | |
901 }; | |
902 | |
8898 | 903 static GList *old_logger_list(GaimLogType type, const char *sn, GaimAccount *account) |
7431 | 904 { |
905 FILE *file; | |
906 char buf[BUF_LONG]; | |
907 struct tm tm; | |
7761 | 908 char month[4]; |
7431 | 909 struct old_logger_data *data = NULL; |
910 char *logfile = g_strdup_printf("%s.log", gaim_normalize(account, sn)); | |
7764 | 911 char *pathstr = g_build_filename(gaim_user_dir(), "logs", logfile, NULL); |
912 GaimStringref *pathref = gaim_stringref_new(pathstr); | |
7431 | 913 char *newlog; |
7761 | 914 int logfound = 0; |
915 int lastoff = 0; | |
916 int newlen; | |
7791 | 917 time_t lasttime = 0; |
7431 | 918 |
919 GaimLog *log = NULL; | |
920 GList *list = NULL; | |
921 | |
7473 | 922 g_free(logfile); |
7764 | 923 g_free(pathstr); |
7473 | 924 |
7764 | 925 if (!(file = fopen(gaim_stringref_value(pathref), "rb"))) { |
926 gaim_stringref_unref(pathref); | |
7431 | 927 return NULL; |
7447 | 928 } |
7436 | 929 |
7431 | 930 while (fgets(buf, BUF_LONG, file)) { |
931 if ((newlog = strstr(buf, "---- New C"))) { | |
932 int length; | |
933 int offset; | |
934 char convostart[32]; | |
935 char *temp = strchr(buf, '@'); | |
7436 | 936 |
7431 | 937 if (temp == NULL || strlen(temp) < 2) |
938 continue; | |
7436 | 939 |
7431 | 940 temp++; |
941 length = strcspn(temp, "-"); | |
942 if (length > 31) length = 31; | |
7436 | 943 |
7431 | 944 offset = ftell(file); |
7436 | 945 |
7761 | 946 if (logfound) { |
947 newlen = offset - lastoff - length; | |
7436 | 948 if(strstr(buf, "----</H3><BR>")) { |
7761 | 949 newlen -= |
950 sizeof("<HR><BR><H3 Align=Center> ---- New Conversation @ ") + | |
951 sizeof("----</H3><BR>") - 2; | |
7436 | 952 } else { |
7761 | 953 newlen -= |
954 sizeof("---- New Conversation @ ") + sizeof("----") - 2; | |
7436 | 955 } |
956 | |
7461 | 957 if(strchr(buf, '\r')) |
7770 | 958 newlen--; |
7461 | 959 |
7761 | 960 if (newlen != 0) { |
961 log = gaim_log_new(GAIM_LOG_IM, sn, account, -1); | |
962 log->logger = &old_logger; | |
963 log->time = lasttime; | |
964 data = g_new0(struct old_logger_data, 1); | |
965 data->offset = lastoff; | |
966 data->length = newlen; | |
7764 | 967 data->pathref = gaim_stringref_ref(pathref); |
7761 | 968 log->logger_data = data; |
7431 | 969 list = g_list_append(list, log); |
7761 | 970 } |
7431 | 971 } |
972 | |
7761 | 973 logfound = 1; |
974 lastoff = offset; | |
7436 | 975 |
7431 | 976 g_snprintf(convostart, length, "%s", temp); |
7676 | 977 sscanf(convostart, "%*s %s %d %d:%d:%d %d", |
978 month, &tm.tm_mday, &tm.tm_hour, &tm.tm_min, &tm.tm_sec, &tm.tm_year); | |
979 /* Ugly hack, in case current locale is not English */ | |
980 if (strcmp(month, "Jan") == 0) { | |
981 tm.tm_mon= 0; | |
982 } else if (strcmp(month, "Feb") == 0) { | |
983 tm.tm_mon = 1; | |
984 } else if (strcmp(month, "Mar") == 0) { | |
985 tm.tm_mon = 2; | |
986 } else if (strcmp(month, "Apr") == 0) { | |
987 tm.tm_mon = 3; | |
988 } else if (strcmp(month, "May") == 0) { | |
989 tm.tm_mon = 4; | |
990 } else if (strcmp(month, "Jun") == 0) { | |
991 tm.tm_mon = 5; | |
992 } else if (strcmp(month, "Jul") == 0) { | |
993 tm.tm_mon = 6; | |
994 } else if (strcmp(month, "Aug") == 0) { | |
995 tm.tm_mon = 7; | |
996 } else if (strcmp(month, "Sep") == 0) { | |
997 tm.tm_mon = 8; | |
998 } else if (strcmp(month, "Oct") == 0) { | |
999 tm.tm_mon = 9; | |
1000 } else if (strcmp(month, "Nov") == 0) { | |
1001 tm.tm_mon = 10; | |
1002 } else if (strcmp(month, "Dec") == 0) { | |
1003 tm.tm_mon = 11; | |
1004 } | |
1005 tm.tm_year -= 1900; | |
7761 | 1006 lasttime = mktime(&tm); |
4184 | 1007 } |
1008 } | |
7613 | 1009 |
7761 | 1010 if (logfound) { |
1011 if ((newlen = ftell(file) - lastoff) != 0) { | |
1012 log = gaim_log_new(GAIM_LOG_IM, sn, account, -1); | |
1013 log->logger = &old_logger; | |
1014 log->time = lasttime; | |
1015 data = g_new0(struct old_logger_data, 1); | |
1016 data->offset = lastoff; | |
1017 data->length = newlen; | |
7764 | 1018 data->pathref = gaim_stringref_ref(pathref); |
7761 | 1019 log->logger_data = data; |
7613 | 1020 list = g_list_append(list, log); |
7761 | 1021 } |
7613 | 1022 } |
1023 | |
7764 | 1024 gaim_stringref_unref(pathref); |
7431 | 1025 fclose(file); |
1026 return list; | |
4184 | 1027 } |
4359
5fb47ec9bfe4
[gaim-migrate @ 4625]
Christian Hammond <chipx86@chipx86.com>
parents:
4227
diff
changeset
|
1028 |
8898 | 1029 static int old_logger_total_size(GaimLogType type, const char *name, GaimAccount *account) |
8096 | 1030 { |
1031 char *logfile = g_strdup_printf("%s.log", gaim_normalize(account, name)); | |
1032 char *pathstr = g_build_filename(gaim_user_dir(), "logs", logfile, NULL); | |
1033 int size; | |
1034 struct stat st; | |
1035 | |
1036 if (stat(pathstr, &st)) | |
1037 size = 0; | |
1038 else | |
1039 size = st.st_size; | |
1040 | |
1041 g_free(logfile); | |
1042 g_free(pathstr); | |
1043 | |
1044 return size; | |
1045 } | |
1046 | |
7616 | 1047 static char * old_logger_read (GaimLog *log, GaimLogReadFlags *flags) |
4359
5fb47ec9bfe4
[gaim-migrate @ 4625]
Christian Hammond <chipx86@chipx86.com>
parents:
4227
diff
changeset
|
1048 { |
7431 | 1049 struct old_logger_data *data = log->logger_data; |
7764 | 1050 FILE *file = fopen(gaim_stringref_value(data->pathref), "rb"); |
7431 | 1051 char *read = g_malloc(data->length + 1); |
1052 fseek(file, data->offset, SEEK_SET); | |
1053 fread(read, data->length, 1, file); | |
8370 | 1054 fclose(file); |
7431 | 1055 read[data->length] = '\0'; |
7436 | 1056 *flags = 0; |
1057 if(strstr(read, "<BR>")) | |
1058 *flags |= GAIM_LOG_READ_NO_NEWLINE; | |
7431 | 1059 return read; |
1060 } | |
4359
5fb47ec9bfe4
[gaim-migrate @ 4625]
Christian Hammond <chipx86@chipx86.com>
parents:
4227
diff
changeset
|
1061 |
7616 | 1062 static int old_logger_size (GaimLog *log) |
7556 | 1063 { |
1064 struct old_logger_data *data = log->logger_data; | |
7616 | 1065 return data ? data->length : 0; |
1066 } | |
1067 | |
1068 static void old_logger_finalize(GaimLog *log) | |
1069 { | |
1070 struct old_logger_data *data = log->logger_data; | |
7764 | 1071 gaim_stringref_unref(data->pathref); |
7616 | 1072 g_free(data); |
7556 | 1073 } |
1074 | |
7431 | 1075 static GaimLogLogger old_logger = { |
1076 "old logger", "old", | |
7616 | 1077 NULL, NULL, |
1078 old_logger_finalize, | |
7431 | 1079 old_logger_list, |
7616 | 1080 old_logger_read, |
8096 | 1081 old_logger_size, |
8573 | 1082 old_logger_total_size, |
1083 NULL | |
7431 | 1084 }; |