Mercurial > pidgin
comparison src/log.c @ 7431:643cbc9a6035
[gaim-migrate @ 8036]
This is good enough for CVS. This is new logging. It centers around the
highly modular "GaimLogLogger," which controls how to write the log. Currently
I only have the plain text logger. I wrote the beginning of an XML logger, but
decided I didn't think it was that great an idea. Plugins can implement loggers
themselves, so you can have, like, an SQL logger or something.
The default logger writes to a file unique to the conversation, and they're saved
on disk in a heirarchical fashion: ~/.gaim/logs/aim/seanegn/robflynn-date.log would
be a conversation I had with Rob on date.
What doesn't work:
System logging
The search button in the log viewer.
Oh, chats probably don't log either, I didn't test.
You can only log in plain text right now.
Obviously, it's not done yet. But you can play around with it, and give it
some love. I'll get back to it tomorrow after school, maybe.
committer: Tailor Script <tailor@pidgin.im>
| author | Sean Egan <seanegan@gmail.com> |
|---|---|
| date | Wed, 05 Nov 2003 06:15:49 +0000 |
| parents | ab828b8c3f22 |
| children | 7cdbd2eb7546 |
comparison
equal
deleted
inserted
replaced
| 7430:783eea64614c | 7431:643cbc9a6035 |
|---|---|
| 1 /* --------------------------------------------------- | 1 /** |
| 2 * Function to remove a log file entry | 2 * @file log.c Logging API |
| 3 * --------------------------------------------------- | 3 * @ingroup core |
| 4 * | |
| 5 * gaim | |
| 6 * | |
| 7 * Copyright (C) 2003 Buzz Lightyear | |
| 8 * | |
| 9 * This program is free software; you can redistribute it and/or modify | |
| 10 * it under the terms of the GNU General Public License as published by | |
| 11 * the Free Software Foundation; either version 2 of the License, or | |
| 12 * (at your option) any later version. | |
| 13 * | |
| 14 * This program is distributed in the hope that it will be useful, | |
| 15 * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
| 16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
| 17 * GNU General Public License for more details. | |
| 18 * | |
| 19 * You should have received a copy of the GNU General Public License | |
| 20 * along with this program; if not, write to the Free Software | |
| 21 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | |
| 4 */ | 22 */ |
| 23 | |
| 24 #include "account.h" | |
| 25 #include "debug.h" | |
| 5 #include "internal.h" | 26 #include "internal.h" |
| 6 | |
| 7 #include "conversation.h" | |
| 8 #include "debug.h" | |
| 9 #include "log.h" | 27 #include "log.h" |
| 10 #include "multi.h" | |
| 11 #include "notify.h" | |
| 12 #include "prefs.h" | 28 #include "prefs.h" |
| 13 #include "prpl.h" | |
| 14 #include "util.h" | 29 #include "util.h" |
| 15 | 30 |
| 16 /* XXX CORE/UI */ | 31 static GaimLogLogger txt_logger; |
| 17 #include "gtkinternal.h" | 32 static GaimLogLogger old_logger; |
| 18 #include "gtkconv.h" | 33 |
| 19 #include "ui.h" | 34 /************************************************************************** |
| 20 | 35 * PUBLIC LOGGING FUNCTIONS *********************************************** |
| 21 GList *log_conversations = NULL; | 36 **************************************************************************/ |
| 22 | 37 |
| 23 void rm_log(struct log_conversation *a) | 38 GaimLog *gaim_log_new(GaimLogType type, const char *name, GaimAccount *account, time_t time) |
| 24 { | 39 { |
| 25 GaimConversation *cnv = gaim_find_conversation(a->name); | 40 GaimLog *log = g_new0(GaimLog, 1); |
| 26 | 41 log->name = g_strdup(name); |
| 27 /* Added the following if statements for sanity check */ | 42 log->account = account; |
| 28 if (!a) | 43 log->time = time; |
| 29 { | 44 log->logger = gaim_log_logger_get(); |
| 30 gaim_notify_error (NULL, NULL, _("Error in specifying buddy conversation."), NULL); | 45 if (log->logger && log->logger->new) |
| 46 log->logger->new(log); | |
| 47 return log; | |
| 48 } | |
| 49 | |
| 50 void gaim_log_free(GaimLog *log) | |
| 51 { | |
| 52 g_return_if_fail(log); | |
| 53 if (log->logger && log->logger->finalize) | |
| 54 log->logger->finalize(log); | |
| 55 g_free(log->name); | |
| 56 g_free(log); | |
| 57 } | |
| 58 | |
| 59 | |
| 60 void gaim_log_write(GaimLog *log, GaimMessageFlags type, | |
| 61 const char *from, time_t time, const char *message) | |
| 62 { | |
| 63 g_return_if_fail(log); | |
| 64 g_return_if_fail(log->logger); | |
| 65 g_return_if_fail(log->logger->write); | |
| 66 | |
| 67 log->logger->write(log, type, from, time, message); | |
| 68 } | |
| 69 | |
| 70 char *gaim_log_read(GaimLog *log, GaimLogReadFlags *flags) | |
| 71 { | |
| 72 g_return_val_if_fail(log && log->logger, NULL); | |
| 73 if (log->logger->read) | |
| 74 return log->logger->read(log, flags); | |
| 75 return (_("<b><font color\"=red\">The logger has no read function</font></b>")); | |
| 76 } | |
| 77 | |
| 78 /**************************************************************************** | |
| 79 * LOGGER FUNCTIONS ********************************************************* | |
| 80 ****************************************************************************/ | |
| 81 | |
| 82 static GaimLogLogger *current_logger = NULL; | |
| 83 static GSList *loggers = NULL; | |
| 84 | |
| 85 static void logger_pref_cb(const char *name, GaimPrefType type, | |
| 86 gpointer value, gpointer data) | |
| 87 { | |
| 88 GaimLogLogger *logger; | |
| 89 GSList *l = loggers; | |
| 90 while (l) { | |
| 91 logger = l->data; | |
| 92 if (!strcmp(logger->id, value)) { | |
| 93 gaim_log_logger_set(logger); | |
| 94 return; | |
| 95 } | |
| 96 l = l->next; | |
| 97 } | |
| 98 gaim_log_logger_set(&txt_logger); | |
| 99 } | |
| 100 | |
| 101 | |
| 102 GaimLogLogger *gaim_log_logger_new(void(*new)(GaimLog *), | |
| 103 void(*write)(GaimLog *, GaimMessageFlags, const char *, | |
| 104 time_t, const char *), | |
| 105 void(*finalize)(GaimLog *), GList*(*list)(const char*, GaimAccount*), | |
| 106 char*(*read)(GaimLog*, GaimLogReadFlags*)) | |
| 107 { | |
| 108 GaimLogLogger *logger = g_new0(GaimLogLogger, 1); | |
| 109 logger->new = new; | |
| 110 logger->write = write; | |
| 111 logger->finalize = finalize; | |
| 112 logger->list = list; | |
| 113 logger->read = read; | |
| 114 return logger; | |
| 115 } | |
| 116 | |
| 117 void gaim_log_logger_free(GaimLogLogger *logger) | |
| 118 { | |
| 119 g_free(logger); | |
| 120 } | |
| 121 | |
| 122 void gaim_log_logger_add (GaimLogLogger *logger) | |
| 123 { | |
| 124 g_return_if_fail(logger); | |
| 125 if (g_slist_find(loggers, logger)) | |
| 31 return; | 126 return; |
| 32 } | 127 loggers = g_slist_append(loggers, logger); |
| 33 cnv = gaim_find_conversation(a->name); | 128 } |
| 34 if (!cnv) | 129 |
| 35 { | 130 void gaim_log_logger_remove (GaimLogLogger *logger) |
| 36 gaim_notify_error (NULL, NULL, _("Unable to find conversation log"), NULL); | 131 { |
| 37 return; | 132 g_return_if_fail(logger); |
| 38 } | 133 g_slist_remove(loggers, logger); |
| 39 | 134 } |
| 40 log_conversations = g_list_remove(log_conversations, a); | 135 |
| 41 } | 136 void gaim_log_logger_set (GaimLogLogger *logger) |
| 42 | 137 { |
| 43 struct log_conversation *find_log_info(const char *name) | 138 g_return_if_fail(logger); |
| 44 { | 139 current_logger = logger; |
| 45 char *pname = g_malloc(BUF_LEN); | 140 } |
| 46 GList *lc = log_conversations; | 141 |
| 47 struct log_conversation *l; | 142 GaimLogLogger *gaim_log_logger_get() |
| 48 | 143 { |
| 49 | 144 return current_logger; |
| 50 strcpy(pname, gaim_normalize(NULL, name)); | 145 } |
| 51 | 146 |
| 52 while (lc) { | 147 GList *gaim_log_logger_get_options(void) |
| 53 l = (struct log_conversation *)lc->data; | 148 { |
| 54 if (!gaim_utf8_strcasecmp(pname, gaim_normalize(NULL, l->name))) { | 149 GSList *n; |
| 55 g_free(pname); | 150 GList *list = NULL; |
| 56 return l; | 151 GaimLogLogger *data; |
| 152 | |
| 153 for (n = loggers; n; n = n->next) { | |
| 154 data = n->data; | |
| 155 if (!data->write) | |
| 156 continue; | |
| 157 list = g_list_append(list, data->name); | |
| 158 list = g_list_append(list, data->id); | |
| 159 } | |
| 160 | |
| 161 return list; | |
| 162 } | |
| 163 | |
| 164 static gint log_compare(GaimLog *a, GaimLog *b) | |
| 165 { | |
| 166 return b->time - a->time; | |
| 167 } | |
| 168 | |
| 169 GList *gaim_log_get_logs(const char *name, GaimAccount *account) | |
| 170 { | |
| 171 GList *logs = NULL; | |
| 172 GSList *n; | |
| 173 for (n = loggers; n; n = n->next) { | |
| 174 GaimLogLogger *logger = n->data; | |
| 175 if (!logger->list) | |
| 176 continue; | |
| 177 logs = g_list_concat(logs, logger->list(name, account)); | |
| 178 } | |
| 179 | |
| 180 return g_list_sort(logs, log_compare); | |
| 181 } | |
| 182 | |
| 183 void gaim_log_init(void) | |
| 184 { | |
| 185 gaim_prefs_add_none("/core/logging"); | |
| 186 gaim_prefs_add_string("/core/logging/format", "txt"); | |
| 187 gaim_log_logger_add(&txt_logger); | |
| 188 gaim_log_logger_add(&old_logger); | |
| 189 gaim_prefs_connect_callback("/core/logging/format", | |
| 190 logger_pref_cb, NULL); | |
| 191 gaim_prefs_trigger_callback("/core/logging/format"); | |
| 192 } | |
| 193 | |
| 194 /**************************************************************************** | |
| 195 * LOGGERS ****************************************************************** | |
| 196 ****************************************************************************/ | |
| 197 | |
| 198 static GList *log_lister_common(const char *screenname, GaimAccount *account, const char *ext, GaimLogLogger *logger) | |
| 199 { | |
| 200 GDir *dir; | |
| 201 GList *list = NULL; | |
| 202 const char *filename; | |
| 203 char *me = g_strdup(gaim_normalize(account, gaim_account_get_username(account))); | |
| 204 | |
| 205 const char *prpl = GAIM_PLUGIN_PROTOCOL_INFO | |
| 206 (gaim_find_prpl(gaim_account_get_protocol(account)))->list_icon(account, NULL); | |
| 207 char *path = g_build_filename(gaim_user_dir(), "logs", prpl, me, gaim_normalize(account, screenname), NULL); | |
| 208 | |
| 209 if (!(dir = g_dir_open(path, 0, NULL))) { | |
| 210 g_free(path); | |
| 211 g_free(me); | |
| 212 return NULL; | |
| 213 } | |
| 214 while ((filename = g_dir_read_name(dir))) { | |
| 215 if (g_str_has_suffix(filename, ext)) { | |
| 216 const char *l = filename; | |
| 217 struct tm time; | |
| 218 GaimLog *log; | |
| 219 char d[5]; | |
| 220 | |
| 221 strncpy(d, l, 4); | |
| 222 d[4] = '\0'; | |
| 223 time.tm_year = atoi(d) - 1900; | |
| 224 l = l + 5; | |
| 225 | |
| 226 strncpy(d, l, 2); | |
| 227 d[2] = '\0'; | |
| 228 time.tm_mon = atoi(d) - 1; | |
| 229 l = l + 3; | |
| 230 | |
| 231 strncpy(d, l, 2); | |
| 232 time.tm_mday = atoi(d); | |
| 233 l = l + 3; | |
| 234 | |
| 235 strncpy(d, l, 2); | |
| 236 time.tm_hour = atoi(d); | |
| 237 l = l + 2; | |
| 238 | |
| 239 strncpy(d, l, 2); | |
| 240 time.tm_min = atoi(d); | |
| 241 l = l + 2; | |
| 242 | |
| 243 strncpy(d, l, 2); | |
| 244 time.tm_sec = atoi(d); | |
| 245 l = l + 2; | |
| 246 log = gaim_log_new(GAIM_LOG_IM, screenname, account, mktime(&time)); | |
| 247 log->logger = logger; | |
| 248 log->logger_data = g_build_filename(path, filename, NULL); | |
| 249 list = g_list_append(list, log); | |
| 57 } | 250 } |
| 58 lc = lc->next; | 251 } |
| 59 } | 252 g_dir_close(dir); |
| 60 g_free(pname); | 253 return list; |
| 61 return NULL; | 254 } |
| 62 } | 255 |
| 63 | 256 #if 0 /* Maybe some other time. */ |
| 64 void update_log_convs() | 257 /**************** |
| 65 { | 258 ** XML LOGGER ** |
| 66 GList *cnv; | 259 ****************/ |
| 67 GaimConversation *c; | 260 |
| 68 GaimGtkConversation *gtkconv; | 261 static const char *str_from_msg_type (GaimMessageFlags type) |
| 69 | 262 { |
| 70 for (cnv = gaim_get_conversations(); cnv != NULL; cnv = cnv->next) { | 263 |
| 71 | 264 return ""; |
| 72 c = (GaimConversation *)cnv->data; | 265 |
| 73 | 266 } |
| 74 if (!GAIM_IS_GTK_CONVERSATION(c)) | 267 |
| 75 continue; | 268 static void xml_logger_write(GaimLog *log, |
| 76 | 269 GaimMessageFlags type, |
| 77 gtkconv = GAIM_GTK_CONVERSATION(c); | 270 const char *from, time_t time, const char *message) |
| 78 | 271 { |
| 79 if (gtkconv->toolbar.log) { | 272 char date[64]; |
| 80 if (gaim_conversation_get_type(c) == GAIM_CONV_CHAT) | 273 char *xhtml = NULL; |
| 81 gtk_widget_set_sensitive(GTK_WIDGET(gtkconv->toolbar.log), | 274 if (!log->logger_data) { |
| 82 !gaim_prefs_get_bool("/gaim/gtk/logging/log_chats")); | 275 /* This log is new. We could use the loggers 'new' function, but |
| 83 else | 276 * creating a new file there would result in empty files in the case |
| 84 gtk_widget_set_sensitive(GTK_WIDGET(gtkconv->toolbar.log), | 277 * that you open a convo with someone, but don't say anything. |
| 85 !gaim_prefs_get_bool("/gaim/gtk/logging/log_ims")); | 278 */ |
| 279 char *ud = gaim_user_dir(); | |
| 280 char *guy = g_strdup(gaim_normalize(log->account, gaim_account_get_username(log->account))); | |
| 281 const char *prpl = GAIM_PLUGIN_PROTOCOL_INFO | |
| 282 (gaim_find_prpl(gaim_account_get_protocol(log->account)))->list_icon(log->account, NULL); | |
| 283 char *dir; | |
| 284 FILE *file; | |
| 285 | |
| 286 strftime(date, sizeof(date), "%F.%H%M%S.xml", localtime(&log->time)); | |
| 287 | |
| 288 dir = g_build_filename(ud, "logs", NULL); | |
| 289 mkdir (dir, S_IRUSR | S_IWUSR | S_IXUSR); | |
| 290 g_free(dir); | |
| 291 dir = g_build_filename(ud, "logs", | |
| 292 prpl, NULL); | |
| 293 mkdir (dir, S_IRUSR | S_IWUSR | S_IXUSR); | |
| 294 g_free(dir); | |
| 295 dir = g_build_filename(ud, "logs", | |
| 296 prpl, guy, NULL); | |
| 297 mkdir (dir, S_IRUSR | S_IWUSR | S_IXUSR); | |
| 298 g_free(dir); | |
| 299 dir = g_build_filename(ud, "logs", | |
| 300 prpl, guy, gaim_normalize(log->account, log->name), NULL); | |
| 301 mkdir (dir, S_IRUSR | S_IWUSR | S_IXUSR); | |
| 302 | |
| 303 char *filename = g_build_filename(dir, date, NULL); | |
| 304 g_free(dir); | |
| 305 | |
| 306 file = fopen(dir, "r"); | |
| 307 if(!file) | |
| 308 mkdir(dir, S_IRUSR | S_IWUSR | S_IXUSR); | |
| 309 else | |
| 310 fclose(file); | |
| 311 | |
| 312 log->logger_data = fopen(filename, "a"); | |
| 313 if (!log->logger_data) { | |
| 314 gaim_debug(GAIM_DEBUG_ERROR, "log", "Could not create log file %s\n", filename); | |
| 315 return; | |
| 86 } | 316 } |
| 87 } | 317 fprintf(log->logger_data, "<?xml version='1.0' encoding='UTF-8' ?>\n" |
| 88 } | 318 "<?xml-stylesheet href='file:///usr/src/web/htdocs/log-stylesheet.xsl' type='text/xml' ?>\n"); |
| 89 | 319 |
| 90 static FILE *open_gaim_log_file(const char *name, int *flag) | 320 strftime(date, sizeof(date), "%F %T", localtime(&log->time)); |
| 91 { | 321 fprintf(log->logger_data, "<conversation time='%s' screenname='%s' protocol='%s'>\n", |
| 92 char *buf; | 322 date, log->name, prpl); |
| 93 char *buf2; | 323 } |
| 94 char log_all_file[256]; | 324 |
| 95 struct stat st; | 325 strftime(date, sizeof(date), "%T", localtime(&time)); |
| 96 FILE *fd; | 326 gaim_markup_html_to_xhtml(message, &xhtml, NULL); |
| 97 #ifndef _WIN32 | 327 if (from) |
| 98 int res; | 328 fprintf(log->logger_data, "<message %s %s from='%s' time='%s'>%s</message>\n", |
| 329 str_from_msg_type(type), | |
| 330 type & GAIM_MESSAGE_SEND ? "direction='sent'" : | |
| 331 type & GAIM_MESSAGE_RECV ? "direction='received'" : "", | |
| 332 from, date, xhtml); | |
| 333 else | |
| 334 fprintf(log->logger_data, "<message %s %s time='%s'>%s</message>\n", | |
| 335 str_from_msg_type(type), | |
| 336 type & GAIM_MESSAGE_SEND ? "direction='sent'" : | |
| 337 type & GAIM_MESSAGE_RECV ? "direction='received'" : "", | |
| 338 date, xhtml); | |
| 339 fflush(log->logger_data); | |
| 340 g_free(xhtml); | |
| 341 } | |
| 342 | |
| 343 static void xml_logger_finalize(GaimLog *log) | |
| 344 { | |
| 345 if (log->logger_data) { | |
| 346 fprintf(log->logger_data, "</conversation>\n"); | |
| 347 fclose(log->logger_data); | |
| 348 log->logger_data = NULL; | |
| 349 } | |
| 350 } | |
| 351 | |
| 352 static GList *xml_logger_list(const char *sn, GaimAccount *account) | |
| 353 { | |
| 354 return log_lister_common(sn, account, ".xml", &xml_logger); | |
| 355 } | |
| 356 | |
| 357 static GaimLogLogger xml_logger = { | |
| 358 N_("XML"), "xml", | |
| 359 NULL, | |
| 360 xml_logger_write, | |
| 361 xml_logger_finalize, | |
| 362 xml_logger_list, | |
| 363 NULL | |
| 364 }; | |
| 99 #endif | 365 #endif |
| 100 gchar *gaim_dir; | 366 |
| 101 | 367 /**************************** |
| 102 buf = g_malloc(BUF_LONG); | 368 ** PLAIN TEXT LOGGER ******* |
| 103 buf2 = g_malloc(BUF_LONG); | 369 ****************************/ |
| 104 gaim_dir = gaim_user_dir(); | 370 |
| 105 | 371 static void txt_logger_write(GaimLog *log, |
| 106 /* Dont log yourself */ | 372 GaimMessageFlags type, |
| 107 strncpy(log_all_file, gaim_dir, 256); | 373 const char *from, time_t time, const char *message) |
| 108 | 374 { |
| 109 #ifndef _WIN32 | 375 char date[64]; |
| 110 stat(log_all_file, &st); | 376 char *stripped = NULL; |
| 111 if (!S_ISDIR(st.st_mode)) | 377 if (!log->logger_data) { |
| 112 unlink(log_all_file); | 378 /* This log is new. We could use the loggers 'new' function, but |
| 113 | 379 * creating a new file there would result in empty files in the case |
| 114 fd = fopen(log_all_file, "r"); | 380 * that you open a convo with someone, but don't say anything. |
| 115 | 381 */ |
| 116 if (!fd) { | 382 char *ud = gaim_user_dir(); |
| 117 res = mkdir(log_all_file, S_IRUSR | S_IWUSR | S_IXUSR); | 383 char *guy = g_strdup(gaim_normalize(log->account, gaim_account_get_username(log->account))); |
| 118 if (res < 0) { | 384 const char *prpl = GAIM_PLUGIN_PROTOCOL_INFO |
| 119 g_snprintf(buf, BUF_LONG, _("Unable to make directory %s for logging"), | 385 (gaim_find_prpl(gaim_account_get_protocol(log->account)))->list_icon(log->account, NULL); |
| 120 log_all_file); | 386 char *dir; |
| 121 gaim_notify_error(NULL, NULL, buf, NULL); | 387 FILE *file; |
| 122 g_free(buf); | 388 |
| 123 g_free(buf2); | 389 strftime(date, sizeof(date), "%F.%H%M%S.txt", localtime(&log->time)); |
| 124 return NULL; | 390 |
| 391 dir = g_build_filename(ud, "logs", NULL); | |
| 392 mkdir (dir, S_IRUSR | S_IWUSR | S_IXUSR); | |
| 393 g_free(dir); | |
| 394 dir = g_build_filename(ud, "logs", | |
| 395 prpl, NULL); | |
| 396 mkdir (dir, S_IRUSR | S_IWUSR | S_IXUSR); | |
| 397 g_free(dir); | |
| 398 dir = g_build_filename(ud, "logs", | |
| 399 prpl, guy, NULL); | |
| 400 mkdir (dir, S_IRUSR | S_IWUSR | S_IXUSR); | |
| 401 g_free(dir); | |
| 402 dir = g_build_filename(ud, "logs", | |
| 403 prpl, guy, gaim_normalize(log->account, log->name), NULL); | |
| 404 mkdir (dir, S_IRUSR | S_IWUSR | S_IXUSR); | |
| 405 | |
| 406 char *filename = g_build_filename(dir, date, NULL); | |
| 407 g_free(dir); | |
| 408 | |
| 409 file = fopen(dir, "r"); | |
| 410 if(!file) | |
| 411 mkdir(dir, S_IRUSR | S_IWUSR | S_IXUSR); | |
| 412 else | |
| 413 fclose(file); | |
| 414 | |
| 415 log->logger_data = fopen(filename, "a"); | |
| 416 if (!log->logger_data) { | |
| 417 gaim_debug(GAIM_DEBUG_ERROR, "log", "Could not create log file %s\n", filename); | |
| 418 return; | |
| 125 } | 419 } |
| 126 } else | 420 strftime(date, sizeof(date), "%F %T", localtime(&log->time)); |
| 127 fclose(fd); | 421 fprintf(log->logger_data, "Conversation with %s at %s on %s (%s)\n", |
| 128 | 422 log->name, date, gaim_account_get_username(log->account), prpl); |
| 129 g_snprintf(log_all_file, 256, "%s" G_DIR_SEPARATOR_S "logs", gaim_dir); | 423 } |
| 130 | 424 |
| 131 if (stat(log_all_file, &st) < 0) | 425 strftime(date, sizeof(date), "%T", localtime(&time)); |
| 132 *flag = 1; | 426 stripped = gaim_markup_strip_html(message); |
| 133 if (!S_ISDIR(st.st_mode)) | 427 fprintf(log->logger_data, "(%s) %s%s %s\n", date, from ? from : "", from ? ":" : "", stripped); |
| 134 unlink(log_all_file); | 428 fflush(log->logger_data); |
| 135 | 429 g_free(stripped); |
| 136 fd = fopen(log_all_file, "r"); | 430 } |
| 137 if (!fd) { | 431 |
| 138 res = mkdir(log_all_file, S_IRUSR | S_IWUSR | S_IXUSR); | 432 static void txt_logger_finalize(GaimLog *log) |
| 139 if (res < 0) { | 433 { |
| 140 g_snprintf(buf, BUF_LONG, _("Unable to make directory %s for logging"), | 434 if (log->logger_data) |
| 141 log_all_file); | 435 fclose(log->logger_data); |
| 142 gaim_notify_error(NULL, NULL, buf, NULL); | 436 } |
| 143 g_free(buf); | 437 |
| 144 g_free(buf2); | 438 static GList *txt_logger_list(const char *sn, GaimAccount *account) |
| 145 return NULL; | 439 { |
| 440 return log_lister_common(sn, account, ".txt", &txt_logger); | |
| 441 } | |
| 442 | |
| 443 static char *txt_logger_read(GaimLog *log, GaimLogReadFlags *flags) | |
| 444 { | |
| 445 char *read, *minus_header; | |
| 446 if (!log->logger_data) | |
| 447 return g_strdup("<font color='red'><b>log->logger_data was NULL!</b></font>"); | |
| 448 if (g_file_get_contents((char *)log->logger_data, &read, NULL, NULL)) { | |
| 449 minus_header = strchr(read, '\n'); | |
| 450 if (!minus_header) | |
| 451 minus_header = g_strdup(read); | |
| 452 else | |
| 453 minus_header = g_strdup(minus_header + 1); | |
| 454 g_free(read); | |
| 455 return minus_header; | |
| 456 } | |
| 457 return g_strdup(_("<font color='red'><b>Could not read file: %s</b></font>")); | |
| 458 } | |
| 459 | |
| 460 static GaimLogLogger txt_logger = { | |
| 461 N_("Plain text"), "txt", | |
| 462 NULL, | |
| 463 txt_logger_write, | |
| 464 txt_logger_finalize, | |
| 465 txt_logger_list, | |
| 466 txt_logger_read | |
| 467 }; | |
| 468 | |
| 469 /**************** | |
| 470 * OLD LOGGER *** | |
| 471 ****************/ | |
| 472 | |
| 473 /* The old logger doesn't write logs, only reads them. This is to include | |
| 474 * old logs in the log viewer transparently. | |
| 475 */ | |
| 476 | |
| 477 struct old_logger_data { | |
| 478 char *path; | |
| 479 int offset; | |
| 480 int length; | |
| 481 }; | |
| 482 | |
| 483 static GList *old_logger_list(const char *sn, GaimAccount *account) | |
| 484 { | |
| 485 FILE *file; | |
| 486 char buf[BUF_LONG]; | |
| 487 struct tm tm; | |
| 488 struct old_logger_data *data = NULL; | |
| 489 char day[4], month[4], year[5]; | |
| 490 char *logfile = g_strdup_printf("%s.log", gaim_normalize(account, sn)); | |
| 491 char *date; | |
| 492 char *path = g_build_filename(gaim_user_dir(), "logs", logfile, NULL); | |
| 493 char *newlog; | |
| 494 | |
| 495 GaimLog *log = NULL; | |
| 496 GList *list = NULL; | |
| 497 | |
| 498 if (!(file = fopen(path, "r"))) | |
| 499 return NULL; | |
| 500 | |
| 501 while (fgets(buf, BUF_LONG, file)) { | |
| 502 if ((newlog = strstr(buf, "---- New C"))) { | |
| 503 int length; | |
| 504 int offset; | |
| 505 GDate gdate; | |
| 506 char convostart[32]; | |
| 507 char *temp = strchr(buf, '@'); | |
| 508 | |
| 509 if (temp == NULL || strlen(temp) < 2) | |
| 510 continue; | |
| 511 | |
| 512 temp++; | |
| 513 length = strcspn(temp, "-"); | |
| 514 if (length > 31) length = 31; | |
| 515 | |
| 516 offset = ftell(file); | |
| 517 | |
| 518 if (data) { | |
| 519 data->length = offset - data->offset - length - | |
| 520 strlen("<HR><BR><H3 Align=Center> ---- New Conversation @ ") - | |
| 521 strlen("----</H3><BR>"); | |
| 522 if (data->length != 0) | |
| 523 list = g_list_append(list, log); | |
| 524 else | |
| 525 gaim_log_free(log); | |
| 526 } | |
| 527 | |
| 528 log = gaim_log_new(GAIM_LOG_IM, sn, account, -1); | |
| 529 log->logger = &old_logger; | |
| 530 | |
| 531 data = g_malloc(sizeof(struct old_logger_data)); | |
| 532 data->offset = offset; | |
| 533 data->path = path; | |
| 534 log->logger_data = data; | |
| 535 | |
| 536 | |
| 537 g_snprintf(convostart, length, "%s", temp); | |
| 538 sscanf(convostart, "%*s %s %s %d:%d:%d %s", | |
| 539 month, day, &tm.tm_hour, &tm.tm_min, &tm.tm_sec, year); | |
| 540 date = g_strdup_printf("%s %s %s", month, day, year); | |
| 541 g_date_set_parse(&gdate, date); | |
| 542 tm.tm_mday = g_date_get_day(&gdate); | |
| 543 tm.tm_mon = g_date_get_month(&gdate) - 1; | |
| 544 tm.tm_year = g_date_get_year(&gdate) - 1900; | |
| 545 log->time = mktime(&tm); | |
| 546 | |
| 146 } | 547 } |
| 147 } else | 548 } |
| 148 fclose(fd); | 549 fclose(file); |
| 149 #else /* _WIN32 */ | 550 return list; |
| 150 g_snprintf(log_all_file, 256, "%s" G_DIR_SEPARATOR_S "logs", gaim_dir); | 551 } |
| 151 | 552 |
| 152 if( _mkdir(log_all_file) < 0 && errno != EEXIST ) { | 553 char * old_logger_read (GaimLog *log, GaimLogReadFlags *flags) |
| 153 g_snprintf(buf, BUF_LONG, _("Unable to make directory %s for logging"), log_all_file); | 554 { |
| 154 gaim_notify_error(NULL, NULL, buf, NULL); | 555 *flags = GAIM_LOG_READ_NO_NEWLINE; |
| 155 g_free(buf); | 556 struct old_logger_data *data = log->logger_data; |
| 156 g_free(buf2); | 557 FILE *file = fopen(data->path, "r"); |
| 157 return NULL; | 558 char *read = g_malloc(data->length + 1); |
| 158 } | 559 fseek(file, data->offset, SEEK_SET); |
| 159 #endif | 560 fread(read, data->length, 1, file); |
| 160 | 561 read[data->length] = '\0'; |
| 161 g_snprintf(log_all_file, 256, "%s" G_DIR_SEPARATOR_S "logs" G_DIR_SEPARATOR_S "%s", gaim_dir, name); | 562 return read; |
| 162 if (stat(log_all_file, &st) < 0) | 563 } |
| 163 *flag = 1; | 564 |
| 164 | 565 static GaimLogLogger old_logger = { |
| 165 gaim_debug(GAIM_DEBUG_INFO, "log", "Logging to: \"%s\"\n", log_all_file); | 566 "old logger", "old", |
| 166 | 567 NULL, NULL, NULL, |
| 167 fd = fopen(log_all_file, "a"); | 568 old_logger_list, |
| 168 | 569 old_logger_read |
| 169 g_free(buf); | 570 }; |
| 170 g_free(buf2); | |
| 171 return fd; | |
| 172 } | |
| 173 | |
| 174 static FILE *open_system_log_file(const char *name) | |
| 175 { | |
| 176 int x; | |
| 177 | |
| 178 if (name) | |
| 179 return open_log_file(name, 2); | |
| 180 else | |
| 181 return open_gaim_log_file("system", &x); | |
| 182 } | |
| 183 | |
| 184 FILE *open_log_file(const char *name, int is_chat) | |
| 185 { | |
| 186 struct stat st; | |
| 187 char realname[256]; | |
| 188 struct log_conversation *l; | |
| 189 FILE *fd; | |
| 190 int flag = 0; | |
| 191 | |
| 192 if (((is_chat == 2) && !gaim_prefs_get_bool("/gaim/gtk/logging/individual_logs")) | |
| 193 || ((is_chat == 1) && !gaim_prefs_get_bool("/gaim/gtk/logging/log_chats")) | |
| 194 || ((is_chat == 0) && !gaim_prefs_get_bool("/gaim/gtk/logging/log_ims"))) { | |
| 195 | |
| 196 l = find_log_info(name); | |
| 197 if (!l) | |
| 198 return NULL; | |
| 199 | |
| 200 if (stat(l->filename, &st) < 0) | |
| 201 flag = 1; | |
| 202 | |
| 203 fd = fopen(l->filename, "a"); | |
| 204 | |
| 205 if (flag) { /* is a new file */ | |
| 206 if (gaim_prefs_get_bool("/gaim/gtk/logging/strip_html")) { | |
| 207 fprintf(fd, _("IM Sessions with %s\n"), name); | |
| 208 } else { | |
| 209 fprintf(fd, "<HTML><HEAD><TITLE>"); | |
| 210 fprintf(fd, _("IM Sessions with %s"), name); | |
| 211 fprintf(fd, "</TITLE></HEAD><BODY BGCOLOR=\"#ffffff\">\n"); | |
| 212 } | |
| 213 } | |
| 214 | |
| 215 return fd; | |
| 216 } | |
| 217 | |
| 218 g_snprintf(realname, sizeof(realname), "%s.log", name); | |
| 219 fd = open_gaim_log_file(realname, &flag); | |
| 220 | |
| 221 if (fd && flag) { /* is a new file */ | |
| 222 if (gaim_prefs_get_bool("/gaim/gtk/logging/strip_html")) { | |
| 223 fprintf(fd, _("IM Sessions with %s\n"), name); | |
| 224 } else { | |
| 225 fprintf(fd, "<HTML><HEAD><TITLE>"); | |
| 226 fprintf(fd, _("IM Sessions with %s"), name); | |
| 227 fprintf(fd, "</TITLE></HEAD><BODY BGCOLOR=\"#ffffff\">\n"); | |
| 228 } | |
| 229 } | |
| 230 | |
| 231 return fd; | |
| 232 } | |
| 233 | |
| 234 void system_log(enum log_event what, GaimConnection *gc, | |
| 235 GaimBuddy *who, int why) | |
| 236 { | |
| 237 GaimAccount *account = NULL; | |
| 238 FILE *fd; | |
| 239 char text[256], html[256]; | |
| 240 | |
| 241 if (gc != NULL) | |
| 242 account = gaim_connection_get_account(gc); | |
| 243 | |
| 244 if ((why & OPT_LOG_MY_SIGNON && | |
| 245 !gaim_prefs_get_bool("/gaim/gtk/logging/log_own_states")) || | |
| 246 (why & OPT_LOG_BUDDY_SIGNON && | |
| 247 !gaim_prefs_get_bool("/gaim/gtk/logging/log_signon_signoff")) || | |
| 248 (why & OPT_LOG_BUDDY_IDLE && | |
| 249 !gaim_prefs_get_bool("/gaim/gtk/logging/log_idle_state")) || | |
| 250 (why & OPT_LOG_BUDDY_AWAY && | |
| 251 !gaim_prefs_get_bool("/gaim/gtk/logging/log_away_state"))) { | |
| 252 | |
| 253 return; | |
| 254 } | |
| 255 | |
| 256 if (gaim_prefs_get_bool("/gaim/gtk/logging/individual_logs")) { | |
| 257 if (why & OPT_LOG_MY_SIGNON) | |
| 258 fd = open_system_log_file(gc ? gaim_account_get_username(account) : NULL); | |
| 259 else | |
| 260 fd = open_system_log_file(who->name); | |
| 261 } else | |
| 262 fd = open_system_log_file(NULL); | |
| 263 | |
| 264 if (!fd) | |
| 265 return; | |
| 266 | |
| 267 if (why & OPT_LOG_MY_SIGNON) { | |
| 268 switch (what) { | |
| 269 case log_signon: | |
| 270 g_snprintf(text, sizeof(text), _("+++ %s (%s) signed on @ %s"), | |
| 271 gaim_account_get_username(account), gc->prpl->info->name, gaim_date_full()); | |
| 272 g_snprintf(html, sizeof(html), "<B>%s</B>", text); | |
| 273 break; | |
| 274 case log_signoff: | |
| 275 g_snprintf(text, sizeof(text), _("+++ %s (%s) signed off @ %s"), | |
| 276 gaim_account_get_username(account), gc->prpl->info->name, gaim_date_full()); | |
| 277 g_snprintf(html, sizeof(html), "<I><FONT COLOR=GRAY>%s</FONT></I>", text); | |
| 278 break; | |
| 279 case log_away: | |
| 280 g_snprintf(text, sizeof(text), _("+++ %s (%s) changed away state @ %s"), | |
| 281 gaim_account_get_username(account), gc->prpl->info->name, gaim_date_full()); | |
| 282 g_snprintf(html, sizeof(html), "<FONT COLOR=OLIVE>%s</FONT>", text); | |
| 283 break; | |
| 284 case log_back: | |
| 285 g_snprintf(text, sizeof(text), _("+++ %s (%s) came back @ %s"), | |
| 286 gaim_account_get_username(account), gc->prpl->info->name, gaim_date_full()); | |
| 287 g_snprintf(html, sizeof(html), "%s", text); | |
| 288 break; | |
| 289 case log_idle: | |
| 290 g_snprintf(text, sizeof(text), _("+++ %s (%s) became idle @ %s"), | |
| 291 gaim_account_get_username(account), gc->prpl->info->name, gaim_date_full()); | |
| 292 g_snprintf(html, sizeof(html), "<FONT COLOR=GRAY>%s</FONT>", text); | |
| 293 break; | |
| 294 case log_unidle: | |
| 295 g_snprintf(text, sizeof(text), _("+++ %s (%s) returned from idle @ %s"), | |
| 296 gaim_account_get_username(account), gc->prpl->info->name, gaim_date_full()); | |
| 297 g_snprintf(html, sizeof(html), "%s", text); | |
| 298 break; | |
| 299 case log_quit: | |
| 300 g_snprintf(text, sizeof(text), _("+++ Program exit @ %s"), gaim_date_full()); | |
| 301 g_snprintf(html, sizeof(html), "<I><FONT COLOR=GRAY>%s</FONT></I>", text); | |
| 302 break; | |
| 303 } | |
| 304 } else if (gaim_get_buddy_alias_only(who)) { | |
| 305 switch (what) { | |
| 306 case log_signon: | |
| 307 g_snprintf(text, sizeof(text), _("%s (%s) reported that %s (%s) signed on @ %s"), | |
| 308 gaim_account_get_username(account), gc->prpl->info->name, gaim_get_buddy_alias(who), who->name, gaim_date_full()); | |
| 309 g_snprintf(html, sizeof(html), "<B>%s</B>", text); | |
| 310 break; | |
| 311 case log_signoff: | |
| 312 g_snprintf(text, sizeof(text), _("%s (%s) reported that %s (%s) signed off @ %s"), | |
| 313 gaim_account_get_username(account), gc->prpl->info->name, gaim_get_buddy_alias(who), who->name, gaim_date_full()); | |
| 314 g_snprintf(html, sizeof(html), "<I><FONT COLOR=GRAY>%s</FONT></I>", text); | |
| 315 break; | |
| 316 case log_away: | |
| 317 g_snprintf(text, sizeof(text), _("%s (%s) reported that %s (%s) went away @ %s"), | |
| 318 gaim_account_get_username(account), gc->prpl->info->name, gaim_get_buddy_alias(who), who->name, gaim_date_full()); | |
| 319 g_snprintf(html, sizeof(html), "<FONT COLOR=OLIVE>%s</FONT>", text); | |
| 320 break; | |
| 321 case log_back: | |
| 322 g_snprintf(text, sizeof(text), _("%s (%s) reported that %s (%s) came back @ %s"), | |
| 323 gaim_account_get_username(account), gc->prpl->info->name, gaim_get_buddy_alias(who), who->name, gaim_date_full()); | |
| 324 g_snprintf(html, sizeof(html), "%s", text); | |
| 325 break; | |
| 326 case log_idle: | |
| 327 g_snprintf(text, sizeof(text), _("%s (%s) reported that %s (%s) became idle @ %s"), | |
| 328 gaim_account_get_username(account), gc->prpl->info->name, gaim_get_buddy_alias(who), who->name, gaim_date_full()); | |
| 329 g_snprintf(html, sizeof(html), "<FONT COLOR=GRAY>%s</FONT>", text); | |
| 330 break; | |
| 331 case log_unidle: | |
| 332 g_snprintf(text, sizeof(text), | |
| 333 _("%s (%s) reported that %s (%s) returned from idle @ %s"), gaim_account_get_username(account), | |
| 334 gc->prpl->info->name, gaim_get_buddy_alias(who), who->name, gaim_date_full()); | |
| 335 g_snprintf(html, sizeof(html), "%s", text); | |
| 336 break; | |
| 337 default: | |
| 338 fclose(fd); | |
| 339 return; | |
| 340 break; | |
| 341 } | |
| 342 } else { | |
| 343 switch (what) { | |
| 344 case log_signon: | |
| 345 g_snprintf(text, sizeof(text), _("%s (%s) reported that %s signed on @ %s"), | |
| 346 gaim_account_get_username(account), gc->prpl->info->name, who->name, gaim_date_full()); | |
| 347 g_snprintf(html, sizeof(html), "<B>%s</B>", text); | |
| 348 break; | |
| 349 case log_signoff: | |
| 350 g_snprintf(text, sizeof(text), _("%s (%s) reported that %s signed off @ %s"), | |
| 351 gaim_account_get_username(account), gc->prpl->info->name, who->name, gaim_date_full()); | |
| 352 g_snprintf(html, sizeof(html), "<I><FONT COLOR=GRAY>%s</FONT></I>", text); | |
| 353 break; | |
| 354 case log_away: | |
| 355 g_snprintf(text, sizeof(text), _("%s (%s) reported that %s went away @ %s"), | |
| 356 gaim_account_get_username(account), gc->prpl->info->name, who->name, gaim_date_full()); | |
| 357 g_snprintf(html, sizeof(html), "<FONT COLOR=OLIVE>%s</FONT>", text); | |
| 358 break; | |
| 359 case log_back: | |
| 360 g_snprintf(text, sizeof(text), _("%s (%s) reported that %s came back @ %s"), | |
| 361 gaim_account_get_username(account), gc->prpl->info->name, who->name, gaim_date_full()); | |
| 362 g_snprintf(html, sizeof(html), "%s", text); | |
| 363 break; | |
| 364 case log_idle: | |
| 365 g_snprintf(text, sizeof(text), _("%s (%s) reported that %s became idle @ %s"), | |
| 366 gaim_account_get_username(account), gc->prpl->info->name, who->name, gaim_date_full()); | |
| 367 g_snprintf(html, sizeof(html), "<FONT COLOR=GRAY>%s</FONT>", text); | |
| 368 break; | |
| 369 case log_unidle: | |
| 370 g_snprintf(text, sizeof(text), | |
| 371 _("%s (%s) reported that %s returned from idle @ %s"), gaim_account_get_username(account), | |
| 372 gc->prpl->info->name, who->name, gaim_date_full()); | |
| 373 g_snprintf(html, sizeof(html), "%s", text); | |
| 374 break; | |
| 375 default: | |
| 376 fclose(fd); | |
| 377 return; | |
| 378 break; | |
| 379 } | |
| 380 } | |
| 381 | |
| 382 if (gaim_prefs_get_bool("/gaim/gtk/logging/strip_html")) | |
| 383 fprintf(fd, "---- %s ----\n", text); | |
| 384 else if (gaim_prefs_get_bool("/gaim/gtk/logging/individual_logs")) | |
| 385 fprintf(fd, "<HR>%s<BR><HR><BR>\n", html); | |
| 386 else | |
| 387 fprintf(fd, "%s<BR>\n", html); | |
| 388 | |
| 389 fclose(fd); | |
| 390 } | |
| 391 | |
| 392 char *html_logize(const char *p) | |
| 393 { | |
| 394 const char *temp_p; | |
| 395 char *buffer_p; | |
| 396 char *buffer_start; | |
| 397 int num_cr = 0; | |
| 398 int char_len = 0; | |
| 399 | |
| 400 for (temp_p = p; *temp_p != '\0'; temp_p++) { | |
| 401 char_len++; | |
| 402 | |
| 403 if ((*temp_p == '\n') || ((*temp_p == '<') && (*(temp_p + 1) == '!'))) | |
| 404 num_cr++; | |
| 405 } | |
| 406 | |
| 407 buffer_p = g_malloc(char_len + (4 * num_cr) + 1); | |
| 408 | |
| 409 for (temp_p = p, buffer_start = buffer_p; | |
| 410 *temp_p != '\0'; | |
| 411 temp_p++) { | |
| 412 | |
| 413 if (*temp_p == '\n') { | |
| 414 *buffer_p++ = '<'; | |
| 415 *buffer_p++ = 'B'; | |
| 416 *buffer_p++ = 'R'; | |
| 417 *buffer_p++ = '>'; | |
| 418 *buffer_p++ = '\n'; | |
| 419 | |
| 420 } else if ((*temp_p == '<') && (*(temp_p + 1) == '!')) { | |
| 421 *buffer_p++ = '&'; | |
| 422 *buffer_p++ = 'l'; | |
| 423 *buffer_p++ = 't'; | |
| 424 *buffer_p++ = ';'; | |
| 425 | |
| 426 } else | |
| 427 *buffer_p++ = *temp_p; | |
| 428 } | |
| 429 | |
| 430 *buffer_p = '\0'; | |
| 431 | |
| 432 return buffer_start; | |
| 433 } |
