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