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