# # add_file "tests/t_automate_keys.at" # # patch "ChangeLog" # from [c4f4a80a415023bcf3db13121d7b8e8d877408c1] # to [86d863c44b004e81501265ab4e5e6a9b344ebd1a] # # patch "automate.cc" # from [5b96e0ff436ae87bdb8bd25121cd53a522af80d6] # to [a8bd4b7f0dcff557067dc0c1697e3d47e9f7c890] # # patch "basic_io.cc" # from [3c575f5fd41d00730c3a0a9eafe84105fa51f28a] # to [f548b2a09749dd603757d8ac491e020a67207eff] # # patch "basic_io.hh" # from [74d226adb3879eeb068b39172c59377a733148ab] # to [c46a35a67fa990a9bd759f09b1cdd2223e7783de] # # patch "commands.cc" # from [e44277638e21b8a20fa0c6f68f067b3799827d0c] # to [0ea3bfbf93bf5ab0a18e4b11a894d7c8f31dc646] # # patch "monotone.texi" # from [f03a883ec62cd2aa2b9e7d972d045630391f9e99] # to [c88edde8d3ac6fcf1f89465a528381a2b0307a6d] # # patch "tests/t_automate_keys.at" # from [] # to [e97dbcb4dc72815d32bfa1f202b9404acbd6ba42] # # patch "testsuite.at" # from [388fd624698f3b9219a28ad8f89df2637d608ca1] # to [c42fcb759b296535e9105e987d6cad39ed3370f6] # ======================================================================== --- ChangeLog c4f4a80a415023bcf3db13121d7b8e8d877408c1 +++ ChangeLog 86d863c44b004e81501265ab4e5e6a9b344ebd1a @@ -1,3 +1,16 @@ +2005-10-07 Timothy Brownawell + + * automate.cc: add "automate keys". like "ls keys", except it + uses basic_io (since "ls keys" is now slightly irregular and not + very well suited to machine interpretation). + * monotone.texi: document it + * tests/t_automate_keys.at: test for it + * testsuite.at: add test + * basic_io.{cc,hh} (stanza): add push_str_multi, for entries + that can take a variable number of string arguments. + * commands.cc: add "automate keys" to automate help + misc minor whitespace cleanup + 2005-10-06 Timothy Brownawell * commands.cc (ls_keys): don't insist on having a db ======================================================================== --- automate.cc 5b96e0ff436ae87bdb8bd25121cd53a522af80d6 +++ automate.cc a8bd4b7f0dcff557067dc0c1697e3d47e9f7c890 @@ -14,6 +14,7 @@ #include #include +#include #include "app_state.hh" #include "basic_io.hh" @@ -23,8 +24,9 @@ #include "revision.hh" #include "transforms.hh" #include "vocab.hh" +#include "keys.hh" -static std::string const interface_version = "1.0"; +static std::string const interface_version = "1.1"; // Name: interface_version // Arguments: none @@ -1263,6 +1265,105 @@ } } +// Name: keys +// Arguments: none +// Added in: 1.1 +// Purpose: Prints all keys in the keystore, and if a database is given +// also all keys in the database, in basic_io format. +// Output format: For each key, a basic_io stanza is printed. The items in +// the stanza are: +// name - the key identifier +// public_hash - the hash of the public half of the key +// private_hash - the hash of the private half of the key +// public_location - where the public half of the key is stored +// private_location - where the private half of the key is stored +// The *_location items may have multiple values, as shown below +// for public_location. +// If the private key does not exist, then the private_hash and +// private_location items will be absent. +// +// Sample output: +// name "address@hidden" +// public_hash [475055ec71ad48f5dfaf875b0fea597b5cbbee64] +// private_hash [7f76dae3f91bb48f80f1871856d9d519770b7f8a] +// public_location "database" "keystore" +// private_location "keystore" +// +// name "address@hidden" +// public_hash [de84b575d5e47254393eba49dce9dc4db98ed42d] +// public_location "database" +// +// name "address@hidden" +// public_hash [7b6ce0bd83240438e7a8c7c207d8654881b763f6] +// private_hash [bfc3263e3257087f531168850801ccefc668312d] +// public_location "keystore" +// private_location "keystore" +// +// Error conditions: None. +static void +automate_keys(std::vector args, std::string const & help_name, + app_state & app, std::ostream & output) +{ + if (args.size() != 0) + throw usage(help_name); + std::vector dbkeys; + std::vector kskeys; + // public_hash, private_hash, public_location, private_location + std::map, hexenc, + std::vector, + std::vector > > items; + if (app.db.database_specified()) + { + transaction_guard guard(app.db); + app.db.get_key_ids("", dbkeys); + guard.commit(); + } + app.keys.get_key_ids("", kskeys); + + for (std::vector::iterator i = dbkeys.begin(); + i != dbkeys.end(); i++) + { + base64 pub_encoded; + hexenc hash_code; + + app.db.get_key(*i, pub_encoded); + key_hash_code(*i, pub_encoded, hash_code); + items[(*i)()].get<0>() = hash_code; + items[(*i)()].get<2>().push_back("database"); + } + + for (std::vector::iterator i = kskeys.begin(); + i != kskeys.end(); i++) + { + keypair kp; + hexenc privhash, pubhash; + app.keys.get_key_pair(*i, kp); + key_hash_code(*i, kp.pub, pubhash); + key_hash_code(*i, kp.priv, privhash); + items[(*i)()].get<0>() = pubhash; + items[(*i)()].get<1>() = privhash; + items[(*i)()].get<2>().push_back("keystore"); + items[(*i)()].get<3>().push_back("keystore"); + } + basic_io::printer prt(output); + for (std::map, hexenc, + std::vector, + std::vector > >::iterator + i = items.begin(); i != items.end(); ++i) + { + basic_io::stanza stz; + stz.push_str_pair("name", i->first); + stz.push_hex_pair("public_hash", i->second.get<0>()()); + if (!i->second.get<1>()().empty()) + stz.push_hex_pair("private_hash", i->second.get<1>()()); + stz.push_str_multi("public_location", i->second.get<2>()); + if (!i->second.get<3>().empty()) + stz.push_str_multi("private_location", i->second.get<3>()); + prt.print_stanza(stz); + } +} + + void automate_command(utf8 cmd, std::vector args, std::string const & root_cmd_name, @@ -1307,6 +1408,8 @@ automate_get_manifest(args, root_cmd_name, app, output); else if (cmd() == "get_file") automate_get_file(args, root_cmd_name, app, output); + else if (cmd() == "keys") + automate_keys(args, root_cmd_name, app, output); else throw usage(root_cmd_name); } ======================================================================== --- basic_io.cc 3c575f5fd41d00730c3a0a9eafe84105fa51f28a +++ basic_io.cc f548b2a09749dd603757d8ac491e020a67207eff @@ -89,6 +89,27 @@ push_str_pair(k, v.as_internal()); } +void basic_io::stanza::push_str_multi(std::string const & k, + std::vector const & v) +{ + for (std::string::const_iterator i = k.begin(); i != k.end(); ++i) + I(std::isalnum(*i) || *i == '_'); + + std::string val; + bool first = true; + for (std::vector::const_iterator i = v.begin(); + i != v.end(); ++i) + { + if (!first) + val += " "; + val += escape(*i); + first = false; + } + entries.push_back(std::make_pair(k, val)); + if (k.size() > indent) + indent = k.size(); +} + basic_io::printer::printer(std::ostream & ost) : empty_output(true), out(ost) {} ======================================================================== --- basic_io.hh 74d226adb3879eeb068b39172c59377a733148ab +++ basic_io.hh c46a35a67fa990a9bd759f09b1cdd2223e7783de @@ -154,6 +154,8 @@ void push_hex_pair(std::string const & k, std::string const & v); void push_str_pair(std::string const & k, std::string const & v); void push_file_pair(std::string const & k, file_path const & v); + void push_str_multi(std::string const & k, + std::vector const & v); }; struct ======================================================================== --- commands.cc e44277638e21b8a20fa0c6f68f067b3799827d0c +++ commands.cc 0ea3bfbf93bf5ab0a18e4b11a894d7c8f31dc646 @@ -599,11 +599,9 @@ guard.commit(); } -static void +static void ls_keys(string const & name, app_state & app, vector const & args) { - - vector pubs; vector privkeys; std::string pattern; @@ -884,7 +882,7 @@ if (app.keys.key_pair_exists(ident)) { - P(F("dropping key pair '%s' from key store\n\n") % ident); + P(F("dropping key pair '%s' from key store\n") % ident); app.keys.delete_key(ident); key_deleted = true; } @@ -3733,7 +3731,8 @@ "select SELECTOR\n" "get_file ID\n" "get_manifest [ID]\n" - "get_revision [ID]\n"), + "get_revision [ID]\n" + "keys\n"), N_("automation interface"), OPT_NONE) { ======================================================================== --- monotone.texi f03a883ec62cd2aa2b9e7d972d045630391f9e99 +++ monotone.texi c88edde8d3ac6fcf1f89465a528381a2b0307a6d @@ -5515,6 +5515,56 @@ @end table + address@hidden monotone automate keys + address@hidden @strong address@hidden Arguments: + +None. + address@hidden Added in: + +1.1 + address@hidden Purpose: + +Print all keys in basic_io format. + address@hidden Sample output: + address@hidden + name "address@hidden" + public_hash [475055ec71ad48f5dfaf875b0fea597b5cbbee64] + private_hash [7f76dae3f91bb48f80f1871856d9d519770b7f8a] + public_location "database" "keystore" +private_location "keystore" + + name "address@hidden" + public_hash [3ac4afcd86af28413b0a23b7d22b9401e15027fc] +public_location "database" + + name "address@hidden" + public_hash [115fdc73d87a5e9901d018462b21a1f53eca33a1] + private_hash [b520d2cfe7d30e4ea1725fc4f34646fc5469b13d] + public_location "keystore" +private_location "keystore" + address@hidden verbatim + address@hidden Output format: + +For each key, a basic_io stanza is printed. The public_location and +private_location items may have multiple values as shown above for public_location, one value for each place that the key is stored. If the +private key does not exist, then the private_hash and private_location +items will be absent. The keys are ordered alphabetically by name. + address@hidden Error conditions: + +None. + address@hidden table + @end ftable @page ======================================================================== --- tests/t_automate_keys.at +++ tests/t_automate_keys.at e97dbcb4dc72815d32bfa1f202b9404acbd6ba42 @@ -0,0 +1,32 @@ +AT_SETUP([automate keys]) +MONOTONE_SETUP + +ADD_FILE(testfile, [foo bar +]) +AT_CHECK(MONOTONE ci -m foobar, [], [ignore], [ignore]) +AT_CHECK((echo address@hidden; echo address@hidden) | MONOTONE genkey address@hidden, [], [ignore], [ignore]) +AT_CHECK((echo address@hidden; echo address@hidden) | MONOTONE genkey address@hidden, [], [ignore], [ignore]) +AT_CHECK(MONOTONE pubkey address@hidden > baz) +AT_CHECK(MONOTONE dropkey address@hidden, [], [ignore], [ignore]) +AT_CHECK(MONOTONE read < baz, [], [ignore], [ignore]) + +# we now have address@hidden in the keystore, address@hidden in both keystore +# and database, and address@hidden in only the database +AT_CHECK(MONOTONE automate keys, [], [stdout], [ignore]) +AT_CHECK(cp stdout output) + +AT_CHECK(sed 's/^$/\n\n/g' < output > processed) +AT_CHECK(grep -A4 'address@hidden' processed > foobar) +AT_CHECK(grep -A4 'address@hidden' processed > foobaz) +AT_CHECK(grep -A4 'address@hidden' processed > tester) + +AT_CHECK(QGREP database foobar, [1]) +AT_CHECK(QGREP private foobaz, [1]) +AT_CHECK(grep keystore tester | QGREP database) +AT_CHECK(grep keystore tester | QGREP -v database) +AT_CHECK(QGREP private tester) +AT_CHECK(QGREP public foobar) +AT_CHECK(QGREP public foobaz) +AT_CHECK(QGREP public tester) + +AT_CLEANUP ======================================================================== --- testsuite.at 388fd624698f3b9219a28ad8f89df2637d608ca1 +++ testsuite.at c42fcb759b296535e9105e987d6cad39ed3370f6 @@ -709,3 +709,4 @@ m4_include(tests/t_unreadable_db.at) m4_include(tests/t_restriction_with_exclude.at) m4_include(tests/t_key_management_without_db.at) +m4_include(tests/t_automate_keys.at)