]> git.wincent.com - passage.git/blob - passage.go
Add caching
[passage.git] / passage.go
1 package main
2
3 import (
4         "encoding/json"
5         "github.com/keybase/go-keychain"
6         "log"
7         "net"
8         "os"
9         "os/signal"
10         "os/user"
11         "syscall"
12 )
13
14 type Request struct {
15         Account string
16         Service string
17 }
18
19 var cache map[string][]byte
20
21 func main() {
22         cache = make(map[string][]byte)
23         path := getSockPath()
24         syscall.Umask(0077)
25         listener, err := net.Listen("unix", path)
26         if err != nil {
27                 log.Fatal(err)
28         }
29         defer listener.Close()
30
31         go func() {
32                 for {
33                         conn, err := listener.Accept()
34                         if err != nil {
35                                 log.Print(err)
36                                 return
37                         }
38                         go handleConnection(conn)
39                 }
40         }()
41         log.Print("Open and ready for business on UNIX socket at ", path)
42
43         // Need to catch signals in order for `defer`-ed clean-up items to run.
44         c := make(chan os.Signal, 1)
45         signal.Notify(c, os.Interrupt, os.Kill, syscall.SIGTERM)
46         sig := <-c
47         log.Print("Got signal ", sig)
48 }
49
50 func getSockPath() string {
51         user, err := user.Current()
52         if err != nil {
53                 log.Fatal(err)
54         }
55         return user.HomeDir + "/.passage.sock"
56 }
57
58 func handleConnection(conn net.Conn) {
59         defer conn.Close()
60         decoder := json.NewDecoder(conn)
61         var request Request
62         err := decoder.Decode(&request)
63         if err != nil {
64                 log.Print(err)
65                 return
66         }
67
68         cache_key, err := json.Marshal(request)
69         if err != nil {
70                 log.Fatal(err)
71         }
72         if cached, exists := cache[string(cache_key)]; exists {
73                 conn.Write(cached)
74                 log.Print("Wrote result from cache")
75                 return
76         }
77
78         query := keychain.NewItem()
79         query.SetSecClass(keychain.SecClassGenericPassword)
80         query.SetService(request.Service)
81         query.SetAccount(request.Account)
82         query.SetMatchLimit(keychain.MatchLimitOne)
83         query.SetReturnData(true)
84         results, err := keychain.QueryItem(query)
85         if err != nil {
86                 log.Print(err)
87         } else if len(results) != 1 {
88                 log.Print("Item not found")
89         } else {
90                 password := results[0].Data
91                 conn.Write(password)
92                 cache[string(cache_key)] = password
93                 log.Print("Wrote result from keychain")
94         }
95 }