103e9f1f16a3af6e73cdb4b6cdea16552929cf5c
[claws.git] / src / common / log.c
1 /*
2  * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client
3  * Copyright (C) 1999-2012 Hiroyuki Yamamoto and the Claws Mail team
4  *
5  * This program is free software; you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation; either version 3 of the License, or
8  * (at your option) any later version.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with this program. If not, see <http://www.gnu.org/licenses/>.
17  * 
18  */
19
20 #ifdef HAVE_CONFIG_H
21 #  include "config.h"
22 #include "claws-features.h"
23 #endif
24
25 #include "defs.h"
26
27 #ifdef G_OS_WIN32
28 #  include <w32lib.h>
29 #endif
30
31 #include <stdio.h>
32 #include <glib.h>
33 #include <glib/gi18n.h>
34
35 #include "utils.h"
36 #include "log.h"
37 #include "hooks.h"
38
39 #define FWRITE(_b,_s,_n,_f)     if (fwrite(_b,_s,_n,_f) != _n) { \
40                                         g_message("log fwrite failed!\n"); \
41                                         return; \
42                                 }
43 #define FPUTS(_b,_f)            if (fputs(_b,_f) == EOF) { \
44                                         g_message("log fputs failed!\n"); \
45                                         return; \
46                                 }
47 #define FFLUSH(_f)              if (fflush(_f) != 0) { \
48                                         g_message("log fflush failed!\n"); \
49                                         return; \
50                                 }
51
52 static FILE *log_fp[LOG_INSTANCE_MAX] = {
53         NULL,
54         NULL
55 };
56
57 static size_t log_size[LOG_INSTANCE_MAX] = {
58         0,
59         0
60 };
61
62 static gchar *log_filename[LOG_INSTANCE_MAX] = {
63         NULL,
64         NULL
65 };
66
67 typedef struct _LogInstanceData LogInstanceData;
68
69 struct _LogInstanceData {
70         const char *hook;
71         gchar *title;
72         int *prefs_logwin_width;
73         int *prefs_logwin_height;
74 };
75
76 static LogInstanceData log_instances[LOG_INSTANCE_MAX] = {
77         { LOG_APPEND_TEXT_HOOKLIST, NULL, NULL, NULL },
78         { DEBUG_FILTERING_APPEND_TEXT_HOOKLIST, NULL, NULL, NULL }
79 };
80
81 gboolean prefs_common_enable_log_standard(void);
82 gboolean prefs_common_enable_log_warning(void);
83 gboolean prefs_common_enable_log_error(void);
84 gboolean prefs_common_enable_log_status(void);
85
86 static gboolean invoke_hook_cb (gpointer data)
87 {
88         LogText *logtext = (LogText *)data;
89         hooks_invoke(get_log_hook(logtext->instance), logtext);
90         g_free(logtext->text);
91         g_free(logtext);
92         return FALSE;
93 }
94
95 void set_log_file(LogInstance instance, const gchar *filename)
96 {
97         gchar *fullname = NULL;
98         if (log_fp[instance])
99                 return;
100
101         if (!g_path_is_absolute(filename)) {
102                 fullname = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
103                                         filename, NULL);
104         } else {
105                 fullname = g_strdup(filename);
106         }
107         /* backup old logfile if existing */
108         if (is_file_exist(fullname)) {
109                 gchar *backupname;
110                 
111                 backupname = g_strconcat(fullname, ".bak", NULL);
112                 claws_unlink(backupname);
113                 if (g_rename(fullname, backupname) < 0)
114                         FILE_OP_ERROR(fullname, "rename");
115                 g_free(backupname);
116         }
117
118         log_fp[instance] = g_fopen(fullname, "wb");
119         if (!log_fp[instance]) {
120                 FILE_OP_ERROR(fullname, "fopen");
121                 log_filename[instance] = NULL;
122                 g_free(fullname);
123                 return;
124         }
125         log_filename[instance] = g_strdup(fullname);
126         log_size[instance] = 0;
127         g_free(fullname);
128 }
129
130 void close_log_file(LogInstance instance)
131 {
132         if (log_fp[instance]) {
133                 fclose(log_fp[instance]);
134                 log_fp[instance] = NULL;
135                 log_size[instance] = 0;
136                 g_free(log_filename[instance]);
137                 log_filename[instance] = NULL;
138         }
139 }
140
141 static void rotate_log(LogInstance instance)
142 {
143         if (log_size[instance] > 10 * 1024* 1024) {
144                 gchar *filename = g_strdup(log_filename[instance]);
145                 debug_print("rotating %s\n", filename);
146                 close_log_file(instance);
147                 set_log_file(instance, filename);
148                 g_free(filename);
149         }
150 }
151
152 const char *get_log_hook(LogInstance instance)
153 {
154         return log_instances[instance].hook;
155 }
156
157 void set_log_title(LogInstance instance, gchar *title)
158 {
159         log_instances[instance].title = title;
160 }
161
162 gchar *get_log_title(LogInstance instance)
163 {
164         return log_instances[instance].title;
165 }
166
167 void set_log_prefs(LogInstance instance, int* logwin_width, int* logwin_height)
168 {
169         log_instances[instance].prefs_logwin_width = logwin_width;
170         log_instances[instance].prefs_logwin_height = logwin_height;
171 }
172
173 void get_log_prefs(LogInstance instance, int** logwin_width, int** logwin_height)
174 {
175         if (logwin_width)
176                 *logwin_width = log_instances[instance].prefs_logwin_width;
177         if (logwin_height)
178                 *logwin_height = log_instances[instance].prefs_logwin_height;
179 }
180
181 void log_print(LogInstance instance, const gchar *format, ...)
182 {
183         va_list args;
184         gchar buf[BUFFSIZE + LOG_TIME_LEN];
185         time_t t;
186         LogText *logtext = g_new0(LogText, 1);
187         struct tm buft;
188
189         time(&t);
190         strftime(buf, LOG_TIME_LEN + 1, "[%H:%M:%S] ", localtime_r(&t, &buft));
191
192         va_start(args, format);
193         g_vsnprintf(buf + LOG_TIME_LEN, BUFFSIZE, format, args);
194         va_end(args);
195
196         if (debug_get_mode()) g_print("%s", buf);
197
198         logtext->instance = instance;
199         logtext->text = g_strdup(buf);
200         logtext->type = LOG_NORMAL;
201         
202         g_timeout_add(0, invoke_hook_cb, logtext);
203
204         if (log_fp[instance] && prefs_common_enable_log_standard()) {
205                 FPUTS(buf, log_fp[instance])
206                 log_size[instance] += strlen(buf);
207                 FFLUSH(log_fp[instance])
208                 rotate_log(instance);
209         }
210 }
211
212 void log_message(LogInstance instance, const gchar *format, ...)
213 {
214         va_list args;
215         gchar buf[BUFFSIZE + LOG_TIME_LEN];
216         time_t t;
217         LogText *logtext = g_new0(LogText, 1);
218         struct tm buft;
219
220         time(&t);
221         strftime(buf, LOG_TIME_LEN + 1, "[%H:%M:%S] ", localtime_r(&t, &buft));
222
223         va_start(args, format);
224         g_vsnprintf(buf + LOG_TIME_LEN, BUFFSIZE, format, args);
225         va_end(args);
226
227         if (debug_get_mode()) g_message("%s", buf + LOG_TIME_LEN);
228
229         logtext->instance = instance;
230         logtext->text = g_strdup(buf + LOG_TIME_LEN);
231         logtext->type = LOG_MSG;
232         
233         g_timeout_add(0, invoke_hook_cb, logtext);
234
235         if (log_fp[instance] && prefs_common_enable_log_standard()) {
236                 FWRITE(buf, 1, LOG_TIME_LEN, log_fp[instance])
237                 FPUTS("* message: ", log_fp[instance])
238                 log_size[instance] += strlen("* message: ");
239                 FPUTS(buf + LOG_TIME_LEN, log_fp[instance])
240                 log_size[instance] += strlen(buf);
241                 FFLUSH(log_fp[instance])
242                 rotate_log(instance);
243         }
244 }
245
246 void log_warning(LogInstance instance, const gchar *format, ...)
247 {
248         va_list args;
249         gchar buf[BUFFSIZE + LOG_TIME_LEN];
250         time_t t;
251         LogText *logtext = g_new0(LogText, 1);
252         struct tm buft;
253
254         time(&t);
255         strftime(buf, LOG_TIME_LEN + 1, "[%H:%M:%S] ", localtime_r(&t, &buft));
256
257         va_start(args, format);
258         g_vsnprintf(buf + LOG_TIME_LEN, BUFFSIZE, format, args);
259         va_end(args);
260
261         g_warning("%s", buf);
262
263         logtext->instance = instance;
264         logtext->text = g_strdup(buf + LOG_TIME_LEN);
265         logtext->type = LOG_WARN;
266         
267         g_timeout_add(0, invoke_hook_cb, logtext);
268
269         if (log_fp[instance] && prefs_common_enable_log_warning()) {
270                 FWRITE(buf, 1, LOG_TIME_LEN, log_fp[instance])
271                 FPUTS("** warning: ", log_fp[instance])
272                 log_size[instance] += strlen("** warning: ");
273                 FPUTS(buf + LOG_TIME_LEN, log_fp[instance])
274                 log_size[instance] += strlen(buf);
275                 FFLUSH(log_fp[instance])
276                 rotate_log(instance);
277         }
278 }
279
280 void log_error(LogInstance instance, const gchar *format, ...)
281 {
282         va_list args;
283         gchar buf[BUFFSIZE + LOG_TIME_LEN];
284         time_t t;
285         LogText *logtext = g_new0(LogText, 1);
286         struct tm buft;
287
288         time(&t);
289         strftime(buf, LOG_TIME_LEN + 1, "[%H:%M:%S] ", localtime_r(&t, &buft));
290
291         va_start(args, format);
292         g_vsnprintf(buf + LOG_TIME_LEN, BUFFSIZE, format, args);
293         va_end(args);
294
295         g_warning("%s", buf);
296
297         logtext->instance = instance;
298         logtext->text = g_strdup(buf + LOG_TIME_LEN);
299         logtext->type = LOG_ERROR;
300         
301         g_timeout_add(0, invoke_hook_cb, logtext);
302
303         if (log_fp[instance] && prefs_common_enable_log_error()) {
304                 FWRITE(buf, 1, LOG_TIME_LEN, log_fp[instance])
305                 FPUTS("*** error: ", log_fp[instance])
306                 log_size[instance] += strlen("*** error: ");
307                 FPUTS(buf + LOG_TIME_LEN, log_fp[instance])
308                 log_size[instance] += strlen(buf);
309                 FFLUSH(log_fp[instance])
310                 rotate_log(instance);
311         }
312 }
313
314 void log_status_ok(LogInstance instance, const gchar *format, ...)
315 {
316         va_list args;
317         gchar buf[BUFFSIZE + LOG_TIME_LEN];
318         time_t t;
319         LogText *logtext = g_new0(LogText, 1);
320         struct tm buft;
321
322         time(&t);
323         strftime(buf, LOG_TIME_LEN + 1, "[%H:%M:%S] ", localtime_r(&t, &buft));
324
325         va_start(args, format);
326         g_vsnprintf(buf + LOG_TIME_LEN, BUFFSIZE, format, args);
327         va_end(args);
328
329         if (debug_get_mode()) g_message("%s", buf + LOG_TIME_LEN);
330
331         logtext->instance = instance;
332         logtext->text = g_strdup(buf + LOG_TIME_LEN);
333         logtext->type = LOG_STATUS_OK;
334         
335         g_timeout_add(0, invoke_hook_cb, logtext);
336
337         if (log_fp[instance] && prefs_common_enable_log_status()) {
338                 FWRITE(buf, 1, LOG_TIME_LEN, log_fp[instance])
339                 FPUTS("* OK: ", log_fp[instance])
340                 log_size[instance] += strlen("* OK: ");
341                 FPUTS(buf + LOG_TIME_LEN, log_fp[instance])
342                 log_size[instance] += strlen(buf);
343                 FFLUSH(log_fp[instance])
344                 rotate_log(instance);
345         }
346 }
347
348 void log_status_nok(LogInstance instance, const gchar *format, ...)
349 {
350         va_list args;
351         gchar buf[BUFFSIZE + LOG_TIME_LEN];
352         time_t t;
353         LogText *logtext = g_new0(LogText, 1);
354         struct tm buft;
355
356         time(&t);
357         strftime(buf, LOG_TIME_LEN + 1, "[%H:%M:%S] ", localtime_r(&t, &buft));
358
359         va_start(args, format);
360         g_vsnprintf(buf + LOG_TIME_LEN, BUFFSIZE, format, args);
361         va_end(args);
362
363         if (debug_get_mode()) g_message("%s", buf + LOG_TIME_LEN);
364
365         logtext->instance = instance;
366         logtext->text = g_strdup(buf + LOG_TIME_LEN);
367         logtext->type = LOG_STATUS_NOK;
368         
369         g_timeout_add(0, invoke_hook_cb, logtext);
370
371         if (log_fp[instance] && prefs_common_enable_log_status()) {
372                 FWRITE(buf, 1, LOG_TIME_LEN, log_fp[instance])
373                 FPUTS("* NOT OK: ", log_fp[instance])
374                 log_size[instance] += strlen("* NOT OK: ");
375                 FPUTS(buf + LOG_TIME_LEN, log_fp[instance])
376                 log_size[instance] += strlen(buf);
377                 FFLUSH(log_fp[instance])
378                 rotate_log(instance);
379         }
380 }
381
382 void log_status_skip(LogInstance instance, const gchar *format, ...)
383 {
384         va_list args;
385         gchar buf[BUFFSIZE + LOG_TIME_LEN];
386         time_t t;
387         LogText *logtext = g_new0(LogText, 1);
388         struct tm buft;
389
390         time(&t);
391         strftime(buf, LOG_TIME_LEN + 1, "[%H:%M:%S] ", localtime_r(&t, &buft));
392
393         va_start(args, format);
394         g_vsnprintf(buf + LOG_TIME_LEN, BUFFSIZE, format, args);
395         va_end(args);
396
397         if (debug_get_mode()) g_message("%s", buf + LOG_TIME_LEN);
398
399         logtext->instance = instance;
400         logtext->text = g_strdup(buf + LOG_TIME_LEN);
401         logtext->type = LOG_STATUS_SKIP;
402         
403         g_timeout_add(0, invoke_hook_cb, logtext);
404
405         if (log_fp[instance] && prefs_common_enable_log_status()) {
406                 FWRITE(buf, 1, LOG_TIME_LEN, log_fp[instance])
407                 FPUTS("* SKIPPED: ", log_fp[instance])
408                 log_size[instance] += strlen("* SKIPPED: ");
409                 FPUTS(buf + LOG_TIME_LEN, log_fp[instance])
410                 log_size[instance] += strlen(buf);
411                 FFLUSH(log_fp[instance])
412                 rotate_log(instance);
413         }
414 }
415
416 #undef FWRITE
417 #undef FPUTS
418 #undef FFLUSH
419