github.com/keybase/client/go@v0.0.0-20240309051027-028f7c731f8b/kbnm/main.go (about) 1 package main 2 3 import ( 4 "encoding/json" 5 "flag" 6 "fmt" 7 "io" 8 "log" 9 "os" 10 11 "github.com/keybase/client/go/kbnm/installer" 12 "github.com/qrtz/nativemessaging" 13 ) 14 15 // internalVersion is the logical version of this code (rather than build). 16 const internalVersion = "1.4.1" 17 18 // Version is the build version of kbnm, overwritten during build with metadata. 19 var Version = "dev" 20 21 // Response from the kbnm service 22 type Response struct { 23 Client int `json:"client"` 24 Status string `json:"status"` 25 Message string `json:"message"` 26 Result interface{} `json:"result,omitempty"` 27 } 28 29 // Request to the kbnm service 30 type Request struct { 31 Client int `json:"client"` 32 Method string `json:"method"` 33 To string `json:"to"` 34 Body string `json:"body"` 35 } 36 37 var plainFlag = flag.Bool("plain", false, "newline-delimited JSON IO, no length prefix") 38 var versionFlag = flag.Bool("version", false, "print the version and exit") 39 40 // process consumes a single message 41 func process(h *handler, in nativemessaging.JSONDecoder, out nativemessaging.JSONEncoder) error { 42 var resp Response 43 var req Request 44 45 // If input fails to parse, we can't guarantee future inputs will 46 // get into a parseable state so we abort after sending an error 47 // response. 48 abortErr := in.Decode(&req) 49 50 var err error 51 if abortErr == nil { 52 resp.Result, err = h.Handle(&req) 53 } 54 55 switch err { 56 case nil: 57 // Success 58 resp.Status = "ok" 59 case io.EOF: 60 // Closed 61 return err 62 default: 63 resp.Status = "error" 64 resp.Message = err.Error() 65 } 66 resp.Client = req.Client 67 68 err = out.Encode(resp) 69 if err != nil { 70 // TODO: Log this somewhere? 71 fmt.Fprintf(os.Stderr, "error: %s", err) 72 os.Exit(1) 73 } 74 75 return abortErr 76 } 77 78 func exit(code int, msg string, a ...interface{}) { 79 fmt.Fprintf(os.Stderr, msg+"\n", a...) 80 os.Exit(code) 81 } 82 83 func main() { 84 flag.Parse() 85 86 if *versionFlag { 87 fmt.Printf("%s-%s\n", internalVersion, Version) 88 os.Exit(0) 89 } 90 91 switch flag.Arg(0) { 92 case "install": 93 kbnmPath, err := findKeybaseBinary(kbnmBinary) 94 if err != nil { 95 exit(2, "error finding kbnm binary: %s", err) 96 } 97 log.Print("installing: ", kbnmPath) 98 if err := installer.InstallKBNM(kbnmPath); err != nil { 99 exit(2, "error installing kbnm whitelist: %s", err) 100 } 101 exit(0, "Installed NativeMessaging whitelists.") 102 case "uninstall": 103 if err := installer.UninstallKBNM(); err != nil { 104 exit(2, "error uninstalling kbnm whitelist: %s", err) 105 } 106 exit(0, "Uninstalled NativeMessaging whitelists.") 107 } 108 109 // Native messages include a prefix which describes the length of each message. 110 var in nativemessaging.JSONDecoder 111 var out nativemessaging.JSONEncoder 112 113 if *plainFlag { 114 // Used for testing interactively 115 in = json.NewDecoder(os.Stdin) 116 out = json.NewEncoder(os.Stdout) 117 } else { 118 // Used as part of the NativeMessaging API 119 in = nativemessaging.NewNativeJSONDecoder(os.Stdin) 120 out = nativemessaging.NewNativeJSONEncoder(os.Stdout) 121 } 122 123 h := newHandler() 124 125 for { 126 err := process(h, in, out) 127 if err == io.EOF { 128 // Clean close 129 break 130 } 131 if err != nil { 132 exit(1, "stream processing error: %s", err) 133 } 134 } 135 }