source: perlconfig.c @ 7dfe886

Last change on this file since 7dfe886 was 7dfe886, checked in by Jason Gross <jgross@mit.edu>, 13 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: 14.8 KB
Line 
1#include <stdio.h>
2#include <stdlib.h>
3#include <string.h>
4#include <sys/types.h>
5#include <sys/stat.h>
6#include <errno.h>
7#define OWL_PERL
8#include "owl.h"
9
10extern XS(boot_BarnOwl);
11extern XS(boot_DynaLoader);
12/* extern XS(boot_DBI); */
13
14void owl_perl_xs_init(pTHX) /* noproto */
15{
16  const char *file = __FILE__;
17  dXSUB_SYS;
18  {
19    newXS("BarnOwl::bootstrap", boot_BarnOwl, file);
20    newXS("DynaLoader::boot_DynaLoader", boot_DynaLoader, file);
21  }
22}
23
24
25G_GNUC_WARN_UNUSED_RESULT SV *owl_new_sv(const char * str)
26{
27  SV *ret = newSVpv(str, 0);
28  if (is_utf8_string((const U8 *)str, strlen(str))) {
29    SvUTF8_on(ret);
30  } else {
31    char *escape = owl_escape_highbit(str);
32    owl_function_error("Internal error! Non-UTF-8 string encountered:\n%s", escape);
33    g_free(escape);
34  }
35  return ret;
36}
37
38G_GNUC_WARN_UNUSED_RESULT AV *owl_new_av(const owl_list *l, SV *(*to_sv)(const void *))
39{
40  AV *ret;
41  int i;
42  void *element;
43
44  ret = newAV();
45
46  for (i = 0; i < owl_list_get_size(l); i++) {
47    element = owl_list_get_element(l, i);
48    av_push(ret, to_sv(element));
49  }
50
51  return ret;
52}
53
54G_GNUC_WARN_UNUSED_RESULT HV *owl_new_hv(const owl_dict *d, SV *(*to_sv)(const void *))
55{
56  HV *ret;
57  owl_list l;
58  const char *key;
59  void *element;
60  int i;
61
62  ret = newHV();
63
64  /* TODO: add an iterator-like interface to owl_dict */
65  owl_list_create(&l);
66  owl_dict_get_keys(d, &l);
67  for (i = 0; i < owl_list_get_size(&l); i++) {
68    key = owl_list_get_element(&l, i);
69    element = owl_dict_find_element(d, key);
70    (void)hv_store(ret, key, strlen(key), to_sv(element), 0);
71  }
72  owl_list_cleanup(&l, g_free);
73
74  return ret;
75}
76
77G_GNUC_WARN_UNUSED_RESULT SV *owl_perlconfig_message2hashref(const owl_message *m)
78{
79  HV *h, *stash;
80  SV *hr;
81  const char *type;
82  char *ptr, *utype, *blessas;
83  int i, j;
84  const owl_pair *pair;
85  const owl_filter *wrap;
86
87  if (!m) return &PL_sv_undef;
88  wrap = owl_global_get_filter(&g, "wordwrap");
89  if(!wrap) {
90      owl_function_error("wrap filter is not defined");
91      return &PL_sv_undef;
92  }
93
94  h = newHV();
95
96#define MSG2H(h,field) (void)hv_store(h, #field, strlen(#field),        \
97                                      owl_new_sv(owl_message_get_##field(m)), 0)
98
99  if (owl_message_is_type_zephyr(m)
100      && owl_message_is_direction_in(m)) {
101    /* Handle zephyr-specific fields... */
102    AV *av_zfields;
103
104    av_zfields = newAV();
105    j=owl_zephyr_get_num_fields(owl_message_get_notice(m));
106    for (i=0; i<j; i++) {
107      ptr=owl_zephyr_get_field_as_utf8(owl_message_get_notice(m), i+1);
108      av_push(av_zfields, owl_new_sv(ptr));
109      g_free(ptr);
110    }
111    (void)hv_store(h, "fields", strlen("fields"), newRV_noinc((SV*)av_zfields), 0);
112
113    (void)hv_store(h, "auth", strlen("auth"), 
114                   owl_new_sv(owl_zephyr_get_authstr(owl_message_get_notice(m))),0);
115  }
116
117  j=owl_list_get_size(&(m->attributes));
118  for(i=0; i<j; i++) {
119    pair=owl_list_get_element(&(m->attributes), i);
120    (void)hv_store(h, owl_pair_get_key(pair), strlen(owl_pair_get_key(pair)),
121                   owl_new_sv(owl_pair_get_value(pair)),0);
122  }
123 
124  MSG2H(h, type);
125  MSG2H(h, direction);
126  MSG2H(h, class);
127  MSG2H(h, instance);
128  MSG2H(h, sender);
129  MSG2H(h, realm);
130  MSG2H(h, recipient);
131  MSG2H(h, opcode);
132  MSG2H(h, hostname);
133  MSG2H(h, body);
134  MSG2H(h, login);
135  MSG2H(h, zsig);
136  MSG2H(h, zwriteline);
137  if (owl_message_get_header(m)) {
138    MSG2H(h, header); 
139  }
140  (void)hv_store(h, "time", strlen("time"), owl_new_sv(owl_message_get_timestr(m)),0);
141  (void)hv_store(h, "unix_time", strlen("unix_time"), newSViv(m->time), 0);
142  (void)hv_store(h, "id", strlen("id"), newSViv(owl_message_get_id(m)),0);
143  (void)hv_store(h, "deleted", strlen("deleted"), newSViv(owl_message_is_delete(m)),0);
144  (void)hv_store(h, "private", strlen("private"), newSViv(owl_message_is_private(m)),0);
145  (void)hv_store(h, "should_wordwrap",
146                 strlen("should_wordwrap"), newSViv(
147                                                    owl_filter_message_match(wrap, m)),0);
148
149  type = owl_message_get_type(m);
150  if(!type || !*type) type = "generic";
151  utype = g_strdup(type);
152  utype[0] = toupper(type[0]);
153  blessas = g_strdup_printf("BarnOwl::Message::%s", utype);
154
155  hr = newRV_noinc((SV*)h);
156  stash =  gv_stashpv(blessas,0);
157  if(!stash) {
158    owl_function_error("No such class: %s for message type %s", blessas, owl_message_get_type(m));
159    stash = gv_stashpv("BarnOwl::Message", 1);
160  }
161  hr = sv_bless(hr,stash);
162  g_free(utype);
163  g_free(blessas);
164  return hr;
165}
166
167G_GNUC_WARN_UNUSED_RESULT SV *owl_perlconfig_curmessage2hashref(void)
168{
169  int curmsg;
170  const owl_view *v;
171  v=owl_global_get_current_view(&g);
172  if (owl_view_get_size(v) < 1) {
173    return &PL_sv_undef;
174  }
175  curmsg=owl_global_get_curmsg(&g);
176  return owl_perlconfig_message2hashref(owl_view_get_element(v, curmsg));
177}
178
179/* XXX TODO: Messages should round-trip properly between
180   message2hashref and hashref2message. Currently we lose
181   zephyr-specific properties stored in the ZNotice_t
182
183   This has been somewhat addressed, but is still not lossless.
184 */
185G_GNUC_WARN_UNUSED_RESULT owl_message *owl_perlconfig_hashref2message(SV *msg)
186{
187  owl_message * m;
188  HE * ent;
189  I32 len;
190  const char *key,*val;
191  HV * hash;
192  struct tm tm;
193
194  hash = (HV*)SvRV(msg);
195
196  m = g_new(owl_message, 1);
197  owl_message_init(m);
198
199  hv_iterinit(hash);
200  while((ent = hv_iternext(hash))) {
201    key = hv_iterkey(ent, &len);
202    val = SvPV_nolen(hv_iterval(hash, ent));
203    if(!strcmp(key, "type")) {
204      owl_message_set_type(m, val);
205    } else if(!strcmp(key, "direction")) {
206      owl_message_set_direction(m, owl_message_parse_direction(val));
207    } else if(!strcmp(key, "private")) {
208      SV * v = hv_iterval(hash, ent);
209      if(SvTRUE(v)) {
210        owl_message_set_isprivate(m);
211      }
212    } else if (!strcmp(key, "hostname")) {
213      owl_message_set_hostname(m, val);
214    } else if (!strcmp(key, "zwriteline")) {
215      owl_message_set_zwriteline(m, val);
216    } else if (!strcmp(key, "time")) {
217      m->timestr = g_strdup(val);
218      strptime(val, "%a %b %d %T %Y", &tm);
219      m->time = mktime(&tm);
220    } else {
221      owl_message_set_attribute(m, key, val);
222    }
223  }
224  if(owl_message_is_type_admin(m)) {
225    if(!owl_message_get_attribute_value(m, "adminheader"))
226      owl_message_set_attribute(m, "adminheader", "");
227  }
228#ifdef HAVE_LIBZEPHYR
229  if (owl_message_is_type_zephyr(m)) {
230    ZNotice_t *n = &(m->notice);
231    n->z_kind = ACKED;
232    n->z_port = 0;
233    n->z_auth = ZAUTH_NO;
234    n->z_checked_auth = 0;
235    n->z_class = zstr(owl_message_get_class(m));
236    n->z_class_inst = zstr(owl_message_get_instance(m));
237    n->z_opcode = zstr(owl_message_get_opcode(m));
238    n->z_sender = zstr(owl_message_get_sender(m));
239    n->z_recipient = zstr(owl_message_get_recipient(m));
240    n->z_default_format = zstr("[zephyr created from perl]");
241    n->z_multinotice = zstr("[zephyr created from perl]");
242    n->z_num_other_fields = 0;
243    n->z_message = g_strdup_printf("%s%c%s", owl_message_get_zsig(m), '\0', owl_message_get_body(m));
244    n->z_message_len = strlen(owl_message_get_zsig(m)) + strlen(owl_message_get_body(m)) + 1;
245  }
246#endif
247  return m;
248}
249
250/* Calls in a scalar context, passing it a hash reference.
251   If return value is non-null, caller must free. */
252G_GNUC_WARN_UNUSED_RESULT char *owl_perlconfig_call_with_message(const char *subname, const owl_message *m)
253{
254  dSP ;
255  int count;
256  SV *msgref, *srv;
257  char *out;
258 
259  ENTER ;
260  SAVETMPS;
261 
262  PUSHMARK(SP) ;
263  msgref = owl_perlconfig_message2hashref(m);
264  XPUSHs(sv_2mortal(msgref));
265  PUTBACK ;
266 
267  count = call_pv(subname, G_SCALAR|G_EVAL);
268 
269  SPAGAIN ;
270
271  if (SvTRUE(ERRSV)) {
272    owl_function_error("Perl Error: '%s'", SvPV_nolen(ERRSV));
273    /* and clear the error */
274    sv_setsv (ERRSV, &PL_sv_undef);
275  }
276
277  if (count != 1) {
278    fprintf(stderr, "bad perl!  no biscuit!  returned wrong count!\n");
279    abort();
280  }
281
282  srv = POPs;
283
284  if (srv) {
285    out = g_strdup(SvPV_nolen(srv));
286  } else {
287    out = NULL;
288  }
289 
290  PUTBACK ;
291  FREETMPS ;
292  LEAVE ;
293
294  return out;
295}
296
297
298/* Calls a method on a perl object representing a message.
299   If the return value is non-null, the caller must free it.
300 */
301G_GNUC_WARN_UNUSED_RESULT char *owl_perlconfig_message_call_method(const owl_message *m, const char *method, int argc, const char **argv)
302{
303  dSP;
304  unsigned int count, i;
305  SV *msgref, *srv;
306  char *out;
307
308  msgref = owl_perlconfig_message2hashref(m);
309
310  ENTER;
311  SAVETMPS;
312
313  PUSHMARK(SP);
314  XPUSHs(sv_2mortal(msgref));
315  for(i=0;i<argc;i++) {
316    XPUSHs(sv_2mortal(owl_new_sv(argv[i])));
317  }
318  PUTBACK;
319
320  count = call_method(method, G_SCALAR|G_EVAL);
321
322  SPAGAIN;
323
324  if(count != 1) {
325    fprintf(stderr, "perl returned wrong count %u\n", count);
326    abort();
327  }
328
329  if (SvTRUE(ERRSV)) {
330    owl_function_error("Error: '%s'", SvPV_nolen(ERRSV));
331    /* and clear the error */
332    sv_setsv (ERRSV, &PL_sv_undef);
333  }
334
335  srv = POPs;
336
337  if (srv) {
338    out = g_strdup(SvPV_nolen(srv));
339  } else {
340    out = NULL;
341  }
342
343  PUTBACK;
344  FREETMPS;
345  LEAVE;
346
347  return out;
348}
349
350/* caller must free result, if not NULL */
351G_GNUC_WARN_UNUSED_RESULT char *owl_perlconfig_initperl(const char *file, int *Pargc, char ***Pargv, char ***Penv)
352{
353  int ret;
354  PerlInterpreter *p;
355  char *err;
356  const char *args[4] = {"", "-e", "0;", NULL};
357  AV *inc;
358  char *path;
359
360  /* create and initialize interpreter */
361  PERL_SYS_INIT3(Pargc, Pargv, Penv);
362  p=perl_alloc();
363  owl_global_set_perlinterp(&g, p);
364  perl_construct(p);
365
366  PL_exit_flags |= PERL_EXIT_DESTRUCT_END;
367
368  owl_global_set_no_have_config(&g);
369
370  ret=perl_parse(p, owl_perl_xs_init, 2, (char **)args, NULL);
371  if (ret || SvTRUE(ERRSV)) {
372    err=g_strdup(SvPV_nolen(ERRSV));
373    sv_setsv(ERRSV, &PL_sv_undef);     /* and clear the error */
374    return(err);
375  }
376
377  ret=perl_run(p);
378  if (ret || SvTRUE(ERRSV)) {
379    err=g_strdup(SvPV_nolen(ERRSV));
380    sv_setsv(ERRSV, &PL_sv_undef);     /* and clear the error */
381    return(err);
382  }
383
384  owl_global_set_have_config(&g);
385
386  /* create legacy variables */
387  get_sv("BarnOwl::id", TRUE);
388  get_sv("BarnOwl::class", TRUE);
389  get_sv("BarnOwl::instance", TRUE);
390  get_sv("BarnOwl::recipient", TRUE);
391  get_sv("BarnOwl::sender", TRUE);
392  get_sv("BarnOwl::realm", TRUE);
393  get_sv("BarnOwl::opcode", TRUE);
394  get_sv("BarnOwl::zsig", TRUE);
395  get_sv("BarnOwl::msg", TRUE);
396  get_sv("BarnOwl::time", TRUE);
397  get_sv("BarnOwl::host", TRUE);
398  get_av("BarnOwl::fields", TRUE);
399
400  if(file) {
401    SV * cfg = get_sv("BarnOwl::configfile", TRUE);
402    sv_setpv(cfg, file);
403  }
404
405  sv_setpv(get_sv("BarnOwl::VERSION", TRUE), OWL_VERSION_STRING);
406
407  /* Add the system lib path to @INC */
408  inc = get_av("INC", 0);
409  path = g_strdup_printf("%s/lib", owl_get_datadir());
410  av_unshift(inc, 1);
411  av_store(inc, 0, owl_new_sv(path));
412  g_free(path);
413
414  eval_pv("use BarnOwl;", FALSE);
415
416  if (SvTRUE(ERRSV)) {
417    err=g_strdup(SvPV_nolen(ERRSV));
418    sv_setsv (ERRSV, &PL_sv_undef);     /* and clear the error */
419    return(err);
420  }
421
422  /* check if we have the formatting function */
423  if (owl_perlconfig_is_function("BarnOwl::format_msg")) {
424    owl_global_set_config_format(&g, 1);
425  }
426
427  return(NULL);
428}
429
430/* returns whether or not a function exists */
431int owl_perlconfig_is_function(const char *fn) {
432  if (get_cv(fn, FALSE)) return(1);
433  else return(0);
434}
435
436/* caller is responsible for freeing returned string */
437G_GNUC_WARN_UNUSED_RESULT char *owl_perlconfig_execute(const char *line)
438{
439  STRLEN len;
440  SV *response;
441  char *out;
442
443  if (!owl_global_have_config(&g)) return NULL;
444
445  ENTER;
446  SAVETMPS;
447  /* execute the subroutine */
448  response = eval_pv(line, FALSE);
449
450  if (SvTRUE(ERRSV)) {
451    owl_function_error("Perl Error: '%s'", SvPV_nolen(ERRSV));
452    sv_setsv (ERRSV, &PL_sv_undef);     /* and clear the error */
453  }
454
455  out = g_strdup(SvPV(response, len));
456  FREETMPS;
457  LEAVE;
458
459  return(out);
460}
461
462void owl_perlconfig_getmsg(const owl_message *m, const char *subname)
463{
464  char *ptr = NULL;
465  if (owl_perlconfig_is_function("BarnOwl::Hooks::_receive_msg")) {
466    ptr = owl_perlconfig_call_with_message(subname?subname
467                                           :"BarnOwl::_receive_msg_legacy_wrap", m);
468  }
469  g_free(ptr);
470}
471
472/* Called on all new messages; receivemsg is only called on incoming ones */
473void owl_perlconfig_newmsg(const owl_message *m, const char *subname)
474{
475  char *ptr = NULL;
476  if (owl_perlconfig_is_function("BarnOwl::Hooks::_new_msg")) {
477    ptr = owl_perlconfig_call_with_message(subname?subname
478                                           :"BarnOwl::Hooks::_new_msg", m);
479  }
480  g_free(ptr);
481}
482
483void owl_perlconfig_new_command(const char *name)
484{
485  dSP;
486
487  ENTER;
488  SAVETMPS;
489
490  PUSHMARK(SP);
491  XPUSHs(sv_2mortal(owl_new_sv(name)));
492  PUTBACK;
493
494  call_pv("BarnOwl::Hooks::_new_command", G_VOID|G_EVAL);
495
496  SPAGAIN;
497
498  if(SvTRUE(ERRSV)) {
499    owl_function_error("%s", SvPV_nolen(ERRSV));
500  }
501
502  FREETMPS;
503  LEAVE;
504}
505
506/* caller must free the result */
507G_GNUC_WARN_UNUSED_RESULT char *owl_perlconfig_perlcmd(const owl_cmd *cmd, int argc, const char *const *argv)
508{
509  int i, count;
510  char * ret = NULL;
511  SV *rv;
512  dSP;
513
514  ENTER;
515  SAVETMPS;
516
517  PUSHMARK(SP);
518  for(i=0;i<argc;i++) {
519    XPUSHs(sv_2mortal(owl_new_sv(argv[i])));
520  }
521  PUTBACK;
522
523  count = call_sv(cmd->cmd_perl, G_SCALAR|G_EVAL);
524
525  SPAGAIN;
526
527  if(SvTRUE(ERRSV)) {
528    owl_function_error("%s", SvPV_nolen(ERRSV));
529    (void)POPs;
530  } else {
531    if(count != 1)
532      croak("Perl command %s returned more than one value!", cmd->name);
533    rv = POPs;
534    if(SvTRUE(rv)) {
535      ret = g_strdup(SvPV_nolen(rv));
536    }
537  }
538
539  FREETMPS;
540  LEAVE;
541
542  return ret;
543}
544
545void owl_perlconfig_cmd_cleanup(owl_cmd *cmd)
546{
547  SvREFCNT_dec(cmd->cmd_perl);
548}
549
550void owl_perlconfig_io_dispatch_destroy(const owl_io_dispatch *d)
551{
552  SvREFCNT_dec(d->data);
553}
554
555void owl_perlconfig_edit_callback(owl_editwin *e)
556{
557  SV *cb = owl_editwin_get_cbdata(e);
558  SV *text;
559  dSP;
560
561  if(cb == NULL) {
562    owl_function_error("Perl callback is NULL!");
563    return;
564  }
565  text = owl_new_sv(owl_editwin_get_text(e));
566
567  ENTER;
568  SAVETMPS;
569
570  PUSHMARK(SP);
571  XPUSHs(sv_2mortal(text));
572  PUTBACK;
573 
574  call_sv(cb, G_DISCARD|G_EVAL);
575
576  if(SvTRUE(ERRSV)) {
577    owl_function_error("%s", SvPV_nolen(ERRSV));
578  }
579
580  FREETMPS;
581  LEAVE;
582}
583
584void owl_perlconfig_dec_refcnt(void *data)
585{
586  SV *v = data;
587  SvREFCNT_dec(v);
588}
589
590void owl_perlconfig_io_dispatch(const owl_io_dispatch *d, void *data)
591{
592  SV *cb = data;
593  dSP;
594  if(cb == NULL) {
595    owl_function_error("Perl callback is NULL!");
596    return;
597  }
598
599  ENTER;
600  SAVETMPS;
601
602  PUSHMARK(SP);
603  PUTBACK;
604
605  call_sv(cb, G_DISCARD|G_EVAL);
606
607  if(SvTRUE(ERRSV)) {
608    owl_function_error("%s", SvPV_nolen(ERRSV));
609  }
610
611  FREETMPS;
612  LEAVE;
613}
614
615void owl_perlconfig_perl_timer(owl_timer *t, void *data)
616{
617  dSP;
618  SV *obj = data;
619
620  if(!SvROK(obj)) {
621    return;
622  }
623
624  ENTER;
625  SAVETMPS;
626
627  PUSHMARK(SP);
628  XPUSHs(obj);
629  PUTBACK;
630
631  call_method("do_callback", G_DISCARD|G_EVAL);
632
633  SPAGAIN;
634
635  if (SvTRUE(ERRSV)) {
636    owl_function_error("Error in callback: '%s'", SvPV_nolen(ERRSV));
637    sv_setsv (ERRSV, &PL_sv_undef);
638  }
639
640  PUTBACK;
641  FREETMPS;
642  LEAVE;
643}
644
645void owl_perlconfig_perl_timer_destroy(owl_timer *t)
646{
647  if(SvOK((SV*)t->data)) {
648    SvREFCNT_dec((SV*)t->data);
649  }
650}
Note: See TracBrowser for help on using the repository browser.