various fixes to make testing possible
[epoint] / cmd / epoint-server / epoint-server.go
1 package main
2
3 import (
4         "crypto/openpgp"
5         "epoint/key"
6         "epoint/server"
7         "flag"
8         "fmt"
9         "io/ioutil"
10         "log"
11         "net/http"
12         "os"
13 )
14
15 var (
16         rootdir = flag.String("dir", "docroot", "root dir for storage")
17         seckey  = flag.String("key", "", "secret key, empty means generated")
18         addr    = flag.String("addr", ":8080", "address to listen on")
19 )
20
21 // todo: http header limit: 64K, body limit: 64K
22
23 func httpError(w http.ResponseWriter, code int, msg string) {
24         log.Printf("error: %d %s", code, msg)
25         http.Error(w, fmt.Sprintf("%d %s\n\n%s\n", code, http.StatusText(code), msg), code)
26 }
27
28 func httpReq(r *http.Request) string {
29         err := r.ParseForm()
30         form := ""
31         if err != nil {
32                 form = err.Error()
33         } else {
34                 a := []string{}
35                 for k := range r.Form {
36                         a = append(a, k)
37                 }
38                 form = fmt.Sprintf("%v", a)
39         }
40         return fmt.Sprintf("%s %s params:%s", r.Method, r.URL, form)
41 }
42
43 func defaultHandler(w http.ResponseWriter, r *http.Request) {
44         log.Printf("%s %s", r.RemoteAddr, httpReq(r))
45         fmt.Fprintf(w, "not implemented: %s %s\n", r.Method, r.URL)
46 }
47
48 func submitHandler(w http.ResponseWriter, r *http.Request) {
49         log.Printf("%s %s", r.RemoteAddr, httpReq(r))
50         draft := r.FormValue("draft")
51         debit := r.FormValue("debit")
52         key := r.FormValue("key")
53         switch {
54         case draft != "":
55                 cert, err := server.EvalDraft([]byte(draft))
56                 if err != nil {
57                         msg := fmt.Sprintf("eval draft failed: %s", err)
58                         httpError(w, 404, msg)
59                 } else {
60                         w.Write(cert)
61                 }
62         case debit != "":
63                 cert, err := server.EvalDebitCert([]byte(debit))
64                 if err != nil {
65                         msg := fmt.Sprintf("eval debit failed: %s", err)
66                         httpError(w, 404, msg)
67                 } else {
68                         w.Write(cert)
69                 }
70         case key != "":
71                 err := server.AddKeys([]byte(key))
72                 if err != nil {
73                         msg := fmt.Sprintf("add keys failed: %s", err)
74                         httpError(w, 404, msg)
75                 } else {
76                         w.Write([]byte("ok\n"))
77                 }
78         default:
79                 msg := fmt.Sprintf("expected key, draft or debit param, got: %s", httpReq(r))
80                 httpError(w, 404, msg)
81         }
82 }
83
84 func initkey(path string) (sk *openpgp.Entity, err error) {
85         if path == "" {
86                 // TODO: serious keygen, server key depends on time
87                 return key.Server([]byte("secret seed"))
88         }
89         d, err := ioutil.ReadFile(path)
90         if err != nil {
91                 return
92         }
93         return key.Parse(d)
94 }
95
96 func main() {
97         flag.Parse()
98         serverkey, err := initkey(*seckey)
99         if err != nil {
100                 log.Fatal(err)
101         }
102         err = server.Init(*rootdir, serverkey)
103         if err != nil {
104                 log.Fatal(err)
105         }
106
107         // TODO: url from key
108         f, err := os.Create(*rootdir + "/form.html")
109         if err != nil {
110                 log.Fatal(err)
111         }
112         _, _ = fmt.Fprintf(f, `<html><head><title>epoint-server submit form</title></head><body>
113 <h2>epoint-server submit form</h2>
114 <h3>web form</h3>
115 <p>submit one document at a time
116 <form method="post" action="/submit">
117 <p>key:<br><textarea name="key" rows="5" cols="80"></textarea>
118 <p>draft:<br><textarea name="draft" rows="5" cols="80"></textarea>
119 <p>debit:<br><textarea name="debit" rows="5" cols="80"></textarea>
120 <p><input type="submit">
121 </form>
122 <h3>command line</h3>
123 <pre>
124 curl --data-urlencode name@path/to/file.txt host/submit
125 </pre>
126 where 'name' is 'key', 'draft' or 'debit'.
127 </body></html>
128 `)
129         _ = f.Close()
130
131         // queries
132         http.Handle("/", http.FileServer(http.Dir(*rootdir)))
133
134         // actions
135         // withdraw, draw, deposit, process, clear
136         http.HandleFunc("/submit", submitHandler)
137
138         log.Printf("start service on %s, server key id: %s\n", *addr, key.Id(serverkey))
139         log.Fatal(http.ListenAndServe(*addr, nil))
140 }