comparison libpurple/plugins/log_reader.c @ 20342:d4cdca890d8e

applied changes from 4d9fac4aab275ee133ce860edc469e0e5c4734ff through 68d63cab97f8861a7a080c3103d5e24a31587473 applied changes from 68d63cab97f8861a7a080c3103d5e24a31587473 through f9d3abd0678a04291edd671c4e5d87c7217984bb
author Luke Schierer <lschiere@pidgin.im>
date Sun, 21 Oct 2007 04:39:02 +0000
parents 3a9709bfde65
children b81af0bef062
comparison
equal deleted inserted replaced
20341:754603796c1e 20342:d4cdca890d8e
101 purple_debug_error("Adium log parse", 101 purple_debug_error("Adium log parse",
102 "Filename timestamp parsing error\n"); 102 "Filename timestamp parsing error\n");
103 } else { 103 } else {
104 char *filename = g_build_filename(path, file, NULL); 104 char *filename = g_build_filename(path, file, NULL);
105 FILE *handle = g_fopen(filename, "rb"); 105 FILE *handle = g_fopen(filename, "rb");
106 char *contents; 106 char contents[57]; /* XXX: This is really inflexible. */
107 char *contents2; 107 char *contents2;
108 struct adium_logger_data *data; 108 struct adium_logger_data *data;
109 size_t rd;
109 PurpleLog *log; 110 PurpleLog *log;
110 111
111 if (!handle) { 112 if (!handle) {
112 g_free(filename); 113 g_free(filename);
113 continue; 114 continue;
114 } 115 }
115 116
116 /* XXX: This is really inflexible. */ 117 rd = fread(contents, 56, 1, handle) == 0;
117 contents = g_malloc(57);
118 fread(contents, 56, 1, handle);
119 fclose(handle); 118 fclose(handle);
120 contents[56] = '\0'; 119 contents[rd] = '\0';
121 120
122 /* XXX: This is fairly inflexible. */ 121 /* XXX: This is fairly inflexible. */
123 contents2 = contents; 122 contents2 = contents;
124 while (*contents2 && *contents2 != '>') 123 while (*contents2 && *contents2 != '>')
125 contents2++; 124 contents2++;
133 if (sscanf(contents2, "%u.%u.%u", 132 if (sscanf(contents2, "%u.%u.%u",
134 &tm.tm_hour, &tm.tm_min, &tm.tm_sec) != 3) { 133 &tm.tm_hour, &tm.tm_min, &tm.tm_sec) != 3) {
135 134
136 purple_debug_error("Adium log parse", 135 purple_debug_error("Adium log parse",
137 "Contents timestamp parsing error\n"); 136 "Contents timestamp parsing error\n");
138 g_free(contents);
139 g_free(filename); 137 g_free(filename);
140 continue; 138 continue;
141 } 139 }
142 g_free(contents);
143 140
144 data = g_new0(struct adium_logger_data, 1); 141 data = g_new0(struct adium_logger_data, 1);
145 data->path = filename; 142 data->path = filename;
146 data->type = ADIUM_HTML; 143 data->type = ADIUM_HTML;
147 144
166 purple_debug_error("Adium log parse", 163 purple_debug_error("Adium log parse",
167 "Filename timestamp parsing error\n"); 164 "Filename timestamp parsing error\n");
168 } else { 165 } else {
169 char *filename = g_build_filename(path, file, NULL); 166 char *filename = g_build_filename(path, file, NULL);
170 FILE *handle = g_fopen(filename, "rb"); 167 FILE *handle = g_fopen(filename, "rb");
171 char *contents; 168 char contents[14]; /* XXX: This is really inflexible. */
172 char *contents2; 169 char *contents2;
173 struct adium_logger_data *data; 170 struct adium_logger_data *data;
174 PurpleLog *log; 171 PurpleLog *log;
172 size_t rd;
175 173
176 if (!handle) { 174 if (!handle) {
177 g_free(filename); 175 g_free(filename);
178 continue; 176 continue;
179 } 177 }
180 178
181 /* XXX: This is really inflexible. */ 179 rd = fread(contents, 13, 1, handle);
182 contents = g_malloc(14);
183 fread(contents, 13, 1, handle);
184 fclose(handle); 180 fclose(handle);
185 contents[13] = '\0'; 181 contents[rd] = '\0';
186 182
187 contents2 = contents; 183 contents2 = contents;
188 while (*contents2 && *contents2 != '(') 184 while (*contents2 && *contents2 != '(')
189 contents2++; 185 contents2++;
190 if (*contents2) 186 if (*contents2)
193 if (sscanf(contents2, "%u.%u.%u", 189 if (sscanf(contents2, "%u.%u.%u",
194 &tm.tm_hour, &tm.tm_min, &tm.tm_sec) != 3) { 190 &tm.tm_hour, &tm.tm_min, &tm.tm_sec) != 3) {
195 191
196 purple_debug_error("Adium log parse", 192 purple_debug_error("Adium log parse",
197 "Contents timestamp parsing error\n"); 193 "Contents timestamp parsing error\n");
198 g_free(contents);
199 g_free(filename); 194 g_free(filename);
200 continue; 195 continue;
201 } 196 }
202
203 g_free(contents);
204 197
205 tm.tm_year -= 1900; 198 tm.tm_year -= 1900;
206 tm.tm_mon -= 1; 199 tm.tm_mon -= 1;
207 200
208 data = g_new0(struct adium_logger_data, 1); 201 data = g_new0(struct adium_logger_data, 1);
1444 1437
1445 read = g_malloc(data->length + 2); 1438 read = g_malloc(data->length + 2);
1446 1439
1447 file = g_fopen(data->path, "rb"); 1440 file = g_fopen(data->path, "rb");
1448 fseek(file, data->offset, SEEK_SET); 1441 fseek(file, data->offset, SEEK_SET);
1449 fread(read, data->length, 1, file); 1442 data->length = fread(read, data->length, 1, file);
1450 fclose(file); 1443 fclose(file);
1451 1444
1452 if (read[data->length-1] == '\n') { 1445 if (read[data->length-1] == '\n') {
1453 read[data->length] = '\0'; 1446 read[data->length] = '\0';
1454 } else { 1447 } else {
1943 g_return_val_if_fail(file != NULL, g_strdup("")); 1936 g_return_val_if_fail(file != NULL, g_strdup(""));
1944 1937
1945 contents = g_malloc(data->length + 2); 1938 contents = g_malloc(data->length + 2);
1946 1939
1947 fseek(file, data->offset, SEEK_SET); 1940 fseek(file, data->offset, SEEK_SET);
1948 fread(contents, data->length, 1, file); 1941 data->length = fread(contents, data->length, 1, file);
1949 fclose(file); 1942 fclose(file);
1950 1943
1951 contents[data->length] = '\n'; 1944 contents[data->length] = '\n';
1952 contents[data->length + 1] = '\0'; 1945 contents[data->length + 1] = '\0';
1953 1946
2092 2085
2093 g_return_if_fail(log != NULL); 2086 g_return_if_fail(log != NULL);
2094 2087
2095 data = log->logger_data; 2088 data = log->logger_data;
2096 2089
2090 g_free(data->path);
2091 g_free(data);
2092 }
2093
2094 /*************************************************************************
2095 * aMSN Logger *
2096 *************************************************************************/
2097
2098 /* The aMSN logger doesn't write logs, only reads them. This is to include
2099 * aMSN logs in the log viewer transparently.
2100 */
2101
2102 static PurpleLogLogger *amsn_logger;
2103
2104 struct amsn_logger_data {
2105 char *path;
2106 int offset;
2107 int length;
2108 };
2109
2110 #define AMSN_LOG_CONV_START "|\"LRED[Conversation started on "
2111 #define AMSN_LOG_CONV_END "|\"LRED[You have closed the window on "
2112 #define AMSN_LOG_CONV_EXTRA "01 Aug 2001 00:00:00]"
2113
2114 /* `log_dir`/username@hotmail.com/logs/buddyname@hotmail.com.log */
2115 /* `log_dir`/username@hotmail.com/logs/Month Year/buddyname@hotmail.com.log */
2116 static GList *amsn_logger_list(PurpleLogType type, const char *sn, PurpleAccount *account)
2117 {
2118 GList *list = NULL;
2119 struct amsn_logger_data *data;
2120 const char *logdir;
2121 char *username;
2122 char *log_path;
2123 char *buddy_log;
2124 char *filename;
2125 GDir *dir;
2126 const char *name;
2127 GError *error;
2128 char *contents;
2129 PurpleLog *log;
2130 GList *files = NULL;
2131 GList *f;
2132
2133 logdir = purple_prefs_get_string("/plugins/core/log_reader/amsn/log_directory");
2134
2135 /* By clearing the log directory path, this logger can be (effectively) disabled. */
2136 if (!logdir || !*logdir)
2137 return NULL;
2138
2139 /* aMSN only works with MSN/WLM */
2140 if (strcmp(account->protocol_id, "prpl-msn"))
2141 return NULL;
2142
2143 username = g_strdup(purple_normalize(account, account->username));
2144 buddy_log = g_strdup_printf("%s.log", purple_normalize(account, sn));
2145 log_path = g_build_filename(logdir, username, "logs", NULL);
2146
2147 /* First check in the top-level */
2148 filename = g_build_filename(log_path, buddy_log, NULL);
2149 if (g_file_test(filename, G_FILE_TEST_EXISTS))
2150 files = g_list_prepend(files, filename);
2151 else
2152 g_free(filename);
2153
2154 /* Check in previous months */
2155 dir = g_dir_open(log_path, 0, NULL);
2156 if (dir) {
2157 while ((name = g_dir_read_name(dir)) != NULL) {
2158 filename = g_build_filename(log_path, name, buddy_log, NULL);
2159 if (g_file_test(filename, G_FILE_TEST_EXISTS))
2160 files = g_list_prepend(files, filename);
2161 else
2162 g_free(filename);
2163 }
2164 g_dir_close(dir);
2165 }
2166
2167 g_free(log_path);
2168
2169 /* New versions use 'friendlier' directory names */
2170 purple_util_chrreplace(username, '@', '_');
2171 purple_util_chrreplace(username, '.', '_');
2172
2173 log_path = g_build_filename(logdir, username, "logs", NULL);
2174
2175 /* First check in the top-level */
2176 filename = g_build_filename(log_path, buddy_log, NULL);
2177 if (g_file_test(filename, G_FILE_TEST_EXISTS))
2178 files = g_list_prepend(files, filename);
2179 else
2180 g_free(filename);
2181
2182 /* Check in previous months */
2183 dir = g_dir_open(log_path, 0, NULL);
2184 if (dir) {
2185 while ((name = g_dir_read_name(dir)) != NULL) {
2186 filename = g_build_filename(log_path, name, buddy_log, NULL);
2187 if (g_file_test(filename, G_FILE_TEST_EXISTS))
2188 files = g_list_prepend(files, filename);
2189 else
2190 g_free(filename);
2191 }
2192 g_dir_close(dir);
2193 }
2194
2195 g_free(log_path);
2196 g_free(username);
2197 g_free(buddy_log);
2198
2199 /* Loop through files looking for logs */
2200 for(f = g_list_first(files); f; f = g_list_next(f)) {
2201 filename = f->data;
2202 purple_debug_info("aMSN logger", "Reading %s\n", filename);
2203 error = NULL;
2204 if (!g_file_get_contents(filename, &contents, NULL, &error)) {
2205 purple_debug_error("aMSN logger",
2206 "Couldn't read file %s: %s \n", filename,
2207 (error && error->message) ?
2208 error->message : "Unknown error");
2209 if (error)
2210 g_error_free(error);
2211 } else {
2212 char *c = contents;
2213 gboolean found_start = FALSE;
2214 char *start_log = c;
2215 int offset = 0;
2216 struct tm tm;
2217 while (c && *c) {
2218 if (purple_str_has_prefix(c, AMSN_LOG_CONV_START)) {
2219 char month[4];
2220 if (sscanf(c + strlen(AMSN_LOG_CONV_START),
2221 "%u %3s %u %u:%u:%u",
2222 &tm.tm_mday, (char*)&month, &tm.tm_year,
2223 &tm.tm_hour, &tm.tm_min, &tm.tm_sec) != 6) {
2224 found_start = FALSE;
2225 purple_debug_error("aMSN logger",
2226 "Error parsing start date for %s\n",
2227 filename);
2228 } else {
2229 const char *months[] = {"Jan", "Feb", "Mar", "Apr", "May", "Jun",
2230 "Jul", "Aug", "Sep", "Oct", "Nov", "Dec", NULL};
2231 tm.tm_year -= 1900;
2232
2233 /* Let the C library deal with
2234 * daylight savings time.
2235 */
2236 tm.tm_isdst = -1;
2237
2238 /* Ugly hack, in case current locale
2239 * is not English. This code is taken
2240 * from log.c.
2241 */
2242 for (tm.tm_mon = 0; months[tm.tm_mon]; tm.tm_mon++) {
2243 if (strcmp(month, months[tm.tm_mon]) == 0)
2244 break;
2245 }
2246 found_start = TRUE;
2247 offset = c - contents;
2248 start_log = c;
2249 }
2250 } else if (purple_str_has_prefix(c, AMSN_LOG_CONV_END) && found_start) {
2251 data = g_new0(struct amsn_logger_data, 1);
2252 data->path = g_strdup(filename);
2253 data->offset = offset;
2254 data->length = c - start_log
2255 + strlen(AMSN_LOG_CONV_END)
2256 + strlen(AMSN_LOG_CONV_EXTRA);
2257 log = purple_log_new(PURPLE_LOG_IM, sn, account, NULL, mktime(&tm), NULL);
2258 log->logger = amsn_logger;
2259 log->logger_data = data;
2260 list = g_list_prepend(list, log);
2261 found_start = FALSE;
2262
2263 purple_debug_info("aMSN logger",
2264 "Found log for %s:"
2265 " path = (%s),"
2266 " offset = (%d),"
2267 " length = (%d)\n",
2268 sn, data->path, data->offset, data->length);
2269 }
2270 c = strstr(c, "\n");
2271 c++;
2272 }
2273
2274 /* I've seen the file end without the AMSN_LOG_CONV_END bit */
2275 if (found_start) {
2276 data = g_new0(struct amsn_logger_data, 1);
2277 data->path = g_strdup(filename);
2278 data->offset = offset;
2279 data->length = c - start_log
2280 + strlen(AMSN_LOG_CONV_END)
2281 + strlen(AMSN_LOG_CONV_EXTRA);
2282 log = purple_log_new(PURPLE_LOG_IM, sn, account, NULL, mktime(&tm), NULL);
2283 log->logger = amsn_logger;
2284 log->logger_data = data;
2285 list = g_list_prepend(list, log);
2286 found_start = FALSE;
2287
2288 purple_debug_info("aMSN logger",
2289 "Found log for %s:"
2290 " path = (%s),"
2291 " offset = (%d),"
2292 " length = (%d)\n",
2293 sn, data->path, data->offset, data->length);
2294 }
2295 g_free(contents);
2296 }
2297 g_free(filename);
2298 }
2299
2300 g_list_free(files);
2301
2302 return list;
2303 }
2304
2305 /* Really it's |"L, but the string's been escaped */
2306 #define AMSN_LOG_FORMAT_TAG "|&quot;L"
2307
2308 static char *amsn_logger_read(PurpleLog *log, PurpleLogReadFlags *flags)
2309 {
2310 struct amsn_logger_data *data;
2311 FILE *file;
2312 char *contents;
2313 char *escaped;
2314 GString *formatted;
2315 char *start;
2316 gboolean in_span = FALSE;
2317
2318 if (flags != NULL)
2319 *flags = PURPLE_LOG_READ_NO_NEWLINE;
2320
2321 g_return_val_if_fail(log != NULL, g_strdup(""));
2322
2323 data = log->logger_data;
2324
2325 g_return_val_if_fail(data->path != NULL, g_strdup(""));
2326 g_return_val_if_fail(data->length > 0, g_strdup(""));
2327
2328 contents = g_malloc(data->length + 2);
2329
2330 file = g_fopen(data->path, "rb");
2331 g_return_val_if_fail(file != NULL, g_strdup(""));
2332
2333 fseek(file, data->offset, SEEK_SET);
2334 data->length = fread(contents, data->length, 1, file);
2335 fclose(file);
2336
2337 contents[data->length] = '\n';
2338 contents[data->length + 1] = '\0';
2339
2340 escaped = g_markup_escape_text(contents, -1);
2341 g_free(contents);
2342 contents = escaped;
2343
2344 formatted = g_string_sized_new(data->length + 2);
2345
2346 start = contents;
2347 while (start && *start) {
2348 char *end;
2349 char *old_tag;
2350 char *tag;
2351 end = strstr(start, "\n");
2352 if (!end)
2353 break;
2354 *end = '\0';
2355 if (purple_str_has_prefix(start, AMSN_LOG_FORMAT_TAG) && in_span) {
2356 /* New format for this line */
2357 g_string_append(formatted, "</span><br>");
2358 in_span = FALSE;
2359 } else if (start != contents) {
2360 /* Continue format from previous line */
2361 g_string_append(formatted, "<br>");
2362 }
2363 old_tag = start;
2364 tag = strstr(start, AMSN_LOG_FORMAT_TAG);
2365 while (tag) {
2366 g_string_append_len(formatted, old_tag, tag - old_tag);
2367 tag += strlen(AMSN_LOG_FORMAT_TAG);
2368 if (in_span) {
2369 g_string_append(formatted, "</span>");
2370 in_span = FALSE;
2371 }
2372 if (*tag == 'C') {
2373 /* |"LCxxxxxx is a hex colour */
2374 char colour[7];
2375 strncpy(colour, tag + 1, 6);
2376 colour[6] = '\0';
2377 g_string_append_printf(formatted, "<span style=\"color: #%s;\">", colour);
2378 /* This doesn't appear to work? */
2379 /* g_string_append_printf(formatted, "<span style=\"color: #%6s;\">", tag + 1); */
2380 in_span = TRUE;
2381 old_tag = tag + 7; /* C + xxxxxx */
2382 } else {
2383 /* |"Lxxx is a 3-digit colour code */
2384 if (purple_str_has_prefix(tag, "RED")) {
2385 g_string_append(formatted, "<span style=\"color: red;\">");
2386 in_span = TRUE;
2387 } else if (purple_str_has_prefix(tag, "GRA")) {
2388 g_string_append(formatted, "<span style=\"color: gray;\">");
2389 in_span = TRUE;
2390 } else if (purple_str_has_prefix(tag, "NOR")) {
2391 g_string_append(formatted, "<span style=\"color: black;\">");
2392 in_span = TRUE;
2393 } else if (purple_str_has_prefix(tag, "ITA")) {
2394 g_string_append(formatted, "<span style=\"color: blue;\">");
2395 in_span = TRUE;
2396 } else if (purple_str_has_prefix(tag, "GRE")) {
2397 g_string_append(formatted, "<span style=\"color: darkgreen;\">");
2398 in_span = TRUE;
2399 } else {
2400 purple_debug_info("aMSN logger", "Unknown colour format: %3s\n", tag);
2401 }
2402 old_tag = tag + 3;
2403 }
2404 tag = strstr(tag, AMSN_LOG_FORMAT_TAG);
2405 }
2406 g_string_append(formatted, old_tag);
2407 start = end + 1;
2408 }
2409 if (in_span)
2410 g_string_append(formatted, "</span>");
2411
2412 g_free(contents);
2413
2414 return g_string_free(formatted, FALSE);
2415 }
2416
2417 static int amsn_logger_size(PurpleLog *log)
2418 {
2419 struct amsn_logger_data *data;
2420 char *text;
2421 int size;
2422
2423 g_return_val_if_fail(log != NULL, 0);
2424
2425 data = log->logger_data;
2426
2427 if (purple_prefs_get_bool("/plugins/core/log_reader/fast_sizes")) {
2428 return data ? data->length : 0;
2429 }
2430
2431 text = amsn_logger_read(log, NULL);
2432 size = strlen(text);
2433 g_free(text);
2434
2435 return size;
2436 }
2437
2438 static void amsn_logger_finalize(PurpleLog *log)
2439 {
2440 struct amsn_logger_data *data;
2441
2442 g_return_if_fail(log != NULL);
2443
2444 data = log->logger_data;
2097 g_free(data->path); 2445 g_free(data->path);
2098 g_free(data); 2446 g_free(data);
2099 } 2447 }
2100 2448
2101 /***************************************************************************** 2449 /*****************************************************************************
2345 path = g_build_filename(PURPLE_LOG_READER_WINDOWS_MOUNT_POINT, 2693 path = g_build_filename(PURPLE_LOG_READER_WINDOWS_MOUNT_POINT,
2346 "Program Files", "QIP", "Users", NULL); 2694 "Program Files", "QIP", "Users", NULL);
2347 #endif 2695 #endif
2348 purple_prefs_add_string("/plugins/core/log_reader/qip/log_directory", path ? path : ""); 2696 purple_prefs_add_string("/plugins/core/log_reader/qip/log_directory", path ? path : "");
2349 g_free(path); 2697 g_free(path);
2698
2699 /* Add aMSN Messenger log directory preference. */
2700 purple_prefs_add_none("/plugins/core/log_reader/amsn");
2701
2702 /* Calculate default aMSN log directory. */
2703 #ifdef _WIN32
2704 folder = wpurple_get_special_folder(CSIDL_PROFILE); /* Silly aMSN, not using CSIDL_APPDATA */
2705 path = g_build_filename(folder, "amsn", NULL);
2706 #else
2707 path = g_build_filename(purple_home_dir(), ".amsn", NULL);
2708 #endif
2709 purple_prefs_add_string("/plugins/core/log_reader/amsn/log_directory", path);
2710 g_free(path);
2350 } 2711 }
2351 2712
2352 static gboolean 2713 static gboolean
2353 plugin_load(PurplePlugin *plugin) 2714 plugin_load(PurplePlugin *plugin)
2354 { 2715 {
2427 trillian_logger_list, 2788 trillian_logger_list,
2428 trillian_logger_read, 2789 trillian_logger_read,
2429 trillian_logger_size); 2790 trillian_logger_size);
2430 purple_log_logger_add(trillian_logger); 2791 purple_log_logger_add(trillian_logger);
2431 2792
2793 /* The names of IM clients are marked for translation at the request of
2794 translators who wanted to transliterate them. Many translators
2795 choose to leave them alone. Choose what's best for your language. */
2796 amsn_logger = purple_log_logger_new("amsn", _("aMSN"), 6,
2797 NULL,
2798 NULL,
2799 amsn_logger_finalize,
2800 amsn_logger_list,
2801 amsn_logger_read,
2802 amsn_logger_size);
2803 purple_log_logger_add(amsn_logger);
2804
2432 return TRUE; 2805 return TRUE;
2433 } 2806 }
2434 2807
2435 static gboolean 2808 static gboolean
2436 plugin_unload(PurplePlugin *plugin) 2809 plugin_unload(PurplePlugin *plugin)
2443 purple_log_logger_remove(messenger_plus_logger); 2816 purple_log_logger_remove(messenger_plus_logger);
2444 #endif 2817 #endif
2445 purple_log_logger_remove(msn_logger); 2818 purple_log_logger_remove(msn_logger);
2446 purple_log_logger_remove(trillian_logger); 2819 purple_log_logger_remove(trillian_logger);
2447 purple_log_logger_remove(qip_logger); 2820 purple_log_logger_remove(qip_logger);
2821 purple_log_logger_remove(amsn_logger);
2448 2822
2449 return TRUE; 2823 return TRUE;
2450 } 2824 }
2451 2825
2452 static PurplePluginPrefFrame * 2826 static PurplePluginPrefFrame *
2501 "/plugins/core/log_reader/msn/log_directory", _("MSN Messenger")); 2875 "/plugins/core/log_reader/msn/log_directory", _("MSN Messenger"));
2502 purple_plugin_pref_frame_add(frame, ppref); 2876 purple_plugin_pref_frame_add(frame, ppref);
2503 2877
2504 ppref = purple_plugin_pref_new_with_name_and_label( 2878 ppref = purple_plugin_pref_new_with_name_and_label(
2505 "/plugins/core/log_reader/trillian/log_directory", _("Trillian")); 2879 "/plugins/core/log_reader/trillian/log_directory", _("Trillian"));
2880 purple_plugin_pref_frame_add(frame, ppref);
2881
2882 ppref = purple_plugin_pref_new_with_name_and_label(
2883 "/plugins/core/log_reader/amsn/log_directory", _("aMSN"));
2506 purple_plugin_pref_frame_add(frame, ppref); 2884 purple_plugin_pref_frame_add(frame, ppref);
2507 2885
2508 return frame; 2886 return frame;
2509 } 2887 }
2510 2888