1 // Copyright (C) 2002 Graydon Hoare <graydon@pobox.com>
2 //               2008 Stephen Leake <stephen_leake@stephe-leake.org>
3 //
4 // This program is made available under the GNU GPL version 2.0 or
5 // greater. See the accompanying file COPYING for details.
6 //
7 // This program is distributed WITHOUT ANY WARRANTY; without even the
8 // implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
9 // PURPOSE.
10 
11 
12 #include "base.hh"
13 
14 #include <set>
15 #include <map>
16 #include <fstream>
17 #include <iostream>
18 
19 #include "lua.hh"
20 
21 #include "app_state.hh"
22 #include "file_io.hh"
23 #include "lua_hooks.hh"
24 #include "sanity.hh"
25 #include "vocab.hh"
26 #include "transforms.hh"
27 #include "paths.hh"
28 #include "project.hh"
29 #include "uri.hh"
30 #include "cmd.hh"
31 #include "commands.hh"
32 #include "globish.hh"
33 #include "simplestring_xform.hh"
34 
35 // defined in std_hooks.c, generated from std_hooks.lua
36 extern char const std_hooks_constant[];
37 
38 using std::make_pair;
39 using std::sort;
40 
panic_thrower(lua_State * st)41 static int panic_thrower(lua_State * st)
42 {
43   throw oops("lua panic");
44 }
45 
46 // this lets the lua callbacks (monotone_*_for_lua) have access to the
47 // app_state the're associated with.
48 // it was added so that the confdir (normally ~/.monotone) can be specified on
49 // the command line (and so known only to the app_state), and still be
50 // available to lua
51 // please *don't* use it for complex things that can throw errors
52 static map<lua_State*, app_state*> map_of_lua_to_app;
53 
54 extern "C"
55 {
56   static int
monotone_get_confdir_for_lua(lua_State * LS)57   monotone_get_confdir_for_lua(lua_State *LS)
58   {
59     map<lua_State*, app_state*>::iterator i = map_of_lua_to_app.find(LS);
60     if (i != map_of_lua_to_app.end())
61       {
62         if (i->second->opts.conf_dir_given
63             || !i->second->opts.no_default_confdir)
64           {
65             system_path dir = i->second->opts.conf_dir;
66             string confdir = dir.as_external();
67             lua_pushstring(LS, confdir.c_str());
68           }
69         else
70           lua_pushnil(LS);
71       }
72     else
73       lua_pushnil(LS);
74     return 1;
75   }
76   // taken from http://medek.wordpress.com/2009/02/03/wrapping-lua-errors-and-print-function/
77   static int
monotone_message(lua_State * LS)78   monotone_message(lua_State *LS)
79   {
80     int nArgs = lua_gettop(LS);
81     lua_getglobal(LS, "tostring");
82 
83     string ret;
84     for (int i = 1; i <= nArgs; ++i)
85       {
86         const char *s;
87         lua_pushvalue(LS, -1);
88         lua_pushvalue(LS, i);
89         lua_call(LS, 1, 1);
90         s = lua_tostring(LS, -1);
91         if (s == NULL)
92           return luaL_error(
93             LS, LUA_QL("tostring") " must return a string to ", LUA_QL("print")
94           );
95 
96         if (i > 1)
97           ret.append("\t");
98 
99         ret.append(s);
100         lua_pop(LS, 1);
101       }
102 
103     string prefixed;
104     prefix_lines_with(_("lua: "), ret, prefixed);
105     P(F("%s") % prefixed);
106     return 0;
107   }
108 }
109 
110 app_state*
get_app_state(lua_State * LS)111 get_app_state(lua_State *LS)
112 {
113   map<lua_State*, app_state*>::iterator i = map_of_lua_to_app.find(LS);
114   if (i != map_of_lua_to_app.end())
115     return i->second;
116   else
117     return NULL;
118 }
119 
120 namespace {
push_key_identity_info(Lua & ll,key_identity_info const & info)121   Lua & push_key_identity_info(Lua & ll,
122                                key_identity_info const & info)
123   {
124     hexenc<id> hexid;
125     encode_hexenc(info.id.inner(), hexid);
126     ll.push_table()
127       .push_str(hexid())
128       .set_field("id")
129       .push_str(info.given_name())
130       .set_field("given_name")
131       .push_str(info.official_name())
132       .set_field("name");
133     return ll;
134   }
135 }
136 
lua_hooks(app_state * app)137 lua_hooks::lua_hooks(app_state * app)
138 {
139   st = luaL_newstate();
140   I(st);
141 
142   lua_atpanic (st, &panic_thrower);
143 
144   luaL_openlibs(st);
145 
146   lua_register(st, "get_confdir", monotone_get_confdir_for_lua);
147   lua_register(st, "message", monotone_message);
148   add_functions(st);
149 
150   // Disable any functions we don't want. This is easiest
151   // to do just by running a lua string.
152   static char const disable_dangerous[] =
153     "os.execute = function(c) "
154     " error(\"os.execute disabled for security reasons.  Try spawn().\") "
155     "end "
156     "io.popen = function(c,t) "
157     " error(\"io.popen disabled for security reasons.  Try spawn_pipe().\") "
158     "end ";
159 
160     if (!run_string(st, disable_dangerous,
161                     "<disabled dangerous functions>"))
162     throw oops("lua error while disabling existing functions");
163 
164   // redirect output to internal message handler which calls into
165   // our user interface code. Note that we send _everything_ to stderr
166   // or as out-of-band progress stream to keep our stdout clean
167   static char const redirect_output[] =
168     "io.write = function(...) "
169     "  message(...) "
170     "end "
171     "print = function(...) "
172     "  message(...) "
173     "end ";
174 
175     if (!run_string(st, redirect_output,
176                     "<redirect output>"))
177     throw oops("lua error while redirecting output");
178 
179   map_of_lua_to_app.insert(make_pair(st, app));
180 }
181 
~lua_hooks()182 lua_hooks::~lua_hooks()
183 {
184   map<lua_State*, app_state*>::iterator i = map_of_lua_to_app.find(st);
185   if (st)
186     lua_close (st);
187   if (i != map_of_lua_to_app.end())
188     map_of_lua_to_app.erase(i);
189 }
190 
191 bool
check_lua_state(lua_State * p_st) const192 lua_hooks::check_lua_state(lua_State * p_st) const
193 {
194   return (p_st == st);
195 }
196 
197 void
add_std_hooks()198 lua_hooks::add_std_hooks()
199 {
200   if (!run_string(st, std_hooks_constant, "<std hooks>"))
201     throw oops("lua error while setting up standard hooks");
202 }
203 
204 void
load_rcfile(utf8 const & rc)205 lua_hooks::load_rcfile(utf8 const & rc)
206 {
207   I(st);
208   if (rc() != "-" && directory_exists(system_path(rc)))
209     run_directory(st, system_path(rc).as_external().c_str(), "*");
210   else
211     {
212       data dat;
213       L(FL("opening rcfile '%s'") % rc);
214       read_data_for_command_line(rc, dat);
215       E(run_string(st, dat().c_str(), rc().c_str()), origin::user,
216         F("lua error while loading rcfile '%s'") % rc);
217       L(FL("'%s' is ok") % rc);
218     }
219 }
220 
221 void
load_rcfile(any_path const & rc,bool required)222 lua_hooks::load_rcfile(any_path const & rc, bool required)
223 {
224   I(st);
225   bool exists;
226   try
227     {
228       exists = path_exists(rc);
229     }
230   catch (recoverable_failure & e)
231     {
232       if (!required)
233         {
234           L(FL("skipping rcfile '%s': %s") % rc % e.what());
235           return;
236         }
237       else
238         throw;
239     }
240 
241   if (exists)
242     {
243       L(FL("opening rcfile '%s'") % rc);
244       E(run_file(st, rc.as_external().c_str()), origin::user,
245         F("lua error while loading '%s'") % rc);
246       L(FL("'%s' is ok") % rc);
247     }
248   else
249     {
250       E(!required, origin::user, F("rcfile '%s' does not exist") % rc);
251       L(FL("skipping nonexistent rcfile '%s'") % rc);
252     }
253 }
254 
255 void
load_rcfiles(options const & opts)256 lua_hooks::load_rcfiles(options const & opts)
257 {
258   // Built-in rc settings are defaults.
259   if (!opts.nostd)
260     add_std_hooks();
261 
262   // ~/.monotone/monotonerc overrides that, and
263   // _MTN/monotonerc overrides *that*.
264 
265   if (!opts.norc)
266     {
267       if (opts.conf_dir_given || !opts.no_default_confdir)
268         {
269           load_rcfile(opts.conf_dir / "monotonerc", false);
270         }
271       load_rcfile(bookkeeping_root / "monotonerc", false);
272     }
273 
274   // Command-line rcfiles override even that.
275 
276   for (args_vector::const_iterator i = opts.extra_rcfiles.begin();
277        i != opts.extra_rcfiles.end(); ++i)
278     load_rcfile(*i);
279 }
280 
281 bool
hook_exists(string const & func_name)282 lua_hooks::hook_exists(string const & func_name)
283 {
284   return Lua(st)
285     .func(func_name)
286     .ok();
287 }
288 
289 // concrete hooks
290 
291 // nb: if you're hooking lua to return your passphrase, you don't care if we
292 // keep a couple extra temporaries of your passphrase around.
293 bool
hook_get_passphrase(key_identity_info const & identity,string & phrase)294 lua_hooks::hook_get_passphrase(key_identity_info const & identity,
295                                string & phrase)
296 {
297   Lua ll(st);
298   ll.func("get_passphrase");
299   push_key_identity_info(ll, identity);
300   ll.call(1,1)
301     .extract_classified_str(phrase);
302   return ll.ok();
303 }
304 
305 bool
hook_get_local_key_name(key_identity_info & info)306 lua_hooks::hook_get_local_key_name(key_identity_info & info)
307 {
308   string local_name;
309   Lua ll(st);
310   ll.func("get_local_key_name");
311   push_key_identity_info(ll, info);
312   ll.call(1, 1)
313     .extract_str(local_name);
314   if (ll.ok())
315     {
316       info.official_name = key_name(local_name, origin::user);
317       return true;
318     }
319   else
320     return false;
321 }
322 
323 bool
hook_persist_phrase_ok()324 lua_hooks::hook_persist_phrase_ok()
325 {
326   bool persist_ok = false;
327   bool executed_ok = Lua(st)
328     .func("persist_phrase_ok")
329     .call(0,1)
330     .extract_bool(persist_ok)
331     .ok();
332   return executed_ok && persist_ok;
333 }
334 
335 bool
hook_expand_selector(string const & sel,string & exp)336 lua_hooks::hook_expand_selector(string const & sel,
337                                 string & exp)
338 {
339   return Lua(st)
340     .func("expand_selector")
341     .push_str(sel)
342     .call(1,1)
343     .extract_str(exp)
344     .ok();
345 }
346 
347 bool
hook_expand_date(string const & sel,string & exp)348 lua_hooks::hook_expand_date(string const & sel,
349                             string & exp)
350 {
351   exp.clear();
352   bool res= Lua(st)
353     .func("expand_date")
354     .push_str(sel)
355     .call(1,1)
356     .extract_str(exp)
357     .ok();
358   return res && exp.size();
359 }
360 
361 bool
hook_get_branch_key(branch_name const & branchname,key_store & keys,project_t & project,key_id & k)362 lua_hooks::hook_get_branch_key(branch_name const & branchname,
363                                key_store & keys,
364                                project_t & project,
365                                key_id & k)
366 {
367   string key;
368   bool ok = Lua(st)
369     .func("get_branch_key")
370     .push_str(branchname())
371     .call(1,1)
372     .extract_str(key)
373     .ok();
374 
375   if (!ok || key.empty())
376     return false;
377   else
378     {
379       key_identity_info identity;
380       project.get_key_identity(keys, *this, external_key_name(key, origin::user), identity);
381       k = identity.id;
382       return true;
383     }
384 }
385 
386 bool
hook_get_author(branch_name const & branchname,key_identity_info const & identity,string & author)387 lua_hooks::hook_get_author(branch_name const & branchname,
388                            key_identity_info const & identity,
389                            string & author)
390 {
391   Lua ll(st);
392   ll.func("get_author")
393     .push_str(branchname());
394   push_key_identity_info(ll, identity);
395   return ll.call(2,1)
396     .extract_str(author)
397     .ok();
398 }
399 
400 bool
hook_edit_comment(external const & user_log_message,external & result)401 lua_hooks::hook_edit_comment(external const & user_log_message,
402                              external & result)
403 {
404   string result_str;
405   bool is_ok = Lua(st)
406                  .func("edit_comment")
407                  .push_str(user_log_message())
408                  .call(1,1)
409                  .extract_str(result_str)
410                  .ok();
411   result = external(result_str, origin::user);
412   return is_ok;
413 }
414 
415 bool
hook_ignore_file(file_path const & p)416 lua_hooks::hook_ignore_file(file_path const & p)
417 {
418   bool ignore_it = false;
419   bool exec_ok = Lua(st)
420     .func("ignore_file")
421     .push_str(p.as_external())
422     .call(1,1)
423     .extract_bool(ignore_it)
424     .ok();
425   return exec_ok && ignore_it;
426 }
427 
428 bool
hook_ignore_branch(branch_name const & branch)429 lua_hooks::hook_ignore_branch(branch_name const & branch)
430 {
431   bool ignore_it = false;
432   bool exec_ok = Lua(st)
433     .func("ignore_branch")
434     .push_str(branch())
435     .call(1,1)
436     .extract_bool(ignore_it)
437     .ok();
438   return exec_ok && ignore_it;
439 }
440 
441 namespace {
442   template<typename ID>
push_key_ident(Lua & ll,ID const & ident)443   Lua & push_key_ident(Lua & ll, ID const & ident)
444   {
445     enum dummp { d = ( sizeof(struct not_a_key_id_type) == sizeof(ID))};
446     return ll;
447   }
448   template<>
push_key_ident(Lua & ll,key_identity_info const & ident)449   Lua & push_key_ident(Lua & ll, key_identity_info const & ident)
450   {
451     return push_key_identity_info(ll, ident);
452   }
453   template<>
push_key_ident(Lua & ll,key_name const & ident)454   Lua & push_key_ident(Lua & ll, key_name const & ident)
455   {
456     return ll.push_str(ident());
457   }
458   template<typename ID>
459   bool
shared_trust_function_body(Lua & ll,std::set<ID> const & signers,id const & hash,cert_name const & name,cert_value const & val)460   shared_trust_function_body(Lua & ll,
461                              std::set<ID> const & signers,
462                              id const & hash,
463                              cert_name const & name,
464                              cert_value const & val)
465   {
466     ll.push_table();
467 
468     int k = 1;
469     for (typename std::set<ID>::const_iterator v = signers.begin();
470          v != signers.end(); ++v)
471       {
472         ll.push_int(k);
473         push_key_ident(ll, *v);
474         ll.set_table();
475         ++k;
476       }
477 
478     hexenc<id> hid(encode_hexenc(hash(), hash.made_from), hash.made_from);
479     bool ok;
480     bool exec_ok = ll
481       .push_str(hid())
482       .push_str(name())
483       .push_str(val())
484       .call(4, 1)
485       .extract_bool(ok)
486       .ok();
487 
488     return exec_ok && ok;
489   }
490 }
491 
492 bool
hook_get_revision_cert_trust(std::set<key_identity_info> const & signers,id const & hash,cert_name const & name,cert_value const & val)493 lua_hooks::hook_get_revision_cert_trust(std::set<key_identity_info> const & signers,
494                                        id const & hash,
495                                        cert_name const & name,
496                                        cert_value const & val)
497 {
498   Lua ll(st);
499   ll.func("get_revision_cert_trust");
500   return shared_trust_function_body(ll, signers, hash, name, val);
501 }
502 
503 bool
hook_get_manifest_cert_trust(std::set<key_name> const & signers,id const & hash,cert_name const & name,cert_value const & val)504 lua_hooks::hook_get_manifest_cert_trust(std::set<key_name> const & signers,
505                                         id const & hash,
506                                         cert_name const & name,
507                                         cert_value const & val)
508 {
509   Lua ll(st);
510   ll.func("get_manifest_cert_trust");
511   return shared_trust_function_body(ll, signers, hash, name, val);
512 }
513 
514 bool
hook_accept_testresult_change(map<key_id,bool> const & old_results,map<key_id,bool> const & new_results)515 lua_hooks::hook_accept_testresult_change(map<key_id, bool> const & old_results,
516                                          map<key_id, bool> const & new_results)
517 {
518   Lua ll(st);
519   ll
520     .func("accept_testresult_change")
521     .push_table();
522 
523   for (map<key_id, bool>::const_iterator i = old_results.begin();
524        i != old_results.end(); ++i)
525     {
526       ll.push_str(i->first.inner()());
527       ll.push_bool(i->second);
528       ll.set_table();
529     }
530 
531   ll.push_table();
532 
533   for (map<key_id, bool>::const_iterator i = new_results.begin();
534        i != new_results.end(); ++i)
535     {
536       ll.push_str(i->first.inner()());
537       ll.push_bool(i->second);
538       ll.set_table();
539     }
540 
541   bool ok;
542   bool exec_ok = ll
543     .call(2, 1)
544     .extract_bool(ok)
545     .ok();
546 
547   return exec_ok && ok;
548 }
549 
550 
551 
552 bool
hook_merge3(file_path const & anc_path,file_path const & left_path,file_path const & right_path,file_path const & merged_path,data const & ancestor,data const & left,data const & right,data & result)553 lua_hooks::hook_merge3(file_path const & anc_path,
554                        file_path const & left_path,
555                        file_path const & right_path,
556                        file_path const & merged_path,
557                        data const & ancestor,
558                        data const & left,
559                        data const & right,
560                        data & result)
561 {
562   string res;
563   bool ok = Lua(st)
564     .func("merge3")
565     .push_str(anc_path.as_external())
566     .push_str(left_path.as_external())
567     .push_str(right_path.as_external())
568     .push_str(merged_path.as_external())
569     .push_str(ancestor())
570     .push_str(left())
571     .push_str(right())
572     .call(7,1)
573     .extract_str(res)
574     .ok();
575   result = data(res, origin::user);
576   return ok;
577 }
578 
579 bool
hook_external_diff(file_path const & path,data const & data_old,data const & data_new,bool is_binary,bool diff_args_provided,string const & diff_args,string const & oldrev,string const & newrev)580 lua_hooks::hook_external_diff(file_path const & path,
581                               data const & data_old,
582                               data const & data_new,
583                               bool is_binary,
584                               bool diff_args_provided,
585                               string const & diff_args,
586                               string const & oldrev,
587                               string const & newrev)
588 {
589   Lua ll(st);
590 
591   ll
592     .func("external_diff")
593     .push_str(path.as_external());
594 
595   if (oldrev.length() != 0)
596     ll.push_str(data_old());
597   else
598     ll.push_nil();
599 
600   ll.push_str(data_new());
601 
602   ll.push_bool(is_binary);
603 
604   if (diff_args_provided)
605     ll.push_str(diff_args);
606   else
607     ll.push_nil();
608 
609   ll.push_str(oldrev);
610   ll.push_str(newrev);
611 
612   return ll.call(7,0).ok();
613 }
614 
615 bool
hook_get_encloser_pattern(file_path const & path,string & pattern)616 lua_hooks::hook_get_encloser_pattern(file_path const & path,
617                                      string & pattern)
618 {
619   bool exec_ok
620     = Lua(st)
621     .func("get_encloser_pattern")
622     .push_str(path.as_external())
623     .call(1, 1)
624     .extract_str(pattern)
625     .ok();
626 
627   // If the hook fails, make sure pattern is set to something sane
628   // (the empty string, which will disable enclosers for this file).
629   if (!exec_ok)
630     pattern = "";
631   return exec_ok;
632 }
633 
634 bool
hook_get_default_command_options(commands::command_id const & cmd,args_vector & args)635 lua_hooks::hook_get_default_command_options(commands::command_id const & cmd,
636                                             args_vector & args)
637 {
638   Lua ll(st);
639   ll.func("get_default_command_options");
640 
641   ll.push_table();
642   int k = 1;
643 
644   // skip the first ID part, the command group, since this is mostly
645   // useless for the hook implementor
646   vector<utf8>::const_iterator i = cmd.begin();
647   i++;
648 
649   for ( ; i != cmd.end(); ++i)
650     {
651       ll.push_int(k);
652       ll.push_str((*i)());
653       ll.set_table();
654       k++;
655     }
656 
657   ll.call(1, 1);
658 
659   ll.begin();
660   while (ll.next())
661     {
662       string arg;
663       ll.extract_str(arg).pop();
664       args.push_back(arg_type(arg, origin::user));
665     }
666   return ll.ok() && !args.empty();
667 }
668 
669 bool
hook_get_date_format_spec(date_format_spec in,string & out)670 lua_hooks::hook_get_date_format_spec(date_format_spec in, string & out)
671 {
672   string in_spec;
673   switch (in)
674   {
675     case date_long:         in_spec = "date_long"; break;
676     case date_short:        in_spec = "date_short"; break;
677     case time_long:         in_spec = "time_long"; break;
678     case time_short:        in_spec = "time_short"; break;
679     case date_time_long:    in_spec = "date_time_long"; break;
680     case date_time_short:   in_spec = "date_time_short"; break;
681     default: I(false);
682   }
683 
684   bool exec_ok
685     = Lua(st)
686     .func("get_date_format_spec")
687     .push_str(in_spec)
688     .call(1, 1)
689     .extract_str(out)
690     .ok();
691 
692   // If the hook fails, disable date formatting.
693   if (!exec_ok)
694     out = "";
695   return exec_ok;
696 }
697 
hook_get_default_database_alias(string & alias)698 bool lua_hooks::hook_get_default_database_alias(string & alias)
699 {
700    bool exec_ok
701      = Lua(st)
702      .func("get_default_database_alias")
703      .call(0, 1)
704      .extract_str(alias)
705      .ok();
706 
707   return exec_ok;
708 }
709 
hook_get_default_database_locations(vector<system_path> & out)710 bool lua_hooks::hook_get_default_database_locations(vector<system_path> & out)
711 {
712   Lua ll(st);
713   ll.func("get_default_database_locations");
714   ll.call(0, 1);
715 
716   ll.begin();
717   while (ll.next())
718     {
719       string path;
720       ll.extract_str(path).pop();
721       out.push_back(system_path(path, origin::user));
722     }
723   return ll.ok();
724 }
725 
hook_get_default_database_glob(globish & out)726 bool lua_hooks::hook_get_default_database_glob(globish & out)
727 {
728    string glob;
729    bool exec_ok
730      = Lua(st)
731      .func("get_default_database_glob")
732      .call(0, 1)
733      .extract_str(glob)
734      .ok();
735 
736   out = globish(glob, origin::user);
737   return exec_ok;
738 }
739 
hook_hook_wrapper(string const & func_name,vector<string> const & args,string & out)740 bool lua_hooks::hook_hook_wrapper(string const & func_name,
741                                   vector<string> const & args,
742                                   string & out)
743 {
744   Lua ll(st);
745   ll.func("hook_wrapper")
746     .push_str(func_name);
747 
748   for (vector<string>::const_iterator i = args.begin();
749         i != args.end(); ++i)
750     {
751       ll.push_str(*i);
752     }
753 
754   ll.call(args.size() + 1, 1);
755   ll.extract_str_nolog(out);
756   return ll.ok();
757 }
758 
759 bool
hook_get_man_page_formatter_command(string & command)760 lua_hooks::hook_get_man_page_formatter_command(string & command)
761 {
762   bool exec_ok
763      = Lua(st)
764      .func("get_man_page_formatter_command")
765      .call(0, 1)
766      .extract_str(command)
767      .ok();
768 
769   return exec_ok;
770 }
771 
772 bool
hook_use_inodeprints()773 lua_hooks::hook_use_inodeprints()
774 {
775   bool use = false, exec_ok = false;
776 
777   exec_ok = Lua(st)
778     .func("use_inodeprints")
779     .call(0, 1)
780     .extract_bool(use)
781     .ok();
782   return use && exec_ok;
783 }
784 
785 bool
hook_get_netsync_client_key(utf8 const & server_address,globish const & include,globish const & exclude,key_store & keys,project_t & project,key_id & k)786 lua_hooks::hook_get_netsync_client_key(utf8 const & server_address,
787                                        globish const & include,
788                                        globish const & exclude,
789                                        key_store & keys,
790                                        project_t & project,
791                                        key_id & k)
792 {
793   string name;
794   bool exec_ok
795     = Lua(st)
796     .func("get_netsync_client_key")
797     .push_str(server_address())
798     .push_str(include())
799     .push_str(exclude())
800     .call(3, 1)
801     .extract_str(name)
802     .ok();
803 
804   if (!exec_ok || name.empty())
805     return false;
806   else
807     {
808       key_identity_info identity;
809       project.get_key_identity(keys, *this, external_key_name(name, origin::user), identity);
810       k = identity.id;
811       return true;
812     }
813 }
814 
815 bool
hook_get_netsync_server_key(vector<utf8> const & server_ports,key_store & keys,project_t & project,key_id & k)816 lua_hooks::hook_get_netsync_server_key(vector<utf8> const & server_ports,
817                                        key_store & keys,
818                                        project_t & project,
819                                        key_id & k)
820 {
821   string name;
822   Lua ll(st);
823   ll.func("get_netsync_server_key");
824 
825   ll.push_table();
826   vector<utf8>::const_iterator i;
827   int j;
828   for (i = server_ports.begin(), j = 1; i != server_ports.end(); ++i, ++j)
829     {
830       ll.push_int(j);
831       ll.push_str((*i)());
832       ll.set_table();
833     }
834 
835   bool exec_ok = ll.call(1, 1)
836                    .extract_str(name)
837                    .ok();
838 
839   if (!exec_ok || name.empty())
840     return false;
841   else
842     {
843       key_identity_info identity;
844       project.get_key_identity(keys, *this, external_key_name(name, origin::user), identity);
845       k = identity.id;
846       return true;
847     }
848 }
849 
850 static void
push_uri(uri_t const & uri,Lua & ll)851 push_uri(uri_t const & uri, Lua & ll)
852 {
853   ll.push_table();
854 
855   if (!uri.scheme.empty())
856     {
857       ll.push_str("scheme");
858       ll.push_str(uri.scheme);
859       ll.set_table();
860     }
861 
862   if (!uri.user.empty())
863     {
864       ll.push_str("user");
865       ll.push_str(uri.user);
866       ll.set_table();
867     }
868 
869   if (!uri.host.empty())
870     {
871       ll.push_str("host");
872       ll.push_str(uri.host);
873       ll.set_table();
874     }
875 
876   if (!uri.port.empty())
877     {
878       ll.push_str("port");
879       ll.push_str(uri.port);
880       ll.set_table();
881     }
882 
883   if (!uri.path.empty())
884     {
885       ll.push_str("path");
886       ll.push_str(uri.path);
887       ll.set_table();
888     }
889 
890   if (!uri.query.empty())
891     {
892       ll.push_str("query");
893       ll.push_str(uri.query);
894       ll.set_table();
895     }
896 
897   if (!uri.fragment.empty())
898     {
899       ll.push_str("fragment");
900       ll.push_str(uri.fragment);
901       ll.set_table();
902     }
903 }
904 
905 bool
hook_get_netsync_connect_command(uri_t const & uri,globish const & include_pattern,globish const & exclude_pattern,bool debug,vector<string> & argv)906 lua_hooks::hook_get_netsync_connect_command(uri_t const & uri,
907                                             globish const & include_pattern,
908                                             globish const & exclude_pattern,
909                                             bool debug,
910                                             vector<string> & argv)
911 {
912   bool cmd = false, exec_ok = false;
913   Lua ll(st);
914   ll.func("get_netsync_connect_command");
915 
916   push_uri(uri, ll);
917 
918   ll.push_table();
919 
920   if (!include_pattern().empty())
921     {
922       ll.push_str("include");
923       ll.push_str(include_pattern());
924       ll.set_table();
925     }
926 
927   if (!exclude_pattern().empty())
928     {
929       ll.push_str("exclude");
930       ll.push_str(exclude_pattern());
931       ll.set_table();
932     }
933 
934   if (debug)
935     {
936       ll.push_str("debug");
937       ll.push_bool(debug);
938       ll.set_table();
939     }
940 
941   ll.call(2,1);
942 
943   ll.begin();
944 
945   argv.clear();
946   while(ll.next())
947     {
948       string s;
949       ll.extract_str(s).pop();
950       argv.push_back(s);
951     }
952   return ll.ok() && !argv.empty();
953 }
954 
955 
956 bool
hook_use_transport_auth(uri_t const & uri)957 lua_hooks::hook_use_transport_auth(uri_t const & uri)
958 {
959   bool use_auth = true;
960   Lua ll(st);
961   ll.func("use_transport_auth");
962   push_uri(uri, ll);
963   ll.call(1,1);
964   ll.extract_bool(use_auth);
965 
966   // NB: we want to return *true* here if there's a failure.
967   return use_auth;
968 }
969 
970 
971 bool
hook_get_netsync_read_permitted(string const & branch,key_identity_info const & identity)972 lua_hooks::hook_get_netsync_read_permitted(string const & branch,
973                                            key_identity_info const & identity)
974 {
975   bool permitted = false, exec_ok = false;
976 
977   Lua ll(st);
978   ll.func("get_netsync_read_permitted")
979     .push_str(branch);
980   push_key_identity_info(ll, identity);
981   exec_ok = ll.call(2,1)
982     .extract_bool(permitted)
983     .ok();
984 
985   return exec_ok && permitted;
986 }
987 
988 // Anonymous no-key version
989 bool
hook_get_netsync_read_permitted(string const & branch)990 lua_hooks::hook_get_netsync_read_permitted(string const & branch)
991 {
992   bool permitted = false, exec_ok = false;
993 
994   exec_ok = Lua(st)
995     .func("get_netsync_read_permitted")
996     .push_str(branch)
997     .push_nil()
998     .call(2,1)
999     .extract_bool(permitted)
1000     .ok();
1001 
1002   return exec_ok && permitted;
1003 }
1004 
1005 bool
hook_get_netsync_write_permitted(key_identity_info const & identity)1006 lua_hooks::hook_get_netsync_write_permitted(key_identity_info const & identity)
1007 {
1008   bool permitted = false, exec_ok = false;
1009 
1010   Lua ll(st);
1011   ll.func("get_netsync_write_permitted");
1012   push_key_identity_info(ll, identity);
1013   exec_ok = ll.call(1,1)
1014     .extract_bool(permitted)
1015     .ok();
1016 
1017   return exec_ok && permitted;
1018 }
1019 
1020 bool
hook_get_remote_automate_permitted(key_identity_info const & identity,vector<string> const & command_line,vector<pair<string,string>> const & command_opts)1021 lua_hooks::hook_get_remote_automate_permitted(key_identity_info const & identity,
1022                                               vector<string> const & command_line,
1023                                               vector<pair<string, string> > const & command_opts)
1024 {
1025   Lua ll(st);
1026   ll.func("get_remote_automate_permitted");
1027   push_key_identity_info(ll, identity);
1028 
1029   int k = 1;
1030 
1031   ll.push_table();
1032   vector<string>::const_iterator l;
1033   for (l = command_line.begin(), k = 1; l != command_line.end(); ++l, ++k)
1034     {
1035       ll.push_int(k);
1036       ll.push_str(*l);
1037       ll.set_table();
1038     }
1039   ll.push_table();
1040   k = 1;
1041   vector<pair<string, string> >::const_iterator o;
1042   for (o = command_opts.begin(), k = 1; o != command_opts.end(); ++o, ++k)
1043     {
1044       ll.push_int(k);
1045 
1046       {
1047         ll.push_table();
1048 
1049         ll.push_str("name");
1050         ll.push_str(o->first);
1051         ll.set_table();
1052 
1053         ll.push_str("value");
1054         ll.push_str(o->second);
1055         ll.set_table();
1056       }
1057 
1058       ll.set_table();
1059     }
1060 
1061   ll.call(3, 1);
1062 
1063   bool permitted(false);
1064   ll.extract_bool(permitted);
1065   return ll.ok() && permitted;
1066 }
1067 
1068 bool
hook_init_attributes(file_path const & filename,map<string,string> & attrs)1069 lua_hooks::hook_init_attributes(file_path const & filename,
1070                                 map<string, string> & attrs)
1071 {
1072   Lua ll(st);
1073 
1074   ll
1075     .push_str("attr_init_functions")
1076     .get_tab();
1077 
1078   L(FL("calling attr_init_function for %s") % filename);
1079   ll.begin();
1080   while (ll.next())
1081     {
1082       L(FL("  calling an attr_init_function for %s") % filename);
1083       ll.push_str(filename.as_external());
1084       ll.call(1, 1);
1085 
1086       if (lua_isstring(st, -1))
1087         {
1088           string key, value;
1089 
1090           ll.extract_str(value);
1091           ll.pop();
1092           ll.extract_str(key);
1093 
1094           attrs[key] = value;
1095           L(FL("  added attr %s = %s") % key % value);
1096         }
1097       else
1098         {
1099           L(FL("  no attr added"));
1100           ll.pop();
1101         }
1102     }
1103 
1104   return ll.pop().ok();
1105 }
1106 
1107 bool
hook_set_attribute(string const & attr,file_path const & filename,string const & value)1108 lua_hooks::hook_set_attribute(string const & attr,
1109                               file_path const & filename,
1110                               string const & value)
1111 {
1112   return Lua(st)
1113     .push_str("attr_functions")
1114     .get_tab()
1115     .push_str(attr)
1116     .get_fn(-2)
1117     .push_str(filename.as_external())
1118     .push_str(value)
1119     .call(2,0)
1120     .ok();
1121 }
1122 
1123 bool
hook_clear_attribute(string const & attr,file_path const & filename)1124 lua_hooks::hook_clear_attribute(string const & attr,
1125                                 file_path const & filename)
1126 {
1127   return Lua(st)
1128     .push_str("attr_functions")
1129     .get_tab()
1130     .push_str(attr)
1131     .get_fn(-2)
1132     .push_str(filename.as_external())
1133     .push_nil()
1134     .call(2,0)
1135     .ok();
1136 }
1137 
1138 bool
hook_validate_changes(revision_data const & new_rev,branch_name const & branchname,bool & validated,string & reason)1139 lua_hooks::hook_validate_changes(revision_data const & new_rev,
1140                                  branch_name const & branchname,
1141                                  bool & validated,
1142                                  string & reason)
1143 {
1144   validated = true;
1145   return Lua(st)
1146     .func("validate_changes")
1147     .push_str(new_rev.inner()())
1148     .push_str(branchname())
1149     .call(2, 2)
1150     .extract_str(reason)
1151     // XXX When validated, the extra returned string is superfluous.
1152     .pop()
1153     .extract_bool(validated)
1154     .ok();
1155 }
1156 
1157 bool
hook_validate_commit_message(utf8 const & message,revision_data const & new_rev,branch_name const & branchname,bool & validated,string & reason)1158 lua_hooks::hook_validate_commit_message(utf8 const & message,
1159                                         revision_data const & new_rev,
1160                                         branch_name const & branchname,
1161                                         bool & validated,
1162                                         string & reason)
1163 {
1164   validated = true;
1165   return Lua(st)
1166     .func("validate_commit_message")
1167     .push_str(message())
1168     .push_str(new_rev.inner()())
1169     .push_str(branchname())
1170     .call(3, 2)
1171     .extract_str(reason)
1172     // XXX When validated, the extra returned string is superfluous.
1173     .pop()
1174     .extract_bool(validated)
1175     .ok();
1176 }
1177 
1178 bool
hook_note_commit(revision_id const & new_id,revision_data const & rdat,map<cert_name,cert_value> const & certs)1179 lua_hooks::hook_note_commit(revision_id const & new_id,
1180                             revision_data const & rdat,
1181                             map<cert_name, cert_value> const & certs)
1182 {
1183   Lua ll(st);
1184   ll
1185     .func("note_commit")
1186     .push_str(encode_hexenc(new_id.inner()(), new_id.inner().made_from))
1187     .push_str(rdat.inner()());
1188 
1189   ll.push_table();
1190 
1191   for (map<cert_name, cert_value>::const_iterator i = certs.begin();
1192        i != certs.end(); ++i)
1193     {
1194       ll.push_str(i->first());
1195       ll.push_str(i->second());
1196       ll.set_table();
1197     }
1198 
1199   ll.call(3, 0);
1200   return ll.ok();
1201 }
1202 
1203 bool
hook_note_netsync_start(size_t session_id,string my_role,int sync_type,string remote_host,key_identity_info const & remote_key,globish include_pattern,globish exclude_pattern)1204 lua_hooks::hook_note_netsync_start(size_t session_id, string my_role,
1205                                    int sync_type, string remote_host,
1206                                    key_identity_info const & remote_key,
1207                                    globish include_pattern,
1208                                    globish exclude_pattern)
1209 {
1210   string type;
1211   switch (sync_type)
1212     {
1213     case 1:
1214       type = "push";
1215       break;
1216     case 2:
1217       type = "pull";
1218       break;
1219     case 3:
1220       type = "sync";
1221       break;
1222     default:
1223       type = "unknown";
1224       break;
1225     }
1226   Lua ll(st);
1227   ll.func("note_netsync_start")
1228     .push_int(session_id)
1229     .push_str(my_role)
1230     .push_str(type)
1231     .push_str(remote_host);
1232   push_key_identity_info(ll, remote_key);
1233   return ll.push_str(include_pattern())
1234     .push_str(exclude_pattern())
1235     .call(7, 0)
1236     .ok();
1237 }
1238 
1239 bool
hook_note_netsync_revision_received(revision_id const & new_id,revision_data const & rdat,std::set<pair<key_identity_info,pair<cert_name,cert_value>>> const & certs,size_t session_id)1240 lua_hooks::hook_note_netsync_revision_received(revision_id const & new_id,
1241                                                revision_data const & rdat,
1242                                                std::set<pair<key_identity_info,
1243                                                pair<cert_name,
1244                                                cert_value> > > const & certs,
1245                                                size_t session_id)
1246 {
1247   Lua ll(st);
1248   ll
1249     .func("note_netsync_revision_received")
1250     .push_str(encode_hexenc(new_id.inner()(), new_id.inner().made_from))
1251     .push_str(rdat.inner()());
1252 
1253   ll.push_table();
1254 
1255   typedef std::set<pair<key_identity_info, pair<cert_name, cert_value> > > cdat;
1256 
1257   int n = 1;
1258   for (cdat::const_iterator i = certs.begin(); i != certs.end(); ++i)
1259     {
1260       ll.push_int(n++);
1261       ll.push_table();
1262       push_key_identity_info(ll, i->first);
1263       ll.set_field("key");
1264       ll.push_str(i->second.first());
1265       ll.set_field("name");
1266       ll.push_str(i->second.second());
1267       ll.set_field("value");
1268       ll.set_table();
1269     }
1270 
1271   ll.push_int(session_id);
1272   ll.call(4, 0);
1273   return ll.ok();
1274 }
1275 
1276 bool
hook_note_netsync_revision_sent(revision_id const & new_id,revision_data const & rdat,std::set<pair<key_identity_info,pair<cert_name,cert_value>>> const & certs,size_t session_id)1277 lua_hooks::hook_note_netsync_revision_sent(revision_id const & new_id,
1278                                            revision_data const & rdat,
1279                                            std::set<pair<key_identity_info,
1280                                            pair<cert_name,
1281                                            cert_value> > > const & certs,
1282                                            size_t session_id)
1283 {
1284   Lua ll(st);
1285   ll
1286     .func("note_netsync_revision_sent")
1287     .push_str(encode_hexenc(new_id.inner()(), new_id.inner().made_from))
1288     .push_str(rdat.inner()());
1289 
1290   ll.push_table();
1291 
1292   typedef std::set<pair<key_identity_info, pair<cert_name, cert_value> > > cdat;
1293 
1294   int n = 1;
1295   for (cdat::const_iterator i = certs.begin(); i != certs.end(); ++i)
1296     {
1297       ll.push_int(n++);
1298       ll.push_table();
1299       push_key_identity_info(ll, i->first);
1300       ll.set_field("key");
1301       ll.push_str(i->second.first());
1302       ll.set_field("name");
1303       ll.push_str(i->second.second());
1304       ll.set_field("value");
1305       ll.set_table();
1306     }
1307 
1308   ll.push_int(session_id);
1309   ll.call(4, 0);
1310   return ll.ok();
1311 }
1312 
1313 bool
hook_note_netsync_pubkey_received(key_identity_info const & identity,size_t session_id)1314 lua_hooks::hook_note_netsync_pubkey_received(key_identity_info const & identity,
1315                                              size_t session_id)
1316 {
1317   Lua ll(st);
1318   ll
1319     .func("note_netsync_pubkey_received");
1320   push_key_identity_info(ll, identity);
1321   ll.push_int(session_id);
1322 
1323   ll.call(2, 0);
1324   return ll.ok();
1325 }
1326 
1327 bool
hook_note_netsync_pubkey_sent(key_identity_info const & identity,size_t session_id)1328 lua_hooks::hook_note_netsync_pubkey_sent(key_identity_info const & identity,
1329                                          size_t session_id)
1330 {
1331   Lua ll(st);
1332   ll
1333     .func("note_netsync_pubkey_sent");
1334   push_key_identity_info(ll, identity);
1335   ll.push_int(session_id);
1336 
1337   ll.call(2, 0);
1338   return ll.ok();
1339 }
1340 
1341 bool
hook_note_netsync_cert_received(revision_id const & rid,key_identity_info const & identity,cert_name const & name,cert_value const & value,size_t session_id)1342 lua_hooks::hook_note_netsync_cert_received(revision_id const & rid,
1343                                            key_identity_info const & identity,
1344                                            cert_name const & name,
1345                                            cert_value const & value,
1346                                            size_t session_id)
1347 {
1348   Lua ll(st);
1349   ll
1350     .func("note_netsync_cert_received")
1351     .push_str(encode_hexenc(rid.inner()(), rid.inner().made_from));
1352   push_key_identity_info(ll, identity);
1353   ll.push_str(name())
1354     .push_str(value())
1355     .push_int(session_id);
1356 
1357   ll.call(5, 0);
1358   return ll.ok();
1359 }
1360 
1361 bool
hook_note_netsync_cert_sent(revision_id const & rid,key_identity_info const & identity,cert_name const & name,cert_value const & value,size_t session_id)1362 lua_hooks::hook_note_netsync_cert_sent(revision_id const & rid,
1363                                        key_identity_info const & identity,
1364                                        cert_name const & name,
1365                                        cert_value const & value,
1366                                        size_t session_id)
1367 {
1368   Lua ll(st);
1369   ll
1370     .func("note_netsync_cert_sent")
1371     .push_str(encode_hexenc(rid.inner()(), rid.inner().made_from));
1372   push_key_identity_info(ll, identity);
1373   ll.push_str(name())
1374     .push_str(value())
1375     .push_int(session_id);
1376 
1377   ll.call(5, 0);
1378   return ll.ok();
1379 }
1380 
1381 bool
hook_note_netsync_end(size_t session_id,int status,size_t bytes_in,size_t bytes_out,size_t certs_in,size_t certs_out,size_t revs_in,size_t revs_out,size_t keys_in,size_t keys_out)1382 lua_hooks::hook_note_netsync_end(size_t session_id, int status,
1383                                  size_t bytes_in, size_t bytes_out,
1384                                  size_t certs_in, size_t certs_out,
1385                                  size_t revs_in, size_t revs_out,
1386                                  size_t keys_in, size_t keys_out)
1387 {
1388   Lua ll(st);
1389   return ll
1390     .func("note_netsync_end")
1391     .push_int(session_id)
1392     .push_int(status)
1393     .push_int(bytes_in)
1394     .push_int(bytes_out)
1395     .push_int(certs_in)
1396     .push_int(certs_out)
1397     .push_int(revs_in)
1398     .push_int(revs_out)
1399     .push_int(keys_in)
1400     .push_int(keys_out)
1401     .call(10, 0)
1402     .ok();
1403 }
1404 
1405 bool
hook_note_mtn_startup(args_vector const & args)1406 lua_hooks::hook_note_mtn_startup(args_vector const & args)
1407 {
1408   Lua ll(st);
1409 
1410   ll.func("note_mtn_startup");
1411 
1412   for (args_vector::const_iterator i = args.begin(); i != args.end(); ++i)
1413     ll.push_str((*i)());
1414 
1415   ll.call(args.size(), 0);
1416   return ll.ok();
1417 }
1418 
1419 bool
hook_unmapped_git_author(string const & unmapped_author,string & fixed_author)1420 lua_hooks::hook_unmapped_git_author(string const & unmapped_author, string & fixed_author)
1421 {
1422   return Lua(st)
1423     .func("unmapped_git_author")
1424     .push_str(unmapped_author)
1425     .call(1,1)
1426     .extract_str(fixed_author)
1427     .ok();
1428 }
1429 
1430 bool
hook_validate_git_author(string const & author)1431 lua_hooks::hook_validate_git_author(string const & author)
1432 {
1433   bool valid = false, exec_ok = false;
1434   exec_ok = Lua(st)
1435     .func("validate_git_author")
1436     .push_str(author)
1437     .call(1,1)
1438     .extract_bool(valid)
1439     .ok();
1440   return valid && exec_ok;
1441 }
1442 
1443 // Local Variables:
1444 // mode: C++
1445 // fill-column: 76
1446 // c-file-style: "gnu"
1447 // indent-tabs-mode: nil
1448 // End:
1449 // vim: et:sw=2:sts=2:ts=2:cino=>2s,{s,\:s,+s,t0,g0,^-2,e-2,n-2,p2s,(0,=s:
1450