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