Mercurial > ~mikael > mcabber > hg
annotate mcabber/src/histolog.c @ 830:80434fde7cfa
Display presence notification timestamps when they exist
These timestamps were used in the roster, but not in the buffer window
message nor in the history logfile. Reported by "ze".
author | Mikael Berthe <mikael@lilotux.net> |
---|---|
date | Wed, 03 May 2006 11:28:41 +0200 |
parents | b16acadd7d53 |
children | a833f3d6119a |
rev | line source |
---|---|
110 | 1 /* |
699 | 2 * histolog.c -- File history handling |
395
3e4f2f98c0bf
Whitespace cleanup (histolog.c)
Mikael Berthe <mikael@lilotux.net>
parents:
394
diff
changeset
|
3 * |
699 | 4 * Copyright (C) 2005, 2006 Mikael Berthe <bmikael@lists.lilotux.net> |
110 | 5 * |
6 * This program is free software; you can redistribute it and/or modify | |
7 * it under the terms of the GNU General Public License as published by | |
8 * the Free Software Foundation; either version 2 of the License, or (at | |
9 * your option) any later version. | |
10 * | |
11 * This program is distributed in the hope that it will be useful, but | |
12 * WITHOUT ANY WARRANTY; without even the implied warranty of | |
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
14 * General Public License for more details. | |
15 * | |
16 * You should have received a copy of the GNU General Public License | |
17 * along with this program; if not, write to the Free Software | |
18 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 | |
19 * USA | |
20 */ | |
21 | |
22 #include <string.h> | |
23 #include <stdlib.h> | |
24 #include <time.h> | |
113 | 25 #include <ctype.h> |
26 #include <sys/types.h> | |
27 #include <sys/stat.h> | |
28 #include <fcntl.h> | |
110 | 29 |
30 #include "histolog.h" | |
178 | 31 #include "hbuf.h" |
113 | 32 #include "jabglue.h" |
241 | 33 #include "utils.h" |
378
2e6c7b1440d1
Improve debugging/logging
Mikael Berthe <mikael@lilotux.net>
parents:
374
diff
changeset
|
34 #include "logprint.h" |
635
d4119cb85aeb
New "load_muc_logs" option
Mikael Berthe <mikael@lilotux.net>
parents:
580
diff
changeset
|
35 #include "settings.h" |
727
1c3620668857
Expand tabs when reading history files
Mikael Berthe <mikael@lilotux.net>
parents:
699
diff
changeset
|
36 #include "utils.h" |
110 | 37 |
38 static guint UseFileLogging; | |
177 | 39 static guint FileLoadLogs; |
110 | 40 static char *RootDir; |
41 | |
42 | |
43 // user_histo_file() | |
44 // Returns history filename for the given jid | |
45 // Note: the caller *must* free the filename after use (if not null). | |
111 | 46 static char *user_histo_file(const char *jid) |
110 | 47 { |
48 char *filename; | |
186 | 49 char *lowerid, *p; |
178 | 50 if (!UseFileLogging && !FileLoadLogs) return NULL; |
110 | 51 |
186 | 52 lowerid = g_strdup(jid); |
53 for (p=lowerid; *p ; p++) | |
54 *p = tolower(*p); | |
55 | |
110 | 56 filename = g_new(char, strlen(RootDir) + strlen(jid) + 1); |
57 strcpy(filename, RootDir); | |
186 | 58 strcat(filename, lowerid); |
59 g_free(lowerid); | |
110 | 60 return filename; |
61 } | |
62 | |
178 | 63 // write_histo_line() |
110 | 64 // Adds a history (multi-)line to the jid's history logfile |
113 | 65 static void write_histo_line(const char *jid, |
66 time_t timestamp, guchar type, guchar info, const char *data) | |
110 | 67 { |
68 guint len = 0; | |
113 | 69 FILE *fp; |
110 | 70 time_t ts; |
113 | 71 const char *p; |
178 | 72 char *filename; |
241 | 73 char str_ts[20]; |
273 | 74 int err; |
110 | 75 |
178 | 76 if (!UseFileLogging) return; |
77 | |
78 filename = user_histo_file(jid); | |
110 | 79 |
80 // If timestamp is null, get current date | |
81 if (timestamp) | |
82 ts = timestamp; | |
83 else | |
84 time(&ts); | |
85 | |
86 if (!data) | |
87 data = ""; | |
88 | |
89 // Count number of extra lines | |
90 for (p=data ; *p ; p++) | |
91 if (*p == '\n') len++; | |
92 | |
241 | 93 /* Line format: "TI yyyymmddThh:mm:ssZ [data]" |
94 * (Old format: "TI DDDDDDDDDD LLL [data])" | |
95 * T=Type, I=Info, yyyymmddThh:mm:ssZ=date, LLL=0-padded-len | |
110 | 96 * |
97 * Types: | |
98 * - M message Info: S (send) R (receive) | |
352 | 99 * - S status Info: [_oifdna] |
110 | 100 * We don't check them, we'll trust the caller. |
101 */ | |
102 | |
113 | 103 fp = fopen(filename, "a"); |
155 | 104 g_free(filename); |
273 | 105 if (!fp) { |
374
bd5638c21834
Improve logging system (traces)
Mikael Berthe <mikael@lilotux.net>
parents:
366
diff
changeset
|
106 scr_LogPrint(LPRINT_LOGNORM, "Unable to write history " |
bd5638c21834
Improve logging system (traces)
Mikael Berthe <mikael@lilotux.net>
parents:
366
diff
changeset
|
107 "(cannot open logfile)"); |
273 | 108 return; |
109 } | |
178 | 110 |
241 | 111 to_iso8601(str_ts, ts); |
273 | 112 err = fprintf(fp, "%c%c %-18.18s %03d %s\n", type, info, str_ts, len, data); |
113 | 113 fclose(fp); |
273 | 114 if (err < 0) { |
374
bd5638c21834
Improve logging system (traces)
Mikael Berthe <mikael@lilotux.net>
parents:
366
diff
changeset
|
115 scr_LogPrint(LPRINT_LOGNORM, "Error while writing to log file: %s", |
bd5638c21834
Improve logging system (traces)
Mikael Berthe <mikael@lilotux.net>
parents:
366
diff
changeset
|
116 strerror(errno)); |
273 | 117 } |
110 | 118 } |
119 | |
178 | 120 // hlog_read_history() |
121 // Reads the jid's history logfile | |
122 void hlog_read_history(const char *jid, GList **p_buddyhbuf, guint width) | |
123 { | |
124 char *filename; | |
125 guchar type, info; | |
126 char *data, *tail; | |
727
1c3620668857
Expand tabs when reading history files
Mikael Berthe <mikael@lilotux.net>
parents:
699
diff
changeset
|
127 char *xtext; |
184 | 128 time_t timestamp; |
129 guint prefix_flags; | |
178 | 130 guint len; |
131 FILE *fp; | |
197 | 132 struct stat bufstat; |
193 | 133 guint err = 0; |
248 | 134 guint ln = 0; // line number |
178 | 135 |
136 if (!FileLoadLogs) return; | |
137 | |
635
d4119cb85aeb
New "load_muc_logs" option
Mikael Berthe <mikael@lilotux.net>
parents:
580
diff
changeset
|
138 if ((roster_gettype(jid) & ROSTER_TYPE_ROOM) && |
d4119cb85aeb
New "load_muc_logs" option
Mikael Berthe <mikael@lilotux.net>
parents:
580
diff
changeset
|
139 (settings_opt_get_int("load_muc_logs") != 1)) |
d4119cb85aeb
New "load_muc_logs" option
Mikael Berthe <mikael@lilotux.net>
parents:
580
diff
changeset
|
140 return; |
d4119cb85aeb
New "load_muc_logs" option
Mikael Berthe <mikael@lilotux.net>
parents:
580
diff
changeset
|
141 |
178 | 142 data = g_new(char, HBB_BLOCKSIZE+32); |
143 if (!data) { | |
374
bd5638c21834
Improve logging system (traces)
Mikael Berthe <mikael@lilotux.net>
parents:
366
diff
changeset
|
144 scr_LogPrint(LPRINT_LOGNORM, "Not enough memory to read history file"); |
178 | 145 return; |
146 } | |
147 | |
148 filename = user_histo_file(jid); | |
149 | |
150 fp = fopen(filename, "r"); | |
151 g_free(filename); | |
152 if (!fp) { g_free(data); return; } | |
153 | |
394
4617d0f029ea
Update "large file" size value to 3MB
Mikael Berthe <mikael@lilotux.net>
parents:
378
diff
changeset
|
154 // If file is large (> 3MB here), display a message to inform the user |
197 | 155 // (it can take a while...) |
156 if (!fstat(fileno(fp), &bufstat)) { | |
394
4617d0f029ea
Update "large file" size value to 3MB
Mikael Berthe <mikael@lilotux.net>
parents:
378
diff
changeset
|
157 if (bufstat.st_size > 3145728) |
4617d0f029ea
Update "large file" size value to 3MB
Mikael Berthe <mikael@lilotux.net>
parents:
378
diff
changeset
|
158 scr_LogPrint(LPRINT_LOGNORM, "Reading <%s> history file...", jid); |
197 | 159 } |
160 | |
178 | 161 /* See write_histo_line() for line format... */ |
162 while (!feof(fp)) { | |
795
b16acadd7d53
Improve support for long messages
Mikael Berthe <mikael@lilotux.net>
parents:
774
diff
changeset
|
163 guint dataoffset = 25; |
259 | 164 if (fgets(data, HBB_BLOCKSIZE+27, fp) == NULL) break; |
248 | 165 ln++; |
178 | 166 |
167 for (tail = data; *tail; tail++) ; | |
168 | |
169 type = data[0]; | |
170 info = data[1]; | |
241 | 171 |
395
3e4f2f98c0bf
Whitespace cleanup (histolog.c)
Mikael Berthe <mikael@lilotux.net>
parents:
394
diff
changeset
|
172 if ((type != 'M' && type != 'S') || |
259 | 173 ((data[11] != 'T') || (data[20] != 'Z') || |
795
b16acadd7d53
Improve support for long messages
Mikael Berthe <mikael@lilotux.net>
parents:
774
diff
changeset
|
174 (data[21] != ' ') || |
b16acadd7d53
Improve support for long messages
Mikael Berthe <mikael@lilotux.net>
parents:
774
diff
changeset
|
175 (data[25] != ' ' && data[26] != ' '))) { |
193 | 176 if (!err) { |
795
b16acadd7d53
Improve support for long messages
Mikael Berthe <mikael@lilotux.net>
parents:
774
diff
changeset
|
177 scr_LogPrint(LPRINT_LOGNORM, |
b16acadd7d53
Improve support for long messages
Mikael Berthe <mikael@lilotux.net>
parents:
774
diff
changeset
|
178 "Error in history file format (%s), l.%u", jid, ln); |
193 | 179 err = 1; |
180 } | |
186 | 181 continue; |
178 | 182 } |
795
b16acadd7d53
Improve support for long messages
Mikael Berthe <mikael@lilotux.net>
parents:
774
diff
changeset
|
183 // The number of lines can be written with 3 or 4 bytes. |
b16acadd7d53
Improve support for long messages
Mikael Berthe <mikael@lilotux.net>
parents:
774
diff
changeset
|
184 if (data[25] != ' ') dataoffset = 26; |
b16acadd7d53
Improve support for long messages
Mikael Berthe <mikael@lilotux.net>
parents:
774
diff
changeset
|
185 data[21] = data[dataoffset] = 0; |
259 | 186 timestamp = from_iso8601(&data[3], 1); |
187 len = (guint) atoi(&data[22]); | |
395
3e4f2f98c0bf
Whitespace cleanup (histolog.c)
Mikael Berthe <mikael@lilotux.net>
parents:
394
diff
changeset
|
188 |
178 | 189 // Some checks |
190 if (((type == 'M') && (info != 'S' && info != 'R')) || | |
277
4d7040cff8ee
Remove busy/occupied status, which does not really exist
Mikael Berthe <mikael@lilotux.net>
parents:
273
diff
changeset
|
191 ((type == 'I') && (!strchr("OAIFDN", info)))) { |
193 | 192 if (!err) { |
374
bd5638c21834
Improve logging system (traces)
Mikael Berthe <mikael@lilotux.net>
parents:
366
diff
changeset
|
193 scr_LogPrint(LPRINT_LOGNORM, "Error in history file format (%s), l.%u", |
bd5638c21834
Improve logging system (traces)
Mikael Berthe <mikael@lilotux.net>
parents:
366
diff
changeset
|
194 jid, ln); |
193 | 195 err = 1; |
196 } | |
186 | 197 continue; |
178 | 198 } |
199 | |
197 | 200 // XXX This will fail when a message is too big |
178 | 201 while (len--) { |
248 | 202 ln++; |
259 | 203 if (fgets(tail, HBB_BLOCKSIZE+27 - (tail-data), fp) == NULL) |
249 | 204 break; |
178 | 205 |
206 while (*tail) tail++; | |
207 } | |
248 | 208 // Small check for too long messages |
795
b16acadd7d53
Improve support for long messages
Mikael Berthe <mikael@lilotux.net>
parents:
774
diff
changeset
|
209 if (tail >= HBB_BLOCKSIZE+dataoffset+1 + data) { |
249 | 210 // Maybe we will have a parse error on next, because this |
211 // message is big (maybe too big). | |
374
bd5638c21834
Improve logging system (traces)
Mikael Berthe <mikael@lilotux.net>
parents:
366
diff
changeset
|
212 scr_LogPrint(LPRINT_LOGNORM, "A message could be too big " |
bd5638c21834
Improve logging system (traces)
Mikael Berthe <mikael@lilotux.net>
parents:
366
diff
changeset
|
213 "in history file..."); |
248 | 214 } |
251 | 215 // Remove last CR (we keep it if the line is empty, too) |
795
b16acadd7d53
Improve support for long messages
Mikael Berthe <mikael@lilotux.net>
parents:
774
diff
changeset
|
216 if ((tail > data+dataoffset+1) && (*(tail-1) == '\n')) |
178 | 217 *(tail-1) = 0; |
218 | |
219 if (type == 'M') { | |
772
464be13343a9
Store most data in UTF-8 internally
Mikael Berthe <mikael@lilotux.net>
parents:
729
diff
changeset
|
220 char *converted; |
178 | 221 if (info == 'S') |
729
39f67cade02c
Use bold font for outgoing messages
Mikael Berthe <mikael@lilotux.net>
parents:
727
diff
changeset
|
222 prefix_flags = HBB_PREFIX_OUT | HBB_PREFIX_HLIGHT; |
178 | 223 else |
184 | 224 prefix_flags = HBB_PREFIX_IN; |
795
b16acadd7d53
Improve support for long messages
Mikael Berthe <mikael@lilotux.net>
parents:
774
diff
changeset
|
225 converted = from_utf8(&data[dataoffset+1]); |
772
464be13343a9
Store most data in UTF-8 internally
Mikael Berthe <mikael@lilotux.net>
parents:
729
diff
changeset
|
226 if (converted) { |
464be13343a9
Store most data in UTF-8 internally
Mikael Berthe <mikael@lilotux.net>
parents:
729
diff
changeset
|
227 xtext = ut_expand_tabs(converted); // Expand tabs |
464be13343a9
Store most data in UTF-8 internally
Mikael Berthe <mikael@lilotux.net>
parents:
729
diff
changeset
|
228 hbuf_add_line(p_buddyhbuf, xtext, timestamp, prefix_flags, width); |
464be13343a9
Store most data in UTF-8 internally
Mikael Berthe <mikael@lilotux.net>
parents:
729
diff
changeset
|
229 if (xtext != converted) |
464be13343a9
Store most data in UTF-8 internally
Mikael Berthe <mikael@lilotux.net>
parents:
729
diff
changeset
|
230 g_free(xtext); |
464be13343a9
Store most data in UTF-8 internally
Mikael Berthe <mikael@lilotux.net>
parents:
729
diff
changeset
|
231 g_free(converted); |
464be13343a9
Store most data in UTF-8 internally
Mikael Berthe <mikael@lilotux.net>
parents:
729
diff
changeset
|
232 } |
193 | 233 err = 0; |
178 | 234 } |
235 } | |
236 fclose(fp); | |
237 g_free(data); | |
238 } | |
239 | |
110 | 240 // hlog_enable() |
241 // Enable logging to files. If root_dir is NULL, then $HOME/.mcabber is used. | |
177 | 242 // If loadfiles is TRUE, we will try to load buddies history logs from file. |
281
f562b9af2de7
Add "const" specifier in prototypes
Mikael Berthe <mikael@lilotux.net>
parents:
277
diff
changeset
|
243 void hlog_enable(guint enable, const char *root_dir, guint loadfiles) |
110 | 244 { |
245 UseFileLogging = enable; | |
177 | 246 FileLoadLogs = loadfiles; |
110 | 247 |
177 | 248 if (enable || loadfiles) { |
110 | 249 if (root_dir) { |
250 int l = strlen(root_dir); | |
251 if (l < 1) { | |
374
bd5638c21834
Improve logging system (traces)
Mikael Berthe <mikael@lilotux.net>
parents:
366
diff
changeset
|
252 scr_LogPrint(LPRINT_LOGNORM, "Error: logging dir name too short"); |
362
d8f147d6e872
Check directory and config file permissions
Mikael Berthe <mikael@lilotux.net>
parents:
352
diff
changeset
|
253 UseFileLogging = FileLoadLogs = FALSE; |
110 | 254 return; |
255 } | |
256 // RootDir must be slash-terminated | |
257 if (root_dir[l-1] == '/') | |
258 RootDir = g_strdup(root_dir); | |
259 else { | |
260 RootDir = g_new(char, l+2); | |
261 strcpy(RootDir, root_dir); | |
262 strcat(RootDir, "/"); | |
263 } | |
264 } else { | |
265 char *home = getenv("HOME"); | |
169 | 266 char *dir = "/.mcabber/histo/"; |
110 | 267 RootDir = g_new(char, strlen(home) + strlen(dir) + 1); |
268 strcpy(RootDir, home); | |
269 strcat(RootDir, dir); | |
270 } | |
362
d8f147d6e872
Check directory and config file permissions
Mikael Berthe <mikael@lilotux.net>
parents:
352
diff
changeset
|
271 // Check directory permissions (should not be readable by group/others) |
d8f147d6e872
Check directory and config file permissions
Mikael Berthe <mikael@lilotux.net>
parents:
352
diff
changeset
|
272 if (checkset_perm(RootDir, TRUE) == -1) { |
d8f147d6e872
Check directory and config file permissions
Mikael Berthe <mikael@lilotux.net>
parents:
352
diff
changeset
|
273 // The directory does not actually exists |
d8f147d6e872
Check directory and config file permissions
Mikael Berthe <mikael@lilotux.net>
parents:
352
diff
changeset
|
274 g_free(RootDir); |
374
bd5638c21834
Improve logging system (traces)
Mikael Berthe <mikael@lilotux.net>
parents:
366
diff
changeset
|
275 scr_LogPrint(LPRINT_LOGNORM, "ERROR: Cannot access " |
bd5638c21834
Improve logging system (traces)
Mikael Berthe <mikael@lilotux.net>
parents:
366
diff
changeset
|
276 "history log directory, logging DISABLED"); |
362
d8f147d6e872
Check directory and config file permissions
Mikael Berthe <mikael@lilotux.net>
parents:
352
diff
changeset
|
277 UseFileLogging = FileLoadLogs = FALSE; |
d8f147d6e872
Check directory and config file permissions
Mikael Berthe <mikael@lilotux.net>
parents:
352
diff
changeset
|
278 } |
d8f147d6e872
Check directory and config file permissions
Mikael Berthe <mikael@lilotux.net>
parents:
352
diff
changeset
|
279 } else { // Disable history logging |
774
46304b773a44
Remove useless checks before g_free() calls
Mikael Berthe <mikael@lilotux.net>
parents:
772
diff
changeset
|
280 g_free(RootDir); |
110 | 281 } |
282 } | |
283 | |
113 | 284 inline void hlog_write_message(const char *jid, time_t timestamp, int sent, |
285 const char *msg) | |
286 { | |
287 write_histo_line(jid, timestamp, 'M', ((sent) ? 'S' : 'R'), msg); | |
288 } | |
289 | |
290 inline void hlog_write_status(const char *jid, time_t timestamp, | |
221 | 291 enum imstatus status, const char *status_msg) |
113 | 292 { |
293 // #1 XXX Check status value? | |
294 // #2 We could add a user-readable comment | |
295 write_histo_line(jid, timestamp, 'S', toupper(imstatus2char[status]), | |
221 | 296 status_msg); |
113 | 297 } |
298 | |
580 | 299 /* vim: set expandtab cindent cinoptions=>2\:2(0: For Vim users... */ |