source: perlconfig.c @ 92ffd89

Last change on this file since 92ffd89 was 92ffd89, checked in by Jason Gross <jgross@mit.edu>, 8 years ago
Refactor perl calls through a single method I don't know the perl/C interface well enough to figure out the best way to standardize the many variants that we use to call perl code. Perhaps we should standardize the error messages, and put less knobs on the boilerplate macro.
  • Property mode set to 100644
File size: 13.0 KB
RevLine 
[908e388]1#define OWL_PERL
[f1e629d]2#include "owl.h"
[f271129]3#include <stdio.h>
[f1e629d]4
[8203afd]5extern XS(boot_BarnOwl);
[908e388]6extern XS(boot_DynaLoader);
[af1920fd]7/* extern XS(boot_DBI); */
[f1e629d]8
[5aa33fd]9void owl_perl_xs_init(pTHX) /* noproto */
[c3acb0b]10{
[e19eb97]11  const char *file = __FILE__;
[f1e629d]12  dXSUB_SYS;
13  {
[8203afd]14    newXS("BarnOwl::bootstrap", boot_BarnOwl, file);
[908e388]15    newXS("DynaLoader::boot_DynaLoader", boot_DynaLoader, file);
[f1e629d]16  }
17}
18
[d1b1cf6]19
[6829afc]20CALLER_OWN SV *owl_new_sv(const char * str)
[d1b1cf6]21{
22  SV *ret = newSVpv(str, 0);
[68f358c]23  if (is_utf8_string((const U8 *)str, strlen(str))) {
[d1b1cf6]24    SvUTF8_on(ret);
25  } else {
[eea72a1]26    char *escape = owl_escape_highbit(str);
27    owl_function_error("Internal error! Non-UTF-8 string encountered:\n%s", escape);
[ddbbcffa]28    g_free(escape);
[d1b1cf6]29  }
30  return ret;
31}
32
[ce68f23]33CALLER_OWN AV *owl_new_av(const GPtrArray *l, SV *(*to_sv)(const void *))
[e67359b]34{
35  AV *ret;
36  int i;
37  void *element;
38
39  ret = newAV();
40
[ce68f23]41  for (i = 0; i < l->len; i++) {
42    element = l->pdata[i];
[e67359b]43    av_push(ret, to_sv(element));
44  }
45
46  return ret;
47}
48
[6829afc]49CALLER_OWN HV *owl_new_hv(const owl_dict *d, SV *(*to_sv)(const void *))
[e7f5970]50{
51  HV *ret;
[ce68f23]52  GPtrArray *keys;
[e7f5970]53  const char *key;
54  void *element;
55  int i;
56
57  ret = newHV();
58
59  /* TODO: add an iterator-like interface to owl_dict */
[ce68f23]60  keys = owl_dict_get_keys(d);
61  for (i = 0; i < keys->len; i++) {
62    key = keys->pdata[i];
[e7f5970]63    element = owl_dict_find_element(d, key);
64    (void)hv_store(ret, key, strlen(key), to_sv(element), 0);
65  }
[ce68f23]66  owl_ptr_array_free(keys, g_free);
[e7f5970]67
68  return ret;
69}
70
[6829afc]71CALLER_OWN SV *owl_perlconfig_message2hashref(const owl_message *m)
[c3acb0b]72{
[1cc95709]73  HV *h, *stash;
[f1e629d]74  SV *hr;
[e19eb97]75  const char *type;
[fa4562c]76  char *ptr, *utype, *blessas;
[d199207]77  const char *f;
78  int i;
[25fb825]79  const owl_pair *pair;
[4542047]80  const owl_filter *wrap;
[f1e629d]81
82  if (!m) return &PL_sv_undef;
[f6b319c]83  wrap = owl_global_get_filter(&g, "wordwrap");
84  if(!wrap) {
85      owl_function_error("wrap filter is not defined");
86      return &PL_sv_undef;
87  }
88
[f1e629d]89  h = newHV();
90
[3ea31b6]91#define MSG2H(h,field) (void)hv_store(h, #field, strlen(#field),        \
[d1b1cf6]92                                      owl_new_sv(owl_message_get_##field(m)), 0)
[f1e629d]93
[6401db3]94  if (owl_message_is_type_zephyr(m) && owl_message_is_direction_in(m)) {
[f1e629d]95    /* Handle zephyr-specific fields... */
[6401db3]96    AV *av_zfields = newAV();
97    if (owl_message_get_notice(m)) {
98      for (f = owl_zephyr_first_raw_field(owl_message_get_notice(m)); f != NULL;
99           f = owl_zephyr_next_raw_field(owl_message_get_notice(m), f)) {
100        ptr = owl_zephyr_field_as_utf8(owl_message_get_notice(m), f);
101        av_push(av_zfields, owl_new_sv(ptr));
102        g_free(ptr);
103      }
104      (void)hv_store(h, "auth", strlen("auth"),
105                     owl_new_sv(owl_zephyr_get_authstr(owl_message_get_notice(m))), 0);
106    } else {
107      /* Incoming zephyrs without a ZNotice_t are pseudo-logins. To appease
108       * existing styles, put in bogus 'auth' and 'fields' keys. */
109      (void)hv_store(h, "auth", strlen("auth"), owl_new_sv("NO"), 0);
[f1e629d]110    }
[3ea31b6]111    (void)hv_store(h, "fields", strlen("fields"), newRV_noinc((SV*)av_zfields), 0);
[f1e629d]112  }
113
[f9df2f0]114  for (i = 0; i < m->attributes->len; i++) {
115    pair = m->attributes->pdata[i];
[3ea31b6]116    (void)hv_store(h, owl_pair_get_key(pair), strlen(owl_pair_get_key(pair)),
[d1b1cf6]117                   owl_new_sv(owl_pair_get_value(pair)),0);
[421c8ef7]118  }
119 
[f1e629d]120  MSG2H(h, type);
121  MSG2H(h, direction);
122  MSG2H(h, class);
123  MSG2H(h, instance);
124  MSG2H(h, sender);
125  MSG2H(h, realm);
126  MSG2H(h, recipient);
127  MSG2H(h, opcode);
128  MSG2H(h, hostname);
129  MSG2H(h, body);
130  MSG2H(h, login);
131  MSG2H(h, zsig);
132  MSG2H(h, zwriteline);
133  if (owl_message_get_header(m)) {
134    MSG2H(h, header); 
135  }
[d1b1cf6]136  (void)hv_store(h, "time", strlen("time"), owl_new_sv(owl_message_get_timestr(m)),0);
[d1ae4a4]137  (void)hv_store(h, "unix_time", strlen("unix_time"), newSViv(m->time), 0);
[3ea31b6]138  (void)hv_store(h, "id", strlen("id"), newSViv(owl_message_get_id(m)),0);
139  (void)hv_store(h, "deleted", strlen("deleted"), newSViv(owl_message_is_delete(m)),0);
140  (void)hv_store(h, "private", strlen("private"), newSViv(owl_message_is_private(m)),0);
141  (void)hv_store(h, "should_wordwrap",
142                 strlen("should_wordwrap"), newSViv(
143                                                    owl_filter_message_match(wrap, m)),0);
[f1e629d]144
[30678ae]145  type = owl_message_get_type(m);
[8fba2ee]146  if(!type || !*type) type = "generic";
[d4927a7]147  utype = g_strdup(type);
[fa4562c]148  utype[0] = toupper(type[0]);
[3472845]149  blessas = g_strdup_printf("BarnOwl::Message::%s", utype);
[f1e629d]150
[19bab8e]151  hr = newRV_noinc((SV*)h);
[1cc95709]152  stash =  gv_stashpv(blessas,0);
153  if(!stash) {
154    owl_function_error("No such class: %s for message type %s", blessas, owl_message_get_type(m));
155    stash = gv_stashpv("BarnOwl::Message", 1);
156  }
157  hr = sv_bless(hr,stash);
[ddbbcffa]158  g_free(utype);
159  g_free(blessas);
[30678ae]160  return hr;
[f1e629d]161}
162
[6829afc]163CALLER_OWN SV *owl_perlconfig_curmessage2hashref(void)
[c3acb0b]164{
[f1e629d]165  int curmsg;
[9e5c9f3]166  const owl_view *v;
[f1e629d]167  v=owl_global_get_current_view(&g);
168  if (owl_view_get_size(v) < 1) {
169    return &PL_sv_undef;
170  }
171  curmsg=owl_global_get_curmsg(&g);
172  return owl_perlconfig_message2hashref(owl_view_get_element(v, curmsg));
173}
174
[30678ae]175/* XXX TODO: Messages should round-trip properly between
176   message2hashref and hashref2message. Currently we lose
177   zephyr-specific properties stored in the ZNotice_t
178 */
[6829afc]179CALLER_OWN owl_message *owl_perlconfig_hashref2message(SV *msg)
[30678ae]180{
181  owl_message * m;
182  HE * ent;
[c107129]183  I32 len;
[e19eb97]184  const char *key,*val;
[30678ae]185  HV * hash;
[ad15610]186  struct tm tm;
[30678ae]187
188  hash = (HV*)SvRV(msg);
189
[96828e4]190  m = g_new(owl_message, 1);
[30678ae]191  owl_message_init(m);
192
[c107129]193  hv_iterinit(hash);
[30678ae]194  while((ent = hv_iternext(hash))) {
195    key = hv_iterkey(ent, &len);
196    val = SvPV_nolen(hv_iterval(hash, ent));
197    if(!strcmp(key, "type")) {
198      owl_message_set_type(m, val);
199    } else if(!strcmp(key, "direction")) {
200      owl_message_set_direction(m, owl_message_parse_direction(val));
201    } else if(!strcmp(key, "private")) {
202      SV * v = hv_iterval(hash, ent);
203      if(SvTRUE(v)) {
204        owl_message_set_isprivate(m);
205      }
206    } else if (!strcmp(key, "hostname")) {
207      owl_message_set_hostname(m, val);
208    } else if (!strcmp(key, "zwriteline")) {
209      owl_message_set_zwriteline(m, val);
210    } else if (!strcmp(key, "time")) {
[937a00e9]211      g_free(m->timestr);
[d4927a7]212      m->timestr = g_strdup(val);
[30678ae]213      strptime(val, "%a %b %d %T %Y", &tm);
214      m->time = mktime(&tm);
215    } else {
216      owl_message_set_attribute(m, key, val);
217    }
218  }
219  if(owl_message_is_type_admin(m)) {
220    if(!owl_message_get_attribute_value(m, "adminheader"))
221      owl_message_set_attribute(m, "adminheader", "");
222  }
223  return m;
224}
[f1e629d]225
226/* Calls in a scalar context, passing it a hash reference.
227   If return value is non-null, caller must free. */
[6829afc]228CALLER_OWN char *owl_perlconfig_call_with_message(const char *subname, const owl_message *m)
[c3acb0b]229{
[92ffd89]230  SV *msgref, *rv;
231  char *out = NULL;
[f1e629d]232
[92ffd89]233  msgref = owl_perlconfig_message2hashref(m);
[f1e629d]234
[92ffd89]235  OWL_PERL_CALL((call_pv(subname, G_SCALAR|G_EVAL))
236                ,
237                XPUSHs(sv_2mortal(msgref));
238                ,
239                "Perl Error: '%s'"
240                ,
241                false
242                ,
243                false
244                ,
245                rv = POPs;
246                if (rv && SvPOK(rv))
247                  out = g_strdup(SvPV_nolen(rv));
248                );
[f1e629d]249  return out;
250}
251
[25729b2]252
253/* Calls a method on a perl object representing a message.
254   If the return value is non-null, the caller must free it.
255 */
[6829afc]256CALLER_OWN char *owl_perlconfig_message_call_method(const owl_message *m, const char *method, int argc, const char **argv)
[25729b2]257{
[92ffd89]258  SV *msgref, *rv;
259  char *out = NULL;
260  int i;
[25729b2]261
262  msgref = owl_perlconfig_message2hashref(m);
263
[92ffd89]264  OWL_PERL_CALL(call_method(method, G_SCALAR|G_EVAL)
265                ,
266                XPUSHs(sv_2mortal(msgref));
267                OWL_PERL_PUSH_ARGS(i, argc, argv);
268                ,
269                "Perl Error: '%s'"
270                ,
271                false
272                ,
273                false
274                ,
275                rv = POPs;
276                if (rv && SvPOK(rv))
277                  out = g_strdup(SvPV_nolen(rv));
278                );
[25729b2]279  return out;
280}
281
[d427f08]282/* caller must free result, if not NULL */
[6829afc]283CALLER_OWN char *owl_perlconfig_initperl(const char *file, int *Pargc, char ***Pargv, char ***Penv)
[c3acb0b]284{
[d03091c]285  int ret;
[f1e629d]286  PerlInterpreter *p;
[4e0f545]287  char *err;
[e19eb97]288  const char *args[4] = {"", "-e", "0;", NULL};
[e5210c9]289  const char *dlerr;
[fd8dfe7]290  AV *inc;
291  char *path;
[f1e629d]292
293  /* create and initialize interpreter */
[e8c6d8f]294  PERL_SYS_INIT3(Pargc, Pargv, Penv);
[f1e629d]295  p=perl_alloc();
[4d86e06]296  owl_global_set_perlinterp(&g, p);
[f1e629d]297  perl_construct(p);
298
[5aa33fd]299  PL_exit_flags |= PERL_EXIT_DESTRUCT_END;
300
[f1e629d]301  owl_global_set_no_have_config(&g);
302
[fa4562c]303  ret=perl_parse(p, owl_perl_xs_init, 2, (char **)args, NULL);
[f1e629d]304  if (ret || SvTRUE(ERRSV)) {
[d4927a7]305    err=g_strdup(SvPV_nolen(ERRSV));
[4e0f545]306    sv_setsv(ERRSV, &PL_sv_undef);     /* and clear the error */
307    return(err);
[f1e629d]308  }
309
310  ret=perl_run(p);
311  if (ret || SvTRUE(ERRSV)) {
[d4927a7]312    err=g_strdup(SvPV_nolen(ERRSV));
[4e0f545]313    sv_setsv(ERRSV, &PL_sv_undef);     /* and clear the error */
314    return(err);
[f1e629d]315  }
316
317  owl_global_set_have_config(&g);
318
319  /* create legacy variables */
[c415aca]320  get_sv("BarnOwl::id", TRUE);
321  get_sv("BarnOwl::class", TRUE);
322  get_sv("BarnOwl::instance", TRUE);
323  get_sv("BarnOwl::recipient", TRUE);
324  get_sv("BarnOwl::sender", TRUE);
325  get_sv("BarnOwl::realm", TRUE);
326  get_sv("BarnOwl::opcode", TRUE);
327  get_sv("BarnOwl::zsig", TRUE);
328  get_sv("BarnOwl::msg", TRUE);
329  get_sv("BarnOwl::time", TRUE);
330  get_sv("BarnOwl::host", TRUE);
331  get_av("BarnOwl::fields", TRUE);
[00f9a7d]332
333  if(file) {
[8203afd]334    SV * cfg = get_sv("BarnOwl::configfile", TRUE);
[00f9a7d]335    sv_setpv(cfg, file);
336  }
337
[f2d71cfa]338  sv_setpv(get_sv("BarnOwl::VERSION", TRUE), OWL_VERSION_STRING);
339
[fd8dfe7]340  /* Add the system lib path to @INC */
341  inc = get_av("INC", 0);
[dde1b4d]342  path = g_build_filename(owl_get_datadir(), "lib", NULL);
[fd8dfe7]343  av_unshift(inc, 1);
[d1b1cf6]344  av_store(inc, 0, owl_new_sv(path));
[ddbbcffa]345  g_free(path);
[fd8dfe7]346
[e5210c9]347  /* Load up perl-Glib. */
348  eval_pv("use Glib;", FALSE);
349
350  /* Now, before BarnOwl tries to use them, get the relevant function pointers out. */
351  dlerr = owl_closure_init();
352  if (dlerr) {
353    return g_strdup(dlerr);
354  }
355
356  /* And now it's safe to import BarnOwl. */
[fd8dfe7]357  eval_pv("use BarnOwl;", FALSE);
[f1e629d]358
359  if (SvTRUE(ERRSV)) {
[d4927a7]360    err=g_strdup(SvPV_nolen(ERRSV));
[27c3a93]361    sv_setsv (ERRSV, &PL_sv_undef);     /* and clear the error */
[4e0f545]362    return(err);
[f1e629d]363  }
364
365  /* check if we have the formatting function */
[8203afd]366  if (owl_perlconfig_is_function("BarnOwl::format_msg")) {
[f1e629d]367    owl_global_set_config_format(&g, 1);
368  }
369
370  return(NULL);
371}
372
373/* returns whether or not a function exists */
[e19eb97]374int owl_perlconfig_is_function(const char *fn) {
[c415aca]375  if (get_cv(fn, FALSE)) return(1);
[f1e629d]376  else return(0);
377}
378
379/* caller is responsible for freeing returned string */
[6829afc]380CALLER_OWN char *owl_perlconfig_execute(const char *line)
[c3acb0b]381{
[f1e629d]382  STRLEN len;
383  SV *response;
[65b2173]384  char *out;
[f1e629d]385
386  if (!owl_global_have_config(&g)) return NULL;
387
[e0096b7]388  ENTER;
389  SAVETMPS;
[f1e629d]390  /* execute the subroutine */
[c415aca]391  response = eval_pv(line, FALSE);
[f1e629d]392
393  if (SvTRUE(ERRSV)) {
[ce6721f]394    owl_function_error("Perl Error: '%s'", SvPV_nolen(ERRSV));
[27c3a93]395    sv_setsv (ERRSV, &PL_sv_undef);     /* and clear the error */
[f1e629d]396  }
397
[d4927a7]398  out = g_strdup(SvPV(response, len));
[e0096b7]399  FREETMPS;
400  LEAVE;
[f1e629d]401
402  return(out);
403}
404
[c08c70a]405void owl_perlconfig_getmsg(const owl_message *m, const char *subname)
[b67ab6b]406{
407  char *ptr = NULL;
408  if (owl_perlconfig_is_function("BarnOwl::Hooks::_receive_msg")) {
409    ptr = owl_perlconfig_call_with_message(subname?subname
410                                           :"BarnOwl::_receive_msg_legacy_wrap", m);
[f1e629d]411  }
[3b8a563]412  g_free(ptr);
[0f9eca7]413}
414
415/* Called on all new messages; receivemsg is only called on incoming ones */
[c08c70a]416void owl_perlconfig_newmsg(const owl_message *m, const char *subname)
[0f9eca7]417{
418  char *ptr = NULL;
419  if (owl_perlconfig_is_function("BarnOwl::Hooks::_new_msg")) {
420    ptr = owl_perlconfig_call_with_message(subname?subname
421                                           :"BarnOwl::Hooks::_new_msg", m);
422  }
[3b8a563]423  g_free(ptr);
[f1e629d]424}
[6922edd]425
[e19eb97]426void owl_perlconfig_new_command(const char *name)
[eb6cedc]427{
[92ffd89]428  OWL_PERL_CALL(call_pv("BarnOwl::Hooks::_new_command", G_VOID|G_EVAL);
429                ,
430                XPUSHs(sv_2mortal(owl_new_sv(name)));
431                ,
432                "Perl Error: '%s'"
433                ,
434                false
435                ,
436                true
437                ,
438                );
[eb6cedc]439}
440
[d427f08]441/* caller must free the result */
[6829afc]442CALLER_OWN char *owl_perlconfig_perlcmd(const owl_cmd *cmd, int argc, const char *const *argv)
[6922edd]443{
[92ffd89]444  int i;
445  SV* rv;
446  char *out = NULL;
447
448  OWL_PERL_CALL(call_sv(cmd->cmd_perl, G_SCALAR|G_EVAL)
449                ,
450                OWL_PERL_PUSH_ARGS(i, argc, argv);
451                ,
452                "Perl Error: '%s'"
453                ,
454                false
455                ,
456                false
457                ,
458                rv = POPs;
459                if (rv && SvPOK(rv))
460                  out = g_strdup(SvPV_nolen(rv));
461                );
462  return out;
[6922edd]463}
464
[8f2d9bf]465void owl_perlconfig_cmd_cleanup(owl_cmd *cmd)
[6922edd]466{
[ff13a6f]467  SvREFCNT_dec(cmd->cmd_perl);
[6922edd]468}
[db8b00b]469
[7803326]470void owl_perlconfig_edit_callback(owl_editwin *e, bool success)
[db8b00b]471{
[4d86e06]472  SV *cb = owl_editwin_get_cbdata(e);
[92ffd89]473  SV *text = owl_new_sv(owl_editwin_get_text(e));
[ad15610]474
[92ffd89]475  if (cb == NULL) {
[db8b00b]476    owl_function_error("Perl callback is NULL!");
[1373d35]477    return;
[db8b00b]478  }
479
[92ffd89]480  OWL_PERL_CALL(call_sv(cb, G_DISCARD|G_EVAL)
481                ,
482                XPUSHs(sv_2mortal(text));
483                XPUSHs(sv_2mortal(newSViv(success)));
484                ,
485                "Perl Error: '%s'"
486                ,
487                false
488                ,
489                true
490                ,
491                );
[1b1cd2c]492}
[db8b00b]493
[1b1cd2c]494void owl_perlconfig_dec_refcnt(void *data)
495{
496  SV *v = data;
497  SvREFCNT_dec(v);
[db8b00b]498}
Note: See TracBrowser for help on using the repository browser.