package main
+import (
+ "crypto/openpgp"
+ "crypto/openpgp/armor"
+ "crypto/rand"
+ "epoint/document"
+ "epoint/key"
+ "fmt"
+ "io"
+ "log"
+ "net/http"
+ "net/url"
+ "os"
+ "strconv"
+)
+
+const usage = `usage: ./epoint-client [k|d|s|v] [args..] < [seed|document]
+server is http://localhost:8080 by default
+
+k - make key, use seed for generation, args: [issuer] denomination
+d - make draft, use seed as signing key, args: targetid value
+s - submit a document, args: k[ey]|d[raft]|c[ert] [server]
+v - verify a document (prints body of the document if ok)
+`
+
+func rnd(n int) (r []byte, err error) {
+ r = make([]byte, n)
+ _, err = io.ReadFull(rand.Reader, r)
+ return
+}
+
+func k(r []byte, issuer, denom string) (err error) {
+ var e *openpgp.Entity
+
+ if issuer == "" {
+ e, err = key.Issuer(r, denom)
+ } else {
+ e, err = key.Holder(r, issuer, denom)
+ }
+ if err != nil {
+ return
+ }
+ w, err := armor.Encode(os.Stdout, openpgp.PublicKeyType, nil)
+ if err != nil {
+ return
+ }
+ // TODO: maybe Serialize should do this internally
+ for _, ident := range e.Identities {
+ err = ident.SelfSignature.SignUserId(ident.UserId.Id, e.PrimaryKey, e.PrivateKey)
+ if err != nil {
+ return
+ }
+ }
+ for _, subkey := range e.Subkeys {
+ err = subkey.Sig.SignKey(subkey.PublicKey, e.PrivateKey)
+ if err != nil {
+ return
+ }
+ }
+
+ err = e.Serialize(w)
+ if err != nil {
+ return
+ }
+ _, err = w.Write([]byte{'\n'})
+ if err != nil {
+ return
+ }
+ err = w.Close()
+ return
+}
+
+func d(r []byte, target, value string) (err error) {
+ v, err := strconv.Atoi64(value)
+ if err != nil {
+ return
+ }
+ e, err := key.Holder(r, "", "")
+ if err != nil {
+ return
+ }
+ draft := new(document.Draft)
+ draft.Drawer = key.Id(e)
+ draft.Beneficiary = target
+ draft.Amount = v
+ draft.Denomination = "" // TODO
+ draft.Issuer = "" // TODO
+ draft.AuthorizedBy = "" // TODO
+ nonce, err := rnd(10)
+ if err != nil {
+ return
+ }
+ draft.Nonce = fmt.Sprintf("%X", nonce)
+ s, _, err := document.Format(draft, e)
+ if err != nil {
+ return
+ }
+ _, err = os.Stdout.Write(s)
+ return
+}
+
+func s(d []byte, cmd, server string) (err error) {
+ m := map[string]string{
+ "k": "key",
+ "d": "draft",
+ "c": "debit",
+ }
+ k, ok := m[cmd]
+ if !ok {
+ err = fmt.Errorf("unknown submit command: %s", cmd)
+ return
+ }
+ resp, err := http.PostForm(server, url.Values{k: {string(d)}})
+ if err != nil {
+ return
+ }
+ fmt.Printf("%v", resp)
+ return
+}
+
+func v(d []byte) (err error) {
+ _, s, err := document.Parse(d)
+ if err != nil {
+ return
+ }
+ _, err = os.Stdout.Write(s.Body)
+ return
+}
+
+func read() []byte {
+ b := make([]byte, 10000)
+ n, err := io.ReadFull(os.Stdin, b)
+ if err != io.ErrUnexpectedEOF {
+ if err == nil {
+ log.Fatal("too much input")
+ }
+ log.Fatal(err)
+ }
+ return b[:n]
+}
+
func main() {
+ if len(os.Args) < 2 {
+ log.Fatal(usage)
+ }
+
+ var err error
+ switch os.Args[1] {
+ case "k":
+ issuer := ""
+ denom := ""
+ if len(os.Args) == 4 {
+ issuer = os.Args[2]
+ denom = os.Args[3]
+ } else if len(os.Args) == 3 {
+ denom = os.Args[2]
+ } else {
+ log.Fatal(usage)
+ }
+ err = k(read(), issuer, denom)
+ case "d":
+ if len(os.Args) != 4 {
+ log.Fatal(usage)
+ }
+ err = d(read(), os.Args[2], os.Args[3])
+ case "s":
+ server := "http://localhost:8080"
+ cmd := ""
+ if len(os.Args) == 4 {
+ cmd = os.Args[2]
+ server = os.Args[3]
+ } else if len(os.Args) == 3 {
+ cmd = os.Args[2]
+ } else {
+ log.Fatal(usage)
+ }
+ err = s(read(), cmd, server+"/submit")
+ case "v":
+ if len(os.Args) != 2 {
+ log.Fatal(usage)
+ }
+ err = v(read())
+ }
+ if err != nil {
+ log.Fatal(err)
+ }
}