├── go.mod ├── README.md ├── main.go └── go.sum /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/boomlinde/ipfs-gopher 2 | 3 | go 1.14 4 | 5 | require github.com/ipfs/go-ipfs-api v0.0.3 6 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ipfs-gopher is a gopher proxy that allows serving gopher over IPFS. For 2 | plain files, it just forwards the file content to the client. For menus, 3 | it uses a simple format with a header to distinguish menus from other 4 | files, and to use the local proxy when resolving IPFS links. 5 | 6 | Example session 7 | --------------- 8 | 9 | $ ipfs-gopher & 10 | $ curl gopher://localhost:7070/1/ipfs/QmRgTQvW7ab1bwjmeL4EQh1AVcWrUJn5ou3g1jv9aAqgRo/menu1 11 | 12 | ## Usage 13 | $ ipfs-gopher -h 14 | Usage of ipfs-gopher: 15 | -daemon string 16 | The address of the IPFS daemon (default "localhost:5001") 17 | -host string 18 | The host to use in IPFS selectors (default "localhost") 19 | -listen string 20 | The address of the proxy (default "localhost:7070") 21 | -port string 22 | The port to use in IPFS selectors (default "7070") 23 | 24 | Menu format 25 | ----------- 26 | 27 | Because the menu content presented to the gopher client depends on 28 | ipfs-gopher settings and thus need to be modified in transit, we need to 29 | separate regular files from menu files. To do this, the menu starts with 30 | a single line, containing: 31 | 32 | <<>> 33 | 34 | ipfs-gopher menus are otherwise much like plain gopher menus: 35 | 36 | 37 | 38 | The most important difference is that the port and host may be omitted. 39 | When they are, the selector should be understood as an IPFS link and 40 | ipfs-gopher will fill in the rest of the columns. Relative IPFS links 41 | may be used as selectors. 42 | 43 | You may also omit the selector (useful for info type entries). Finally, 44 | ipfs-gopher menu files may use Unix style line endings (LF) as well as 45 | network style line endings (CRLF). 46 | 47 | Linking to ipfs-gopher holes 48 | ---------------------------- 49 | 50 | Because the settings are client dependent, I suggest that any non-IPFS 51 | gopher hole that wants to link to an ipfs-gopher hole should do so 52 | through the hostname "ipfs-gopher" on port 7070. This way, users can 53 | create a host alias for their preferred host. If not, a Sufficiently 54 | Smart™ Gopher client could also replace the "ipfs-gopher" hostname with 55 | the preferred hostname and the port with the preferred port. 56 | 57 | TODO 58 | ---- 59 | 60 | - Report IPFS errors to client 61 | -------------------------------------------------------------------------------- /main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "bufio" 5 | "flag" 6 | "fmt" 7 | "io" 8 | "log" 9 | "net" 10 | "path" 11 | "path/filepath" 12 | "strings" 13 | 14 | shell "github.com/ipfs/go-ipfs-api" 15 | ) 16 | 17 | const menumarker = "<<>>" 18 | const maxline = 4096 19 | 20 | var host string 21 | var port string 22 | var daemon string 23 | var listen string 24 | 25 | func fix(line string, dir string) string { 26 | fields := strings.Split(line, "\t") 27 | switch len(fields) { 28 | case 1: 29 | if fields[0] == "." { 30 | break 31 | } 32 | if fields[0] != "" { 33 | fields = append(fields, "fake", host, port) 34 | } 35 | case 2: 36 | fields = append(fields, host, port) 37 | if strings.HasPrefix(fields[1], "./") { 38 | fields[1] = dir + fields[1][1:] 39 | 40 | } 41 | case 3: 42 | fields = append(fields, host, "70") 43 | } 44 | return strings.Join(fields, "\t") + "\r\n" 45 | } 46 | 47 | func forward(dst io.Writer, src io.Reader, selector string) error { 48 | dir := path.Dir(selector) 49 | // Read the marker if any from the destination 50 | markerbuf := make([]byte, len(menumarker)) 51 | n, err := src.Read(markerbuf) 52 | if err != nil { 53 | return err 54 | } 55 | 56 | // If we don't have a menu marker, copy what we buffered to the destination 57 | // and then copy the rest of the src 58 | if string(markerbuf) != menumarker { 59 | if _, err := dst.Write(markerbuf[:n]); err != nil { 60 | return err 61 | } 62 | if _, err := io.Copy(dst, src); err != nil { 63 | return err 64 | } 65 | return nil 66 | } 67 | 68 | // Otherwise we have a menu. 69 | rd := bufio.NewReaderSize(src, maxline) 70 | 71 | // Discard the remaining line 72 | _, err = rd.ReadBytes('\n') 73 | if err != nil { 74 | return err 75 | } 76 | 77 | // Fix the menu lines 78 | for { 79 | line, _, err := rd.ReadLine() 80 | if err != nil { 81 | if err == io.EOF { 82 | break 83 | } 84 | return err 85 | } 86 | fixed := fix(string(line), dir) 87 | if _, err := dst.Write([]byte(fixed)); err != nil { 88 | return err 89 | } 90 | } 91 | return nil 92 | } 93 | 94 | func handledir(sh *shell.Shell, selector string, out io.Writer) error { 95 | dir, err := sh.List(selector) 96 | if err != nil { 97 | return err 98 | } 99 | 100 | if _, err := out.Write([]byte(fix(fmt.Sprintf("iListing %s", selector), ""))); err != nil { 101 | return err 102 | } 103 | if _, err := out.Write([]byte(fix("i", ""))); err != nil { 104 | return err 105 | } 106 | 107 | for _, entry := range dir { 108 | path := path.Join(selector, entry.Name) 109 | switch entry.Type { 110 | case shell.TSymlink: 111 | fallthrough 112 | case shell.TRaw: 113 | fallthrough 114 | case shell.TFile: 115 | _, err := out.Write([]byte(fix(fmt.Sprintf("%c%s\t%s", filetype(entry.Name), entry.Name, path), ""))) 116 | if err != nil { 117 | return err 118 | } 119 | case shell.TDirectory: 120 | _, err := out.Write([]byte(fix(fmt.Sprintf("1%s\t%s", entry.Name, path), ""))) 121 | if err != nil { 122 | return err 123 | } 124 | } 125 | } 126 | 127 | if _, err := out.Write([]byte(fix(".", ""))); err != nil { 128 | return err 129 | } 130 | 131 | return nil 132 | } 133 | 134 | func fetch(sh *shell.Shell, selector string, out io.Writer) error { 135 | rc, err := sh.Cat(selector) 136 | if err != nil { 137 | // This may be a directory. Error value in that case seems entirely 138 | // dependent on the daemon implementation, so we just assume that it is 139 | // and try fetching it as a directory instead of returning the error 140 | return handledir(sh, selector, out) 141 | } 142 | defer rc.Close() 143 | err = forward(out, rc, selector) 144 | if err != nil { 145 | return err 146 | } 147 | 148 | return nil 149 | } 150 | 151 | func handle(conn net.Conn, sh *shell.Shell) { 152 | defer conn.Close() 153 | rd := bufio.NewReader(conn) 154 | selector, _, err := rd.ReadLine() 155 | if err != nil { 156 | log.Printf("failed to read selector: %v", err) 157 | return 158 | } 159 | err = fetch(sh, string(selector), conn) 160 | if err != nil { 161 | log.Printf("failed to fetch: %v", err) 162 | return 163 | } 164 | } 165 | 166 | func main() { 167 | flag.StringVar(&host, "host", "localhost", "The host to use in IPFS selectors") 168 | flag.StringVar(&port, "port", "7070", "The port to use in IPFS selectors") 169 | flag.StringVar(&daemon, "daemon", "localhost:5001", "The address of the IPFS daemon") 170 | flag.StringVar(&listen, "listen", "localhost:7070", "The address of the proxy") 171 | flag.Parse() 172 | 173 | sh := shell.NewShell(daemon) 174 | 175 | listener, err := net.Listen("tcp", listen) 176 | if err != nil { 177 | panic(err) 178 | } 179 | defer listener.Close() 180 | 181 | for { 182 | conn, err := listener.Accept() 183 | if err != nil { 184 | log.Printf("failed to accept connection: %v", err) 185 | continue 186 | } 187 | go handle(conn, sh) 188 | } 189 | } 190 | 191 | var filetypes = map[string]rune{ 192 | ".txt": '0', ".gif": 'g', ".jpg": 'I', ".jpeg": 'I', 193 | ".png": 'I', ".html": 'h', ".htm": 'h', ".ogg": 's', 194 | ".mp3": 's', ".wav": 's', ".mod": 's', ".it": 's', 195 | ".xm": 's', ".mid": 's', ".vgm": 's', ".s": '0', 196 | ".c": '0', ".py": '0', ".h": '0', ".md": '0', ".go": '0', 197 | ".fs": '0', ".xml": '0', ".css": 0, ".ts": 0, ".svg": 'g', 198 | } 199 | 200 | func filetype(name string) rune { 201 | fp, ok := filetypes[strings.ToLower(filepath.Ext(name))] 202 | if !ok { 203 | return '9' 204 | } 205 | 206 | return fp 207 | } 208 | -------------------------------------------------------------------------------- /go.sum: -------------------------------------------------------------------------------- 1 | github.com/aead/siphash v1.0.1/go.mod h1:Nywa3cDsYNNK3gaciGTWPwHt0wlpNV15vwmswBAUSII= 2 | github.com/btcsuite/btcd v0.0.0-20190213025234-306aecffea32 h1:qkOC5Gd33k54tobS36cXdAzJbeHaduLtnLQQwNoIi78= 3 | github.com/btcsuite/btcd v0.0.0-20190213025234-306aecffea32/go.mod h1:DrZx5ec/dmnfpw9KyYoQyYo7d0KEvTkk/5M/vbZjAr8= 4 | github.com/btcsuite/btclog v0.0.0-20170628155309-84c8d2346e9f/go.mod h1:TdznJufoqS23FtqVCzL0ZqgP5MqXbb4fg/WgDys70nA= 5 | github.com/btcsuite/btcutil v0.0.0-20190207003914-4c204d697803/go.mod h1:+5NJ2+qvTyV9exUAL/rxXi3DcLg2Ts+ymUAY5y4NvMg= 6 | github.com/btcsuite/go-socks v0.0.0-20170105172521-4720035b7bfd/go.mod h1:HHNXQzUsZCxOoE+CPiyCTO6x34Zs86zZUiwtpXoGdtg= 7 | github.com/btcsuite/goleveldb v0.0.0-20160330041536-7834afc9e8cd/go.mod h1:F+uVaaLLH7j4eDXPRvw78tMflu7Ie2bzYOH4Y8rRKBY= 8 | github.com/btcsuite/snappy-go v0.0.0-20151229074030-0bdef8d06723/go.mod h1:8woku9dyThutzjeg+3xrA5iCpBRH8XEEg3lh6TiUghc= 9 | github.com/btcsuite/websocket v0.0.0-20150119174127-31079b680792/go.mod h1:ghJtEyQwv5/p4Mg4C0fgbePVuGr935/5ddU9Z3TmDRY= 10 | github.com/btcsuite/winsvc v1.0.0/go.mod h1:jsenWakMcC0zFBFurPLEAyrnc/teJEM1O46fmI40EZs= 11 | github.com/cheekybits/is v0.0.0-20150225183255-68e9c0620927/go.mod h1:h/aW8ynjgkuj+NQRlZcDbAbM1ORAbXjXX77sX7T289U= 12 | github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= 13 | github.com/davecgh/go-spew v0.0.0-20171005155431-ecdeabc65495/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 14 | github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= 15 | github.com/gogo/protobuf v1.2.1 h1:/s5zKNz0uPFCZ5hddgPdo2TK2TVrUNMn0OOX8/aZMTE= 16 | github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4= 17 | github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= 18 | github.com/gxed/hashland/keccakpg v0.0.1/go.mod h1:kRzw3HkwxFU1mpmPP8v1WyQzwdGfmKFJ6tItnhQ67kU= 19 | github.com/gxed/hashland/murmur3 v0.0.1/go.mod h1:KjXop02n4/ckmZSnY2+HKcLud/tcmvhST0bie/0lS48= 20 | github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= 21 | github.com/ipfs/go-cid v0.0.1/go.mod h1:GHWU/WuQdMPmIosc4Yn1bcCT7dSeX4lBafM7iqUPQvM= 22 | github.com/ipfs/go-ipfs-api v0.0.3 h1:1XZBfVDGj0GyyO5WItLrz2opCwezIm9LfFcBfe+sRxM= 23 | github.com/ipfs/go-ipfs-api v0.0.3/go.mod h1:EgBqlEzrA22SnNKq4tcP2GDPKxbfF+uRTd2YFmR1uUk= 24 | github.com/ipfs/go-ipfs-files v0.0.6 h1:sMRtPiSmDrTA2FEiFTtk1vWgO2Dkg7bxXKJ+s8/cDAc= 25 | github.com/ipfs/go-ipfs-files v0.0.6/go.mod h1:lVYE6sgAdtZN5825beJjSAHibw7WOBNPDWz5LaJeukg= 26 | github.com/ipfs/go-ipfs-util v0.0.1/go.mod h1:spsl5z8KUnrve+73pOhSVZND1SIxPW5RyBCNzQxlJBc= 27 | github.com/jbenet/goprocess v0.0.0-20160826012719-b497e2f366b8/go.mod h1:Ly/wlsjFq/qrU3Rar62tu1gASgGw6chQbSh/XgIIXCY= 28 | github.com/jessevdk/go-flags v0.0.0-20141203071132-1679536dcc89/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= 29 | github.com/jrick/logrotate v1.0.0/go.mod h1:LNinyqDIJnpAur+b8yyulnQw/wDuN1+BYKlTRt3OuAQ= 30 | github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q= 31 | github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= 32 | github.com/kkdai/bstream v0.0.0-20161212061736-f391b8402d23/go.mod h1:J+Gs4SYgM6CZQHDETBtE9HaSEkGmuNXF86RwHhHUvq4= 33 | github.com/libp2p/go-flow-metrics v0.0.1 h1:0gxuFd2GuK7IIP5pKljLwps6TvcuYgvG7Atqi3INF5s= 34 | github.com/libp2p/go-flow-metrics v0.0.1/go.mod h1:Iv1GH0sG8DtYN3SVJ2eG221wMiNpZxBdp967ls1g+k8= 35 | github.com/libp2p/go-libp2p-core v0.0.1 h1:HSTZtFIq/W5Ue43Zw+uWZyy2Vl5WtF0zDjKN8/DT/1I= 36 | github.com/libp2p/go-libp2p-core v0.0.1/go.mod h1:g/VxnTZ/1ygHxH3dKok7Vno1VfpvGcGip57wjTU4fco= 37 | github.com/libp2p/go-libp2p-crypto v0.1.0 h1:k9MFy+o2zGDNGsaoZl0MA3iZ75qXxr9OOoAZF+sD5OQ= 38 | github.com/libp2p/go-libp2p-crypto v0.1.0/go.mod h1:sPUokVISZiy+nNuTTH/TY+leRSxnFj/2GLjtOTW90hI= 39 | github.com/libp2p/go-libp2p-metrics v0.1.0 h1:v7YMUTHNobFaQeqaMfJJMbnK3EPlZeb6/KFm4gE9dks= 40 | github.com/libp2p/go-libp2p-metrics v0.1.0/go.mod h1:rpoJmXWFxnj7qs5sJ02sxSzrhaZvpqBn8GCG6Sx6E1k= 41 | github.com/libp2p/go-libp2p-peer v0.2.0 h1:EQ8kMjaCUwt/Y5uLgjT8iY2qg0mGUT0N1zUjer50DsY= 42 | github.com/libp2p/go-libp2p-peer v0.2.0/go.mod h1:RCffaCvUyW2CJmG2gAWVqwePwW7JMgxjsHm7+J5kjWY= 43 | github.com/minio/blake2b-simd v0.0.0-20160723061019-3f5f724cb5b1 h1:lYpkrQH5ajf0OXOcUbGjvZxxijuBwbbmlSxLiuofa+g= 44 | github.com/minio/blake2b-simd v0.0.0-20160723061019-3f5f724cb5b1/go.mod h1:pD8RvIylQ358TN4wwqatJ8rNavkEINozVn9DtGI3dfQ= 45 | github.com/minio/sha256-simd v0.0.0-20190131020904-2d45a736cd16/go.mod h1:2FMWW+8GMoPweT6+pI63m9YE3Lmw4J71hV56Chs1E/U= 46 | github.com/minio/sha256-simd v0.1.1-0.20190913151208-6de447530771 h1:MHkK1uRtFbVqvAgvWxafZe54+5uBxLluGylDiKgdhwo= 47 | github.com/minio/sha256-simd v0.1.1-0.20190913151208-6de447530771/go.mod h1:B5e1o+1/KgNmWrSQK08Y6Z1Vb5pwIktudl0J58iy0KM= 48 | github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y= 49 | github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= 50 | github.com/mr-tron/base58 v1.1.0/go.mod h1:xcD2VGqlgYjBdcBLw+TuYLr8afG+Hj8g2eTVqeSzSU8= 51 | github.com/mr-tron/base58 v1.1.1/go.mod h1:xcD2VGqlgYjBdcBLw+TuYLr8afG+Hj8g2eTVqeSzSU8= 52 | github.com/mr-tron/base58 v1.1.2 h1:ZEw4I2EgPKDJ2iEw0cNmLB3ROrEmkOtXIkaG7wZg+78= 53 | github.com/mr-tron/base58 v1.1.2/go.mod h1:BinMc/sQntlIE1frQmRFPUoPA1Zkr8VRgBdjWI2mNwc= 54 | github.com/multiformats/go-base32 v0.0.3/go.mod h1:pLiuGC8y0QR3Ue4Zug5UzK9LjgbkL8NSQj0zQ5Nz/AA= 55 | github.com/multiformats/go-multiaddr v0.0.2/go.mod h1:xKVEak1K9cS1VdmPZW3LSIb6lgmoS58qz/pzqmAxV44= 56 | github.com/multiformats/go-multiaddr v0.1.0/go.mod h1:xKVEak1K9cS1VdmPZW3LSIb6lgmoS58qz/pzqmAxV44= 57 | github.com/multiformats/go-multiaddr v0.2.0 h1:lR52sFwcTCuQb6bTfnXF6zA2XfyYvyd+5a9qECv/J90= 58 | github.com/multiformats/go-multiaddr v0.2.0/go.mod h1:0nO36NvPpyV4QzvTLi/lafl2y95ncPj0vFwVF6k6wJ4= 59 | github.com/multiformats/go-multiaddr-net v0.1.1 h1:jFFKUuXTXv+3ARyHZi3XUqQO+YWMKgBdhEvuGRfnL6s= 60 | github.com/multiformats/go-multiaddr-net v0.1.1/go.mod h1:5JNbcfBOP4dnhoZOv10JJVkJO0pCCEf8mTnipAo2UZQ= 61 | github.com/multiformats/go-multibase v0.0.1/go.mod h1:bja2MqRZ3ggyXtZSEDKpl0uO/gviWFaSteVbWT51qgs= 62 | github.com/multiformats/go-multihash v0.0.1/go.mod h1:w/5tugSrLEbWqlcgJabL3oHFKTwfvkofsjW2Qa1ct4U= 63 | github.com/multiformats/go-multihash v0.0.8 h1:wrYcW5yxSi3dU07n5jnuS5PrNwyHy0zRHGVoUugWvXg= 64 | github.com/multiformats/go-multihash v0.0.8/go.mod h1:YSLudS+Pi8NHE7o6tb3D8vrpKa63epEDmG8nTduyAew= 65 | github.com/multiformats/go-varint v0.0.1 h1:TR/0rdQtnNxuN2IhiB639xC3tWM4IUi7DkTBVTdGW/M= 66 | github.com/multiformats/go-varint v0.0.1/go.mod h1:3Ls8CIEsrijN6+B7PbrXRPxHRPuXSrVKRY101jdMZYE= 67 | github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= 68 | github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= 69 | github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= 70 | github.com/spacemonkeygo/openssl v0.0.0-20181017203307-c2dcc5cca94a h1:/eS3yfGjQKG+9kayBkj0ip1BGhq6zJ3eaVksphxAaek= 71 | github.com/spacemonkeygo/openssl v0.0.0-20181017203307-c2dcc5cca94a/go.mod h1:7AyxJNCJ7SBZ1MfVQCWD6Uqo2oubI2Eq2y2eqf+A5r0= 72 | github.com/spacemonkeygo/spacelog v0.0.0-20180420211403-2296661a0572 h1:RC6RW7j+1+HkWaX/Yh71Ee5ZHaHYt7ZP4sQgUrm6cDU= 73 | github.com/spacemonkeygo/spacelog v0.0.0-20180420211403-2296661a0572/go.mod h1:w0SWMsp6j9O/dk4/ZpIhL+3CkG8ofA2vuv7k+ltqUMc= 74 | github.com/spaolacci/murmur3 v1.1.0 h1:7c1g84S4BPRrfL5Xrdp6fOJ206sU9y293DDHaoy0bLI= 75 | github.com/spaolacci/murmur3 v1.1.0/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= 76 | github.com/whyrusleeping/tar-utils v0.0.0-20180509141711-8c6c8ba81d5c h1:GGsyl0dZ2jJgVT+VvWBf/cNijrHRhkrTjkmp5wg7li0= 77 | github.com/whyrusleeping/tar-utils v0.0.0-20180509141711-8c6c8ba81d5c/go.mod h1:xxcJeBb7SIUl/Wzkz1eVKJE/CB34YNrqX2TQI6jY9zs= 78 | golang.org/x/crypto v0.0.0-20170930174604-9419663f5a44/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= 79 | golang.org/x/crypto v0.0.0-20190211182817-74369b46fc67/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= 80 | golang.org/x/crypto v0.0.0-20190225124518-7f87c0fbb88b/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= 81 | golang.org/x/crypto v0.0.0-20190228161510-8dd112bcdc25/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= 82 | golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= 83 | golang.org/x/crypto v0.0.0-20190611184440-5c40567a22f8 h1:1wopBVtVdWnn03fZelqdXTqk7U7zPQCb+T4rbU9ZEoU= 84 | golang.org/x/crypto v0.0.0-20190611184440-5c40567a22f8/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= 85 | golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 86 | golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= 87 | golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 88 | golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 89 | golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 90 | golang.org/x/sys v0.0.0-20190219092855-153ac476189d/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 91 | golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 92 | golang.org/x/sys v0.0.0-20190228124157-a34e9553db1e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 93 | golang.org/x/sys v0.0.0-20190302025703-b6889370fb10/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 94 | golang.org/x/sys v0.0.0-20190412213103-97732733099d h1:+R4KGOnez64A81RvjARKc4UT5/tI9ujCIVX+P5KiHuI= 95 | golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 96 | golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= 97 | golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= 98 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 99 | gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= 100 | gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= 101 | gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 102 | --------------------------------------------------------------------------------