source: logging.c @ b470451

release-1.8release-1.9
Last change on this file since b470451 was d427f08, checked in by Nelson Elhage <nelhage@mit.edu>, 10 years ago
Use G_GNUC_WARN_UNUSED_RESULT Have gcc warn us when we ignore the result of a function that requires the caller to free the result, or an initilization function that can fail. This might help (slightly) with preventing leaks and segfaults. Additionally changed some functions that should never fail to not return values. (The owl_list_* functions changed only fail if list->size < 0, which we assume is not the case elsewhere.)
  • Property mode set to 100644
File size: 13.3 KB
Line 
1#include "owl.h"
2#include <stdlib.h>
3#include <string.h>
4#include <ctype.h>
5#include <sys/param.h>
6
7typedef struct _owl_log_entry { /* noproto */
8  char *filename;
9  char *message;
10} owl_log_entry;
11
12
13static GMainContext *log_context;
14static GMainLoop *log_loop;
15static GThread *logging_thread;
16
17/* This is now the one function that should be called to log a
18 * message.  It will do all the work necessary by calling the other
19 * functions in this file as necessary.
20 */
21void owl_log_message(const owl_message *m) {
22  owl_function_debugmsg("owl_log_message: entering");
23
24  if (m == NULL) {
25    owl_function_debugmsg("owl_log_message: passed null message");
26    return;
27  }
28
29  /* should we be logging this message? */
30  if (!owl_log_shouldlog_message(m)) {
31    owl_function_debugmsg("owl_log_message: not logging message");
32    return;
33  }
34
35  /* handle incmoing messages */
36  if (owl_message_is_direction_in(m)) {
37    owl_log_incoming(m);
38    owl_function_debugmsg("owl_log_message: leaving");
39    return;
40  }
41
42  /* handle outgoing messages */
43  owl_log_outgoing(m);
44
45  owl_function_debugmsg("owl_log_message: leaving");
46}
47
48/* Return 1 if we should log the given message, otherwise return 0 */
49int owl_log_shouldlog_message(const owl_message *m) {
50  const owl_filter *f;
51
52  /* If there's a logfilter and this message matches it, log */
53  f=owl_global_get_filter(&g, owl_global_get_logfilter(&g));
54  if (f && owl_filter_message_match(f, m)) return(1);
55
56  /* otherwise we do things based on the logging variables */
57
58  /* skip login/logout messages if appropriate */
59  if (!owl_global_is_loglogins(&g) && owl_message_is_loginout(m)) return(0);
60     
61  /* check direction */
62  if ((owl_global_get_loggingdirection(&g)==OWL_LOGGING_DIRECTION_IN) && owl_message_is_direction_out(m)) {
63    return(0);
64  }
65  if ((owl_global_get_loggingdirection(&g)==OWL_LOGGING_DIRECTION_OUT) && owl_message_is_direction_in(m)) {
66    return(0);
67  }
68
69  if (owl_message_is_type_zephyr(m)) {
70    if (owl_message_is_personal(m) && !owl_global_is_logging(&g)) return(0);
71    if (!owl_message_is_personal(m) && !owl_global_is_classlogging(&g)) return(0);
72  } else {
73    if (owl_message_is_private(m) || owl_message_is_loginout(m)) {
74      if (!owl_global_is_logging(&g)) return(0);
75    } else {
76      if (!owl_global_is_classlogging(&g)) return(0);
77    }
78  }
79  return(1);
80}
81
82G_GNUC_WARN_UNUSED_RESULT char *owl_log_zephyr(const owl_message *m)
83{
84    char *tmp = NULL;
85    GString *buffer = NULL;
86    buffer = g_string_new("");
87    tmp = short_zuser(owl_message_get_sender(m));
88    g_string_append_printf(buffer, "Class: %s Instance: %s", 
89                           owl_message_get_class(m), 
90                           owl_message_get_instance(m));
91    if (strcmp(owl_message_get_opcode(m), "")) {
92      g_string_append_printf(buffer, " Opcode: %s", 
93                             owl_message_get_opcode(m));
94    }
95    g_string_append_printf(buffer, "\n");
96    g_string_append_printf(buffer, "Time: %s Host: %s\n", 
97                           owl_message_get_timestr(m), 
98                           owl_message_get_hostname(m));
99    g_string_append_printf(buffer, "From: %s <%s>\n\n", 
100                           owl_message_get_zsig(m), tmp);
101    g_string_append_printf(buffer, "%s\n\n", owl_message_get_body(m));
102    g_free(tmp);
103    return g_string_free(buffer, FALSE);
104}
105
106G_GNUC_WARN_UNUSED_RESULT char *owl_log_aim(const owl_message *m)
107{
108    GString *buffer = NULL;
109    buffer = g_string_new("");
110    g_string_append_printf(buffer, "From: <%s> To: <%s>\n", 
111                           owl_message_get_sender(m), owl_message_get_recipient(m));
112    g_string_append_printf(buffer, "Time: %s\n\n", 
113                           owl_message_get_timestr(m));
114    if (owl_message_is_login(m)) {
115        g_string_append_printf(buffer, "LOGIN\n\n");
116    } else if (owl_message_is_logout(m)) {
117        g_string_append_printf(buffer, "LOGOUT\n\n");
118    } else {
119        g_string_append_printf(buffer, "%s\n\n", owl_message_get_body(m));
120    }
121    return g_string_free(buffer, FALSE);
122}
123
124G_GNUC_WARN_UNUSED_RESULT char *owl_log_jabber(const owl_message *m)
125{
126    GString *buffer = NULL;
127    buffer = g_string_new("");
128    g_string_append_printf(buffer, "From: <%s> To: <%s>\n",
129                           owl_message_get_sender(m), 
130                           owl_message_get_recipient(m));
131    g_string_append_printf(buffer, "Time: %s\n\n", 
132                           owl_message_get_timestr(m));
133    g_string_append_printf(buffer, "%s\n\n", owl_message_get_body(m));
134    return g_string_free(buffer, FALSE);
135}
136
137G_GNUC_WARN_UNUSED_RESULT char *owl_log_generic(const owl_message *m)
138{
139    GString *buffer;
140    buffer = g_string_new("");
141    g_string_append_printf(buffer, "From: <%s> To: <%s>\n", 
142                           owl_message_get_sender(m), 
143                           owl_message_get_recipient(m));
144    g_string_append_printf(buffer, "Time: %s\n\n", 
145                           owl_message_get_timestr(m));
146    g_string_append_printf(buffer, "%s\n\n", 
147                           owl_message_get_body(m));
148    return g_string_free(buffer, FALSE);
149}
150
151static void owl_log_error_main_thread(gpointer data)
152{
153  owl_function_error("%s", (const char*)data);
154}
155
156static void owl_log_error(const char *message)
157{
158  char *data = g_strdup(message);
159  owl_select_post_task(owl_log_error_main_thread,
160                       data, g_free, g_main_context_default());
161}
162
163static void owl_log_write_entry(gpointer data)
164{
165  owl_log_entry *msg = (owl_log_entry*)data;
166  FILE *file = NULL;
167  file = fopen(msg->filename, "a");
168  if (!file) {
169    owl_log_error("Unable to open file for logging");
170    return;
171  }
172  fprintf(file, "%s", msg->message);
173  fclose(file);
174}
175
176static void owl_log_entry_free(void *data)
177{
178  owl_log_entry *msg = (owl_log_entry*)data;
179  if (msg) {
180    g_free(msg->message);
181    g_free(msg->filename);
182    g_free(msg);
183  }
184}
185
186void owl_log_enqueue_message(const char *buffer, const char *filename)
187{
188  owl_log_entry *log_msg = NULL; 
189  log_msg = g_new(owl_log_entry,1);
190  log_msg->message = g_strdup(buffer);
191  log_msg->filename = g_strdup(filename);
192  owl_select_post_task(owl_log_write_entry, log_msg, 
193                       owl_log_entry_free, log_context);
194}
195
196void owl_log_append(const owl_message *m, const char *filename) {
197  char *buffer = NULL;
198  if (owl_message_is_type_zephyr(m)) {
199    buffer = owl_log_zephyr(m);
200  } else if (owl_message_is_type_jabber(m)) {
201    buffer = owl_log_jabber(m);
202  } else if (owl_message_is_type_aim(m)) {
203    buffer = owl_log_aim(m);
204  } else {
205    buffer = owl_log_generic(m);
206  }
207  owl_log_enqueue_message(buffer, filename);
208  g_free(buffer);
209}
210
211void owl_log_outgoing(const owl_message *m)
212{
213  char *filename, *logpath;
214  char *to, *temp;
215  GList *cc;
216
217  /* expand ~ in path names */
218  logpath = owl_util_makepath(owl_global_get_logpath(&g));
219
220  /* Figure out what path to log to */
221  if (owl_message_is_type_zephyr(m)) {
222    /* If this has CC's, do all but the "recipient" which we'll do below */
223    cc = owl_message_get_cc_without_recipient(m);
224    while (cc != NULL) {
225      temp = short_zuser(cc->data);
226      filename = g_strdup_printf("%s/%s", logpath, temp);
227      owl_log_append(m, filename);
228
229      g_free(filename);
230      g_free(temp);
231      g_free(cc->data);
232      cc = g_list_delete_link(cc, cc);
233    }
234
235    to = short_zuser(owl_message_get_recipient(m));
236  } else if (owl_message_is_type_jabber(m)) {
237    to = g_strdup_printf("jabber:%s", owl_message_get_recipient(m));
238    g_strdelimit(to, "/", '_');
239  } else if (owl_message_is_type_aim(m)) {
240    char *temp2;
241    temp = owl_aim_normalize_screenname(owl_message_get_recipient(m));
242    temp2 = g_utf8_strdown(temp,-1);
243    to = g_strdup_printf("aim:%s", temp2);
244    g_free(temp2);
245    g_free(temp);
246  } else {
247    to = g_strdup("loopback");
248  }
249
250  filename = g_strdup_printf("%s/%s", logpath, to);
251  owl_log_append(m, filename);
252  g_free(to);
253  g_free(filename);
254
255  filename = g_strdup_printf("%s/all", logpath);
256  owl_log_append(m, filename);
257  g_free(logpath);
258  g_free(filename);
259}
260
261
262void owl_log_outgoing_zephyr_error(const owl_zwrite *zw, const char *text)
263{
264  char *filename, *logpath;
265  char *tobuff, *recip;
266  owl_message *m;
267  GString *msgbuf;
268  /* create a present message so we can pass it to
269   * owl_log_shouldlog_message(void)
270   */
271  m = g_new(owl_message, 1);
272  /* recip_index = 0 because there can only be one recipient anyway */
273  owl_message_create_from_zwrite(m, zw, text, 0);
274  if (!owl_log_shouldlog_message(m)) {
275    owl_message_delete(m);
276    return;
277  }
278  owl_message_delete(m);
279
280  /* chop off a local realm */
281  recip = owl_zwrite_get_recip_n_with_realm(zw, 0);
282  tobuff = short_zuser(recip);
283  g_free(recip);
284
285  /* expand ~ in path names */
286  logpath = owl_util_makepath(owl_global_get_logpath(&g));
287  filename = g_strdup_printf("%s/%s", logpath, tobuff);
288  msgbuf = g_string_new("");
289  g_string_printf(msgbuf, "ERROR (owl): %s\n%s\n", tobuff, text);
290  if (text[strlen(text)-1] != '\n') {
291    g_string_append_printf(msgbuf, "\n");
292  }
293  owl_log_enqueue_message(msgbuf->str, filename);
294  g_string_free(msgbuf, TRUE);
295
296  filename = g_strdup_printf("%s/all", logpath);
297  g_free(logpath);
298  msgbuf = g_string_new("");
299  g_string_printf(msgbuf, "ERROR (owl): %s\n%s\n", tobuff, text);
300  if (text[strlen(text)-1] != '\n') {
301    g_string_append_printf(msgbuf, "\n");
302  }
303  owl_log_enqueue_message(msgbuf->str, filename);
304  g_string_free(msgbuf, TRUE);
305
306  g_free(tobuff);
307}
308
309void owl_log_incoming(const owl_message *m)
310{
311  char *filename, *allfilename, *logpath;
312  const char *from=NULL;
313  char *frombuff=NULL;
314  int len, ch, i, personal;
315
316  /* figure out if it's a "personal" message or not */
317  if (owl_message_is_type_zephyr(m)) {
318    if (owl_message_is_personal(m)) {
319      personal = 1;
320    } else {
321      personal = 0;
322    }
323  } else if (owl_message_is_type_jabber(m)) {
324    /* This needs to be fixed to handle groupchat */
325    const char* msgtype = owl_message_get_attribute_value(m,"jtype");
326    if (msgtype && !strcmp(msgtype,"groupchat")) {
327      personal = 0;
328    } else {
329      personal = 1;
330    }
331  } else {
332    if (owl_message_is_private(m) || owl_message_is_loginout(m)) {
333      personal = 1;
334    } else {
335      personal = 0;
336    }
337  }
338
339
340  if (owl_message_is_type_zephyr(m)) {
341    if (personal) {
342      from=frombuff=short_zuser(owl_message_get_sender(m));
343    } else {
344      from=frombuff=g_strdup(owl_message_get_class(m));
345    }
346  } else if (owl_message_is_type_aim(m)) {
347    /* we do not yet handle chat rooms */
348    char *normalto, *temp;
349    temp = owl_aim_normalize_screenname(owl_message_get_sender(m));
350    normalto = g_utf8_strdown(temp, -1);
351    from=frombuff=g_strdup_printf("aim:%s", normalto);
352    g_free(normalto);
353    g_free(temp);
354  } else if (owl_message_is_type_loopback(m)) {
355    from=frombuff=g_strdup("loopback");
356  } else if (owl_message_is_type_jabber(m)) {
357    if (personal) {
358      from=frombuff=g_strdup_printf("jabber:%s", 
359                                    owl_message_get_sender(m));
360    } else {
361      from=frombuff=g_strdup_printf("jabber:%s", 
362                                    owl_message_get_recipient(m));
363    }
364  } else {
365    from=frombuff=g_strdup("unknown");
366  }
367 
368  /* check for malicious sender formats */
369  len=strlen(frombuff);
370  if (len<1 || len>35) from="weird";
371  if (strchr(frombuff, '/')) from="weird";
372
373  ch=frombuff[0];
374  if (!g_ascii_isalnum(ch)) from="weird";
375
376  for (i=0; i<len; i++) {
377    if (frombuff[i]<'!' || frombuff[i]>='~') from="weird";
378  }
379
380  if (!strcmp(frombuff, ".") || !strcasecmp(frombuff, "..")) from="weird";
381
382  if (!personal) {
383    if (strcmp(from, "weird")) {
384      char* temp = g_utf8_strdown(frombuff, -1);
385      if (temp) {
386        g_free(frombuff);
387        from = frombuff = temp;
388      }
389    }
390  }
391
392  /* create the filename (expanding ~ in path names) */
393  if (personal) {
394    logpath = owl_util_makepath(owl_global_get_logpath(&g));
395    filename = g_strdup_printf("%s/%s", logpath, from);
396    allfilename = g_strdup_printf("%s/all", logpath);
397    owl_log_append(m, allfilename);
398    g_free(allfilename);
399  } else {
400    logpath = owl_util_makepath(owl_global_get_classlogpath(&g));
401    filename = g_strdup_printf("%s/%s", logpath, from);
402  }
403
404  owl_log_append(m, filename);
405  g_free(filename);
406
407  if (personal && owl_message_is_type_zephyr(m)) {
408    /* We want to log to all of the CC'd people who were not us, or
409     * the sender, as well.
410     */
411    char *temp;
412    GList *cc;
413    cc = owl_message_get_cc_without_recipient(m);
414    while (cc != NULL) {
415      temp = short_zuser(cc->data);
416      if (strcasecmp(temp, frombuff) != 0) {
417        filename = g_strdup_printf("%s/%s", logpath, temp);
418        owl_log_append(m, filename);
419        g_free(filename);
420      }
421
422      g_free(temp);
423      g_free(cc->data);
424      cc = g_list_delete_link(cc, cc);
425    }
426  }
427
428  g_free(frombuff);
429  g_free(logpath);
430}
431
432static gpointer owl_log_thread_func(gpointer data)
433{
434  log_context = g_main_context_new();
435  log_loop = g_main_loop_new(log_context, FALSE);
436  g_main_loop_run(log_loop);
437  return NULL;
438}
439
440void owl_log_init(void) 
441{
442  GError *error = NULL;
443  logging_thread = g_thread_create(owl_log_thread_func,
444                                   NULL,
445                                   TRUE,
446                                   &error);
447  if (error) {
448    endwin();
449    fprintf(stderr, "Error spawning logging thread: %s\n", error->message);
450    fflush(stderr);
451    exit(1);
452  }
453 
454}
455
456static void owl_log_quit_func(gpointer data)
457{
458  g_main_loop_quit(log_loop);
459}
460
461void owl_log_shutdown(void)
462{
463  owl_select_post_task(owl_log_quit_func, NULL,
464                       NULL, log_context);
465  g_thread_join(logging_thread);
466}
Note: See TracBrowser for help on using the repository browser.