monotone

monotone Mtn Source Tree

Root/cmd_list.cc

1// Copyright (C) 2002 Graydon Hoare <graydon@pobox.com>
2//
3// This program is made available under the GNU GPL version 2.0 or
4// greater. See the accompanying file COPYING for details.
5//
6// This program is distributed WITHOUT ANY WARRANTY; without even the
7// implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
8// PURPOSE.
9
10#include <algorithm>
11#include <map>
12#include <utility>
13#include <iostream>
14
15#include <boost/tuple/tuple.hpp>
16
17#include "basic_io.hh"
18#include "cert.hh"
19#include "charset.hh"
20#include "cmd.hh"
21#include "database.hh"
22#include "globish.hh"
23#include "keys.hh"
24#include "restrictions.hh"
25#include "revision.hh"
26#include "simplestring_xform.hh"
27#include "transforms.hh"
28#include "ui.hh"
29#include "vocab_cast.hh"
30
31using std::cout;
32using std::make_pair;
33using std::map;
34using std::ostream_iterator;
35using std::pair;
36using std::set;
37using std::sort;
38using std::string;
39using std::vector;
40
41static void
42ls_certs(string const & name, app_state & app, vector<utf8> const & args)
43{
44 if (args.size() != 1)
45 throw usage(name);
46
47 vector<cert> certs;
48
49 transaction_guard guard(app.db, false);
50
51 revision_id ident;
52 complete(app, idx(args, 0)(), ident);
53 vector< revision<cert> > ts;
54 // FIXME_PROJECTS: after projects are implemented,
55 // use the app.db version instead if no project is specified.
56 app.get_project().get_revision_certs(ident, ts);
57
58 for (size_t i = 0; i < ts.size(); ++i)
59 certs.push_back(idx(ts, i).inner());
60
61 {
62 set<rsa_keypair_id> checked;
63 for (size_t i = 0; i < certs.size(); ++i)
64 {
65 if (checked.find(idx(certs, i).key) == checked.end() &&
66 !app.db.public_key_exists(idx(certs, i).key))
67 P(F("no public key '%s' found in database")
68 % idx(certs, i).key);
69 checked.insert(idx(certs, i).key);
70 }
71 }
72
73 // Make the output deterministic; this is useful for the test suite, in
74 // particular.
75 sort(certs.begin(), certs.end());
76
77 string str = _("Key : %s\n"
78 "Sig : %s\n"
79 "Name : %s\n"
80 "Value : %s\n");
81 string extra_str = " : %s\n";
82
83 string::size_type colon_pos = str.find(':');
84
85 if (colon_pos != string::npos)
86 {
87 string substr(str, 0, colon_pos);
88 colon_pos = display_width(utf8(substr));
89 extra_str = string(colon_pos, ' ') + ": %s\n";
90 }
91
92 for (size_t i = 0; i < certs.size(); ++i)
93 {
94 cert_status status = check_cert(app, idx(certs, i));
95 cert_value tv;
96 decode_base64(idx(certs, i).value, tv);
97 string washed;
98 if (guess_binary(tv()))
99 {
100 washed = "<binary data>";
101 }
102 else
103 {
104 washed = tv();
105 }
106
107 string stat;
108 switch (status)
109 {
110 case cert_ok:
111 stat = _("ok");
112 break;
113 case cert_bad:
114 stat = _("bad");
115 break;
116 case cert_unknown:
117 stat = _("unknown");
118 break;
119 }
120
121 vector<string> lines;
122 split_into_lines(washed, lines);
123 I(lines.size() > 0);
124
125 cout << string(guess_terminal_width(), '-') << '\n'
126 << (i18n_format(str)
127 % idx(certs, i).key()
128 % stat
129 % idx(certs, i).name()
130 % idx(lines, 0));
131
132 for (size_t i = 1; i < lines.size(); ++i)
133 cout << (i18n_format(extra_str) % idx(lines, i));
134 }
135
136 if (certs.size() > 0)
137 cout << '\n';
138
139 guard.commit();
140}
141
142static void
143ls_keys(string const & name, app_state & app,
144 vector<utf8> const & args)
145{
146 vector<rsa_keypair_id> pubs;
147 vector<rsa_keypair_id> privkeys;
148 string pattern;
149 if (args.size() == 1)
150 pattern = idx(args, 0)();
151 else if (args.size() > 1)
152 throw usage(name);
153
154 if (app.db.database_specified())
155 {
156 transaction_guard guard(app.db, false);
157 app.db.get_key_ids(pattern, pubs);
158 guard.commit();
159 }
160 app.keys.get_key_ids(pattern, privkeys);
161
162 // true if it is in the database, false otherwise
163 map<rsa_keypair_id, bool> pubkeys;
164 for (vector<rsa_keypair_id>::const_iterator i = pubs.begin();
165 i != pubs.end(); i++)
166 pubkeys[*i] = true;
167
168 bool all_in_db = true;
169 for (vector<rsa_keypair_id>::const_iterator i = privkeys.begin();
170 i != privkeys.end(); i++)
171 {
172 if (pubkeys.find(*i) == pubkeys.end())
173 {
174 pubkeys[*i] = false;
175 all_in_db = false;
176 }
177 }
178
179 if (pubkeys.size() > 0)
180 {
181 cout << "\n[public keys]\n";
182 for (map<rsa_keypair_id, bool>::iterator i = pubkeys.begin();
183 i != pubkeys.end(); i++)
184 {
185 base64<rsa_pub_key> pub_encoded;
186 hexenc<id> hash_code;
187 rsa_keypair_id keyid = i->first;
188 bool indb = i->second;
189
190 if (indb)
191 app.db.get_key(keyid, pub_encoded);
192 else
193 {
194 keypair kp;
195 app.keys.get_key_pair(keyid, kp);
196 pub_encoded = kp.pub;
197 }
198 key_hash_code(keyid, pub_encoded, hash_code);
199 if (indb)
200 cout << hash_code << ' ' << keyid << '\n';
201 else
202 cout << hash_code << ' ' << keyid << " (*)\n";
203 }
204 if (!all_in_db)
205 cout << (F("(*) - only in %s/")
206 % app.keys.get_key_dir()) << '\n';
207 cout << '\n';
208 }
209
210 if (privkeys.size() > 0)
211 {
212 cout << "\n[private keys]\n";
213 for (vector<rsa_keypair_id>::iterator i = privkeys.begin();
214 i != privkeys.end(); i++)
215 {
216 keypair kp;
217 hexenc<id> hash_code;
218 app.keys.get_key_pair(*i, kp);
219 key_hash_code(*i, kp.priv, hash_code);
220 cout << hash_code << ' ' << *i << '\n';
221 }
222 cout << '\n';
223 }
224
225 if (pubkeys.size() == 0 &&
226 privkeys.size() == 0)
227 {
228 if (args.size() == 0)
229 P(F("no keys found"));
230 else
231 W(F("no keys found matching '%s'") % idx(args, 0)());
232 }
233}
234
235static void
236ls_branches(string name, app_state & app, vector<utf8> const & args)
237{
238 globish inc("*");
239 globish exc;
240 if (args.size() == 1)
241 inc = globish(idx(args,0)());
242 else if (args.size() > 1)
243 throw usage(name);
244 vector<globish> excludes;
245 typecast_vocab_container(app.opts.exclude_patterns, excludes);
246 combine_and_check_globish(excludes, exc);
247 globish_matcher match(inc, exc);
248 set<branch_name> names;
249 app.get_project().get_branch_list(names);
250
251 for (set<branch_name>::const_iterator i = names.begin();
252 i != names.end(); ++i)
253 {
254 if (match((*i)()) && !app.lua.hook_ignore_branch(*i))
255 {
256 cout << *i << '\n';
257 }
258 }
259}
260
261static void
262ls_epochs(string name, app_state & app, vector<utf8> const & args)
263{
264 map<branch_name, epoch_data> epochs;
265 app.db.get_epochs(epochs);
266
267 if (args.size() == 0)
268 {
269 for (map<branch_name, epoch_data>::const_iterator
270 i = epochs.begin();
271 i != epochs.end(); ++i)
272 {
273 cout << i->second << ' ' << i->first << '\n';
274 }
275 }
276 else
277 {
278 for (vector<utf8>::const_iterator i = args.begin();
279 i != args.end();
280 ++i)
281 {
282 map<branch_name, epoch_data>::const_iterator j = epochs.find(branch_name((*i)()));
283 N(j != epochs.end(), F("no epoch for branch %s") % *i);
284 cout << j->second << ' ' << j->first << '\n';
285 }
286 }
287}
288
289static void
290ls_tags(string name, app_state & app, vector<utf8> const & args)
291{
292 set<tag_t> tags;
293 app.get_project().get_tags(tags);
294
295 for (set<tag_t>::const_iterator i = tags.begin(); i != tags.end(); ++i)
296 {
297 cout << i->name << ' '
298 << i->ident << ' '
299 << i->key << '\n';
300 }
301}
302
303static void
304ls_vars(string name, app_state & app, vector<utf8> const & args)
305{
306 bool filterp;
307 var_domain filter;
308 if (args.size() == 0)
309 {
310 filterp = false;
311 }
312 else if (args.size() == 1)
313 {
314 filterp = true;
315 internalize_var_domain(idx(args, 0), filter);
316 }
317 else
318 throw usage(name);
319
320 map<var_key, var_value> vars;
321 app.db.get_vars(vars);
322 for (map<var_key, var_value>::const_iterator i = vars.begin();
323 i != vars.end(); ++i)
324 {
325 if (filterp && !(i->first.first == filter))
326 continue;
327 external ext_domain, ext_name;
328 externalize_var_domain(i->first.first, ext_domain);
329 cout << ext_domain << ": "
330 << i->first.second << ' '
331 << i->second << '\n';
332 }
333}
334
335static void
336ls_known(app_state & app, vector<utf8> const & args)
337{
338 roster_t new_roster;
339 temp_node_id_source nis;
340
341 app.require_workspace();
342 app.work.get_current_roster_shape(new_roster, nis);
343
344 node_restriction mask(args_to_paths(args),
345 args_to_paths(app.opts.exclude_patterns),
346 app.opts.depth,
347 new_roster, app);
348
349 // to be printed sorted
350 vector<split_path> print_paths;
351
352 node_map const & nodes = new_roster.all_nodes();
353 for (node_map::const_iterator i = nodes.begin();
354 i != nodes.end(); ++i)
355 {
356 node_id nid = i->first;
357
358 if (!new_roster.is_root(nid)
359 && mask.includes(new_roster, nid))
360 {
361 split_path sp;
362 new_roster.get_name(nid, sp);
363 print_paths.push_back(sp);
364 }
365 }
366
367 sort(print_paths.begin(), print_paths.end());
368 for (vector<split_path>::const_iterator sp = print_paths.begin();
369 sp != print_paths.end(); sp++)
370 {
371 cout << *sp << '\n';
372 }
373}
374
375static void
376ls_unknown_or_ignored(app_state & app, bool want_ignored,
377 vector<utf8> const & args)
378{
379 app.require_workspace();
380
381 vector<file_path> roots = args_to_paths(args);
382 path_restriction mask(roots, args_to_paths(app.opts.exclude_patterns),
383 app.opts.depth, app);
384 path_set unknown, ignored;
385
386 // if no starting paths have been specified use the workspace root
387 if (roots.empty())
388 roots.push_back(file_path());
389
390 app.work.find_unknown_and_ignored(mask, roots, unknown, ignored);
391
392 if (want_ignored)
393 for (path_set::const_iterator i = ignored.begin();
394 i != ignored.end(); ++i)
395 cout << file_path(*i) << '\n';
396 else
397 for (path_set::const_iterator i = unknown.begin();
398 i != unknown.end(); ++i)
399 cout << file_path(*i) << '\n';
400}
401
402static void
403ls_missing(app_state & app, vector<utf8> const & args)
404{
405 temp_node_id_source nis;
406 roster_t current_roster_shape;
407 app.work.get_current_roster_shape(current_roster_shape, nis);
408 node_restriction mask(args_to_paths(args),
409 args_to_paths(app.opts.exclude_patterns),
410 app.opts.depth,
411 current_roster_shape, app);
412
413 path_set missing;
414 app.work.find_missing(current_roster_shape, mask, missing);
415
416 for (path_set::const_iterator i = missing.begin();
417 i != missing.end(); ++i)
418 {
419 cout << file_path(*i) << '\n';
420 }
421}
422
423
424static void
425ls_changed(app_state & app, vector<utf8> const & args)
426{
427 parent_map parents;
428 roster_t new_roster;
429 temp_node_id_source nis;
430
431 app.require_workspace();
432
433 app.work.get_current_roster_shape(new_roster, nis);
434 app.work.update_current_roster_from_filesystem(new_roster);
435
436 app.work.get_parent_rosters(parents);
437
438 node_restriction mask(args_to_paths(args),
439 args_to_paths(app.opts.exclude_patterns),
440 app.opts.depth,
441 parents, new_roster, app);
442
443 revision_t rrev;
444 make_restricted_revision(parents, new_roster, mask, rrev);
445
446 // to be printed sorted, with duplicates removed
447 set<split_path> print_paths;
448
449 for (edge_map::const_iterator i = rrev.edges.begin();
450 i != rrev.edges.end(); i++)
451 {
452 set<node_id> nodes;
453 roster_t const & old_roster
454 = *safe_get(parents, edge_old_revision(i)).first;
455 select_nodes_modified_by_cset(edge_changes(i),
456 old_roster, new_roster, nodes);
457
458 for (set<node_id>::const_iterator i = nodes.begin(); i != nodes.end();
459 ++i)
460 {
461 split_path sp;
462 if (old_roster.has_node(*i))
463 old_roster.get_name(*i, sp);
464 else
465 new_roster.get_name(*i, sp);
466 print_paths.insert(sp);
467 }
468 }
469
470 for (set<split_path>::const_iterator sp = print_paths.begin();
471 sp != print_paths.end(); sp++)
472 {
473 cout << *sp << '\n';
474 }
475
476}
477
478
479CMD(list, N_("informative"),
480 N_("certs ID\n"
481 "keys [PATTERN]\n"
482 "branches [PATTERN]\n"
483 "epochs [BRANCH [...]]\n"
484 "tags\n"
485 "vars [DOMAIN]\n"
486 "known\n"
487 "unknown\n"
488 "ignored\n"
489 "missing\n"
490 "changed"),
491 N_("show database objects, or the current workspace manifest, \n"
492 "or known, unknown, intentionally ignored, missing, or \n"
493 "changed-state files"),
494 options::opts::depth | options::opts::exclude)
495{
496 if (args.size() == 0)
497 throw usage(name);
498
499 vector<utf8>::const_iterator i = args.begin();
500 ++i;
501 vector<utf8> removed (i, args.end());
502 if (idx(args, 0)() == "certs")
503 ls_certs(name, app, removed);
504 else if (idx(args, 0)() == "keys")
505 ls_keys(name, app, removed);
506 else if (idx(args, 0)() == "branches")
507 ls_branches(name, app, removed);
508 else if (idx(args, 0)() == "epochs")
509 ls_epochs(name, app, removed);
510 else if (idx(args, 0)() == "tags")
511 ls_tags(name, app, removed);
512 else if (idx(args, 0)() == "vars")
513 ls_vars(name, app, removed);
514 else if (idx(args, 0)() == "known")
515 ls_known(app, removed);
516 else if (idx(args, 0)() == "unknown")
517 ls_unknown_or_ignored(app, false, removed);
518 else if (idx(args, 0)() == "ignored")
519 ls_unknown_or_ignored(app, true, removed);
520 else if (idx(args, 0)() == "missing")
521 ls_missing(app, removed);
522 else if (idx(args, 0)() == "changed")
523 ls_changed(app, removed);
524 else
525 throw usage(name);
526}
527
528ALIAS(ls, list);
529
530namespace
531{
532 namespace syms
533 {
534 symbol const key("key");
535 symbol const signature("signature");
536 symbol const name("name");
537 symbol const value("value");
538 symbol const trust("trust");
539
540 symbol const public_hash("public_hash");
541 symbol const private_hash("private_hash");
542 symbol const public_location("public_location");
543 symbol const private_location("private_location");
544 }
545};
546
547// Name: keys
548// Arguments: none
549// Added in: 1.1
550// Purpose: Prints all keys in the keystore, and if a database is given
551// also all keys in the database, in basic_io format.
552// Output format: For each key, a basic_io stanza is printed. The items in
553// the stanza are:
554// name - the key identifier
555// public_hash - the hash of the public half of the key
556// private_hash - the hash of the private half of the key
557// public_location - where the public half of the key is stored
558// private_location - where the private half of the key is stored
559// The *_location items may have multiple values, as shown below
560// for public_location.
561// If the private key does not exist, then the private_hash and
562// private_location items will be absent.
563//
564// Sample output:
565// name "tbrownaw@gmail.com"
566// public_hash [475055ec71ad48f5dfaf875b0fea597b5cbbee64]
567// private_hash [7f76dae3f91bb48f80f1871856d9d519770b7f8a]
568// public_location "database" "keystore"
569// private_location "keystore"
570//
571// name "njs@pobox.com"
572// public_hash [de84b575d5e47254393eba49dce9dc4db98ed42d]
573// public_location "database"
574//
575// name "foo@bar.com"
576// public_hash [7b6ce0bd83240438e7a8c7c207d8654881b763f6]
577// private_hash [bfc3263e3257087f531168850801ccefc668312d]
578// public_location "keystore"
579// private_location "keystore"
580//
581// Error conditions: None.
582AUTOMATE(keys, "", options::opts::none)
583{
584 N(args.size() == 0,
585 F("no arguments needed"));
586
587 vector<rsa_keypair_id> dbkeys;
588 vector<rsa_keypair_id> kskeys;
589 // public_hash, private_hash, public_location, private_location
590 map<string, boost::tuple<hexenc<id>, hexenc<id>,
591 vector<string>,
592 vector<string> > > items;
593 if (app.db.database_specified())
594 {
595 transaction_guard guard(app.db, false);
596 app.db.get_key_ids("", dbkeys);
597 guard.commit();
598 }
599 app.keys.get_key_ids("", kskeys);
600
601 for (vector<rsa_keypair_id>::iterator i = dbkeys.begin();
602 i != dbkeys.end(); i++)
603 {
604 base64<rsa_pub_key> pub_encoded;
605 hexenc<id> hash_code;
606
607 app.db.get_key(*i, pub_encoded);
608 key_hash_code(*i, pub_encoded, hash_code);
609 items[(*i)()].get<0>() = hash_code;
610 items[(*i)()].get<2>().push_back("database");
611 }
612
613 for (vector<rsa_keypair_id>::iterator i = kskeys.begin();
614 i != kskeys.end(); i++)
615 {
616 keypair kp;
617 hexenc<id> privhash, pubhash;
618 app.keys.get_key_pair(*i, kp);
619 key_hash_code(*i, kp.pub, pubhash);
620 key_hash_code(*i, kp.priv, privhash);
621 items[(*i)()].get<0>() = pubhash;
622 items[(*i)()].get<1>() = privhash;
623 items[(*i)()].get<2>().push_back("keystore");
624 items[(*i)()].get<3>().push_back("keystore");
625 }
626 basic_io::printer prt;
627 for (map<string, boost::tuple<hexenc<id>, hexenc<id>,
628 vector<string>,
629 vector<string> > >::iterator
630 i = items.begin(); i != items.end(); ++i)
631 {
632 basic_io::stanza stz;
633 stz.push_str_pair(syms::name, i->first);
634 stz.push_hex_pair(syms::public_hash, i->second.get<0>());
635 if (!i->second.get<1>()().empty())
636 stz.push_hex_pair(syms::private_hash, i->second.get<1>());
637 stz.push_str_multi(syms::public_location, i->second.get<2>());
638 if (!i->second.get<3>().empty())
639 stz.push_str_multi(syms::private_location, i->second.get<3>());
640 prt.print_stanza(stz);
641 }
642 output.write(prt.buf.data(), prt.buf.size());
643}
644
645// Name: certs
646// Arguments:
647// 1: a revision id
648// Added in: 1.0
649// Purpose: Prints all certificates associated with the given revision
650// ID. Each certificate is contained in a basic IO stanza. For each
651// certificate, the following values are provided:
652//
653// 'key' : a string indicating the key used to sign this certificate.
654// 'signature': a string indicating the status of the signature.
655// Possible values of this string are:
656// 'ok' : the signature is correct
657// 'bad' : the signature is invalid
658// 'unknown' : signature was made with an unknown key
659// 'name' : the name of this certificate
660// 'value' : the value of this certificate
661// 'trust' : is this certificate trusted by the defined trust metric
662// Possible values of this string are:
663// 'trusted' : this certificate is trusted
664// 'untrusted' : this certificate is not trusted
665//
666// Output format: All stanzas are formatted by basic_io. Stanzas are
667// seperated by a blank line. Values will be escaped, '\' -> '\\' and
668// '"' -> '\"'.
669//
670// Error conditions: If a certificate is signed with an unknown public
671// key, a warning message is printed to stderr. If the revision
672// specified is unknown or invalid prints an error message to stderr
673// and exits with status 1.
674AUTOMATE(certs, N_("REV"), options::opts::none)
675{
676 N(args.size() == 1,
677 F("wrong argument count"));
678
679 vector<cert> certs;
680
681 transaction_guard guard(app.db, false);
682
683 revision_id rid(idx(args, 0)());
684 N(app.db.revision_exists(rid), F("No such revision %s") % rid);
685 hexenc<id> ident(rid.inner());
686
687 vector< revision<cert> > ts;
688 // FIXME_PROJECTS: after projects are implemented,
689 // use the app.db version instead if no project is specified.
690 app.get_project().get_revision_certs(rid, ts);
691
692 for (size_t i = 0; i < ts.size(); ++i)
693 certs.push_back(idx(ts, i).inner());
694
695 {
696 set<rsa_keypair_id> checked;
697 for (size_t i = 0; i < certs.size(); ++i)
698 {
699 if (checked.find(idx(certs, i).key) == checked.end() &&
700 !app.db.public_key_exists(idx(certs, i).key))
701 W(F("no public key '%s' found in database")
702 % idx(certs, i).key);
703 checked.insert(idx(certs, i).key);
704 }
705 }
706
707 // Make the output deterministic; this is useful for the test suite,
708 // in particular.
709 sort(certs.begin(), certs.end());
710
711 basic_io::printer pr;
712
713 for (size_t i = 0; i < certs.size(); ++i)
714 {
715 basic_io::stanza st;
716 cert_status status = check_cert(app, idx(certs, i));
717 cert_value tv;
718 cert_name name = idx(certs, i).name;
719 set<rsa_keypair_id> signers;
720
721 decode_base64(idx(certs, i).value, tv);
722
723 rsa_keypair_id keyid = idx(certs, i).key;
724 signers.insert(keyid);
725
726 bool trusted =
727 app.lua.hook_get_revision_cert_trust(signers, ident,
728 name, tv);
729
730 st.push_str_pair(syms::key, keyid());
731
732 string stat;
733 switch (status)
734 {
735 case cert_ok:
736 stat = "ok";
737 break;
738 case cert_bad:
739 stat = "bad";
740 break;
741 case cert_unknown:
742 stat = "unknown";
743 break;
744 }
745 st.push_str_pair(syms::signature, stat);
746
747 st.push_str_pair(syms::name, name());
748 st.push_str_pair(syms::value, tv());
749 st.push_str_pair(syms::trust, (trusted ? "trusted" : "untrusted"));
750
751 pr.print_stanza(st);
752 }
753 output.write(pr.buf.data(), pr.buf.size());
754
755 guard.commit();
756}
757
758
759// Local Variables:
760// mode: C++
761// fill-column: 76
762// c-file-style: "gnu"
763// indent-tabs-mode: nil
764// End:
765// vim: et:sw=2:sts=2:ts=2:cino=>2s,{s,\:s,+s,t0,g0,^-2,e-2,n-2,p2s,(0,=s:

Archive Download this file

Branches

Tags

Quick Links:     www.monotone.ca    -     Downloads    -     Documentation    -     Wiki    -     Code Forge    -     Build Status