Implement safe_fclose() so we can respect the "Metadata handling"
[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 #include "safe_fclose.h"
39
40 #define FWRITE(_b,_s,_n,_f)     if (fwrite(_b,_s,_n,_f) != _n) { \
41                                         g_message("log fwrite failed!\n"); \
42                                         return; \
43                                 }
44 #define FPUTS(_b,_f)            if (fputs(_b,_f) == EOF) { \
45                                         g_message("log fputs failed!\n"); \
46                                         return; \
47                                 }
48 #define FFLUSH(_f)              if (fflush(_f) != 0) { \
49                                         g_message("log fflush failed!\n"); \
50                                         return; \
51                                 }
52
53 static FILE *log_fp[LOG_INSTANCE_MAX] = {
54         NULL,
55         NULL
56 };
57
58 static size_t log_size[LOG_INSTANCE_MAX] = {
59         0,
60         0
61 };
62
63 static gchar *log_filename[LOG_INSTANCE_MAX] = {
64         NULL,
65         NULL
66 };
67
68 /* read-only */
69 static gboolean log_error_capability[LOG_INSTANCE_MAX] = {
70         TRUE,
71         FALSE
72 };
73
74 typedef struct _LogInstanceData LogInstanceData;
75
76 struct _LogInstanceData {
77         const char *hook;
78         gchar *title;
79         int *prefs_logwin_width;
80         int *prefs_logwin_height;
81 };
82
83 static LogInstanceData log_instances[LOG_INSTANCE_MAX] = {
84         { LOG_APPEND_TEXT_HOOKLIST, NULL, NULL, NULL },
85         { DEBUG_FILTERING_APPEND_TEXT_HOOKLIST, NULL, NULL, NULL }
86 };
87
88 gboolean prefs_common_enable_log_standard(void);
89 gboolean prefs_common_enable_log_warning(void);
90 gboolean prefs_common_enable_log_error(void);
91 gboolean prefs_common_enable_log_status(void);
92
93 static gboolean invoke_hook_cb (gpointer data)
94 {
95         LogText *logtext = (LogText *)data;
96         hooks_invoke(get_log_hook(logtext->instance), logtext);
97         g_free(logtext->text);
98         g_free(logtext);
99         return FALSE;
100 }
101
102 void set_log_file(LogInstance instance, const gchar *filename)
103 {
104         gchar *fullname = NULL;
105         if (log_fp[instance])
106                 return;
107
108         if (!g_path_is_absolute(filename)) {
109                 fullname = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
110                                         filename, NULL);
111         } else {
112                 fullname = g_strdup(filename);
113         }
114         /* backup old logfile if existing */
115         if (is_file_exist(fullname)) {
116                 gchar *backupname;
117                 
118                 backupname = g_strconcat(fullname, ".bak", NULL);
119                 claws_unlink(backupname);
120                 if (g_rename(fullname, backupname) < 0)
121                         FILE_OP_ERROR(fullname, "rename");
122                 g_free(backupname);
123         }
124
125         log_fp[instance] = g_fopen(fullname, "wb");
126         if (!log_fp[instance]) {
127                 FILE_OP_ERROR(fullname, "fopen");
128                 log_filename[instance] = NULL;
129                 g_free(fullname);
130                 return;
131         }
132         log_filename[instance] = g_strdup(fullname);
133         log_size[instance] = 0;
134         g_free(fullname);
135 }
136
137 void close_log_file(LogInstance instance)
138 {
139         if (log_fp[instance]) {
140                 safe_fclose(log_fp[instance]);
141                 log_fp[instance] = NULL;
142                 log_size[instance] = 0;
143                 g_free(log_filename[instance]);
144                 log_filename[instance] = NULL;
145         }
146 }
147
148 static void rotate_log(LogInstance instance)
149 {
150         if (log_size[instance] > 10 * 1024* 1024) {
151                 gchar *filename = g_strdup(log_filename[instance]);
152                 debug_print("rotating %s\n", filename);
153                 close_log_file(instance);
154                 set_log_file(instance, filename);
155                 g_free(filename);
156         }
157 }
158
159 const char *get_log_hook(LogInstance instance)
160 {
161         return log_instances[instance].hook;
162 }
163
164 void set_log_title(LogInstance instance, gchar *title)
165 {
166         log_instances[instance].title = title;
167 }
168
169 gchar *get_log_title(LogInstance instance)
170 {
171         return log_instances[instance].title;
172 }
173
174 void set_log_prefs(LogInstance instance, int* logwin_width, int* logwin_height)
175 {
176         log_instances[instance].prefs_logwin_width = logwin_width;
177         log_instances[instance].prefs_logwin_height = logwin_height;
178 }
179
180 void get_log_prefs(LogInstance instance, int** logwin_width, int** logwin_height)
181 {
182         if (logwin_width)
183                 *logwin_width = log_instances[instance].prefs_logwin_width;
184         if (logwin_height)
185                 *logwin_height = log_instances[instance].prefs_logwin_height;
186 }
187
188 gboolean get_log_error_capability(LogInstance instance)
189 {
190         return log_error_capability[instance];
191
192 }
193
194 void log_print(LogInstance instance, const gchar *format, ...)
195 {
196         va_list args;
197         gchar buf[BUFFSIZE + LOG_TIME_LEN];
198         time_t t;
199         LogText *logtext = g_new0(LogText, 1);
200         struct tm buft;
201
202         time(&t);
203         strftime(buf, LOG_TIME_LEN + 1, "[%H:%M:%S] ", localtime_r(&t, &buft));
204
205         va_start(args, format);
206         g_vsnprintf(buf + LOG_TIME_LEN, BUFFSIZE, format, args);
207         va_end(args);
208
209         if (debug_get_mode()) g_print("%s", buf);
210
211         logtext->instance = instance;
212         logtext->text = g_strdup(buf);
213         logtext->type = LOG_NORMAL;
214         
215         g_timeout_add(0, invoke_hook_cb, logtext);
216
217         if (log_fp[instance] && prefs_common_enable_log_standard()) {
218                 FPUTS(buf, log_fp[instance])
219                 log_size[instance] += strlen(buf);
220                 FFLUSH(log_fp[instance])
221                 rotate_log(instance);
222         }
223 }
224
225 void log_message(LogInstance instance, const gchar *format, ...)
226 {
227         va_list args;
228         gchar buf[BUFFSIZE + LOG_TIME_LEN];
229         time_t t;
230         LogText *logtext = g_new0(LogText, 1);
231         struct tm buft;
232
233         time(&t);
234         strftime(buf, LOG_TIME_LEN + 1, "[%H:%M:%S] ", localtime_r(&t, &buft));
235
236         va_start(args, format);
237         g_vsnprintf(buf + LOG_TIME_LEN, BUFFSIZE, format, args);
238         va_end(args);
239
240         if (debug_get_mode()) g_message("%s", buf + LOG_TIME_LEN);
241
242         logtext->instance = instance;
243         logtext->text = g_strdup(buf + LOG_TIME_LEN);
244         logtext->type = LOG_MSG;
245         
246         g_timeout_add(0, invoke_hook_cb, logtext);
247
248         if (log_fp[instance] && prefs_common_enable_log_standard()) {
249                 FWRITE(buf, 1, LOG_TIME_LEN, log_fp[instance])
250                 FPUTS("* message: ", log_fp[instance])
251                 log_size[instance] += strlen("* message: ");
252                 FPUTS(buf + LOG_TIME_LEN, log_fp[instance])
253                 log_size[instance] += strlen(buf);
254                 FFLUSH(log_fp[instance])
255                 rotate_log(instance);
256         }
257 }
258
259 void log_warning(LogInstance instance, const gchar *format, ...)
260 {
261         va_list args;
262         gchar buf[BUFFSIZE + LOG_TIME_LEN];
263         time_t t;
264         LogText *logtext = g_new0(LogText, 1);
265         struct tm buft;
266
267         time(&t);
268         strftime(buf, LOG_TIME_LEN + 1, "[%H:%M:%S] ", localtime_r(&t, &buft));
269
270         va_start(args, format);
271         g_vsnprintf(buf + LOG_TIME_LEN, BUFFSIZE, format, args);
272         va_end(args);
273
274         g_warning("%s", buf);
275
276         logtext->instance = instance;
277         logtext->text = g_strdup(buf + LOG_TIME_LEN);
278         logtext->type = LOG_WARN;
279         
280         g_timeout_add(0, invoke_hook_cb, logtext);
281
282         if (log_fp[instance] && prefs_common_enable_log_warning()) {
283                 FWRITE(buf, 1, LOG_TIME_LEN, log_fp[instance])
284                 FPUTS("** warning: ", log_fp[instance])
285                 log_size[instance] += strlen("** warning: ");
286                 FPUTS(buf + LOG_TIME_LEN, log_fp[instance])
287                 log_size[instance] += strlen(buf);
288                 FFLUSH(log_fp[instance])
289                 rotate_log(instance);
290         }
291 }
292
293 void log_error(LogInstance instance, const gchar *format, ...)
294 {
295         va_list args;
296         gchar buf[BUFFSIZE + LOG_TIME_LEN];
297         time_t t;
298         LogText *logtext = g_new0(LogText, 1);
299         struct tm buft;
300
301         time(&t);
302         strftime(buf, LOG_TIME_LEN + 1, "[%H:%M:%S] ", localtime_r(&t, &buft));
303
304         va_start(args, format);
305         g_vsnprintf(buf + LOG_TIME_LEN, BUFFSIZE, format, args);
306         va_end(args);
307
308         g_warning("%s", buf);
309
310         logtext->instance = instance;
311         logtext->text = g_strdup(buf + LOG_TIME_LEN);
312         logtext->type = LOG_ERROR;
313         
314         g_timeout_add(0, invoke_hook_cb, logtext);
315
316         if (log_fp[instance] && prefs_common_enable_log_error()) {
317                 FWRITE(buf, 1, LOG_TIME_LEN, log_fp[instance])
318                 FPUTS("*** error: ", log_fp[instance])
319                 log_size[instance] += strlen("*** error: ");
320                 FPUTS(buf + LOG_TIME_LEN, log_fp[instance])
321                 log_size[instance] += strlen(buf);
322                 FFLUSH(log_fp[instance])
323                 rotate_log(instance);
324         }
325 }
326
327 void log_status_ok(LogInstance instance, const gchar *format, ...)
328 {
329         va_list args;
330         gchar buf[BUFFSIZE + LOG_TIME_LEN];
331         time_t t;
332         LogText *logtext = g_new0(LogText, 1);
333         struct tm buft;
334
335         time(&t);
336         strftime(buf, LOG_TIME_LEN + 1, "[%H:%M:%S] ", localtime_r(&t, &buft));
337
338         va_start(args, format);
339         g_vsnprintf(buf + LOG_TIME_LEN, BUFFSIZE, format, args);
340         va_end(args);
341
342         if (debug_get_mode()) g_message("%s", buf + LOG_TIME_LEN);
343
344         logtext->instance = instance;
345         logtext->text = g_strdup(buf + LOG_TIME_LEN);
346         logtext->type = LOG_STATUS_OK;
347         
348         g_timeout_add(0, invoke_hook_cb, logtext);
349
350         if (log_fp[instance] && prefs_common_enable_log_status()) {
351                 FWRITE(buf, 1, LOG_TIME_LEN, log_fp[instance])
352                 FPUTS("* OK: ", log_fp[instance])
353                 log_size[instance] += strlen("* OK: ");
354                 FPUTS(buf + LOG_TIME_LEN, log_fp[instance])
355                 log_size[instance] += strlen(buf);
356                 FFLUSH(log_fp[instance])
357                 rotate_log(instance);
358         }
359 }
360
361 void log_status_nok(LogInstance instance, const gchar *format, ...)
362 {
363         va_list args;
364         gchar buf[BUFFSIZE + LOG_TIME_LEN];
365         time_t t;
366         LogText *logtext = g_new0(LogText, 1);
367         struct tm buft;
368
369         time(&t);
370         strftime(buf, LOG_TIME_LEN + 1, "[%H:%M:%S] ", localtime_r(&t, &buft));
371
372         va_start(args, format);
373         g_vsnprintf(buf + LOG_TIME_LEN, BUFFSIZE, format, args);
374         va_end(args);
375
376         if (debug_get_mode()) g_message("%s", buf + LOG_TIME_LEN);
377
378         logtext->instance = instance;
379         logtext->text = g_strdup(buf + LOG_TIME_LEN);
380         logtext->type = LOG_STATUS_NOK;
381         
382         g_timeout_add(0, invoke_hook_cb, logtext);
383
384         if (log_fp[instance] && prefs_common_enable_log_status()) {
385                 FWRITE(buf, 1, LOG_TIME_LEN, log_fp[instance])
386                 FPUTS("* NOT OK: ", log_fp[instance])
387                 log_size[instance] += strlen("* NOT OK: ");
388                 FPUTS(buf + LOG_TIME_LEN, log_fp[instance])
389                 log_size[instance] += strlen(buf);
390                 FFLUSH(log_fp[instance])
391                 rotate_log(instance);
392         }
393 }
394
395 void log_status_skip(LogInstance instance, const gchar *format, ...)
396 {
397         va_list args;
398         gchar buf[BUFFSIZE + LOG_TIME_LEN];
399         time_t t;
400         LogText *logtext = g_new0(LogText, 1);
401         struct tm buft;
402
403         time(&t);
404         strftime(buf, LOG_TIME_LEN + 1, "[%H:%M:%S] ", localtime_r(&t, &buft));
405
406         va_start(args, format);
407         g_vsnprintf(buf + LOG_TIME_LEN, BUFFSIZE, format, args);
408         va_end(args);
409
410         if (debug_get_mode()) g_message("%s", buf + LOG_TIME_LEN);
411
412         logtext->instance = instance;
413         logtext->text = g_strdup(buf + LOG_TIME_LEN);
414         logtext->type = LOG_STATUS_SKIP;
415         
416         g_timeout_add(0, invoke_hook_cb, logtext);
417
418         if (log_fp[instance] && prefs_common_enable_log_status()) {
419                 FWRITE(buf, 1, LOG_TIME_LEN, log_fp[instance])
420                 FPUTS("* SKIPPED: ", log_fp[instance])
421                 log_size[instance] += strlen("* SKIPPED: ");
422                 FPUTS(buf + LOG_TIME_LEN, log_fp[instance])
423                 log_size[instance] += strlen(buf);
424                 FFLUSH(log_fp[instance])
425                 rotate_log(instance);
426         }
427 }
428
429 #undef FWRITE
430 #undef FPUTS
431 #undef FFLUSH
432