| |
1 #ifdef HAVE_CONFIG_H |
| |
2 # include <config.h> |
| |
3 #endif |
| |
4 |
| |
5 #include <stdio.h> |
| |
6 |
| |
7 #ifndef GAIM_PLUGINS |
| |
8 # define GAIM_PLUGINS |
| |
9 #endif |
| |
10 |
| |
11 #include "internal.h" |
| |
12 |
| |
13 #include "debug.h" |
| |
14 #include "log.h" |
| |
15 #include "plugin.h" |
| |
16 #include "pluginpref.h" |
| |
17 #include "prefs.h" |
| |
18 #include "stringref.h" |
| |
19 #include "util.h" |
| |
20 #include "version.h" |
| |
21 #include "xmlnode.h" |
| |
22 |
| |
23 /* This must be the last Gaim header included. */ |
| |
24 #ifdef _WIN32 |
| |
25 #include "win32dep.h" |
| |
26 #endif |
| |
27 |
| |
28 /* Where is the Windows partition mounted? */ |
| |
29 #ifndef GAIM_LOG_READER_WINDOWS_MOUNT_POINT |
| |
30 #define GAIM_LOG_READER_WINDOWS_MOUNT_POINT "/mnt/windows" |
| |
31 #endif |
| |
32 |
| |
33 enum name_guesses { |
| |
34 NAME_GUESS_UNKNOWN, |
| |
35 NAME_GUESS_ME, |
| |
36 NAME_GUESS_THEM |
| |
37 }; |
| |
38 |
| |
39 |
| |
40 /***************************************************************************** |
| |
41 * Adium Logger * |
| |
42 *****************************************************************************/ |
| |
43 |
| |
44 /* The adium logger doesn't write logs, only reads them. This is to include |
| |
45 * Adium logs in the log viewer transparently. |
| |
46 */ |
| |
47 |
| |
48 static GaimLogLogger *adium_logger; |
| |
49 |
| |
50 enum adium_log_type { |
| |
51 ADIUM_HTML, |
| |
52 ADIUM_TEXT, |
| |
53 }; |
| |
54 |
| |
55 struct adium_logger_data { |
| |
56 char *path; |
| |
57 enum adium_log_type type; |
| |
58 }; |
| |
59 |
| |
60 static GList *adium_logger_list(GaimLogType type, const char *sn, GaimAccount *account) |
| |
61 { |
| |
62 GList *list = NULL; |
| |
63 const char *logdir; |
| |
64 GaimPlugin *plugin; |
| |
65 GaimPluginProtocolInfo *prpl_info; |
| |
66 char *prpl_name; |
| |
67 char *temp; |
| |
68 char *path; |
| |
69 GDir *dir; |
| |
70 |
| |
71 g_return_val_if_fail(sn != NULL, list); |
| |
72 g_return_val_if_fail(account != NULL, list); |
| |
73 |
| |
74 logdir = gaim_prefs_get_string("/plugins/core/log_reader/adium/log_directory"); |
| |
75 |
| |
76 /* By clearing the log directory path, this logger can be (effectively) disabled. */ |
| |
77 if (!*logdir) |
| |
78 return list; |
| |
79 |
| |
80 plugin = gaim_find_prpl(gaim_account_get_protocol_id(account)); |
| |
81 if (!plugin) |
| |
82 return NULL; |
| |
83 |
| |
84 prpl_info = GAIM_PLUGIN_PROTOCOL_INFO(plugin); |
| |
85 if (!prpl_info->list_icon) |
| |
86 return NULL; |
| |
87 |
| |
88 prpl_name = g_ascii_strup(prpl_info->list_icon(account, NULL), -1); |
| |
89 |
| |
90 temp = g_strdup_printf("%s.%s", prpl_name, account->username); |
| |
91 path = g_build_filename(logdir, temp, sn, NULL); |
| |
92 g_free(temp); |
| |
93 |
| |
94 dir = g_dir_open(path, 0, NULL); |
| |
95 if (dir) { |
| |
96 const gchar *file; |
| |
97 |
| |
98 while ((file = g_dir_read_name(dir))) { |
| |
99 if (!gaim_str_has_prefix(file, sn)) |
| |
100 continue; |
| |
101 if (gaim_str_has_suffix(file, ".html") || gaim_str_has_suffix(file, ".AdiumHTMLLog")) { |
| |
102 struct tm tm; |
| |
103 const char *date = file; |
| |
104 |
| |
105 date += strlen(sn) + 2; |
| |
106 if (sscanf(date, "%u|%u|%u", |
| |
107 &tm.tm_year, &tm.tm_mon, &tm.tm_mday) != 3) { |
| |
108 |
| |
109 gaim_debug(GAIM_DEBUG_ERROR, "Adium log parse", |
| |
110 "Filename timestamp parsing error\n"); |
| |
111 } else { |
| |
112 char *filename = g_build_filename(path, file, NULL); |
| |
113 FILE *handle = g_fopen(filename, "rb"); |
| |
114 char *contents; |
| |
115 char *contents2; |
| |
116 struct adium_logger_data *data; |
| |
117 GaimLog *log; |
| |
118 |
| |
119 if (!handle) { |
| |
120 g_free(filename); |
| |
121 continue; |
| |
122 } |
| |
123 |
| |
124 /* XXX: This is really inflexible. */ |
| |
125 contents = g_malloc(57); |
| |
126 fread(contents, 56, 1, handle); |
| |
127 fclose(handle); |
| |
128 contents[56] = '\0'; |
| |
129 |
| |
130 /* XXX: This is fairly inflexible. */ |
| |
131 contents2 = contents; |
| |
132 while (*contents2 && *contents2 != '>') |
| |
133 contents2++; |
| |
134 if (*contents2) |
| |
135 contents2++; |
| |
136 while (*contents2 && *contents2 != '>') |
| |
137 contents2++; |
| |
138 if (*contents2) |
| |
139 contents2++; |
| |
140 |
| |
141 if (sscanf(contents2, "%u.%u.%u", |
| |
142 &tm.tm_hour, &tm.tm_min, &tm.tm_sec) != 3) { |
| |
143 |
| |
144 gaim_debug(GAIM_DEBUG_ERROR, "Adium log parse", |
| |
145 "Contents timestamp parsing error\n"); |
| |
146 g_free(contents); |
| |
147 g_free(filename); |
| |
148 continue; |
| |
149 } |
| |
150 g_free(contents); |
| |
151 |
| |
152 data = g_new0(struct adium_logger_data, 1); |
| |
153 data->path = filename; |
| |
154 data->type = ADIUM_HTML; |
| |
155 |
| |
156 tm.tm_year -= 1900; |
| |
157 tm.tm_mon -= 1; |
| |
158 |
| |
159 /* XXX: Look into this later... Should we pass in a struct tm? */ |
| |
160 log = gaim_log_new(GAIM_LOG_IM, sn, account, NULL, mktime(&tm), NULL); |
| |
161 log->logger = adium_logger; |
| |
162 log->logger_data = data; |
| |
163 |
| |
164 list = g_list_append(list, log); |
| |
165 } |
| |
166 } else if (gaim_str_has_suffix(file, ".adiumLog")) { |
| |
167 struct tm tm; |
| |
168 const char *date = file; |
| |
169 |
| |
170 date += strlen(sn) + 2; |
| |
171 if (sscanf(date, "%u|%u|%u", |
| |
172 &tm.tm_year, &tm.tm_mon, &tm.tm_mday) != 3) { |
| |
173 |
| |
174 gaim_debug(GAIM_DEBUG_ERROR, "Adium log parse", |
| |
175 "Filename timestamp parsing error\n"); |
| |
176 } else { |
| |
177 char *filename = g_build_filename(path, file, NULL); |
| |
178 FILE *handle = g_fopen(filename, "rb"); |
| |
179 char *contents; |
| |
180 char *contents2; |
| |
181 struct adium_logger_data *data; |
| |
182 GaimLog *log; |
| |
183 |
| |
184 if (!handle) { |
| |
185 g_free(filename); |
| |
186 continue; |
| |
187 } |
| |
188 |
| |
189 /* XXX: This is really inflexible. */ |
| |
190 contents = g_malloc(14); |
| |
191 fread(contents, 13, 1, handle); |
| |
192 fclose(handle); |
| |
193 contents[13] = '\0'; |
| |
194 |
| |
195 contents2 = contents; |
| |
196 while (*contents2 && *contents2 != '(') |
| |
197 contents2++; |
| |
198 if (*contents2) |
| |
199 contents2++; |
| |
200 |
| |
201 if (sscanf(contents2, "%u.%u.%u", |
| |
202 &tm.tm_hour, &tm.tm_min, &tm.tm_sec) != 3) { |
| |
203 |
| |
204 gaim_debug(GAIM_DEBUG_ERROR, "Adium log parse", |
| |
205 "Contents timestamp parsing error\n"); |
| |
206 g_free(contents); |
| |
207 g_free(filename); |
| |
208 continue; |
| |
209 } |
| |
210 |
| |
211 g_free(contents); |
| |
212 |
| |
213 tm.tm_year -= 1900; |
| |
214 tm.tm_mon -= 1; |
| |
215 |
| |
216 data = g_new0(struct adium_logger_data, 1); |
| |
217 data->path = filename; |
| |
218 data->type = ADIUM_TEXT; |
| |
219 |
| |
220 /* XXX: Look into this later... Should we pass in a struct tm? */ |
| |
221 log = gaim_log_new(GAIM_LOG_IM, sn, account, NULL, mktime(&tm), NULL); |
| |
222 log->logger = adium_logger; |
| |
223 log->logger_data = data; |
| |
224 |
| |
225 list = g_list_append(list, log); |
| |
226 } |
| |
227 } |
| |
228 } |
| |
229 g_dir_close(dir); |
| |
230 } |
| |
231 |
| |
232 g_free(prpl_name); |
| |
233 g_free(path); |
| |
234 |
| |
235 return list; |
| |
236 } |
| |
237 |
| |
238 static char *adium_logger_read (GaimLog *log, GaimLogReadFlags *flags) |
| |
239 { |
| |
240 struct adium_logger_data *data; |
| |
241 GError *error = NULL; |
| |
242 gchar *read = NULL; |
| |
243 gsize length; |
| |
244 |
| |
245 g_return_val_if_fail(log != NULL, g_strdup("")); |
| |
246 |
| |
247 data = log->logger_data; |
| |
248 |
| |
249 g_return_val_if_fail(data->path != NULL, g_strdup("")); |
| |
250 |
| |
251 gaim_debug(GAIM_DEBUG_INFO, "Adium log read", |
| |
252 "Reading %s\n", data->path); |
| |
253 if (!g_file_get_contents(data->path, &read, &length, &error)) { |
| |
254 gaim_debug(GAIM_DEBUG_ERROR, "Adium log read", |
| |
255 "Error reading log\n"); |
| |
256 if (error) |
| |
257 g_error_free(error); |
| |
258 return g_strdup(""); |
| |
259 } |
| |
260 |
| |
261 if (data->type != ADIUM_HTML) { |
| |
262 char *escaped = g_markup_escape_text(read, -1); |
| |
263 g_free(read); |
| |
264 read = escaped; |
| |
265 } |
| |
266 |
| |
267 #ifdef WIN32 |
| |
268 /* This problem only seems to show up on Windows. |
| |
269 * The BOM is displaying as a space at the beginning of the log. |
| |
270 */ |
| |
271 if (gaim_str_has_prefix(read, "\xef\xbb\xbf")) |
| |
272 { |
| |
273 /* FIXME: This feels so wrong... */ |
| |
274 char *temp = g_strdup(&(read[3])); |
| |
275 g_free(read); |
| |
276 read = temp; |
| |
277 } |
| |
278 #endif |
| |
279 |
| |
280 /* TODO: Apply formatting. |
| |
281 * Replace the above hack with something better, since we'll |
| |
282 * be looping over the entire log file contents anyway. |
| |
283 */ |
| |
284 |
| |
285 return read; |
| |
286 } |
| |
287 |
| |
288 static int adium_logger_size (GaimLog *log) |
| |
289 { |
| |
290 struct adium_logger_data *data; |
| |
291 char *text; |
| |
292 size_t size; |
| |
293 |
| |
294 g_return_val_if_fail(log != NULL, 0); |
| |
295 |
| |
296 data = log->logger_data; |
| |
297 |
| |
298 if (gaim_prefs_get_bool("/plugins/core/log_reader/fast_sizes")) { |
| |
299 struct stat st; |
| |
300 |
| |
301 if (!data->path || stat(data->path, &st)) |
| |
302 st.st_size = 0; |
| |
303 |
| |
304 return st.st_size; |
| |
305 } |
| |
306 |
| |
307 text = adium_logger_read(log, NULL); |
| |
308 size = strlen(text); |
| |
309 g_free(text); |
| |
310 |
| |
311 return size; |
| |
312 } |
| |
313 |
| |
314 static void adium_logger_finalize(GaimLog *log) |
| |
315 { |
| |
316 struct adium_logger_data *data; |
| |
317 |
| |
318 g_return_if_fail(log != NULL); |
| |
319 |
| |
320 data = log->logger_data; |
| |
321 |
| |
322 g_free(data->path); |
| |
323 } |
| |
324 |
| |
325 |
| |
326 /***************************************************************************** |
| |
327 * Fire Logger * |
| |
328 *****************************************************************************/ |
| |
329 |
| |
330 /* The fire logger doesn't write logs, only reads them. This is to include |
| |
331 * Fire logs in the log viewer transparently. |
| |
332 */ |
| |
333 |
| |
334 static GaimLogLogger *fire_logger; |
| |
335 |
| |
336 struct fire_logger_data { |
| |
337 }; |
| |
338 |
| |
339 static GList *fire_logger_list(GaimLogType type, const char *sn, GaimAccount *account) |
| |
340 { |
| |
341 /* TODO: Do something here. */ |
| |
342 return NULL; |
| |
343 } |
| |
344 |
| |
345 static char * fire_logger_read (GaimLog *log, GaimLogReadFlags *flags) |
| |
346 { |
| |
347 struct fire_logger_data *data; |
| |
348 |
| |
349 g_return_val_if_fail(log != NULL, g_strdup("")); |
| |
350 |
| |
351 data = log->logger_data; |
| |
352 |
| |
353 /* TODO: Do something here. */ |
| |
354 return g_strdup(""); |
| |
355 } |
| |
356 |
| |
357 static int fire_logger_size (GaimLog *log) |
| |
358 { |
| |
359 g_return_val_if_fail(log != NULL, 0); |
| |
360 |
| |
361 if (gaim_prefs_get_bool("/plugins/core/log_reader/fast_sizes")) |
| |
362 return 0; |
| |
363 |
| |
364 /* TODO: Do something here. */ |
| |
365 return 0; |
| |
366 } |
| |
367 |
| |
368 static void fire_logger_finalize(GaimLog *log) |
| |
369 { |
| |
370 g_return_if_fail(log != NULL); |
| |
371 |
| |
372 /* TODO: Do something here. */ |
| |
373 } |
| |
374 |
| |
375 |
| |
376 /***************************************************************************** |
| |
377 * Messenger Plus! Logger * |
| |
378 *****************************************************************************/ |
| |
379 |
| |
380 /* The messenger_plus logger doesn't write logs, only reads them. This is to include |
| |
381 * Messenger Plus! logs in the log viewer transparently. |
| |
382 */ |
| |
383 |
| |
384 static GaimLogLogger *messenger_plus_logger; |
| |
385 |
| |
386 struct messenger_plus_logger_data { |
| |
387 }; |
| |
388 |
| |
389 static GList *messenger_plus_logger_list(GaimLogType type, const char *sn, GaimAccount *account) |
| |
390 { |
| |
391 /* TODO: Do something here. */ |
| |
392 return NULL; |
| |
393 } |
| |
394 |
| |
395 static char * messenger_plus_logger_read (GaimLog *log, GaimLogReadFlags *flags) |
| |
396 { |
| |
397 struct messenger_plus_logger_data *data = log->logger_data; |
| |
398 |
| |
399 g_return_val_if_fail(log != NULL, g_strdup("")); |
| |
400 |
| |
401 data = log->logger_data; |
| |
402 |
| |
403 /* TODO: Do something here. */ |
| |
404 return g_strdup(""); |
| |
405 } |
| |
406 |
| |
407 static int messenger_plus_logger_size (GaimLog *log) |
| |
408 { |
| |
409 g_return_val_if_fail(log != NULL, 0); |
| |
410 |
| |
411 if (gaim_prefs_get_bool("/plugins/core/log_reader/fast_sizes")) |
| |
412 return 0; |
| |
413 |
| |
414 /* TODO: Do something here. */ |
| |
415 return 0; |
| |
416 } |
| |
417 |
| |
418 static void messenger_plus_logger_finalize(GaimLog *log) |
| |
419 { |
| |
420 g_return_if_fail(log != NULL); |
| |
421 |
| |
422 /* TODO: Do something here. */ |
| |
423 } |
| |
424 |
| |
425 |
| |
426 /***************************************************************************** |
| |
427 * MSN Messenger Logger * |
| |
428 *****************************************************************************/ |
| |
429 |
| |
430 /* The msn logger doesn't write logs, only reads them. This is to include |
| |
431 * MSN Messenger message histories in the log viewer transparently. |
| |
432 */ |
| |
433 |
| |
434 static GaimLogLogger *msn_logger; |
| |
435 |
| |
436 struct msn_logger_data { |
| |
437 xmlnode *root; |
| |
438 xmlnode *message; |
| |
439 const char *session_id; |
| |
440 int last_log; |
| |
441 GString *text; |
| |
442 }; |
| |
443 |
| |
444 static time_t msn_logger_parse_timestamp(xmlnode *message) |
| |
445 { |
| |
446 const char *date; |
| |
447 const char *time; |
| |
448 struct tm tm; |
| |
449 char am_pm; |
| |
450 |
| |
451 g_return_val_if_fail(message != NULL, (time_t)0); |
| |
452 |
| |
453 date = xmlnode_get_attrib(message, "Date"); |
| |
454 if (!(date && *date)) { |
| |
455 gaim_debug(GAIM_DEBUG_ERROR, "MSN log timestamp parse", |
| |
456 "Attribute missing: %s\n", "Date"); |
| |
457 return (time_t)0; |
| |
458 } |
| |
459 |
| |
460 time = xmlnode_get_attrib(message, "Time"); |
| |
461 if (!(time && *time)) { |
| |
462 gaim_debug(GAIM_DEBUG_ERROR, "MSN log timestamp parse", |
| |
463 "Attribute missing: %s\n", "Time"); |
| |
464 return (time_t)0; |
| |
465 } |
| |
466 |
| |
467 if (sscanf(date, "%u/%u/%u", &tm.tm_mon, &tm.tm_mday, &tm.tm_year) != 3) |
| |
468 gaim_debug(GAIM_DEBUG_ERROR, "MSN log timestamp parse", |
| |
469 "%s parsing error\n", "Date"); |
| |
470 |
| |
471 if (sscanf(time, "%u:%u:%u %c", &tm.tm_hour, &tm.tm_min, &tm.tm_sec, &am_pm) != 4) |
| |
472 gaim_debug(GAIM_DEBUG_ERROR, "MSN log timestamp parse", |
| |
473 "%s parsing error\n", "Time"); |
| |
474 |
| |
475 tm.tm_year -= 1900; |
| |
476 tm.tm_mon -= 1; |
| |
477 if (am_pm == 'P') { |
| |
478 tm.tm_hour += 12; |
| |
479 } else if (tm.tm_hour == 12) { |
| |
480 /* 12 AM = 00 hr */ |
| |
481 tm.tm_hour = 0; |
| |
482 } |
| |
483 /* Let the C library deal with daylight savings time. */ |
| |
484 tm.tm_isdst = -1; |
| |
485 |
| |
486 return mktime(&tm); |
| |
487 } |
| |
488 |
| |
489 |
| |
490 static GList *msn_logger_list(GaimLogType type, const char *sn, GaimAccount *account) |
| |
491 { |
| |
492 GList *list = NULL; |
| |
493 char *username; |
| |
494 GaimBuddy *buddy; |
| |
495 const char *logdir; |
| |
496 const char *savedfilename = NULL; |
| |
497 char *logfile; |
| |
498 char *path; |
| |
499 GError *error = NULL; |
| |
500 gchar *contents = NULL; |
| |
501 gsize length; |
| |
502 xmlnode *root; |
| |
503 xmlnode *message; |
| |
504 const char *old_session_id = ""; |
| |
505 struct msn_logger_data *data = NULL; |
| |
506 |
| |
507 g_return_val_if_fail(sn != NULL, list); |
| |
508 g_return_val_if_fail(account != NULL, list); |
| |
509 |
| |
510 if (strcmp(account->protocol_id, "prpl-msn")) |
| |
511 return list; |
| |
512 |
| |
513 logdir = gaim_prefs_get_string("/plugins/core/log_reader/msn/log_directory"); |
| |
514 |
| |
515 /* By clearing the log directory path, this logger can be (effectively) disabled. */ |
| |
516 if (!*logdir) |
| |
517 return list; |
| |
518 |
| |
519 buddy = gaim_find_buddy(account, sn); |
| |
520 |
| |
521 if ((username = g_strdup(gaim_account_get_string( |
| |
522 account, "log_reader_msn_log_folder", NULL)))) { |
| |
523 /* As a special case, we allow the null string to kill the parsing |
| |
524 * straight away. This would allow the user to deal with the case |
| |
525 * when two account have the same username at different domains and |
| |
526 * only one has logs stored. |
| |
527 */ |
| |
528 if (!*username) { |
| |
529 g_free(username); |
| |
530 return list; |
| |
531 } |
| |
532 } else { |
| |
533 username = g_strdup(gaim_normalize(account, account->username)); |
| |
534 } |
| |
535 |
| |
536 if (buddy) |
| |
537 savedfilename = gaim_blist_node_get_string(&buddy->node, "log_reader_msn_log_filename"); |
| |
538 |
| |
539 if (savedfilename) { |
| |
540 /* As a special case, we allow the null string to kill the parsing |
| |
541 * straight away. This would allow the user to deal with the case |
| |
542 * when two buddies have the same username at different domains and |
| |
543 * only one has logs stored. |
| |
544 */ |
| |
545 if (!*savedfilename) { |
| |
546 g_free(username); |
| |
547 return list; |
| |
548 } |
| |
549 |
| |
550 logfile = g_strdup(savedfilename); |
| |
551 } else { |
| |
552 logfile = g_strdup_printf("%s.xml", gaim_normalize(account, sn)); |
| |
553 } |
| |
554 |
| |
555 path = g_build_filename(logdir, username, "History", logfile, NULL); |
| |
556 |
| |
557 if (!g_file_test(path, G_FILE_TEST_EXISTS)) { |
| |
558 gboolean found = FALSE; |
| |
559 char *at_sign; |
| |
560 GDir *dir; |
| |
561 |
| |
562 g_free(path); |
| |
563 |
| |
564 if (savedfilename) { |
| |
565 /* We had a saved filename, but it doesn't exist. |
| |
566 * Returning now is the right course of action because we don't |
| |
567 * want to detect another file incorrectly. |
| |
568 */ |
| |
569 g_free(username); |
| |
570 g_free(logfile); |
| |
571 return list; |
| |
572 } |
| |
573 |
| |
574 /* Perhaps we're using a new version of MSN with the weird numbered folders. |
| |
575 * I don't know how the numbers are calculated, so I'm going to attempt to |
| |
576 * find logs by pattern matching... |
| |
577 */ |
| |
578 |
| |
579 at_sign = g_strrstr(username, "@"); |
| |
580 if (at_sign) |
| |
581 *at_sign = '\0'; |
| |
582 |
| |
583 dir = g_dir_open(logdir, 0, NULL); |
| |
584 if (dir) { |
| |
585 const gchar *name; |
| |
586 |
| |
587 while ((name = g_dir_read_name(dir))) { |
| |
588 const char *c = name; |
| |
589 |
| |
590 if (!gaim_str_has_prefix(c, username)) |
| |
591 continue; |
| |
592 |
| |
593 c += strlen(username); |
| |
594 while (*c) { |
| |
595 if (!g_ascii_isdigit(*c)) |
| |
596 break; |
| |
597 |
| |
598 c++; |
| |
599 } |
| |
600 |
| |
601 path = g_build_filename(logdir, name, NULL); |
| |
602 /* The !c makes sure we got to the end of the while loop above. */ |
| |
603 if (!*c && g_file_test(path, G_FILE_TEST_IS_DIR)) { |
| |
604 char *history_path = g_build_filename( |
| |
605 path, "History", NULL); |
| |
606 if (g_file_test(history_path, G_FILE_TEST_IS_DIR)) { |
| |
607 gaim_account_set_string(account, |
| |
608 "log_reader_msn_log_folder", name); |
| |
609 g_free(path); |
| |
610 path = history_path; |
| |
611 found = TRUE; |
| |
612 break; |
| |
613 } |
| |
614 g_free(path); |
| |
615 g_free(history_path); |
| |
616 } |
| |
617 else |
| |
618 g_free(path); |
| |
619 } |
| |
620 g_dir_close(dir); |
| |
621 } |
| |
622 g_free(username); |
| |
623 |
| |
624 if (!found) { |
| |
625 g_free(logfile); |
| |
626 return list; |
| |
627 } |
| |
628 |
| |
629 /* If we've reached this point, we've found a History folder. */ |
| |
630 |
| |
631 username = g_strdup(gaim_normalize(account, sn)); |
| |
632 at_sign = g_strrstr(username, "@"); |
| |
633 if (at_sign) |
| |
634 *at_sign = '\0'; |
| |
635 |
| |
636 found = FALSE; |
| |
637 dir = g_dir_open(path, 0, NULL); |
| |
638 if (dir) { |
| |
639 const gchar *name; |
| |
640 |
| |
641 while ((name = g_dir_read_name(dir))) { |
| |
642 const char *c = name; |
| |
643 |
| |
644 if (!gaim_str_has_prefix(c, username)) |
| |
645 continue; |
| |
646 |
| |
647 c += strlen(username); |
| |
648 while (*c) { |
| |
649 if (!g_ascii_isdigit(*c)) |
| |
650 break; |
| |
651 |
| |
652 c++; |
| |
653 } |
| |
654 |
| |
655 path = g_build_filename(path, name, NULL); |
| |
656 if (!strcmp(c, ".xml") && |
| |
657 g_file_test(path, G_FILE_TEST_EXISTS)) { |
| |
658 found = TRUE; |
| |
659 g_free(logfile); |
| |
660 logfile = g_strdup(name); |
| |
661 break; |
| |
662 } |
| |
663 else |
| |
664 g_free(path); |
| |
665 } |
| |
666 g_dir_close(dir); |
| |
667 } |
| |
668 g_free(username); |
| |
669 |
| |
670 if (!found) { |
| |
671 g_free(logfile); |
| |
672 return list; |
| |
673 } |
| |
674 } else { |
| |
675 g_free(username); |
| |
676 g_free(logfile); |
| |
677 logfile = NULL; /* No sense saving the obvious buddy@domain.com. */ |
| |
678 } |
| |
679 |
| |
680 gaim_debug(GAIM_DEBUG_INFO, "MSN log read", |
| |
681 "Reading %s\n", path); |
| |
682 if (!g_file_get_contents(path, &contents, &length, &error)) { |
| |
683 g_free(path); |
| |
684 gaim_debug(GAIM_DEBUG_ERROR, "MSN log read", |
| |
685 "Error reading log\n"); |
| |
686 if (error) |
| |
687 g_error_free(error); |
| |
688 return list; |
| |
689 } |
| |
690 g_free(path); |
| |
691 |
| |
692 /* Reading the file was successful... |
| |
693 * Save its name if it involves the crazy numbers. The idea here is that you could |
| |
694 * then tweak the blist.xml file by hand if need be. This would be the case if two |
| |
695 * buddies have the same username at different domains. One set of logs would get |
| |
696 * detected for both buddies. |
| |
697 */ |
| |
698 if (buddy && logfile) { |
| |
699 gaim_blist_node_set_string(&buddy->node, "log_reader_msn_log_filename", logfile); |
| |
700 g_free(logfile); |
| |
701 } |
| |
702 |
| |
703 root = xmlnode_from_str(contents, length); |
| |
704 g_free(contents); |
| |
705 if (!root) |
| |
706 return list; |
| |
707 |
| |
708 for (message = xmlnode_get_child(root, "Message"); message; |
| |
709 message = xmlnode_get_next_twin(message)) { |
| |
710 const char *session_id; |
| |
711 |
| |
712 session_id = xmlnode_get_attrib(message, "SessionID"); |
| |
713 if (!session_id) { |
| |
714 gaim_debug(GAIM_DEBUG_ERROR, "MSN log parse", |
| |
715 "Error parsing message: %s\n", "SessionID missing"); |
| |
716 continue; |
| |
717 } |
| |
718 |
| |
719 if (strcmp(session_id, old_session_id)) { |
| |
720 /* |
| |
721 * The session ID differs from the last message. |
| |
722 * Thus, this is the start of a new conversation. |
| |
723 */ |
| |
724 GaimLog *log; |
| |
725 |
| |
726 data = g_new0(struct msn_logger_data, 1); |
| |
727 data->root = root; |
| |
728 data->message = message; |
| |
729 data->session_id = session_id; |
| |
730 data->text = NULL; |
| |
731 data->last_log = FALSE; |
| |
732 |
| |
733 /* XXX: Look into this later... Should we pass in a struct tm? */ |
| |
734 log = gaim_log_new(GAIM_LOG_IM, sn, account, NULL, msn_logger_parse_timestamp(message), NULL); |
| |
735 log->logger = msn_logger; |
| |
736 log->logger_data = data; |
| |
737 |
| |
738 list = g_list_append(list, log); |
| |
739 } |
| |
740 old_session_id = session_id; |
| |
741 } |
| |
742 |
| |
743 if (data) |
| |
744 data->last_log = TRUE; |
| |
745 |
| |
746 return list; |
| |
747 } |
| |
748 |
| |
749 static char * msn_logger_read (GaimLog *log, GaimLogReadFlags *flags) |
| |
750 { |
| |
751 struct msn_logger_data *data; |
| |
752 GString *text = NULL; |
| |
753 xmlnode *message; |
| |
754 |
| |
755 g_return_val_if_fail(log != NULL, g_strdup("")); |
| |
756 |
| |
757 data = log->logger_data; |
| |
758 |
| |
759 if (data->text) { |
| |
760 /* The GTK code which displays the logs g_free()s whatever is |
| |
761 * returned from this function. Thus, we can't reuse the str |
| |
762 * part of the GString. The only solution is to free it and |
| |
763 * start over. |
| |
764 */ |
| |
765 g_string_free(data->text, FALSE); |
| |
766 } |
| |
767 |
| |
768 text = g_string_new(""); |
| |
769 |
| |
770 if (!data->root || !data->message || !data->session_id) { |
| |
771 /* Something isn't allocated correctly. */ |
| |
772 gaim_debug(GAIM_DEBUG_ERROR, "MSN log parse", |
| |
773 "Error parsing message: %s\n", "Internal variables inconsistent"); |
| |
774 data->text = text; |
| |
775 |
| |
776 return text->str; |
| |
777 } |
| |
778 |
| |
779 for (message = data->message; message; |
| |
780 message = xmlnode_get_next_twin(message)) { |
| |
781 |
| |
782 const char *new_session_id; |
| |
783 xmlnode *text_node; |
| |
784 const char *from_name = NULL; |
| |
785 const char *to_name = NULL; |
| |
786 xmlnode *from; |
| |
787 xmlnode *to; |
| |
788 enum name_guesses name_guessed = NAME_GUESS_UNKNOWN; |
| |
789 const char *their_name; |
| |
790 time_t time_unix; |
| |
791 struct tm *tm_new; |
| |
792 char *timestamp; |
| |
793 char *tmp; |
| |
794 const char *style; |
| |
795 |
| |
796 new_session_id = xmlnode_get_attrib(message, "SessionID"); |
| |
797 |
| |
798 /* If this triggers, something is wrong with the XML. */ |
| |
799 if (!new_session_id) { |
| |
800 gaim_debug(GAIM_DEBUG_ERROR, "MSN log parse", |
| |
801 "Error parsing message: %s\n", "New SessionID missing"); |
| |
802 break; |
| |
803 } |
| |
804 |
| |
805 if (strcmp(new_session_id, data->session_id)) { |
| |
806 /* The session ID differs from the first message. |
| |
807 * Thus, this is the start of a new conversation. |
| |
808 */ |
| |
809 break; |
| |
810 } |
| |
811 |
| |
812 text_node = xmlnode_get_child(message, "Text"); |
| |
813 if (!text_node) |
| |
814 continue; |
| |
815 |
| |
816 from = xmlnode_get_child(message, "From"); |
| |
817 if (from) { |
| |
818 xmlnode *user = xmlnode_get_child(from, "User"); |
| |
819 |
| |
820 if (user) { |
| |
821 from_name = xmlnode_get_attrib(user, "FriendlyName"); |
| |
822 |
| |
823 /* This saves a check later. */ |
| |
824 if (!*from_name) |
| |
825 from_name = NULL; |
| |
826 } |
| |
827 } |
| |
828 |
| |
829 to = xmlnode_get_child(message, "To"); |
| |
830 if (to) { |
| |
831 xmlnode *user = xmlnode_get_child(to, "User"); |
| |
832 if (user) { |
| |
833 to_name = xmlnode_get_attrib(user, "FriendlyName"); |
| |
834 |
| |
835 /* This saves a check later. */ |
| |
836 if (!*to_name) |
| |
837 to_name = NULL; |
| |
838 } |
| |
839 } |
| |
840 |
| |
841 their_name = from_name; |
| |
842 if (from_name && gaim_prefs_get_bool("/plugins/core/log_reader/use_name_heuristics")) { |
| |
843 const char *friendly_name = gaim_connection_get_display_name(log->account->gc); |
| |
844 |
| |
845 if (friendly_name != NULL) { |
| |
846 int friendly_name_length = strlen(friendly_name); |
| |
847 int alias_length = strlen(log->account->alias); |
| |
848 GaimBuddy *buddy = gaim_find_buddy(log->account, log->name); |
| |
849 gboolean from_name_matches; |
| |
850 gboolean to_name_matches; |
| |
851 |
| |
852 if (buddy && buddy->alias) |
| |
853 their_name = buddy->alias; |
| |
854 |
| |
855 /* Try to guess which user is me. |
| |
856 * The first step is to determine if either of the names matches either my |
| |
857 * friendly name or alias. For this test, "match" is defined as: |
| |
858 * ^(friendly_name|alias)([^a-zA-Z0-9].*)?$ |
| |
859 */ |
| |
860 from_name_matches = (gaim_str_has_prefix(from_name, friendly_name) && |
| |
861 !isalnum(*(from_name + friendly_name_length))) || |
| |
862 (gaim_str_has_prefix(from_name, log->account->alias) && |
| |
863 !isalnum(*(from_name + alias_length))); |
| |
864 |
| |
865 to_name_matches = to_name != NULL && ( |
| |
866 (gaim_str_has_prefix(to_name, friendly_name) && |
| |
867 !isalnum(*(to_name + friendly_name_length))) || |
| |
868 (gaim_str_has_prefix(to_name, log->account->alias) && |
| |
869 !isalnum(*(to_name + alias_length)))); |
| |
870 |
| |
871 if (from_name_matches) { |
| |
872 if (!to_name_matches) { |
| |
873 name_guessed = NAME_GUESS_ME; |
| |
874 } |
| |
875 } else if (to_name_matches) { |
| |
876 name_guessed = NAME_GUESS_THEM; |
| |
877 } else { |
| |
878 if (buddy && buddy->alias) { |
| |
879 char *alias = g_strdup(buddy->alias); |
| |
880 |
| |
881 /* "Truncate" the string at the first non-alphanumeric |
| |
882 * character. The idea is to relax the comparison. |
| |
883 */ |
| |
884 char *temp; |
| |
885 for (temp = alias; *temp ; temp++) { |
| |
886 if (!isalnum(*temp)) { |
| |
887 *temp = '\0'; |
| |
888 break; |
| |
889 } |
| |
890 } |
| |
891 alias_length = strlen(alias); |
| |
892 |
| |
893 /* Try to guess which user is them. |
| |
894 * The first step is to determine if either of the names |
| |
895 * matches their alias. For this test, "match" is |
| |
896 * defined as: ^alias([^a-zA-Z0-9].*)?$ |
| |
897 */ |
| |
898 from_name_matches = (gaim_str_has_prefix( |
| |
899 from_name, alias) && |
| |
900 !isalnum(*(from_name + |
| |
901 alias_length))); |
| |
902 |
| |
903 to_name_matches = to_name && (gaim_str_has_prefix( |
| |
904 to_name, alias) && |
| |
905 !isalnum(*(to_name + |
| |
906 alias_length))); |
| |
907 |
| |
908 g_free(alias); |
| |
909 |
| |
910 if (from_name_matches) { |
| |
911 if (!to_name_matches) { |
| |
912 name_guessed = NAME_GUESS_THEM; |
| |
913 } |
| |
914 } else if (to_name_matches) { |
| |
915 name_guessed = NAME_GUESS_ME; |
| |
916 } else if (buddy->server_alias) { |
| |
917 friendly_name_length = |
| |
918 strlen(buddy->server_alias); |
| |
919 |
| |
920 /* Try to guess which user is them. |
| |
921 * The first step is to determine if either of |
| |
922 * the names matches their friendly name. For |
| |
923 * this test, "match" is defined as: |
| |
924 * ^friendly_name([^a-zA-Z0-9].*)?$ |
| |
925 */ |
| |
926 from_name_matches = (gaim_str_has_prefix( |
| |
927 from_name, |
| |
928 buddy->server_alias) && |
| |
929 !isalnum(*(from_name + |
| |
930 friendly_name_length))); |
| |
931 |
| |
932 to_name_matches = to_name && ( |
| |
933 (gaim_str_has_prefix( |
| |
934 to_name, buddy->server_alias) && |
| |
935 !isalnum(*(to_name + |
| |
936 friendly_name_length)))); |
| |
937 |
| |
938 if (from_name_matches) { |
| |
939 if (!to_name_matches) { |
| |
940 name_guessed = NAME_GUESS_THEM; |
| |
941 } |
| |
942 } else if (to_name_matches) { |
| |
943 name_guessed = NAME_GUESS_ME; |
| |
944 } |
| |
945 } |
| |
946 } |
| |
947 } |
| |
948 } |
| |
949 } |
| |
950 |
| |
951 if (name_guessed != NAME_GUESS_UNKNOWN) { |
| |
952 text = g_string_append(text, "<span style=\"color: #"); |
| |
953 if (name_guessed == NAME_GUESS_ME) |
| |
954 text = g_string_append(text, "16569E"); |
| |
955 else |
| |
956 text = g_string_append(text, "A82F2F"); |
| |
957 text = g_string_append(text, ";\">"); |
| |
958 } |
| |
959 |
| |
960 time_unix = msn_logger_parse_timestamp(message); |
| |
961 tm_new = localtime(&time_unix); |
| |
962 |
| |
963 timestamp = g_strdup_printf("<font size=\"2\">(%02u:%02u:%02u)</font> ", |
| |
964 tm_new->tm_hour, tm_new->tm_min, tm_new->tm_sec); |
| |
965 text = g_string_append(text, timestamp); |
| |
966 g_free(timestamp); |
| |
967 |
| |
968 if (from_name) { |
| |
969 text = g_string_append(text, "<b>"); |
| |
970 |
| |
971 if (name_guessed == NAME_GUESS_ME) |
| |
972 text = g_string_append(text, log->account->alias); |
| |
973 else if (name_guessed == NAME_GUESS_THEM) |
| |
974 text = g_string_append(text, their_name); |
| |
975 else |
| |
976 text = g_string_append(text, from_name); |
| |
977 |
| |
978 text = g_string_append(text, ":</b> "); |
| |
979 } |
| |
980 |
| |
981 if (name_guessed != NAME_GUESS_UNKNOWN) |
| |
982 text = g_string_append(text, "</span>"); |
| |
983 |
| |
984 style = xmlnode_get_attrib(text_node, "Style"); |
| |
985 |
| |
986 tmp = xmlnode_get_data(text_node); |
| |
987 if (style && *style) { |
| |
988 text = g_string_append(text, "<span style=\""); |
| |
989 text = g_string_append(text, style); |
| |
990 text = g_string_append(text, "\">"); |
| |
991 text = g_string_append(text, tmp); |
| |
992 text = g_string_append(text, "</span>\n"); |
| |
993 } else { |
| |
994 text = g_string_append(text, tmp); |
| |
995 text = g_string_append(text, "\n"); |
| |
996 } |
| |
997 g_free(tmp); |
| |
998 } |
| |
999 |
| |
1000 data->text = text; |
| |
1001 |
| |
1002 return text->str; |
| |
1003 } |
| |
1004 |
| |
1005 static int msn_logger_size (GaimLog *log) |
| |
1006 { |
| |
1007 char *text; |
| |
1008 size_t size; |
| |
1009 |
| |
1010 g_return_val_if_fail(log != NULL, 0); |
| |
1011 |
| |
1012 if (gaim_prefs_get_bool("/plugins/core/log_reader/fast_sizes")) |
| |
1013 return 0; |
| |
1014 |
| |
1015 text = msn_logger_read(log, NULL); |
| |
1016 size = strlen(text); |
| |
1017 g_free(text); |
| |
1018 |
| |
1019 return size; |
| |
1020 } |
| |
1021 |
| |
1022 static void msn_logger_finalize(GaimLog *log) |
| |
1023 { |
| |
1024 struct msn_logger_data *data; |
| |
1025 |
| |
1026 g_return_if_fail(log != NULL); |
| |
1027 |
| |
1028 data = log->logger_data; |
| |
1029 |
| |
1030 if (data->last_log) |
| |
1031 xmlnode_free(data->root); |
| |
1032 |
| |
1033 if (data->text) |
| |
1034 g_string_free(data->text, FALSE); |
| |
1035 } |
| |
1036 |
| |
1037 |
| |
1038 /***************************************************************************** |
| |
1039 * Trillian Logger * |
| |
1040 *****************************************************************************/ |
| |
1041 |
| |
1042 /* The trillian logger doesn't write logs, only reads them. This is to include |
| |
1043 * Trillian logs in the log viewer transparently. |
| |
1044 */ |
| |
1045 |
| |
1046 static GaimLogLogger *trillian_logger; |
| |
1047 static void trillian_logger_finalize(GaimLog *log); |
| |
1048 |
| |
1049 struct trillian_logger_data { |
| |
1050 char *path; /* FIXME: Change this to use GaimStringref like log.c:old_logger_list */ |
| |
1051 int offset; |
| |
1052 int length; |
| |
1053 char *their_nickname; |
| |
1054 }; |
| |
1055 |
| |
1056 static GList *trillian_logger_list(GaimLogType type, const char *sn, GaimAccount *account) |
| |
1057 { |
| |
1058 GList *list = NULL; |
| |
1059 const char *logdir; |
| |
1060 GaimPlugin *plugin; |
| |
1061 GaimPluginProtocolInfo *prpl_info; |
| |
1062 char *prpl_name; |
| |
1063 const char *buddy_name; |
| |
1064 char *filename; |
| |
1065 char *path; |
| |
1066 GError *error = NULL; |
| |
1067 gchar *contents = NULL; |
| |
1068 gsize length; |
| |
1069 gchar *line; |
| |
1070 gchar *c; |
| |
1071 |
| |
1072 g_return_val_if_fail(sn != NULL, list); |
| |
1073 g_return_val_if_fail(account != NULL, list); |
| |
1074 |
| |
1075 logdir = gaim_prefs_get_string("/plugins/core/log_reader/trillian/log_directory"); |
| |
1076 |
| |
1077 /* By clearing the log directory path, this logger can be (effectively) disabled. */ |
| |
1078 if (!*logdir) |
| |
1079 return list; |
| |
1080 |
| |
1081 plugin = gaim_find_prpl(gaim_account_get_protocol_id(account)); |
| |
1082 if (!plugin) |
| |
1083 return NULL; |
| |
1084 |
| |
1085 prpl_info = GAIM_PLUGIN_PROTOCOL_INFO(plugin); |
| |
1086 if (!prpl_info->list_icon) |
| |
1087 return NULL; |
| |
1088 |
| |
1089 prpl_name = g_ascii_strup(prpl_info->list_icon(account, NULL), -1); |
| |
1090 |
| |
1091 buddy_name = gaim_normalize(account, sn); |
| |
1092 |
| |
1093 filename = g_strdup_printf("%s.log", buddy_name); |
| |
1094 path = g_build_filename( |
| |
1095 logdir, prpl_name, filename, NULL); |
| |
1096 |
| |
1097 gaim_debug(GAIM_DEBUG_INFO, "Trillian log list", |
| |
1098 "Reading %s\n", path); |
| |
1099 /* FIXME: There's really no need to read the entire file at once. |
| |
1100 * See src/log.c:old_logger_list for a better approach. |
| |
1101 */ |
| |
1102 if (!g_file_get_contents(path, &contents, &length, &error)) { |
| |
1103 if (error) { |
| |
1104 g_error_free(error); |
| |
1105 error = NULL; |
| |
1106 } |
| |
1107 g_free(path); |
| |
1108 |
| |
1109 path = g_build_filename( |
| |
1110 logdir, prpl_name, "Query", filename, NULL); |
| |
1111 gaim_debug(GAIM_DEBUG_INFO, "Trillian log list", |
| |
1112 "Reading %s\n", path); |
| |
1113 if (!g_file_get_contents(path, &contents, &length, &error)) { |
| |
1114 if (error) |
| |
1115 g_error_free(error); |
| |
1116 } |
| |
1117 } |
| |
1118 g_free(filename); |
| |
1119 |
| |
1120 if (contents) { |
| |
1121 struct trillian_logger_data *data = NULL; |
| |
1122 int offset = 0; |
| |
1123 int last_line_offset = 0; |
| |
1124 |
| |
1125 line = contents; |
| |
1126 c = contents; |
| |
1127 while (*c) { |
| |
1128 offset++; |
| |
1129 |
| |
1130 if (*c != '\n') { |
| |
1131 c++; |
| |
1132 continue; |
| |
1133 } |
| |
1134 |
| |
1135 *c = '\0'; |
| |
1136 if (gaim_str_has_prefix(line, "Session Close ")) { |
| |
1137 if (data && !data->length) { |
| |
1138 if (!(data->length = last_line_offset - data->offset)) { |
| |
1139 /* This log had no data, so we remove it. */ |
| |
1140 GList *last = g_list_last(list); |
| |
1141 |
| |
1142 gaim_debug(GAIM_DEBUG_INFO, "Trillian log list", |
| |
1143 "Empty log. Offset %i\n", data->offset); |
| |
1144 |
| |
1145 trillian_logger_finalize((GaimLog *)last->data); |
| |
1146 list = g_list_delete_link(list, last); |
| |
1147 } |
| |
1148 } |
| |
1149 } else if (line[0] && line[1] && line [3] && |
| |
1150 gaim_str_has_prefix(&line[3], "sion Start ")) { |
| |
1151 |
| |
1152 char *their_nickname = line; |
| |
1153 char *timestamp; |
| |
1154 |
| |
1155 if (data && !data->length) |
| |
1156 data->length = last_line_offset - data->offset; |
| |
1157 |
| |
1158 while (*their_nickname && (*their_nickname != ':')) |
| |
1159 their_nickname++; |
| |
1160 their_nickname++; |
| |
1161 |
| |
1162 /* This code actually has nothing to do with |
| |
1163 * the timestamp YET. I'm simply using this |
| |
1164 * variable for now to NUL-terminate the |
| |
1165 * their_nickname string. |
| |
1166 */ |
| |
1167 timestamp = their_nickname; |
| |
1168 while (*timestamp && *timestamp != ')') |
| |
1169 timestamp++; |
| |
1170 |
| |
1171 if (*timestamp == ')') { |
| |
1172 char *month; |
| |
1173 struct tm tm; |
| |
1174 |
| |
1175 *timestamp = '\0'; |
| |
1176 if (line[0] && line[1] && line[2]) |
| |
1177 timestamp += 3; |
| |
1178 |
| |
1179 /* Now we start dealing with the timestamp. */ |
| |
1180 |
| |
1181 /* Skip over the day name. */ |
| |
1182 while (*timestamp && (*timestamp != ' ')) |
| |
1183 timestamp++; |
| |
1184 *timestamp = '\0'; |
| |
1185 timestamp++; |
| |
1186 |
| |
1187 /* Parse out the month. */ |
| |
1188 month = timestamp; |
| |
1189 while (*timestamp && (*timestamp != ' ')) |
| |
1190 timestamp++; |
| |
1191 *timestamp = '\0'; |
| |
1192 timestamp++; |
| |
1193 |
| |
1194 /* Parse the day, time, and year. */ |
| |
1195 if (sscanf(timestamp, "%u %u:%u:%u %u", |
| |
1196 &tm.tm_mday, &tm.tm_hour, |
| |
1197 &tm.tm_min, &tm.tm_sec, |
| |
1198 &tm.tm_year) != 5) { |
| |
1199 |
| |
1200 gaim_debug(GAIM_DEBUG_ERROR, |
| |
1201 "Trillian log timestamp parse", |
| |
1202 "Session Start parsing error\n"); |
| |
1203 } else { |
| |
1204 GaimLog *log; |
| |
1205 |
| |
1206 tm.tm_year -= 1900; |
| |
1207 |
| |
1208 /* Let the C library deal with |
| |
1209 * daylight savings time. |
| |
1210 */ |
| |
1211 tm.tm_isdst = -1; |
| |
1212 |
| |
1213 /* Ugly hack, in case current locale |
| |
1214 * is not English. This code is taken |
| |
1215 * from log.c. |
| |
1216 */ |
| |
1217 if (strcmp(month, "Jan") == 0) { |
| |
1218 tm.tm_mon= 0; |
| |
1219 } else if (strcmp(month, "Feb") == 0) { |
| |
1220 tm.tm_mon = 1; |
| |
1221 } else if (strcmp(month, "Mar") == 0) { |
| |
1222 tm.tm_mon = 2; |
| |
1223 } else if (strcmp(month, "Apr") == 0) { |
| |
1224 tm.tm_mon = 3; |
| |
1225 } else if (strcmp(month, "May") == 0) { |
| |
1226 tm.tm_mon = 4; |
| |
1227 } else if (strcmp(month, "Jun") == 0) { |
| |
1228 tm.tm_mon = 5; |
| |
1229 } else if (strcmp(month, "Jul") == 0) { |
| |
1230 tm.tm_mon = 6; |
| |
1231 } else if (strcmp(month, "Aug") == 0) { |
| |
1232 tm.tm_mon = 7; |
| |
1233 } else if (strcmp(month, "Sep") == 0) { |
| |
1234 tm.tm_mon = 8; |
| |
1235 } else if (strcmp(month, "Oct") == 0) { |
| |
1236 tm.tm_mon = 9; |
| |
1237 } else if (strcmp(month, "Nov") == 0) { |
| |
1238 tm.tm_mon = 10; |
| |
1239 } else if (strcmp(month, "Dec") == 0) { |
| |
1240 tm.tm_mon = 11; |
| |
1241 } |
| |
1242 |
| |
1243 data = g_new0( |
| |
1244 struct trillian_logger_data, 1); |
| |
1245 data->path = g_strdup(path); |
| |
1246 data->offset = offset; |
| |
1247 data->length = 0; |
| |
1248 data->their_nickname = |
| |
1249 g_strdup(their_nickname); |
| |
1250 |
| |
1251 /* XXX: Look into this later... Should we pass in a struct tm? */ |
| |
1252 log = gaim_log_new(GAIM_LOG_IM, |
| |
1253 sn, account, NULL, mktime(&tm), NULL); |
| |
1254 log->logger = trillian_logger; |
| |
1255 log->logger_data = data; |
| |
1256 |
| |
1257 list = g_list_append(list, log); |
| |
1258 } |
| |
1259 } |
| |
1260 } |
| |
1261 c++; |
| |
1262 line = c; |
| |
1263 last_line_offset = offset; |
| |
1264 } |
| |
1265 |
| |
1266 g_free(contents); |
| |
1267 } |
| |
1268 g_free(path); |
| |
1269 |
| |
1270 g_free(prpl_name); |
| |
1271 |
| |
1272 return list; |
| |
1273 } |
| |
1274 |
| |
1275 static char * trillian_logger_read (GaimLog *log, GaimLogReadFlags *flags) |
| |
1276 { |
| |
1277 struct trillian_logger_data *data; |
| |
1278 char *read; |
| |
1279 FILE *file; |
| |
1280 GaimBuddy *buddy; |
| |
1281 char *escaped; |
| |
1282 GString *formatted; |
| |
1283 char *c; |
| |
1284 char *line; |
| |
1285 |
| |
1286 g_return_val_if_fail(log != NULL, g_strdup("")); |
| |
1287 |
| |
1288 data = log->logger_data; |
| |
1289 |
| |
1290 g_return_val_if_fail(data->path != NULL, g_strdup("")); |
| |
1291 g_return_val_if_fail(data->length > 0, g_strdup("")); |
| |
1292 g_return_val_if_fail(data->their_nickname != NULL, g_strdup("")); |
| |
1293 |
| |
1294 gaim_debug(GAIM_DEBUG_INFO, "Trillian log read", |
| |
1295 "Reading %s\n", data->path); |
| |
1296 |
| |
1297 read = g_malloc(data->length + 2); |
| |
1298 |
| |
1299 file = g_fopen(data->path, "rb"); |
| |
1300 fseek(file, data->offset, SEEK_SET); |
| |
1301 fread(read, data->length, 1, file); |
| |
1302 fclose(file); |
| |
1303 |
| |
1304 if (read[data->length-1] == '\n') { |
| |
1305 read[data->length] = '\0'; |
| |
1306 } else { |
| |
1307 read[data->length] = '\n'; |
| |
1308 read[data->length+1] = '\0'; |
| |
1309 } |
| |
1310 |
| |
1311 /* Load miscellaneous data. */ |
| |
1312 buddy = gaim_find_buddy(log->account, log->name); |
| |
1313 |
| |
1314 escaped = g_markup_escape_text(read, -1); |
| |
1315 g_free(read); |
| |
1316 read = escaped; |
| |
1317 |
| |
1318 /* Apply formatting... */ |
| |
1319 formatted = g_string_new(""); |
| |
1320 c = read; |
| |
1321 line = read; |
| |
1322 while (*c) |
| |
1323 { |
| |
1324 if (*c == '\n') |
| |
1325 { |
| |
1326 char *link_temp_line; |
| |
1327 char *link; |
| |
1328 char *timestamp; |
| |
1329 char *footer = NULL; |
| |
1330 *c = '\0'; |
| |
1331 |
| |
1332 /* Convert links. |
| |
1333 * |
| |
1334 * The format is (Link: URL)URL |
| |
1335 * So, I want to find each occurance of "(Link: " and replace that chunk with: |
| |
1336 * <a href=" |
| |
1337 * Then, replace the next ")" with: |
| |
1338 * "> |
| |
1339 * Then, replace the next " " (or add this if the end-of-line is reached) with: |
| |
1340 * </a> |
| |
1341 */ |
| |
1342 link_temp_line = NULL; |
| |
1343 while ((link = g_strstr_len(line, strlen(line), "(Link: "))) { |
| |
1344 GString *temp; |
| |
1345 |
| |
1346 if (!*link) |
| |
1347 continue; |
| |
1348 |
| |
1349 *link = '\0'; |
| |
1350 link++; |
| |
1351 |
| |
1352 temp = g_string_new(line); |
| |
1353 g_string_append(temp, "<a href=\""); |
| |
1354 |
| |
1355 if (strlen(link) >= 6) { |
| |
1356 link += (sizeof("(Link: ") - 1); |
| |
1357 |
| |
1358 while (*link && *link != ')') { |
| |
1359 g_string_append_c(temp, *link); |
| |
1360 link++; |
| |
1361 } |
| |
1362 if (link) { |
| |
1363 link++; |
| |
1364 |
| |
1365 g_string_append(temp, "\">"); |
| |
1366 while (*link && *link != ' ') { |
| |
1367 g_string_append_c(temp, *link); |
| |
1368 link++; |
| |
1369 } |
| |
1370 g_string_append(temp, "</a>"); |
| |
1371 } |
| |
1372 |
| |
1373 g_string_append(temp, link); |
| |
1374 |
| |
1375 /* Free the last round's line. */ |
| |
1376 if (link_temp_line) |
| |
1377 g_free(line); |
| |
1378 |
| |
1379 line = temp->str; |
| |
1380 g_string_free(temp, FALSE); |
| |
1381 |
| |
1382 /* Save this memory location so we can free it later. */ |
| |
1383 link_temp_line = line; |
| |
1384 } |
| |
1385 } |
| |
1386 |
| |
1387 timestamp = ""; |
| |
1388 if (*line == '[') { |
| |
1389 timestamp = line; |
| |
1390 while (*timestamp && *timestamp != ']') |
| |
1391 timestamp++; |
| |
1392 if (*timestamp == ']') { |
| |
1393 *timestamp = '\0'; |
| |
1394 line++; |
| |
1395 /* TODO: Parse the timestamp and convert it to Gaim's format. */ |
| |
1396 g_string_append_printf(formatted, |
| |
1397 "<font size=\"2\">(%s)</font> ", line); |
| |
1398 line = timestamp; |
| |
1399 if (line[1] && line[2]) |
| |
1400 line += 2; |
| |
1401 } |
| |
1402 |
| |
1403 if (gaim_str_has_prefix(line, "*** ")) { |
| |
1404 line += (sizeof("*** ") - 1); |
| |
1405 g_string_append(formatted, "<b>"); |
| |
1406 footer = "</b>"; |
| |
1407 if (gaim_str_has_prefix(line, "NOTE: This user is offline.")) { |
| |
1408 line = _("User is offline."); |
| |
1409 } else if (gaim_str_has_prefix(line, |
| |
1410 "NOTE: Your status is currently set to ")) { |
| |
1411 |
| |
1412 line += (sizeof("NOTE: ") - 1); |
| |
1413 } else if (gaim_str_has_prefix(line, "Auto-response sent to ")) { |
| |
1414 g_string_append(formatted, _("Auto-response sent:")); |
| |
1415 while (*line && *line != ':') |
| |
1416 line++; |
| |
1417 if (*line) |
| |
1418 line++; |
| |
1419 g_string_append(formatted, "</b>"); |
| |
1420 footer = NULL; |
| |
1421 } else if (strstr(line, " signed off ")) { |
| |
1422 if (buddy != NULL && buddy->alias) |
| |
1423 g_string_append_printf(formatted, |
| |
1424 _("%s has signed off."), buddy->alias); |
| |
1425 else |
| |
1426 g_string_append_printf(formatted, |
| |
1427 _("%s has signed off."), log->name); |
| |
1428 line = ""; |
| |
1429 } else if (strstr(line, " signed on ")) { |
| |
1430 if (buddy != NULL && buddy->alias) |
| |
1431 g_string_append(formatted, buddy->alias); |
| |
1432 else |
| |
1433 g_string_append(formatted, log->name); |
| |
1434 line = " logged in."; |
| |
1435 } else if (gaim_str_has_prefix(line, |
| |
1436 "One or more messages may have been undeliverable.")) { |
| |
1437 |
| |
1438 g_string_append(formatted, |
| |
1439 "<span style=\"color: #ff0000;\">"); |
| |
1440 g_string_append(formatted, |
| |
1441 _("One or more messages may have been " |
| |
1442 "undeliverable.")); |
| |
1443 line = ""; |
| |
1444 footer = "</span></b>"; |
| |
1445 } else if (gaim_str_has_prefix(line, |
| |
1446 "You have been disconnected.")) { |
| |
1447 |
| |
1448 g_string_append(formatted, |
| |
1449 "<span style=\"color: #ff0000;\">"); |
| |
1450 g_string_append(formatted, |
| |
1451 _("You were disconnected from the server.")); |
| |
1452 line = ""; |
| |
1453 footer = "</span></b>"; |
| |
1454 } else if (gaim_str_has_prefix(line, |
| |
1455 "You are currently disconnected.")) { |
| |
1456 |
| |
1457 g_string_append(formatted, |
| |
1458 "<span style=\"color: #ff0000;\">"); |
| |
1459 line = _("You are currently disconnected. Messages " |
| |
1460 "will not be received unless you are " |
| |
1461 "logged in."); |
| |
1462 footer = "</span></b>"; |
| |
1463 } else if (gaim_str_has_prefix(line, |
| |
1464 "Your previous message has not been sent.")) { |
| |
1465 |
| |
1466 g_string_append(formatted, |
| |
1467 "<span style=\"color: #ff0000;\">"); |
| |
1468 |
| |
1469 if (gaim_str_has_prefix(line, |
| |
1470 "Your previous message has not been sent. " |
| |
1471 "Reason: Maximum length exceeded.")) { |
| |
1472 |
| |
1473 g_string_append(formatted, |
| |
1474 _("Message could not be sent because " |
| |
1475 "the maximum length was exceeded.")); |
| |
1476 line = ""; |
| |
1477 } else { |
| |
1478 g_string_append(formatted, |
| |
1479 _("Message could not be sent.")); |
| |
1480 line += (sizeof( |
| |
1481 "Your previous message " |
| |
1482 "has not been sent. ") - 1); |
| |
1483 } |
| |
1484 |
| |
1485 footer = "</span></b>"; |
| |
1486 } |
| |
1487 } else if (gaim_str_has_prefix(line, data->their_nickname)) { |
| |
1488 if (buddy != NULL && buddy->alias) { |
| |
1489 line += strlen(data->their_nickname) + 2; |
| |
1490 g_string_append_printf(formatted, |
| |
1491 "<span style=\"color: #A82F2F;\">" |
| |
1492 "<b>%s</b></span>: ", buddy->alias); |
| |
1493 } |
| |
1494 } else { |
| |
1495 char *line2 = line; |
| |
1496 while (*line2 && *line2 != ':') |
| |
1497 line2++; |
| |
1498 if (*line2 == ':') { |
| |
1499 line2++; |
| |
1500 line = line2; |
| |
1501 g_string_append_printf(formatted, |
| |
1502 "<span style=\"color: #16569E;\">" |
| |
1503 "<b>%s</b></span>:", log->account->alias); |
| |
1504 } |
| |
1505 } |
| |
1506 } |
| |
1507 |
| |
1508 g_string_append(formatted, line); |
| |
1509 |
| |
1510 if (footer) |
| |
1511 g_string_append(formatted, footer); |
| |
1512 |
| |
1513 g_string_append_c(formatted, '\n'); |
| |
1514 |
| |
1515 if (link_temp_line) |
| |
1516 g_free(link_temp_line); |
| |
1517 |
| |
1518 c++; |
| |
1519 line = c; |
| |
1520 } else |
| |
1521 c++; |
| |
1522 } |
| |
1523 |
| |
1524 g_free(read); |
| |
1525 read = formatted->str; |
| |
1526 g_string_free(formatted, FALSE); |
| |
1527 |
| |
1528 return read; |
| |
1529 } |
| |
1530 |
| |
1531 static int trillian_logger_size (GaimLog *log) |
| |
1532 { |
| |
1533 struct trillian_logger_data *data; |
| |
1534 char *text; |
| |
1535 size_t size; |
| |
1536 |
| |
1537 g_return_val_if_fail(log != NULL, 0); |
| |
1538 |
| |
1539 data = log->logger_data; |
| |
1540 |
| |
1541 if (gaim_prefs_get_bool("/plugins/core/log_reader/fast_sizes")) { |
| |
1542 return data ? data->length : 0; |
| |
1543 } |
| |
1544 |
| |
1545 text = trillian_logger_read(log, NULL); |
| |
1546 size = strlen(text); |
| |
1547 g_free(text); |
| |
1548 |
| |
1549 return size; |
| |
1550 } |
| |
1551 |
| |
1552 static void trillian_logger_finalize(GaimLog *log) |
| |
1553 { |
| |
1554 struct trillian_logger_data *data; |
| |
1555 |
| |
1556 g_return_if_fail(log != NULL); |
| |
1557 |
| |
1558 data = log->logger_data; |
| |
1559 |
| |
1560 g_free(data->path); |
| |
1561 g_free(data->their_nickname); |
| |
1562 |
| |
1563 } |
| |
1564 |
| |
1565 |
| |
1566 /***************************************************************************** |
| |
1567 * Plugin Code * |
| |
1568 *****************************************************************************/ |
| |
1569 |
| |
1570 static void |
| |
1571 init_plugin(GaimPlugin *plugin) |
| |
1572 { |
| |
1573 char *path; |
| |
1574 #ifdef _WIN32 |
| |
1575 char *folder; |
| |
1576 #endif |
| |
1577 |
| |
1578 g_return_if_fail(plugin != NULL); |
| |
1579 |
| |
1580 gaim_prefs_add_none("/plugins/core/log_reader"); |
| |
1581 |
| |
1582 |
| |
1583 /* Add general preferences. */ |
| |
1584 |
| |
1585 gaim_prefs_add_bool("/plugins/core/log_reader/fast_sizes", FALSE); |
| |
1586 gaim_prefs_add_bool("/plugins/core/log_reader/use_name_heuristics", TRUE); |
| |
1587 |
| |
1588 |
| |
1589 /* Add Adium log directory preference. */ |
| |
1590 gaim_prefs_add_none("/plugins/core/log_reader/adium"); |
| |
1591 |
| |
1592 /* Calculate default Adium log directory. */ |
| |
1593 #ifdef _WIN32 |
| |
1594 path = ""; |
| |
1595 #else |
| |
1596 path = g_build_filename(gaim_home_dir(), "Library", "Application Support", |
| |
1597 "Adium 2.0", "Users", "Default", "Logs", NULL); |
| |
1598 #endif |
| |
1599 |
| |
1600 gaim_prefs_add_string("/plugins/core/log_reader/adium/log_directory", path); |
| |
1601 |
| |
1602 #ifndef _WIN32 |
| |
1603 g_free(path); |
| |
1604 #endif |
| |
1605 |
| |
1606 |
| |
1607 /* Add Fire log directory preference. */ |
| |
1608 gaim_prefs_add_none("/plugins/core/log_reader/fire"); |
| |
1609 |
| |
1610 /* Calculate default Fire log directory. */ |
| |
1611 #ifdef _WIN32 |
| |
1612 path = ""; |
| |
1613 #else |
| |
1614 path = g_build_filename(gaim_home_dir(), "Library", "Application Support", |
| |
1615 "Fire", "Sessions", NULL); |
| |
1616 #endif |
| |
1617 |
| |
1618 gaim_prefs_add_string("/plugins/core/log_reader/fire/log_directory", path); |
| |
1619 |
| |
1620 #ifndef _WIN32 |
| |
1621 g_free(path); |
| |
1622 #endif |
| |
1623 |
| |
1624 |
| |
1625 /* Add Messenger Plus! log directory preference. */ |
| |
1626 gaim_prefs_add_none("/plugins/core/log_reader/messenger_plus"); |
| |
1627 |
| |
1628 /* Calculate default Messenger Plus! log directory. */ |
| |
1629 #ifdef _WIN32 |
| |
1630 folder = wgaim_get_special_folder(CSIDL_PERSONAL); |
| |
1631 if (folder) { |
| |
1632 #endif |
| |
1633 path = g_build_filename( |
| |
1634 #ifdef _WIN32 |
| |
1635 folder, |
| |
1636 #else |
| |
1637 GAIM_LOG_READER_WINDOWS_MOUNT_POINT, "Documents and Settings", |
| |
1638 g_get_user_name(), "My Documents", |
| |
1639 #endif |
| |
1640 "My Chat Logs", NULL); |
| |
1641 #ifdef _WIN32 |
| |
1642 g_free(folder); |
| |
1643 } else /* !folder */ |
| |
1644 path = g_strdup(""); |
| |
1645 #endif |
| |
1646 |
| |
1647 gaim_prefs_add_string("/plugins/core/log_reader/messenger_plus/log_directory", path); |
| |
1648 g_free(path); |
| |
1649 |
| |
1650 |
| |
1651 /* Add MSN Messenger log directory preference. */ |
| |
1652 gaim_prefs_add_none("/plugins/core/log_reader/msn"); |
| |
1653 |
| |
1654 /* Calculate default MSN message history directory. */ |
| |
1655 #ifdef _WIN32 |
| |
1656 folder = wgaim_get_special_folder(CSIDL_PERSONAL); |
| |
1657 if (folder) { |
| |
1658 #endif |
| |
1659 path = g_build_filename( |
| |
1660 #ifdef _WIN32 |
| |
1661 folder, |
| |
1662 #else |
| |
1663 GAIM_LOG_READER_WINDOWS_MOUNT_POINT, "Documents and Settings", |
| |
1664 g_get_user_name(), "My Documents", |
| |
1665 #endif |
| |
1666 "My Received Files", NULL); |
| |
1667 #ifdef _WIN32 |
| |
1668 g_free(folder); |
| |
1669 } else /* !folder */ |
| |
1670 path = g_strdup(""); |
| |
1671 #endif |
| |
1672 |
| |
1673 gaim_prefs_add_string("/plugins/core/log_reader/msn/log_directory", path); |
| |
1674 g_free(path); |
| |
1675 |
| |
1676 |
| |
1677 /* Add Trillian log directory preference. */ |
| |
1678 gaim_prefs_add_none("/plugins/core/log_reader/trillian"); |
| |
1679 |
| |
1680 #ifdef _WIN32 |
| |
1681 /* XXX: While a major hack, this is the most reliable way I could |
| |
1682 * think of to determine the Trillian installation directory. |
| |
1683 */ |
| |
1684 HKEY hKey; |
| |
1685 char buffer[1024] = ""; |
| |
1686 DWORD size = (sizeof(buffer) - 1); |
| |
1687 DWORD type; |
| |
1688 gboolean found = FALSE; |
| |
1689 |
| |
1690 path = NULL; |
| |
1691 /* TODO: Test this after removing the trailing "\\". */ |
| |
1692 if(ERROR_SUCCESS == RegOpenKeyEx(HKEY_CLASSES_ROOT, "Trillian.SkinZip\\shell\\Add\\command\\", |
| |
1693 0, KEY_QUERY_VALUE, &hKey)) { |
| |
1694 |
| |
1695 if(ERROR_SUCCESS == RegQueryValueEx(hKey, "", NULL, &type, (LPBYTE)buffer, &size)) { |
| |
1696 char *value = buffer; |
| |
1697 char *temp; |
| |
1698 |
| |
1699 /* Ensure the data is null terminated. */ |
| |
1700 value[size] = '\0'; |
| |
1701 |
| |
1702 /* Break apart buffer. */ |
| |
1703 if (*value == '"') { |
| |
1704 value++; |
| |
1705 temp = value; |
| |
1706 while (*temp && *temp != '"') |
| |
1707 temp++; |
| |
1708 } else { |
| |
1709 temp = value; |
| |
1710 while (*temp && *temp != ' ') |
| |
1711 temp++; |
| |
1712 } |
| |
1713 *temp = '\0'; |
| |
1714 |
| |
1715 /* Set path. */ |
| |
1716 if (gaim_str_has_suffix(value, "trillian.exe")) |
| |
1717 { |
| |
1718 value[strlen(value) - (sizeof("trillian.exe") - 1)] = '\0'; |
| |
1719 path = g_build_filename(value, "users", "default", "talk.ini", NULL); |
| |
1720 } |
| |
1721 } |
| |
1722 RegCloseKey(hKey); |
| |
1723 } |
| |
1724 |
| |
1725 if (!path) { |
| |
1726 char *folder = wgaim_get_special_folder(CSIDL_PROGRAM_FILES); |
| |
1727 if (folder) { |
| |
1728 path = g_build_filename(folder, "Trillian", |
| |
1729 "users", "default", "talk.ini", NULL); |
| |
1730 g_free(folder); |
| |
1731 } |
| |
1732 } |
| |
1733 |
| |
1734 if (path) { |
| |
1735 /* Read talk.ini file to find the log directory. */ |
| |
1736 GError *error = NULL; |
| |
1737 |
| |
1738 #if 0 && GLIB_CHECK_VERSION(2,6,0) /* FIXME: Not tested yet. */ |
| |
1739 GKeyFile *key_file; |
| |
1740 |
| |
1741 gaim_debug(GAIM_DEBUG_INFO, "Trillian talk.ini read", |
| |
1742 "Reading %s\n", path); |
| |
1743 if (!g_key_file_load_from_file(key_file, path, G_KEY_FILE_NONE, GError &error)) { |
| |
1744 gaim_debug(GAIM_DEBUG_ERROR, "Trillian talk.ini read", |
| |
1745 "Error reading talk.ini\n"); |
| |
1746 if (error) |
| |
1747 g_error_free(error); |
| |
1748 } else { |
| |
1749 char *logdir = g_key_file_get_string(key_file, "Logging", "Directory", &error); |
| |
1750 if (error) { |
| |
1751 gaim_debug(GAIM_DEBUG_ERROR, "Trillian talk.ini read", |
| |
1752 "Error reading Directory value from Logging section\n"); |
| |
1753 g_error_free(error); |
| |
1754 } |
| |
1755 |
| |
1756 if (logdir) { |
| |
1757 g_strchomp(logdir); |
| |
1758 gaim_prefs_add_string( |
| |
1759 "/plugins/core/log_reader/trillian/log_directory", logdir); |
| |
1760 found = TRUE; |
| |
1761 } |
| |
1762 |
| |
1763 g_key_file_free(key_file); |
| |
1764 } |
| |
1765 #else /* !GLIB_CHECK_VERSION(2,6,0) */ |
| |
1766 gsize length; |
| |
1767 gchar *contents = NULL; |
| |
1768 |
| |
1769 gaim_debug(GAIM_DEBUG_INFO, "Trillian talk.ini read", |
| |
1770 "Reading %s\n", path); |
| |
1771 if (!g_file_get_contents(path, &contents, &length, &error)) { |
| |
1772 gaim_debug(GAIM_DEBUG_ERROR, "Trillian talk.ini read", |
| |
1773 "Error reading talk.ini\n"); |
| |
1774 if (error) |
| |
1775 g_error_free(error); |
| |
1776 } else { |
| |
1777 char *line = contents; |
| |
1778 while (*contents) { |
| |
1779 if (*contents == '\n') { |
| |
1780 *contents = '\0'; |
| |
1781 |
| |
1782 /* XXX: This assumes the first Directory key is under [Logging]. */ |
| |
1783 if (gaim_str_has_prefix(line, "Directory=")) { |
| |
1784 line += (sizeof("Directory=") - 1); |
| |
1785 g_strchomp(line); |
| |
1786 gaim_prefs_add_string( |
| |
1787 "/plugins/core/log_reader/trillian/log_directory", |
| |
1788 line); |
| |
1789 found = TRUE; |
| |
1790 } |
| |
1791 |
| |
1792 contents++; |
| |
1793 line = contents; |
| |
1794 } else |
| |
1795 contents++; |
| |
1796 } |
| |
1797 g_free(path); |
| |
1798 g_free(contents); |
| |
1799 } |
| |
1800 #endif /* !GTK_CHECK_VERSION(2,6,0) */ |
| |
1801 } /* path */ |
| |
1802 |
| |
1803 if (!found) { |
| |
1804 #endif /* defined(_WIN32) */ |
| |
1805 |
| |
1806 /* Calculate default Trillian log directory. */ |
| |
1807 #ifdef _WIN32 |
| |
1808 folder = wgaim_get_special_folder(CSIDL_PROGRAM_FILES); |
| |
1809 if (folder) { |
| |
1810 #endif |
| |
1811 path = g_build_filename( |
| |
1812 #ifdef _WIN32 |
| |
1813 folder, |
| |
1814 #else |
| |
1815 GAIM_LOG_READER_WINDOWS_MOUNT_POINT, "Program Files", |
| |
1816 #endif |
| |
1817 "Trillian", "users", "default", "logs", NULL); |
| |
1818 #ifdef _WIN32 |
| |
1819 g_free(folder); |
| |
1820 } else /* !folder */ |
| |
1821 path = g_strdup(""); |
| |
1822 #endif |
| |
1823 |
| |
1824 gaim_prefs_add_string("/plugins/core/log_reader/trillian/log_directory", path); |
| |
1825 g_free(path); |
| |
1826 |
| |
1827 #ifdef _WIN32 |
| |
1828 } /* !found */ |
| |
1829 #endif |
| |
1830 } |
| |
1831 |
| |
1832 static gboolean |
| |
1833 plugin_load(GaimPlugin *plugin) |
| |
1834 { |
| |
1835 g_return_val_if_fail(plugin != NULL, FALSE); |
| |
1836 |
| |
1837 /* The names of IM clients are marked for translation at the request of |
| |
1838 translators who wanted to transliterate them. Many translators |
| |
1839 choose to leave them alone. Choose what's best for your language. */ |
| |
1840 adium_logger = gaim_log_logger_new("adium", _("Adium"), 6, |
| |
1841 NULL, |
| |
1842 NULL, |
| |
1843 adium_logger_finalize, |
| |
1844 adium_logger_list, |
| |
1845 adium_logger_read, |
| |
1846 adium_logger_size); |
| |
1847 gaim_log_logger_add(adium_logger); |
| |
1848 |
| |
1849 /* The names of IM clients are marked for translation at the request of |
| |
1850 translators who wanted to transliterate them. Many translators |
| |
1851 choose to leave them alone. Choose what's best for your language. */ |
| |
1852 fire_logger = gaim_log_logger_new("fire", _("Fire"), 6, |
| |
1853 NULL, |
| |
1854 NULL, |
| |
1855 fire_logger_finalize, |
| |
1856 fire_logger_list, |
| |
1857 fire_logger_read, |
| |
1858 fire_logger_size); |
| |
1859 gaim_log_logger_add(fire_logger); |
| |
1860 |
| |
1861 /* The names of IM clients are marked for translation at the request of |
| |
1862 translators who wanted to transliterate them. Many translators |
| |
1863 choose to leave them alone. Choose what's best for your language. */ |
| |
1864 messenger_plus_logger = gaim_log_logger_new("messenger_plus", _("Messenger Plus!"), 6, |
| |
1865 NULL, |
| |
1866 NULL, |
| |
1867 messenger_plus_logger_finalize, |
| |
1868 messenger_plus_logger_list, |
| |
1869 messenger_plus_logger_read, |
| |
1870 messenger_plus_logger_size); |
| |
1871 gaim_log_logger_add(messenger_plus_logger); |
| |
1872 |
| |
1873 /* The names of IM clients are marked for translation at the request of |
| |
1874 translators who wanted to transliterate them. Many translators |
| |
1875 choose to leave them alone. Choose what's best for your language. */ |
| |
1876 msn_logger = gaim_log_logger_new("msn", _("MSN Messenger"), 6, |
| |
1877 NULL, |
| |
1878 NULL, |
| |
1879 msn_logger_finalize, |
| |
1880 msn_logger_list, |
| |
1881 msn_logger_read, |
| |
1882 msn_logger_size); |
| |
1883 gaim_log_logger_add(msn_logger); |
| |
1884 |
| |
1885 /* The names of IM clients are marked for translation at the request of |
| |
1886 translators who wanted to transliterate them. Many translators |
| |
1887 choose to leave them alone. Choose what's best for your language. */ |
| |
1888 trillian_logger = gaim_log_logger_new("trillian", _("Trillian"), 6, |
| |
1889 NULL, |
| |
1890 NULL, |
| |
1891 trillian_logger_finalize, |
| |
1892 trillian_logger_list, |
| |
1893 trillian_logger_read, |
| |
1894 trillian_logger_size); |
| |
1895 gaim_log_logger_add(trillian_logger); |
| |
1896 |
| |
1897 return TRUE; |
| |
1898 } |
| |
1899 |
| |
1900 static gboolean |
| |
1901 plugin_unload(GaimPlugin *plugin) |
| |
1902 { |
| |
1903 g_return_val_if_fail(plugin != NULL, FALSE); |
| |
1904 |
| |
1905 gaim_log_logger_remove(adium_logger); |
| |
1906 gaim_log_logger_remove(fire_logger); |
| |
1907 gaim_log_logger_remove(messenger_plus_logger); |
| |
1908 gaim_log_logger_remove(msn_logger); |
| |
1909 gaim_log_logger_remove(trillian_logger); |
| |
1910 |
| |
1911 return TRUE; |
| |
1912 } |
| |
1913 |
| |
1914 static GaimPluginPrefFrame * |
| |
1915 get_plugin_pref_frame(GaimPlugin *plugin) |
| |
1916 { |
| |
1917 GaimPluginPrefFrame *frame; |
| |
1918 GaimPluginPref *ppref; |
| |
1919 |
| |
1920 g_return_val_if_fail(plugin != NULL, FALSE); |
| |
1921 |
| |
1922 frame = gaim_plugin_pref_frame_new(); |
| |
1923 |
| |
1924 |
| |
1925 /* Add general preferences. */ |
| |
1926 |
| |
1927 ppref = gaim_plugin_pref_new_with_label(_("General Log Reading Configuration")); |
| |
1928 gaim_plugin_pref_frame_add(frame, ppref); |
| |
1929 |
| |
1930 ppref = gaim_plugin_pref_new_with_name_and_label( |
| |
1931 "/plugins/core/log_reader/fast_sizes", _("Fast size calculations")); |
| |
1932 gaim_plugin_pref_frame_add(frame, ppref); |
| |
1933 |
| |
1934 ppref = gaim_plugin_pref_new_with_name_and_label( |
| |
1935 "/plugins/core/log_reader/use_name_heuristics", _("Use name heuristics")); |
| |
1936 gaim_plugin_pref_frame_add(frame, ppref); |
| |
1937 |
| |
1938 |
| |
1939 /* Add Log Directory preferences. */ |
| |
1940 |
| |
1941 ppref = gaim_plugin_pref_new_with_label(_("Log Directory")); |
| |
1942 gaim_plugin_pref_frame_add(frame, ppref); |
| |
1943 |
| |
1944 ppref = gaim_plugin_pref_new_with_name_and_label( |
| |
1945 "/plugins/core/log_reader/adium/log_directory", _("Adium")); |
| |
1946 gaim_plugin_pref_frame_add(frame, ppref); |
| |
1947 |
| |
1948 ppref = gaim_plugin_pref_new_with_name_and_label( |
| |
1949 "/plugins/core/log_reader/fire/log_directory", _("Fire")); |
| |
1950 gaim_plugin_pref_frame_add(frame, ppref); |
| |
1951 |
| |
1952 ppref = gaim_plugin_pref_new_with_name_and_label( |
| |
1953 "/plugins/core/log_reader/messenger_plus/log_directory", _("Messenger Plus!")); |
| |
1954 gaim_plugin_pref_frame_add(frame, ppref); |
| |
1955 |
| |
1956 ppref = gaim_plugin_pref_new_with_name_and_label( |
| |
1957 "/plugins/core/log_reader/msn/log_directory", _("MSN Messenger")); |
| |
1958 gaim_plugin_pref_frame_add(frame, ppref); |
| |
1959 |
| |
1960 ppref = gaim_plugin_pref_new_with_name_and_label( |
| |
1961 "/plugins/core/log_reader/trillian/log_directory", _("Trillian")); |
| |
1962 gaim_plugin_pref_frame_add(frame, ppref); |
| |
1963 |
| |
1964 return frame; |
| |
1965 } |
| |
1966 |
| |
1967 static GaimPluginUiInfo prefs_info = { |
| |
1968 get_plugin_pref_frame, |
| |
1969 0, /* page_num (reserved) */ |
| |
1970 NULL /* frame (reserved) */ |
| |
1971 }; |
| |
1972 |
| |
1973 static GaimPluginInfo info = |
| |
1974 { |
| |
1975 GAIM_PLUGIN_MAGIC, |
| |
1976 GAIM_MAJOR_VERSION, |
| |
1977 GAIM_MINOR_VERSION, |
| |
1978 GAIM_PLUGIN_STANDARD, /**< type */ |
| |
1979 NULL, /**< ui_requirement */ |
| |
1980 0, /**< flags */ |
| |
1981 NULL, /**< dependencies */ |
| |
1982 GAIM_PRIORITY_DEFAULT, /**< priority */ |
| |
1983 "core-log_reader", /**< id */ |
| |
1984 N_("Log Reader"), /**< name */ |
| |
1985 VERSION, /**< version */ |
| |
1986 |
| |
1987 /** summary */ |
| |
1988 N_("Includes other IM clients' logs in the " |
| |
1989 "log viewer."), |
| |
1990 |
| |
1991 /** description */ |
| |
1992 N_("When viewing logs, this plugin will include " |
| |
1993 "logs from other IM clients. Currently, this " |
| |
1994 "includes Adium, Fire, Messenger Plus!, " |
| |
1995 "MSN Messenger, and Trillian."), |
| |
1996 |
| |
1997 "Richard Laager <rlaager@users.sf.net>", /**< author */ |
| |
1998 GAIM_WEBSITE, /**< homepage */ |
| |
1999 plugin_load, /**< load */ |
| |
2000 plugin_unload, /**< unload */ |
| |
2001 NULL, /**< destroy */ |
| |
2002 NULL, /**< ui_info */ |
| |
2003 NULL, /**< extra_info */ |
| |
2004 &prefs_info, /**< prefs_info */ |
| |
2005 NULL /**< actions */ |
| |
2006 }; |
| |
2007 |
| |
2008 GAIM_INIT_PLUGIN(log_reader, init_plugin, info) |