github.com/digdeepmining/go-atheios@v1.5.13-0.20180902133602-d5687a2e6f43/cmd/swarm/upload.go (about) 1 // Copyright 2016 The go-ethereum Authors 2 // This file is part of go-ethereum. 3 // 4 // go-ethereum is free software: you can redistribute it and/or modify 5 // it under the terms of the GNU General Public License as published by 6 // the Free Software Foundation, either version 3 of the License, or 7 // (at your option) any later version. 8 // 9 // go-ethereum is distributed in the hope that it will be useful, 10 // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 // GNU General Public License for more details. 13 // 14 // You should have received a copy of the GNU General Public License 15 // along with go-ethereum. If not, see <http://www.gnu.org/licenses/>. 16 17 // Command bzzup uploads files to the swarm HTTP API. 18 package main 19 20 import ( 21 "bytes" 22 "encoding/json" 23 "fmt" 24 "io" 25 "io/ioutil" 26 "log" 27 "mime" 28 "net/http" 29 "os" 30 "os/user" 31 "path" 32 "path/filepath" 33 "strings" 34 35 "gopkg.in/urfave/cli.v1" 36 ) 37 38 func upload(ctx *cli.Context) { 39 args := ctx.Args() 40 var ( 41 bzzapi = strings.TrimRight(ctx.GlobalString(SwarmApiFlag.Name), "/") 42 recursive = ctx.GlobalBool(SwarmRecursiveUploadFlag.Name) 43 wantManifest = ctx.GlobalBoolT(SwarmWantManifestFlag.Name) 44 defaultPath = ctx.GlobalString(SwarmUploadDefaultPath.Name) 45 ) 46 if len(args) != 1 { 47 log.Fatal("need filename as the first and only argument") 48 } 49 50 var ( 51 file = args[0] 52 client = &client{api: bzzapi} 53 ) 54 fi, err := os.Stat(expandPath(file)) 55 if err != nil { 56 log.Fatal(err) 57 } 58 if fi.IsDir() { 59 if !recursive { 60 log.Fatal("argument is a directory and recursive upload is disabled") 61 } 62 if !wantManifest { 63 log.Fatal("manifest is required for directory uploads") 64 } 65 mhash, err := client.uploadDirectory(file, defaultPath) 66 if err != nil { 67 log.Fatal(err) 68 } 69 fmt.Println(mhash) 70 return 71 } 72 entry, err := client.uploadFile(file, fi) 73 if err != nil { 74 log.Fatalln("upload failed:", err) 75 } 76 mroot := manifest{[]manifestEntry{entry}} 77 if !wantManifest { 78 // Print the manifest. This is the only output to stdout. 79 mrootJSON, _ := json.MarshalIndent(mroot, "", " ") 80 fmt.Println(string(mrootJSON)) 81 return 82 } 83 hash, err := client.uploadManifest(mroot) 84 if err != nil { 85 log.Fatalln("manifest upload failed:", err) 86 } 87 fmt.Println(hash) 88 } 89 90 // Expands a file path 91 // 1. replace tilde with users home dir 92 // 2. expands embedded environment variables 93 // 3. cleans the path, e.g. /a/b/../c -> /a/c 94 // Note, it has limitations, e.g. ~someuser/tmp will not be expanded 95 func expandPath(p string) string { 96 if strings.HasPrefix(p, "~/") || strings.HasPrefix(p, "~\\") { 97 if home := homeDir(); home != "" { 98 p = home + p[1:] 99 } 100 } 101 return path.Clean(os.ExpandEnv(p)) 102 } 103 104 func homeDir() string { 105 if home := os.Getenv("HOME"); home != "" { 106 return home 107 } 108 if usr, err := user.Current(); err == nil { 109 return usr.HomeDir 110 } 111 return "" 112 } 113 114 // client wraps interaction with the swarm HTTP gateway. 115 type client struct { 116 api string 117 } 118 119 // manifest is the JSON representation of a swarm manifest. 120 type manifestEntry struct { 121 Hash string `json:"hash,omitempty"` 122 ContentType string `json:"contentType,omitempty"` 123 Path string `json:"path,omitempty"` 124 } 125 126 // manifest is the JSON representation of a swarm manifest. 127 type manifest struct { 128 Entries []manifestEntry `json:"entries,omitempty"` 129 } 130 131 func (c *client) uploadDirectory(dir string, defaultPath string) (string, error) { 132 mhash, err := c.postRaw("application/json", 2, ioutil.NopCloser(bytes.NewReader([]byte("{}")))) 133 if err != nil { 134 return "", fmt.Errorf("failed to upload empty manifest") 135 } 136 if len(defaultPath) > 0 { 137 fi, err := os.Stat(defaultPath) 138 if err != nil { 139 return "", err 140 } 141 mhash, err = c.uploadToManifest(mhash, "", defaultPath, fi) 142 if err != nil { 143 return "", err 144 } 145 } 146 prefix := filepath.ToSlash(filepath.Clean(dir)) + "/" 147 err = filepath.Walk(dir, func(path string, fi os.FileInfo, err error) error { 148 if err != nil || fi.IsDir() { 149 return err 150 } 151 if !strings.HasPrefix(path, dir) { 152 return fmt.Errorf("path %s outside directory %s", path, dir) 153 } 154 uripath := strings.TrimPrefix(filepath.ToSlash(filepath.Clean(path)), prefix) 155 mhash, err = c.uploadToManifest(mhash, uripath, path, fi) 156 return err 157 }) 158 return mhash, err 159 } 160 161 func (c *client) uploadFile(file string, fi os.FileInfo) (manifestEntry, error) { 162 hash, err := c.uploadFileContent(file, fi) 163 m := manifestEntry{ 164 Hash: hash, 165 ContentType: mime.TypeByExtension(filepath.Ext(fi.Name())), 166 } 167 return m, err 168 } 169 170 func (c *client) uploadFileContent(file string, fi os.FileInfo) (string, error) { 171 fd, err := os.Open(file) 172 if err != nil { 173 return "", err 174 } 175 defer fd.Close() 176 log.Printf("uploading file %s (%d bytes)", file, fi.Size()) 177 return c.postRaw("application/octet-stream", fi.Size(), fd) 178 } 179 180 func (c *client) uploadManifest(m manifest) (string, error) { 181 jsm, err := json.Marshal(m) 182 if err != nil { 183 panic(err) 184 } 185 log.Println("uploading manifest") 186 return c.postRaw("application/json", int64(len(jsm)), ioutil.NopCloser(bytes.NewReader(jsm))) 187 } 188 189 func (c *client) uploadToManifest(mhash string, path string, fpath string, fi os.FileInfo) (string, error) { 190 fd, err := os.Open(fpath) 191 if err != nil { 192 return "", err 193 } 194 defer fd.Close() 195 log.Printf("uploading file %s (%d bytes) and adding path %v", fpath, fi.Size(), path) 196 req, err := http.NewRequest("PUT", c.api+"/bzz:/"+mhash+"/"+path, fd) 197 if err != nil { 198 return "", err 199 } 200 req.Header.Set("content-type", mime.TypeByExtension(filepath.Ext(fi.Name()))) 201 req.ContentLength = fi.Size() 202 resp, err := http.DefaultClient.Do(req) 203 if err != nil { 204 return "", err 205 } 206 defer resp.Body.Close() 207 if resp.StatusCode >= 400 { 208 return "", fmt.Errorf("bad status: %s", resp.Status) 209 } 210 content, err := ioutil.ReadAll(resp.Body) 211 return string(content), err 212 } 213 214 func (c *client) postRaw(mimetype string, size int64, body io.ReadCloser) (string, error) { 215 req, err := http.NewRequest("POST", c.api+"/bzzr:/", body) 216 if err != nil { 217 return "", err 218 } 219 req.Header.Set("content-type", mimetype) 220 req.ContentLength = size 221 resp, err := http.DefaultClient.Do(req) 222 if err != nil { 223 return "", err 224 } 225 defer resp.Body.Close() 226 if resp.StatusCode >= 400 { 227 return "", fmt.Errorf("bad status: %s", resp.Status) 228 } 229 content, err := ioutil.ReadAll(resp.Body) 230 return string(content), err 231 } 232 233 func (c *client) downloadManifest(mhash string) (manifest, error) { 234 235 mroot := manifest{} 236 req, err := http.NewRequest("GET", c.api + "/bzzr:/" + mhash, nil) 237 if err != nil { 238 return mroot, err 239 } 240 resp, err := http.DefaultClient.Do(req) 241 if err != nil { 242 return mroot, err 243 } 244 defer resp.Body.Close() 245 246 if resp.StatusCode >= 400 { 247 return mroot, fmt.Errorf("bad status: %s", resp.Status) 248 249 } 250 content, err := ioutil.ReadAll(resp.Body) 251 252 err = json.Unmarshal(content, &mroot) 253 if err != nil { 254 return mroot, fmt.Errorf("Manifest %v is malformed: %v", mhash, err) 255 } 256 return mroot, err 257 }