├── .gitignore ├── .travis.yml ├── .travis └── after_success ├── LICENSE ├── Makefile ├── README.md ├── app ├── api │ ├── api.go │ └── api_test.go ├── backend │ └── fs │ │ ├── fs.go │ │ └── fs_test.go ├── config │ ├── config.go │ └── config_test.go ├── events │ ├── events.go │ └── events_test.go ├── metrics │ ├── metrics.go │ └── metrics_test.go ├── model │ ├── batch.go │ ├── context.go │ ├── link.go │ └── testdata │ │ ├── image.gif │ │ ├── image.jpg │ │ └── image.png ├── output │ └── output.go ├── shared │ └── shared.go └── tokens │ └── token.go ├── doc ├── screenshot-web-interface-bin.png ├── screenshot-web-interface-new-bin.png └── screenshot-web-interface-uploading.png ├── etc ├── hitch │ └── hitch.conf.example ├── systemd │ └── filebin.service.example └── varnish │ ├── varnish.params.example │ └── varnish.vcl.example ├── go.mod ├── go.sum ├── main.go ├── static ├── bootstrap │ ├── css │ │ ├── bootstrap.css.map │ │ └── bootstrap.min.css │ ├── fonts │ │ ├── glyphicons-halflings-regular.eot │ │ ├── glyphicons-halflings-regular.svg │ │ ├── glyphicons-halflings-regular.ttf │ │ ├── glyphicons-halflings-regular.woff │ │ └── glyphicons-halflings-regular.woff2 │ └── js │ │ └── bootstrap.min.js ├── css │ ├── custom.css │ ├── font-awesome.css │ └── upload.css ├── fonts │ ├── FontAwesome.otf │ ├── fontawesome-webfont.eot │ ├── fontawesome-webfont.svg │ ├── fontawesome-webfont.ttf │ ├── fontawesome-webfont.woff │ └── fontawesome-webfont.woff2 ├── img │ └── favicon.png └── js │ ├── jquery.js │ ├── popper.min.js │ ├── showhide.js │ ├── sorttable.js │ └── upload.js ├── templates ├── api.html ├── bins.html ├── counters.html ├── dashboard.html ├── doc.html ├── events.html ├── invalidtokenarchive.html ├── invalidtokenfile.html ├── newbin.html ├── readme.html ├── tokens.html ├── viewalbum.html └── viewbin.html ├── tools └── git │ ├── LICENSE │ └── pre-commit └── vendor ├── github.com ├── GeertJohan │ └── go.rice │ │ ├── .gitignore │ │ ├── .travis.yml │ │ ├── AUTHORS │ │ ├── LICENSE │ │ ├── README.md │ │ ├── appended.go │ │ ├── box.go │ │ ├── config.go │ │ ├── debug.go │ │ ├── embedded.go │ │ ├── embedded │ │ └── embedded.go │ │ ├── file.go │ │ ├── go.mod │ │ ├── go.sum │ │ ├── http.go │ │ ├── sort.go │ │ ├── virtual.go │ │ └── walk.go ├── daaku │ └── go.zipexe │ │ ├── go.mod │ │ ├── license │ │ ├── readme.md │ │ └── zipexe.go ├── disintegration │ └── imaging │ │ ├── .travis.yml │ │ ├── LICENSE │ │ ├── README.md │ │ ├── adjust.go │ │ ├── convolution.go │ │ ├── doc.go │ │ ├── effects.go │ │ ├── go.mod │ │ ├── go.sum │ │ ├── histogram.go │ │ ├── io.go │ │ ├── resize.go │ │ ├── scanner.go │ │ ├── tools.go │ │ ├── transform.go │ │ └── utils.go ├── dustin │ └── go-humanize │ │ ├── .travis.yml │ │ ├── LICENSE │ │ ├── README.markdown │ │ ├── big.go │ │ ├── bigbytes.go │ │ ├── bytes.go │ │ ├── comma.go │ │ ├── commaf.go │ │ ├── ftoa.go │ │ ├── humanize.go │ │ ├── number.go │ │ ├── ordinals.go │ │ ├── si.go │ │ └── times.go ├── espebra │ └── filebin │ │ ├── LICENSE │ │ └── app │ │ ├── api │ │ └── api.go │ │ ├── backend │ │ └── fs │ │ │ └── fs.go │ │ ├── config │ │ └── config.go │ │ ├── events │ │ └── events.go │ │ ├── metrics │ │ └── metrics.go │ │ ├── model │ │ ├── batch.go │ │ ├── context.go │ │ └── link.go │ │ ├── output │ │ └── output.go │ │ ├── shared │ │ └── shared.go │ │ └── tokens │ │ └── token.go ├── gorilla │ ├── handlers │ │ ├── LICENSE │ │ ├── README.md │ │ ├── canonical.go │ │ ├── compress.go │ │ ├── cors.go │ │ ├── doc.go │ │ ├── go.mod │ │ ├── handlers.go │ │ ├── handlers_go18.go │ │ ├── handlers_pre18.go │ │ ├── logging.go │ │ ├── proxy_headers.go │ │ └── recovery.go │ └── mux │ │ ├── AUTHORS │ │ ├── LICENSE │ │ ├── README.md │ │ ├── context.go │ │ ├── doc.go │ │ ├── go.mod │ │ ├── middleware.go │ │ ├── mux.go │ │ ├── regexp.go │ │ ├── route.go │ │ └── test_helpers.go └── rwcarlsen │ └── goexif │ ├── LICENSE │ ├── exif │ ├── README.md │ ├── exif.go │ ├── fields.go │ └── sample1.jpg │ └── tiff │ ├── sample1.tif │ ├── tag.go │ └── tiff.go ├── golang.org └── x │ └── image │ ├── AUTHORS │ ├── CONTRIBUTORS │ ├── LICENSE │ ├── PATENTS │ ├── bmp │ ├── reader.go │ └── writer.go │ ├── ccitt │ ├── reader.go │ ├── table.go │ └── writer.go │ └── tiff │ ├── buffer.go │ ├── compress.go │ ├── consts.go │ ├── fuzz.go │ ├── lzw │ └── reader.go │ ├── reader.go │ └── writer.go └── modules.txt /.gitignore: -------------------------------------------------------------------------------- 1 | *.pyc 2 | files/* 3 | temp/* 4 | .vagrant/ 5 | bindata_assetfs.go 6 | app/output/templates.go 7 | 8 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: go 2 | 3 | before_install: 4 | - sudo apt-get update -q 5 | - sudo apt-get install curl 6 | 7 | go: 8 | - tip 9 | 10 | install: 11 | - make get-deps 12 | 13 | script: 14 | - make check 15 | - make install 16 | 17 | after_success: 18 | - source ./.travis/after_success 19 | -------------------------------------------------------------------------------- /.travis/after_success: -------------------------------------------------------------------------------- 1 | #!bash 2 | 3 | FILE="$HOME/gopath/bin/filebin" 4 | echo "Binary: ${FILE}" 5 | 6 | CHECKSUM=$(sha256sum ${FILE} | cut -d ' ' -f1) 7 | echo "Checksum: ${CHECKSUM}" 8 | 9 | /usr/bin/curl -v --data-binary "@${FILE}" https://filebin.net/ -H "Filename: filebin" -H "Bin: filebin-ci" -H "Content-SHA256: ${CHECKSUM}" 10 | 11 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2015-2016, Espen Braastad 2 | All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without 5 | modification, are permitted provided that the following conditions are met: 6 | * Redistributions of source code must retain the above copyright 7 | notice, this list of conditions and the following disclaimer. 8 | * Redistributions in binary form must reproduce the above copyright 9 | notice, this list of conditions and the following disclaimer in the 10 | documentation and/or other materials provided with the distribution. 11 | * Neither the name of the nor the 12 | names of its contributors may be used to endorse or promote products 13 | derived from this software without specific prior written permission. 14 | 15 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 16 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 17 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 18 | DISCLAIMED. IN NO EVENT SHALL BE LIABLE FOR ANY 19 | DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 20 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 21 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 22 | ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 23 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 24 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 25 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | HASH=`git rev-parse HEAD` 2 | 3 | prepare: 4 | rm -f templates.rice-box.go 5 | rm -f static.rice-box.go 6 | rice embed-go -v -i . 7 | 8 | check: 9 | go test -cover -v github.com/espebra/filebin/app/api github.com/espebra/filebin/app/model github.com/espebra/filebin/app/config github.com/espebra/filebin/app/backend/fs github.com/espebra/filebin/app/metrics github.com/espebra/filebin/app/events 10 | 11 | get-deps: 12 | go mod vendor -v 13 | go get github.com/GeertJohan/go.rice 14 | go get github.com/GeertJohan/go.rice/rice 15 | 16 | build: prepare 17 | go build -mod=vendor -ldflags "-X main.githash=${HASH}" 18 | 19 | install: prepare 20 | go install -mod=vendor -ldflags "-X main.githash=${HASH}" 21 | -------------------------------------------------------------------------------- /app/api/api_test.go: -------------------------------------------------------------------------------- 1 | package api 2 | 3 | import ( 4 | "math/rand" 5 | "testing" 6 | ) 7 | 8 | func TestTriggers(t *testing.T) { 9 | var err error 10 | 11 | err = triggerNewBinHandler("/bin/echo", "bin") 12 | if err != nil { 13 | t.Fatal(err) 14 | } 15 | 16 | err = triggerDeleteBinHandler("/bin/echo", "bin") 17 | if err != nil { 18 | t.Fatal(err) 19 | } 20 | 21 | err = triggerUploadFileHandler("/bin/echo", "bin", "filename") 22 | if err != nil { 23 | t.Fatal(err) 24 | } 25 | 26 | err = triggerDeleteFileHandler("/bin/echo", "bin", "filename") 27 | if err != nil { 28 | t.Fatal(err) 29 | } 30 | 31 | err = triggerNewBinHandler("unknowncommand", "bin") 32 | if err == nil { 33 | t.Fatal("This should fail") 34 | } 35 | 36 | err = triggerDeleteBinHandler("unknowncommand", "bin") 37 | if err == nil { 38 | t.Fatal("This should fail") 39 | } 40 | 41 | err = triggerUploadFileHandler("unknowncommand", "bin", "filename") 42 | if err == nil { 43 | t.Fatal("This should fail") 44 | } 45 | 46 | err = triggerDeleteFileHandler("unknowncommand", "bin", "filename") 47 | if err == nil { 48 | t.Fatal("This should fail") 49 | } 50 | } 51 | 52 | func TestRandomString(t *testing.T) { 53 | rand.Seed(1) 54 | str := randomString(16) 55 | if str != "fpllngzieyoh43e0" { 56 | t.Fatal("Random string from known seed is not", str) 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /app/backend/fs/fs_test.go: -------------------------------------------------------------------------------- 1 | package fs 2 | 3 | import ( 4 | "bytes" 5 | "io/ioutil" 6 | "log" 7 | "os" 8 | "strconv" 9 | "testing" 10 | "time" 11 | ) 12 | 13 | const ( 14 | EXPIRATION = 3 15 | ) 16 | 17 | var ( 18 | be Backend 19 | ) 20 | 21 | func uploadFile(bin string, filename string, data string) (File, error) { 22 | body := ioutil.NopCloser(bytes.NewBufferString(data)) 23 | 24 | f, err := be.UploadFile(bin, filename, body) 25 | return f, err 26 | } 27 | 28 | func TestMain(m *testing.M) { 29 | log := log.New(os.Stdout, "- ", log.LstdFlags) 30 | 31 | baseurl := "http://127.0.0.1" 32 | 33 | filedir, err := ioutil.TempDir("", "filebin-filedir") 34 | if err != nil { 35 | log.Fatal(err) 36 | } 37 | 38 | tempdir, err := ioutil.TempDir("", "filebin-tempdir") 39 | if err != nil { 40 | log.Fatal(err) 41 | } 42 | 43 | be, err = InitBackend(baseurl, filedir, tempdir, int64(EXPIRATION), log, []string{}) 44 | if err != nil { 45 | log.Println(err) 46 | os.Exit(2) 47 | } 48 | 49 | retCode := m.Run() 50 | 51 | // Clean up 52 | os.RemoveAll(filedir) 53 | os.RemoveAll(tempdir) 54 | 55 | os.Exit(retCode) 56 | } 57 | 58 | func TestInfo(t *testing.T) { 59 | info := be.Info() 60 | expected := "FS backend from " + be.filedir 61 | if info != expected { 62 | t.Fatal("Unexpected info string: " + info) 63 | } 64 | } 65 | 66 | func TestUploadFile(t *testing.T) { 67 | f, err := uploadFile("testbin", "testfile", "Some content") 68 | if err != nil { 69 | t.Fatal(err) 70 | } 71 | if f.Filename != "testfile" { 72 | t.Fatal("Unexpected filename: " + f.Filename) 73 | } 74 | if f.Bytes != 12 { 75 | t.Fatal("Unexpected file size: " + strconv.FormatInt(f.Bytes, 10)) 76 | } 77 | if f.MIME != "application/octet-stream" { 78 | t.Fatal("Unexpected MIME: " + f.MIME) 79 | } 80 | numLinks := len(f.Links) 81 | if numLinks != 2 { 82 | t.Fatal("Unexpected number of links: " + strconv.Itoa(numLinks)) 83 | } 84 | 85 | files, err := be.getFiles("testbin") 86 | if err != nil { 87 | t.Fatal(err) 88 | } 89 | if len(files) != 1 { 90 | t.Fatal("Unexpected number of files: " + strconv.Itoa(len(files))) 91 | } 92 | } 93 | 94 | func TestGetFileMetaData(t *testing.T) { 95 | bin := "testbin" 96 | filename := "testfile" 97 | 98 | f, err := be.GetFileMetaData(bin, filename) 99 | if err != nil { 100 | t.Fatal(err) 101 | } 102 | if f.Filename != "testfile" { 103 | t.Fatal("Unexpected filename: " + f.Filename) 104 | } 105 | if f.Bytes != 12 { 106 | t.Fatal("Unexpected file size: " + strconv.FormatInt(f.Bytes, 10)) 107 | } 108 | if f.MIME != "application/octet-stream" { 109 | t.Fatal("Unexpected MIME: " + f.MIME) 110 | } 111 | } 112 | 113 | func TestGetFile(t *testing.T) { 114 | bin := "testbin" 115 | filename := "testfile" 116 | 117 | fp, err := be.GetFile(bin, filename) 118 | if err != nil { 119 | t.Fatal(err) 120 | } 121 | 122 | buf := new(bytes.Buffer) 123 | buf.ReadFrom(fp) 124 | content := buf.String() 125 | if content != "Some content" { 126 | t.Fatal("Unexpected content: " + content) 127 | } 128 | } 129 | 130 | func TestGetBinMetaData(t *testing.T) { 131 | bin := "testbin" 132 | 133 | b, err := be.GetBinMetaData(bin) 134 | if err != nil { 135 | t.Fatal(err) 136 | } 137 | 138 | if b.Bytes != 12 { 139 | t.Fatal("Unexpected bin size: " + strconv.FormatInt(b.Bytes, 10)) 140 | } 141 | 142 | if b.Bin != bin { 143 | t.Fatal("Unexpected bin id: " + b.Bin) 144 | } 145 | 146 | fileNum := len(b.Files) 147 | if fileNum != 1 { 148 | t.Fatal("Unexpected file count: " + strconv.Itoa(fileNum)) 149 | } 150 | 151 | if b.Expired != false { 152 | t.Fatal("Bin has unexpectedly expired") 153 | } 154 | } 155 | 156 | func TestAllMetaData(t *testing.T) { 157 | fileNum := len(be.files) 158 | if fileNum != 1 { 159 | t.Fatal("Unexpected file count: " + strconv.Itoa(fileNum)) 160 | } 161 | } 162 | 163 | func TestExpiredBin(t *testing.T) { 164 | time.Sleep(EXPIRATION * time.Second) 165 | 166 | bin := "testbin" 167 | b, err := be.GetBinMetaData(bin) 168 | if err != nil { 169 | t.Fatal(err) 170 | } 171 | 172 | if b.Expired != true { 173 | t.Fatal("Bin has unexpectedly not expired") 174 | } 175 | } 176 | 177 | func TestDeleteFile(t *testing.T) { 178 | bin := "testbin" 179 | filename := "testfile" 180 | 181 | if _, err := be.DeleteFile(bin, filename); err != nil { 182 | t.Fatal(err) 183 | } 184 | 185 | if _, err := be.GetFileMetaData(bin, filename); err == nil { 186 | t.Fatal("Unexpected success when reading deleted file") 187 | } 188 | 189 | _, err := be.DeleteFile(bin, filename) 190 | expected := "File not found" 191 | if err.Error() != expected { 192 | t.Fatal(err) 193 | } 194 | } 195 | 196 | func TestDeleteBin(t *testing.T) { 197 | bin := "2bdeleted" 198 | _, err := uploadFile(bin, "testfile2", "Some other content") 199 | if err != nil { 200 | t.Fatal(err) 201 | } 202 | 203 | if _, err := be.DeleteBin(bin); err != nil { 204 | t.Fatal(err) 205 | } 206 | 207 | if _, err := be.GetBinMetaData(bin); err == nil { 208 | t.Fatal("Unexpected success when reading deleted bin") 209 | } 210 | 211 | _, err = be.DeleteBin(bin) 212 | expected := "Bin " + bin + " does not exist" 213 | if err.Error() != expected { 214 | t.Fatal(err) 215 | } 216 | } 217 | -------------------------------------------------------------------------------- /app/config/config.go: -------------------------------------------------------------------------------- 1 | package config 2 | 3 | type filters []string 4 | 5 | func (i *filters) String() string { 6 | return "string representation" 7 | } 8 | 9 | func (i *filters) Set(value string) error { 10 | *i = append(*i, value) 11 | return nil 12 | } 13 | 14 | type Configuration struct { 15 | Host string 16 | Port int 17 | ReadTimeout int 18 | WriteTimeout int 19 | MaxHeaderBytes int 20 | Filedir string 21 | Baseurl string 22 | Tempdir string 23 | Expiration int64 24 | TriggerNewBin string 25 | TriggerUploadFile string 26 | TriggerDownloadBin string 27 | TriggerDownloadFile string 28 | TriggerDeleteBin string 29 | TriggerDeleteFile string 30 | TriggerExpireBin string 31 | ClientAddrHeader string 32 | DefaultBinLength int 33 | Workers int 34 | Version bool 35 | CacheInvalidation bool 36 | AdminUsername string 37 | AdminPassword string 38 | AccessLog string 39 | Filters filters 40 | HotLinking bool 41 | } 42 | 43 | var Global Configuration 44 | 45 | func init() { 46 | Global = Configuration{ 47 | Host: "127.0.0.1", 48 | Port: 31337, 49 | ReadTimeout: 3600, 50 | WriteTimeout: 3600, 51 | MaxHeaderBytes: 1 << 20, 52 | // 7776000 = 3 months 53 | Expiration: 7776000, 54 | Baseurl: "http://localhost:31337", 55 | Filedir: "/srv/filebin/files", 56 | Tempdir: "/tmp", 57 | DefaultBinLength: 16, 58 | Workers: 1, 59 | CacheInvalidation: false, 60 | AdminUsername: "admin", 61 | ClientAddrHeader: "", 62 | AccessLog: "/var/log/filebin/access.log", 63 | Filters: []string{}, 64 | HotLinking: true, 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /app/config/config_test.go: -------------------------------------------------------------------------------- 1 | package config 2 | 3 | import ( 4 | "testing" 5 | ) 6 | 7 | func TestInit(t *testing.T) { 8 | var cfg = Global 9 | 10 | if cfg.Port == 0 { 11 | t.Fatal("Missing Port") 12 | } 13 | 14 | if cfg.Host == "" { 15 | t.Fatal("Missing Host") 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /app/events/events_test.go: -------------------------------------------------------------------------------- 1 | package events 2 | 3 | import ( 4 | "os" 5 | "strconv" 6 | "testing" 7 | ) 8 | 9 | var ( 10 | ev Events 11 | ) 12 | 13 | func TestMain(test *testing.M) { 14 | ev = Init() 15 | retCode := test.Run() 16 | os.Exit(retCode) 17 | } 18 | 19 | func TestSimpleEvent(t *testing.T) { 20 | e1 := ev.New("source1", []string{"tag1"}, "bin1", "file1") 21 | 22 | if len(ev.GetEventsInProgress(0, 100)) != 1 { 23 | t.Fatal("Unexpected number of events. Not 1.") 24 | } 25 | 26 | e1.Done() 27 | 28 | if len(ev.GetEventsInProgress(0, 100)) != 0 { 29 | t.Fatal("Unexpected number of events. Not 0.") 30 | } 31 | 32 | if len(ev.GetAllEvents(0, 100)) != 1 { 33 | t.Fatal("Unexpected number of events. Not 1.") 34 | } 35 | } 36 | 37 | func TestComplexEvents(t *testing.T) { 38 | var text string 39 | var count int 40 | 41 | e2 := ev.New("source2", []string{"tag2", "footag2"}, "bin2", "file2") 42 | e3 := ev.New("source3", []string{"tag3", "footag3"}, "bin3", "file3") 43 | e2.Update("e2 text number 1", 1) 44 | e3.Update("e3 text number 1", 1) 45 | e3.Done() 46 | e2.Update("e2 text number 2", 2) 47 | 48 | count = len(ev.GetEventsInProgress(0, 100)) 49 | if count != 1 { 50 | t.Fatal("Unexpected number of open events. Not 1: ", count) 51 | } 52 | 53 | count = len(ev.GetAllEvents(0, 100)) 54 | if count != 3 { 55 | t.Fatal("Unexpected number of events. Not 3: ", count) 56 | } 57 | 58 | e2.Done() 59 | 60 | count = len(ev.GetEventsInProgress(0, 100)) 61 | if count != 0 { 62 | t.Fatal("Unexpected number of open events. Not 0: ", count) 63 | } 64 | 65 | text = e2.Text() 66 | if text != "e2 text number 2" { 67 | t.Fatal("Unexpected text: " + text) 68 | } 69 | 70 | text = e3.Text() 71 | if text != "e3 text number 1" { 72 | t.Fatal("Unexpected text: " + text) 73 | } 74 | } 75 | 76 | func TestGetEventsInProgress(t *testing.T) { 77 | var count int 78 | 79 | for i := 0; i < 20000; i++ { 80 | id := strconv.Itoa(i) 81 | e := ev.New("source"+id, []string{"tag" + id}, "bin"+id, "file"+id) 82 | 83 | if i == 10000 { 84 | e.Done() 85 | } 86 | } 87 | 88 | count = len(ev.GetEventsInProgress(0, 100000)) 89 | if count != 5000 { 90 | t.Fatal("Unexpected number of open events. Not 5000: ", count) 91 | } 92 | 93 | count = len(ev.GetAllEvents(0, 100000)) 94 | if count != 5000 { 95 | t.Fatal("Unexpected number of events. Not 5000: ", count) 96 | } 97 | 98 | events := ev.GetEventsInProgress(0, 100000) 99 | bin := events[0].Bin() 100 | if bin != "bin19999" { 101 | t.Fatal("The sorting is broken. Last event should come first: " + bin) 102 | } 103 | } 104 | 105 | func TestGetEventsByTags(t *testing.T) { 106 | var count int 107 | 108 | _ = ev.New("foo", []string{"admin", "dashboard"}, "", "") 109 | _ = ev.New("bar", []string{"admin", "events"}, "", "") 110 | _ = ev.New("baz", []string{"admin", "counters"}, "", "") 111 | 112 | count = len(ev.GetEventsByTags([]string{"admin"}, 0, 2)) 113 | if count != 2 { 114 | t.Fatal("Unexpected number of (admin) events. Not 2: ", count) 115 | } 116 | 117 | count = len(ev.GetEventsByTags([]string{"admin", "dashboard"}, 0, 100)) 118 | if count != 3 { 119 | t.Fatal("Unexpected number of (admin, dashboard) events. Not 3: ", count) 120 | } 121 | 122 | count = len(ev.GetEventsByTags([]string{"dashboard"}, 0, 100)) 123 | if count != 1 { 124 | t.Fatal("Unexpected number of events. Not 1: ", count) 125 | } 126 | } 127 | -------------------------------------------------------------------------------- /app/metrics/metrics.go: -------------------------------------------------------------------------------- 1 | package metrics 2 | 3 | import ( 4 | "sync" 5 | "time" 6 | ) 7 | 8 | type Metrics struct { 9 | sync.RWMutex 10 | startTime time.Time 11 | stats map[string]int64 12 | } 13 | 14 | func Init() Metrics { 15 | m := Metrics{} 16 | m.Lock() 17 | m.startTime = time.Now().UTC() 18 | m.stats = make(map[string]int64) 19 | m.Unlock() 20 | return m 21 | } 22 | 23 | func (m *Metrics) Uptime() time.Duration { 24 | m.RLock() 25 | defer m.RUnlock() 26 | return time.Since(m.startTime) 27 | } 28 | 29 | func (m *Metrics) StartTime() time.Time { 30 | m.RLock() 31 | defer m.RUnlock() 32 | return m.startTime 33 | } 34 | 35 | func (m *Metrics) GetStats() map[string]int64 { 36 | m.RLock() 37 | defer m.RUnlock() 38 | return m.stats 39 | } 40 | 41 | func (m *Metrics) Get(key string) (int64, bool) { 42 | m.RLock() 43 | defer m.RUnlock() 44 | value, found := m.stats[key] 45 | return value, found 46 | } 47 | 48 | func (m *Metrics) IncrSet(key string, i int64) int64 { 49 | newValue := i 50 | m.Lock() 51 | defer m.Unlock() 52 | currentValue, found := m.stats[key] 53 | if found { 54 | newValue = currentValue + i 55 | } 56 | m.stats[key] = newValue 57 | return newValue 58 | } 59 | 60 | func (m *Metrics) Incr(key string) int64 { 61 | m.Lock() 62 | defer m.Unlock() 63 | currentValue, _ := m.stats[key] 64 | newValue := currentValue + 1 65 | m.stats[key] = newValue 66 | return newValue 67 | } 68 | 69 | func (m *Metrics) Decr(key string) int64 { 70 | m.Lock() 71 | defer m.Unlock() 72 | currentValue, _ := m.stats[key] 73 | newValue := currentValue - 1 74 | m.stats[key] = newValue 75 | return newValue 76 | } 77 | -------------------------------------------------------------------------------- /app/metrics/metrics_test.go: -------------------------------------------------------------------------------- 1 | package metrics 2 | 3 | import ( 4 | "os" 5 | "testing" 6 | ) 7 | 8 | var ( 9 | m Metrics 10 | ) 11 | 12 | func TestMain(test *testing.M) { 13 | m = Init() 14 | retCode := test.Run() 15 | os.Exit(retCode) 16 | } 17 | 18 | func TestIncrSet(t *testing.T) { 19 | value, found := m.Get("foo") 20 | if found == true { 21 | t.Fatal("The key was unexpectedly found") 22 | } 23 | 24 | if value != 0 { 25 | t.Fatal("The value is not 0") 26 | } 27 | 28 | value = m.IncrSet("foo", 1) 29 | if value != 1 { 30 | t.Fatal("The value is not 1") 31 | } 32 | 33 | value = m.IncrSet("foo", 1) 34 | if value != 2 { 35 | t.Fatal("The value is not 2") 36 | } 37 | } 38 | 39 | func TestGet(t *testing.T) { 40 | value, found := m.Get("foo") 41 | if found == false { 42 | t.Fatal("The key does not exist") 43 | } 44 | if value != 2 { 45 | t.Fatal("The value is not 2. Weird.") 46 | } 47 | } 48 | 49 | func TestGetAll(t *testing.T) { 50 | stats := m.GetStats() 51 | if len(stats) != 1 { 52 | t.Fatal("The number of stats is not 1") 53 | } 54 | if stats["foo"] != 2 { 55 | t.Fatal("The value is not 2. Weird.") 56 | } 57 | } 58 | 59 | func TestIncr(t *testing.T) { 60 | if m.Incr("bar") != 1 { 61 | t.Fatal("The value is not 1") 62 | } 63 | if m.Incr("bar") != 2 { 64 | t.Fatal("The value is not 2") 65 | } 66 | if m.Incr("bar") != 3 { 67 | t.Fatal("The value is not 3") 68 | } 69 | } 70 | 71 | func TestDecr(t *testing.T) { 72 | if m.Decr("bar") != 2 { 73 | t.Fatal("The value is not 2") 74 | } 75 | if m.Decr("bar") != 1 { 76 | t.Fatal("The value is not 1") 77 | } 78 | if m.Decr("bar") != 0 { 79 | t.Fatal("The value is not 0") 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /app/model/batch.go: -------------------------------------------------------------------------------- 1 | package model 2 | 3 | import ( 4 | "github.com/espebra/filebin/app/backend/fs" 5 | "github.com/espebra/filebin/app/shared" 6 | "math/rand" 7 | "time" 8 | ) 9 | 10 | // Dispatcher function to spawn a number of workers 11 | func StartDispatcher(nworkers int, WorkQueue chan Job, backend *fs.Backend) { 12 | for i := 0; i < nworkers; i++ { 13 | go StartWorker(WorkQueue, backend) 14 | } 15 | } 16 | 17 | func StartWorker(WorkQueue chan Job, backend *fs.Backend) { 18 | for { 19 | select { 20 | case j := <-WorkQueue: 21 | startTime := time.Now().UTC() 22 | 23 | if err := backend.GenerateThumbnail(j.Bin, j.Filename, 115, 115, true); err != nil { 24 | j.Log.Println(err.Error()) 25 | break 26 | } 27 | 28 | if err := backend.GenerateThumbnail(j.Bin, j.Filename, 1140, 0, false); err != nil { 29 | j.Log.Println(err.Error()) 30 | break 31 | } 32 | 33 | if j.Cfg.CacheInvalidation { 34 | links := backend.GenerateLinks(j.Bin, j.Filename) 35 | for _, l := range links { 36 | if err := shared.PurgeURL(l.Href, j.Log); err != nil { 37 | j.Log.Println(err) 38 | } 39 | } 40 | } 41 | 42 | finishTime := time.Now().UTC() 43 | elapsedTime := finishTime.Sub(startTime) 44 | j.Log.Println("Batch job completed: " + j.Bin + "/" + j.Filename + " (" + elapsedTime.String() + ")") 45 | } 46 | } 47 | } 48 | 49 | func randomString(n int) string { 50 | var letters = []rune("abcdefghijklmnopqrstuvwxyz0123456789") 51 | b := make([]rune, n) 52 | for i := range b { 53 | b[i] = letters[rand.Intn(len(letters))] 54 | } 55 | return string(b) 56 | } 57 | -------------------------------------------------------------------------------- /app/model/context.go: -------------------------------------------------------------------------------- 1 | package model 2 | 3 | import ( 4 | "github.com/GeertJohan/go.rice" 5 | "github.com/espebra/filebin/app/backend/fs" 6 | "github.com/espebra/filebin/app/config" 7 | "github.com/espebra/filebin/app/events" 8 | "github.com/espebra/filebin/app/metrics" 9 | "github.com/espebra/filebin/app/tokens" 10 | "log" 11 | ) 12 | 13 | type Job struct { 14 | Bin string 15 | Filename string 16 | Log *log.Logger 17 | Cfg *config.Configuration 18 | } 19 | 20 | type Context struct { 21 | TemplateBox *rice.Box 22 | StaticBox *rice.Box 23 | Baseurl string 24 | Log *log.Logger 25 | WorkQueue chan Job 26 | Backend *fs.Backend 27 | Metrics *metrics.Metrics 28 | Events *events.Events 29 | Tokens *tokens.Tokens 30 | Token string 31 | RemoteAddr string 32 | } 33 | -------------------------------------------------------------------------------- /app/model/link.go: -------------------------------------------------------------------------------- 1 | package model 2 | 3 | type Link struct { 4 | Rel string 5 | Href string 6 | } 7 | -------------------------------------------------------------------------------- /app/model/testdata/image.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/espebra/filebin/a152fedd40a578eae20f91ac313852bb61ed4462/app/model/testdata/image.gif -------------------------------------------------------------------------------- /app/model/testdata/image.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/espebra/filebin/a152fedd40a578eae20f91ac313852bb61ed4462/app/model/testdata/image.jpg -------------------------------------------------------------------------------- /app/model/testdata/image.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/espebra/filebin/a152fedd40a578eae20f91ac313852bb61ed4462/app/model/testdata/image.png -------------------------------------------------------------------------------- /app/output/output.go: -------------------------------------------------------------------------------- 1 | package output 2 | 3 | import ( 4 | "encoding/json" 5 | "html/template" 6 | "io" 7 | "net/http" 8 | "strconv" 9 | 10 | "github.com/espebra/filebin/app/model" 11 | ) 12 | 13 | func JSONresponse(w http.ResponseWriter, status int, d interface{}, ctx model.Context) { 14 | dj, err := json.MarshalIndent(d, "", " ") 15 | if err != nil { 16 | ctx.Log.Println("Unable to convert response to json: ", err) 17 | http.Error(w, "Failed while generating a response", http.StatusInternalServerError) 18 | return 19 | } 20 | 21 | w.WriteHeader(status) 22 | ctx.Log.Println("Response status: " + strconv.Itoa(status)) 23 | io.WriteString(w, string(dj)) 24 | } 25 | 26 | // This function is a hack. Need to figure out a better way to do this. 27 | func HTMLresponse(w http.ResponseWriter, tpl string, status int, d interface{}, ctx model.Context) { 28 | box := ctx.TemplateBox 29 | t := template.New(tpl) 30 | 31 | var templateString string 32 | var err error 33 | 34 | templateString, err = box.String(tpl + ".html") 35 | if err != nil { 36 | ctx.Log.Fatalln(err) 37 | } 38 | t, err = t.Parse(templateString) 39 | if err != nil { 40 | ctx.Log.Fatalln(err) 41 | } 42 | 43 | w.WriteHeader(status) 44 | ctx.Log.Println("Response status: " + strconv.Itoa(status)) 45 | 46 | // To send multiple structs to the template 47 | err = t.Execute(w, map[string]interface{}{ 48 | "Data": d, 49 | "Ctx": ctx, 50 | }) 51 | if err != nil { 52 | ctx.Log.Panicln(err) 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /app/shared/shared.go: -------------------------------------------------------------------------------- 1 | package shared 2 | 3 | import ( 4 | "log" 5 | "net/http" 6 | "time" 7 | ) 8 | 9 | func PurgeURL(url string, log *log.Logger) error { 10 | timeout := time.Duration(2 * time.Second) 11 | client := &http.Client{ 12 | Timeout: timeout, 13 | } 14 | 15 | // Invalidate the file 16 | req, err := http.NewRequest("PURGE", url, nil) 17 | if err != nil { 18 | log.Println("Unable to purge " + url + ": " + err.Error()) 19 | return err 20 | } 21 | 22 | resp, err := client.Do(req) 23 | if err == nil { 24 | defer resp.Body.Close() 25 | } 26 | 27 | if err != nil { 28 | log.Println("Unable to purge " + url + ": " + err.Error()) 29 | return err 30 | } 31 | 32 | log.Println("Purging " + url + ": " + resp.Status) 33 | 34 | return nil 35 | } 36 | -------------------------------------------------------------------------------- /app/tokens/token.go: -------------------------------------------------------------------------------- 1 | package tokens 2 | 3 | import ( 4 | "github.com/dustin/go-humanize" 5 | "math/rand" 6 | "sync" 7 | "time" 8 | "log" 9 | ) 10 | 11 | type Token struct { 12 | ValidTo time.Time 13 | ExpiresReadable string 14 | Id string 15 | VerifiedCount int 16 | } 17 | 18 | type Tokens struct { 19 | sync.RWMutex 20 | tokens []Token 21 | } 22 | 23 | func Init() Tokens { 24 | t := Tokens{} 25 | return t 26 | } 27 | 28 | func (t *Tokens) Generate() string { 29 | t.Cleanup() 30 | 31 | var token Token 32 | token.Id = RandomString(8) 33 | now := time.Now().UTC() 34 | token.ValidTo = now.Add(5 * time.Minute) 35 | 36 | t.Lock() 37 | t.tokens = append([]Token{token}, t.tokens...) 38 | t.Unlock() 39 | return token.Id 40 | } 41 | 42 | func (t *Tokens) Verify(token string) bool { 43 | t.Lock() 44 | found := false 45 | now := time.Now().UTC() 46 | for i, data := range t.tokens { 47 | if data.Id == token { 48 | if now.Before(data.ValidTo) { 49 | t.tokens[i].VerifiedCount = t.tokens[i].VerifiedCount + 1 50 | found = true 51 | } 52 | } 53 | } 54 | t.Unlock() 55 | return found 56 | } 57 | 58 | func (t *Tokens) Cleanup() { 59 | var valid []Token 60 | t.Lock() 61 | if len(t.tokens) > 500 { 62 | now := time.Now().UTC() 63 | for _, data := range t.tokens { 64 | if now.Before(data.ValidTo) { 65 | valid = append(valid, data) 66 | } 67 | } 68 | before := len(t.tokens) 69 | t.tokens = valid 70 | after := len(t.tokens) 71 | log.Println("Token clean up:", before-after, "tokens have been removed.") 72 | } 73 | t.Unlock() 74 | 75 | } 76 | 77 | func (t *Tokens) GetAllTokens() []Token { 78 | t.RLock() 79 | defer t.RUnlock() 80 | 81 | var r []Token 82 | for _, data := range t.tokens { 83 | data.ExpiresReadable = humanize.Time(data.ValidTo) 84 | r = append(r, data) 85 | } 86 | return r 87 | } 88 | 89 | func RandomString(n int) string { 90 | var letters = []rune("abcdefghijklmnopqrstuvwxyz0123456789") 91 | b := make([]rune, n) 92 | for i := range b { 93 | b[i] = letters[rand.Intn(len(letters))] 94 | } 95 | return string(b) 96 | } 97 | -------------------------------------------------------------------------------- /doc/screenshot-web-interface-bin.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/espebra/filebin/a152fedd40a578eae20f91ac313852bb61ed4462/doc/screenshot-web-interface-bin.png -------------------------------------------------------------------------------- /doc/screenshot-web-interface-new-bin.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/espebra/filebin/a152fedd40a578eae20f91ac313852bb61ed4462/doc/screenshot-web-interface-new-bin.png -------------------------------------------------------------------------------- /doc/screenshot-web-interface-uploading.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/espebra/filebin/a152fedd40a578eae20f91ac313852bb61ed4462/doc/screenshot-web-interface-uploading.png -------------------------------------------------------------------------------- /etc/hitch/hitch.conf.example: -------------------------------------------------------------------------------- 1 | # Example configuration for /etc/hitch/hitch.conf 2 | 3 | frontend = "[*]:443" 4 | backend = "[127.0.0.1]:80" 5 | pem-file = "/etc/ssl/private/example.com.pem" 6 | ciphers = "" 7 | prefer-server-ciphers = off 8 | ssl-engine = "" 9 | workers = 1 10 | backlog = 100 11 | keepalive = 3600 12 | chroot = "" 13 | user = "hitch" 14 | group = "hitch" 15 | quiet = on 16 | syslog = on 17 | syslog-facility = "daemon" 18 | daemon = off 19 | write-ip = off 20 | write-proxy-v1 = off 21 | write-proxy-v2 = off 22 | proxy-proxy = off 23 | sni-nomatch-abort = off 24 | -------------------------------------------------------------------------------- /etc/systemd/filebin.service.example: -------------------------------------------------------------------------------- 1 | # Example configuration for /etc/systemd/system/filebin.service 2 | 3 | [Unit] 4 | Description=Filebin 5 | After=network.target 6 | 7 | [Service] 8 | ExecStart=/usr/bin/filebin --filedir /srv/filebin/files --tempdir /srv/filebin/temp --baseurl https://filebin.example.com --port 8080 --cache-invalidation --access-log /var/log/filebin/access.log 9 | StandardOutput=syslog 10 | StandardError=syslog 11 | SyslogIdentifier=filebin 12 | PrivateTmp=true 13 | User=filebin 14 | Group=filebin 15 | 16 | [Install] 17 | WantedBy=multi-user.target 18 | -------------------------------------------------------------------------------- /etc/varnish/varnish.params.example: -------------------------------------------------------------------------------- 1 | # Example configuration for /etc/varnish/varnish.params 2 | 3 | RELOAD_VCL=1 4 | VARNISH_VCL_CONF=/etc/varnish/default.vcl 5 | VARNISH_LISTEN_PORT=80 6 | VARNISH_ADMIN_LISTEN_ADDRESS=127.0.0.1 7 | VARNISH_ADMIN_LISTEN_PORT=6082 8 | VARNISH_SECRET_FILE=/etc/varnish/secret 9 | VARNISH_STORAGE="malloc,256M" 10 | VARNISH_TTL=120 11 | VARNISH_USER=varnish 12 | VARNISH_GROUP=varnish 13 | DAEMON_OPTS="-p first_byte_timeout=600" 14 | -------------------------------------------------------------------------------- /etc/varnish/varnish.vcl.example: -------------------------------------------------------------------------------- 1 | # Example configuration for /etc/varnish/default.vcl 2 | 3 | vcl 4.0; 4 | import std; 5 | 6 | backend default { 7 | .host = "127.0.0.1"; 8 | .port = "8080"; 9 | } 10 | 11 | acl acceptable_on_http { 12 | "127.0.0.1"; 13 | } 14 | 15 | acl purgers { 16 | "127.0.0.1"; 17 | } 18 | 19 | sub vcl_recv { 20 | if (client.ip !~ acceptable_on_http) { 21 | # Force https 22 | return (synth(750, "https://" + req.http.host + req.url)); 23 | } 24 | 25 | if (req.method == "PURGE") { 26 | if (!client.ip ~ purgers) { 27 | return (synth(405)); 28 | } 29 | return (purge); 30 | } 31 | 32 | # Workaround below since Varnish doesn't handle larger response 33 | # bodies that are larger than cache size well. 34 | 35 | # File downloads 36 | if (req.url ~ "^/[^/]+/[^\?]+$") { 37 | return(pipe); 38 | } 39 | # Archive downloads 40 | if (req.url ~ "^/archive/.*") { 41 | return(pipe); 42 | } 43 | } 44 | 45 | sub vcl_synth { 46 | # Permanent redirect 47 | if (resp.status == 750) { 48 | set resp.http.location = resp.reason; 49 | set resp.status = 301; 50 | return (deliver); 51 | } 52 | } 53 | 54 | sub vcl_backend_error { 55 | set beresp.http.Content-Type = "text/plain"; 56 | set beresp.http.Retry-After = "5"; 57 | 58 | # Non-html version of the backend error 59 | synthetic({"Filebin is currently not available ("} + beresp.status + {")"}); 60 | return (deliver); 61 | } 62 | 63 | sub vcl_pipe { 64 | set bereq.http.connection = "close"; 65 | } 66 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module filebin 2 | 3 | go 1.13 4 | 5 | require ( 6 | github.com/GeertJohan/go.rice v1.0.0 7 | github.com/disintegration/imaging v1.6.2 8 | github.com/dustin/go-humanize v1.0.0 9 | github.com/espebra/filebin v0.0.0-20190808211437-aa2884848e3f 10 | github.com/gorilla/handlers v1.4.2 11 | github.com/gorilla/mux v1.7.3 12 | github.com/rwcarlsen/goexif v0.0.0-20190401172101-9e8deecbddbd 13 | ) 14 | -------------------------------------------------------------------------------- /go.sum: -------------------------------------------------------------------------------- 1 | github.com/GeertJohan/go.incremental v1.0.0/go.mod h1:6fAjUhbVuX1KcMD3c8TEgVUqmo4seqhv0i0kdATSkM0= 2 | github.com/GeertJohan/go.rice v1.0.0 h1:KkI6O9uMaQU3VEKaj01ulavtF7o1fWT7+pk/4voiMLQ= 3 | github.com/GeertJohan/go.rice v1.0.0/go.mod h1:eH6gbSOAUv07dQuZVnBmoDP8mgsM1rtixis4Tib9if0= 4 | github.com/akavel/rsrc v0.8.0/go.mod h1:uLoCtb9J+EyAqh+26kdrTgmzRBFPGOolLWKpdxkKq+c= 5 | github.com/daaku/go.zipexe v1.0.0 h1:VSOgZtH418pH9L16hC/JrgSNJbbAL26pj7lmD1+CGdY= 6 | github.com/daaku/go.zipexe v1.0.0/go.mod h1:z8IiR6TsVLEYKwXAoE/I+8ys/sDkgTzSL0CLnGVd57E= 7 | github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 8 | github.com/disintegration/imaging v1.6.2 h1:w1LecBlG2Lnp8B3jk5zSuNqd7b4DXhcjwek1ei82L+c= 9 | github.com/disintegration/imaging v1.6.2/go.mod h1:44/5580QXChDfwIclfc/PCwrr44amcmDAg8hxG0Ewe4= 10 | github.com/dustin/go-humanize v1.0.0 h1:VSnTsYCnlFHaM2/igO1h6X3HA71jcobQuxemgkq4zYo= 11 | github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= 12 | github.com/espebra/filebin v0.0.0-20190808211437-aa2884848e3f h1:QUo41HncMh4hCA+aPn3FgYpm9w9KBS48cx54BHzTeqk= 13 | github.com/espebra/filebin v0.0.0-20190808211437-aa2884848e3f/go.mod h1:wOkivKcCGgy8Dj7tLRLY009twbLU+E5LdP+oLw/2lfY= 14 | github.com/gorilla/handlers v1.4.2 h1:0QniY0USkHQ1RGCLfKxeNHK9bkDHGRYGNDFBCS+YARg= 15 | github.com/gorilla/handlers v1.4.2/go.mod h1:Qkdc/uu4tH4g6mTK6auzZ766c4CA0Ng8+o/OAirnOIQ= 16 | github.com/gorilla/mux v1.7.3 h1:gnP5JzjVOuiZD07fKKToCAOjS0yOpj/qPETTXCCS6hw= 17 | github.com/gorilla/mux v1.7.3/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs= 18 | github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= 19 | github.com/nkovacs/streamquote v0.0.0-20170412213628-49af9bddb229/go.mod h1:0aYXnNPJ8l7uZxf45rWW1a/uME32OF0rhiYGNQ2oF2E= 20 | github.com/rwcarlsen/goexif v0.0.0-20190401172101-9e8deecbddbd h1:CmH9+J6ZSsIjUK3dcGsnCnO41eRBOnY12zwkn5qVwgc= 21 | github.com/rwcarlsen/goexif v0.0.0-20190401172101-9e8deecbddbd/go.mod h1:hPqNNc0+uJM6H+SuU8sEs5K5IQeKccPqeSjfgcKGgPk= 22 | github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc= 23 | github.com/valyala/fasttemplate v1.0.1/go.mod h1:UQGH1tvbgY+Nz5t2n7tXsz52dQxojPUpymEIMZ47gx8= 24 | golang.org/x/image v0.0.0-20191009234506-e7c1f5e7dbb8 h1:hVwzHzIUGRjiF7EcUjqNxk3NCfkPxbDKRdnNE1Rpg0U= 25 | golang.org/x/image v0.0.0-20191009234506-e7c1f5e7dbb8/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= 26 | golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= 27 | -------------------------------------------------------------------------------- /static/bootstrap/fonts/glyphicons-halflings-regular.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/espebra/filebin/a152fedd40a578eae20f91ac313852bb61ed4462/static/bootstrap/fonts/glyphicons-halflings-regular.eot -------------------------------------------------------------------------------- /static/bootstrap/fonts/glyphicons-halflings-regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/espebra/filebin/a152fedd40a578eae20f91ac313852bb61ed4462/static/bootstrap/fonts/glyphicons-halflings-regular.ttf -------------------------------------------------------------------------------- /static/bootstrap/fonts/glyphicons-halflings-regular.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/espebra/filebin/a152fedd40a578eae20f91ac313852bb61ed4462/static/bootstrap/fonts/glyphicons-halflings-regular.woff -------------------------------------------------------------------------------- /static/bootstrap/fonts/glyphicons-halflings-regular.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/espebra/filebin/a152fedd40a578eae20f91ac313852bb61ed4462/static/bootstrap/fonts/glyphicons-halflings-regular.woff2 -------------------------------------------------------------------------------- /static/css/custom.css: -------------------------------------------------------------------------------- 1 | .nav-pills>li+li { 2 | margin-left: 10px; // 2px by default 3 | } 4 | 5 | .thumbnails { 6 | margin-top: 10px; 7 | } 8 | 9 | .table { 10 | margin-top: 10px; 11 | } 12 | 13 | .thumb { 14 | padding: 1px 1px 1px 1px; 15 | } 16 | 17 | .dark-background { 18 | background-color: #BBBBBB; 19 | } 20 | 21 | .progress { 22 | margin-bottom: 0 !important; 23 | } 24 | 25 | .fileUpload { 26 | position: relative; 27 | overflow: hidden; 28 | } 29 | .fileUpload input.upload { 30 | position: absolute; 31 | top: 0; 32 | right: 0; 33 | margin: 0; 34 | padding: 0; 35 | font-size: 20px; 36 | cursor: pointer; 37 | opacity: 0; 38 | filter: alpha(opacity=0); 39 | } 40 | -------------------------------------------------------------------------------- /static/css/upload.css: -------------------------------------------------------------------------------- 1 | html, body { 2 | margin: 0; 3 | height: 100%; 4 | } 5 | 6 | .hide { 7 | display: none; 8 | position: relative; 9 | } 10 | 11 | #fileCount { 12 | display: none; 13 | position: relative; 14 | } 15 | 16 | #files { 17 | margin-top: 30px; 18 | min-height: 30px; 19 | } 20 | 21 | #files { 22 | -moz-box-shadow: 0 0 20px #ccc; 23 | } 24 | 25 | #fileList { 26 | list-style: none; 27 | padding: 0; 28 | margin-bottom: 30px; 29 | margin-left: 0px; 30 | } 31 | 32 | #fileList li { 33 | margin: 0; 34 | padding-top: 15px; 35 | overflow: auto; 36 | position: relative; 37 | } 38 | 39 | 40 | .loader { 41 | position: absolute; 42 | bottom: 10px; 43 | right: 0; 44 | color: orange; 45 | } 46 | 47 | .loadingIndicator { 48 | width: 0%; 49 | height: 2px; 50 | background-color: #c09853; 51 | position: absolute; 52 | bottom: 0; 53 | left: 0; 54 | } 55 | -------------------------------------------------------------------------------- /static/fonts/FontAwesome.otf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/espebra/filebin/a152fedd40a578eae20f91ac313852bb61ed4462/static/fonts/FontAwesome.otf -------------------------------------------------------------------------------- /static/fonts/fontawesome-webfont.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/espebra/filebin/a152fedd40a578eae20f91ac313852bb61ed4462/static/fonts/fontawesome-webfont.eot -------------------------------------------------------------------------------- /static/fonts/fontawesome-webfont.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/espebra/filebin/a152fedd40a578eae20f91ac313852bb61ed4462/static/fonts/fontawesome-webfont.ttf -------------------------------------------------------------------------------- /static/fonts/fontawesome-webfont.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/espebra/filebin/a152fedd40a578eae20f91ac313852bb61ed4462/static/fonts/fontawesome-webfont.woff -------------------------------------------------------------------------------- /static/fonts/fontawesome-webfont.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/espebra/filebin/a152fedd40a578eae20f91ac313852bb61ed4462/static/fonts/fontawesome-webfont.woff2 -------------------------------------------------------------------------------- /static/img/favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/espebra/filebin/a152fedd40a578eae20f91ac313852bb61ed4462/static/img/favicon.png -------------------------------------------------------------------------------- /static/js/showhide.js: -------------------------------------------------------------------------------- 1 | function showHide(shID) { 2 | if (document.getElementById(shID)) { 3 | if (document.getElementById(shID+'-show').style.display != 'none') { 4 | document.getElementById(shID+'-show').style.display = 'none'; 5 | document.getElementById(shID).style.display = 'block'; 6 | } 7 | else { 8 | document.getElementById(shID+'-show').style.display = 'inline'; 9 | document.getElementById(shID).style.display = 'none'; 10 | } 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /static/js/sorttable.js: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/espebra/filebin/a152fedd40a578eae20f91ac313852bb61ed4462/static/js/sorttable.js -------------------------------------------------------------------------------- /templates/api.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | Filebin 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 |
27 | 28 |
29 |
30 |

31 | Filebin 32 |

33 |
34 |
35 |
36 |

37 |
38 | Back 39 |

40 |
41 |
42 |
43 | 44 |

45 | Upload file 46 |

47 | 48 |
Specific filename and bin
49 | 50 |

The following command will upload /var/tmp/some file to the bin examplebin. The target filename will be set to myfile. 51 | 52 |

 53 |                 
 54 | $ curl -H "bin: examplebin" -H "filename: myfile" --data-binary "@/var/tmp/some file" {{ .Ctx.Baseurl }}
 55 |                 
 56 |             
57 | 58 |
Auto-generated bin and target filename
59 | 60 |

The following command will upload /var/tmp/some file, and the bin will be automatically generated. The target filename is not specified, so it will become the SHA256 checksum of the content.

61 | 62 |
 63 |                 
 64 | $ curl --data-binary "@/var/tmp/some file" {{ .Ctx.Baseurl }}
 65 |                 
 66 |             
67 | 68 |

69 | Show bin meta data 70 |

71 | 72 |

The following command will show the metadata of the bin examplebin.

73 | 74 |
 75 |                 
 76 | $ curl -H "content-type: application/json" {{ .Ctx.Baseurl }}/examplebin
 77 |                 
 78 |             
79 | 80 |

81 | Download file 82 |

83 | 84 |

The following command will download somefile from the bin examplebin. The file will be stored as myfile.

85 | 86 |
 87 |                 
 88 | $ curl -o myfile {{ .Ctx.Baseurl }}/examplebin/somefile
 89 |                 
 90 |             
91 | 92 |

93 | Download bin 94 |

95 | 96 |

The following command will download all files in the bin examplebin. The file will be stored as the zip archive mybin.zip.

97 | 98 |
 99 |                 
100 | $ curl -o mybin.zip {{ .Ctx.Baseurl }}/archive/examplebin
101 | $ unzip mybin.zip
102 |                 
103 |             
104 | 105 |

106 | Delete file 107 |

108 | 109 |

The following command will delete somefile from the bin examplebin. The file will no longer be available at {{ .Ctx.Baseurl }}/examplebin/somefile.

110 | 111 |
112 |                 
113 | $ curl -X DELETE {{ .Ctx.Baseurl }}/examplebin/somefile
114 |                 
115 |             
116 | 117 |

118 | Delete bin 119 |

120 | 121 |

The following command will delete the bin examplebin, including all the files in it. The bin will no longer be available at {{ .Ctx.Baseurl }}/examplebin.

122 | 123 |
124 |                 
125 | $ curl -X DELETE {{ .Ctx.Baseurl }}/examplebin
126 |                 
127 |             
128 |
129 | 130 | 131 | -------------------------------------------------------------------------------- /templates/bins.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | Filebin administrator 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 |
25 |
26 |
27 |

28 |

47 |

48 |
49 |
50 |
51 | 52 | Current time: {{ .Data.Now.Format "2006-01-02 15:04:05 UTC" }}
53 | Uptime: {{ .Data.Uptime }} 54 |
55 |
56 |
57 |
58 | 59 |
60 | 61 |
62 |
63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | {{ range $index, $value := .Data.Bins }} 75 | {{ if .Expired }} 76 | 77 | {{ else }} 78 | 79 | {{ end }} 80 | 81 | 82 | 83 | 84 | 85 | {{ end }} 86 | 87 |
BinLast updatedFilesSize
{{ .Bin }}{{ .UpdatedReadable }}{{ .Files | len }}{{ .BytesReadable }}
88 |
89 |
90 |
91 | 92 | 93 | -------------------------------------------------------------------------------- /templates/counters.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | Filebin administrator 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 |
25 |
26 |
27 |

28 |

47 |

48 |
49 |
50 |
51 | 52 | Current time: {{ .Data.Now.Format "2006-01-02 15:04:05 UTC" }}
53 | Uptime: {{ .Data.Uptime }} 54 |
55 |
56 |
57 |
58 | 59 |
60 | 61 |

Showing {{ .Data.Counters|len }} system counters.

62 | 63 |
64 |
65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | {{ range $index, $value := .Data.Counters }} 74 | 77 | 80 | 81 | {{ end }} 82 | 83 |
KeyValue
75 | {{ $index }} 76 | 78 | {{ $value }} 79 |
84 |
85 |
86 |
87 | 88 | 89 | 90 | -------------------------------------------------------------------------------- /templates/doc.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | Filebin 9 | 10 | 11 | 12 | 13 | 14 | 15 |
16 | 32 | 33 |
34 |
35 |

36 | Filebin 37 |

38 |
39 |
40 |
41 | 42 | 43 | -------------------------------------------------------------------------------- /templates/invalidtokenarchive.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | Filebin 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 |
24 |
25 |
26 |
27 |
28 | Archive download 29 |
30 |
31 |

32 | 33 |

34 |
Bin name
35 |
{{ $.Data.Bin }}
36 |
Number of files
37 |
{{ len $.Data.Files }}
38 |
Total size
39 |
{{ $.Data.BytesReadable }}
40 |
Updated
41 |
{{ .Data.UpdatedReadable }}
42 |
Expires
43 |
{{ .Data.ExpiresReadable }}
44 |
45 | 46 |

47 |
48 | 49 | 63 |
64 |
65 |
66 |
67 | 68 | 69 | -------------------------------------------------------------------------------- /templates/invalidtokenfile.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | Filebin 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 |
24 |
25 |
26 |
27 |
28 | File download 29 |
30 |
31 |
32 |
33 |

34 | 35 |

36 |
File name
37 |
{{ .Data.Filename }}
38 |
File size
39 |
{{ .Data.BytesReadable }}
40 |
Content type
41 |
{{ .Data.MIME }}
42 |
Uploaded
43 |
{{ .Data.CreatedReadable }}
44 |
45 | 46 |

47 |
48 | {{- range $index, $l := .Data.Links -}} 49 | {{- if eq $l.Rel "thumbnail" -}} 50 |
51 | 52 |
53 | {{- end -}} 54 | {{- end -}} 55 |
56 |
57 | 58 | 80 |
81 |
82 |
83 |
84 | 85 | 86 | -------------------------------------------------------------------------------- /templates/newbin.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | Filebin 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 51 | 52 | 53 | 54 |
55 | 56 | 57 |
58 |
59 |

60 | Filebin 61 |

62 |
63 |
64 | 65 | 66 |
67 |
68 |
69 |

70 | Convenient file sharing on the web, without 71 | registration. Simply upload files and share 72 | the URL. The files will expire automatically 73 | {{ .Data.ExpiresReadable }}. 74 |

75 |

76 | 1. 77 | Click Upload files below, or drag and drop the files 78 | into this browser window. 79 |

80 |

81 | 2. 82 | Wait until the file uploads complete. 83 |

84 |

85 | 3. 86 | Distribute the URL to share access to the files. 87 |

88 | 89 | 90 |

91 | Upload files 92 | 93 |

94 |
95 |
96 |
97 |
98 | 99 | 100 | 101 | 102 | 103 |
104 |
105 | 106 | 107 | -------------------------------------------------------------------------------- /templates/readme.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | Filebin readme 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 |
23 |
24 |
25 |

26 | Filebin 27 |

28 |
29 |
30 |
31 | 32 | 33 | -------------------------------------------------------------------------------- /templates/tokens.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | Filebin administrator 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 |
25 |
26 |
27 |

28 |

45 |

46 |
47 |
48 |
49 | 50 | Current time: {{ .Data.Now.Format "2006-01-02 15:04:05 UTC" }}
51 | Uptime: {{ .Data.Uptime }} 52 |
53 |
54 |
55 |
56 | 57 |
58 | 59 |

{{ len .Data.Tokens }} active tokens.

60 | 61 |
62 |
63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | {{ range $i, $t := .Data.Tokens }} 74 | 75 | 76 | 77 | 78 | 79 | 80 | {{ end }} 81 | 82 |
TokenVerified countExpiresValid to
{{ $t.Id }}{{ $t.VerifiedCount }}{{ $t.ExpiresReadable }}{{ $t.ValidTo.Format "2006-01-02 15:04:05 UTC" }}
83 |
84 |
85 |
86 | 87 | 88 | -------------------------------------------------------------------------------- /templates/viewalbum.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | Filebin 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 |
23 | 24 |
25 |
26 |

27 | Filebin 28 |

29 |
30 |
31 |
32 |

33 |
34 | Back 35 |

36 |
37 |
38 |
39 | 40 | {{ range $index, $value := .Data.Files }} 41 | {{ $image := .GetLink "album item" }} 42 | {{ $file := .GetLink "file" }} 43 | {{ if $image }} 44 |
45 | 48 | File {{ .Filename }} in bin {{ .Bin }}. 49 | 52 |
53 |
54 | {{ .Filename }} 55 | (Original) 56 |
57 |
58 | {{ if ne .DateTimeString "" }} 59 | {{ .DateTimeString }} 60 | {{ end }} 61 | 62 | {{ if .Longitude }} {{ if .Latitude }} 63 | 64 | 65 | 66 | {{ end }} {{ end }} 67 |
68 |
69 |
70 | {{ end }} 71 | {{ end }} 72 | 73 | {{ if gt (.Data.Files | len) 1 }} 74 |
75 |
76 |
77 |
78 |
79 |

80 | Back 81 |

82 |
83 |
84 |
85 | {{ end }} 86 |
87 | 88 | 89 | -------------------------------------------------------------------------------- /tools/git/LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2012 The Go Authors. All rights reserved. 2 | 3 | Redistribution and use in source and binary forms, with or without 4 | modification, are permitted provided that the following conditions are 5 | met: 6 | 7 | * Redistributions of source code must retain the above copyright 8 | notice, this list of conditions and the following disclaimer. 9 | * Redistributions in binary form must reproduce the above 10 | copyright notice, this list of conditions and the following disclaimer 11 | in the documentation and/or other materials provided with the 12 | distribution. 13 | * Neither the name of Google Inc. nor the names of its 14 | contributors may be used to endorse or promote products derived from 15 | this software without specific prior written permission. 16 | 17 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 18 | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 19 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 20 | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 21 | OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 22 | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 23 | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 24 | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 25 | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 26 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 27 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 | -------------------------------------------------------------------------------- /tools/git/pre-commit: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # Copyright 2012 The Go Authors. All rights reserved. 3 | # Use of this source code is governed by a BSD-style 4 | # license that can be found in the LICENSE file. 5 | 6 | # git gofmt pre-commit hook 7 | # 8 | # To use, store as .git/hooks/pre-commit inside your repository and make sure 9 | # it has execute permissions. 10 | # 11 | # This script does not handle file names that contain spaces. 12 | 13 | gofiles=$(git diff --cached --name-only --diff-filter=ACM | grep '.go$') 14 | [ -z "$gofiles" ] && exit 0 15 | 16 | unformatted=$(gofmt -l $gofiles) 17 | [ -z "$unformatted" ] && exit 0 18 | 19 | # Some files are not gofmt'd. Print message and fail. 20 | 21 | echo >&2 "Go files must be formatted with gofmt. Please run:" 22 | for fn in $unformatted; do 23 | echo >&2 " gofmt -w $PWD/$fn" 24 | done 25 | 26 | exit 1 27 | -------------------------------------------------------------------------------- /vendor/github.com/GeertJohan/go.rice/.gitignore: -------------------------------------------------------------------------------- 1 | /example/example 2 | /example/example.exe 3 | /rice/rice 4 | /rice/rice.exe 5 | 6 | *.rice-box.go 7 | *.rice-box.syso 8 | .wercker 9 | -------------------------------------------------------------------------------- /vendor/github.com/GeertJohan/go.rice/.travis.yml: -------------------------------------------------------------------------------- 1 | language: go 2 | 3 | go: 4 | - master 5 | - 1.11.x 6 | - 1.10.x 7 | - 1.9.x 8 | - 1.8.x 9 | 10 | install: 11 | - go get -t ./... 12 | - env 13 | script: 14 | - go build -x ./... 15 | - go test -cover ./... 16 | - go vet ./... 17 | -------------------------------------------------------------------------------- /vendor/github.com/GeertJohan/go.rice/AUTHORS: -------------------------------------------------------------------------------- 1 | Geert-Johan Riemer 2 | Paul Maddox 3 | Vincent Petithory 4 | 5 | -------------------------------------------------------------------------------- /vendor/github.com/GeertJohan/go.rice/LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2013, Geert-Johan Riemer 2 | All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without 5 | modification, are permitted provided that the following conditions are met: 6 | 7 | 1. Redistributions of source code must retain the above copyright notice, this 8 | list of conditions and the following disclaimer. 9 | 2. Redistributions in binary form must reproduce the above copyright notice, 10 | this list of conditions and the following disclaimer in the documentation 11 | and/or other materials provided with the distribution. 12 | 13 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 14 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 15 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 16 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR 17 | ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 18 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 19 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 20 | ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 21 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 22 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -------------------------------------------------------------------------------- /vendor/github.com/GeertJohan/go.rice/appended.go: -------------------------------------------------------------------------------- 1 | package rice 2 | 3 | import ( 4 | "archive/zip" 5 | "log" 6 | "os" 7 | "path/filepath" 8 | "strings" 9 | "time" 10 | 11 | "github.com/daaku/go.zipexe" 12 | ) 13 | 14 | // appendedBox defines an appended box 15 | type appendedBox struct { 16 | Name string // box name 17 | Files map[string]*appendedFile // appended files (*zip.File) by full path 18 | } 19 | 20 | type appendedFile struct { 21 | zipFile *zip.File 22 | dir bool 23 | dirInfo *appendedDirInfo 24 | children []*appendedFile 25 | content []byte 26 | } 27 | 28 | // appendedBoxes is a public register of appendes boxes 29 | var appendedBoxes = make(map[string]*appendedBox) 30 | 31 | func init() { 32 | // find if exec is appended 33 | thisFile, err := os.Executable() 34 | if err != nil { 35 | return // not appended or cant find self executable 36 | } 37 | thisFile, err = filepath.EvalSymlinks(thisFile) 38 | if err != nil { 39 | return 40 | } 41 | closer, rd, err := zipexe.OpenCloser(thisFile) 42 | if err != nil { 43 | return // not appended 44 | } 45 | defer closer.Close() 46 | 47 | for _, f := range rd.File { 48 | // get box and file name from f.Name 49 | fileParts := strings.SplitN(strings.TrimLeft(filepath.ToSlash(f.Name), "/"), "/", 2) 50 | boxName := fileParts[0] 51 | var fileName string 52 | if len(fileParts) > 1 { 53 | fileName = fileParts[1] 54 | } 55 | 56 | // find box or create new one if doesn't exist 57 | box := appendedBoxes[boxName] 58 | if box == nil { 59 | box = &appendedBox{ 60 | Name: boxName, 61 | Files: make(map[string]*appendedFile), 62 | } 63 | appendedBoxes[boxName] = box 64 | } 65 | 66 | // create and add file to box 67 | af := &appendedFile{ 68 | zipFile: f, 69 | } 70 | if f.Comment == "dir" { 71 | af.dir = true 72 | af.dirInfo = &appendedDirInfo{ 73 | name: filepath.Base(af.zipFile.Name), 74 | //++ TODO: use zip modtime when that is set correctly: af.zipFile.ModTime() 75 | time: time.Now(), 76 | } 77 | } else { 78 | // this is a file, we need it's contents so we can create a bytes.Reader when the file is opened 79 | // make a new byteslice 80 | af.content = make([]byte, af.zipFile.FileInfo().Size()) 81 | // ignore reading empty files from zip (empty file still is a valid file to be read though!) 82 | if len(af.content) > 0 { 83 | // open io.ReadCloser 84 | rc, err := af.zipFile.Open() 85 | if err != nil { 86 | af.content = nil // this will cause an error when the file is being opened or seeked (which is good) 87 | // TODO: it's quite blunt to just log this stuff. but this is in init, so rice.Debug can't be changed yet.. 88 | log.Printf("error opening appended file %s: %v", af.zipFile.Name, err) 89 | } else { 90 | _, err = rc.Read(af.content) 91 | rc.Close() 92 | if err != nil { 93 | af.content = nil // this will cause an error when the file is being opened or seeked (which is good) 94 | // TODO: it's quite blunt to just log this stuff. but this is in init, so rice.Debug can't be changed yet.. 95 | log.Printf("error reading data for appended file %s: %v", af.zipFile.Name, err) 96 | } 97 | } 98 | } 99 | } 100 | 101 | // add appendedFile to box file list 102 | box.Files[fileName] = af 103 | 104 | // add to parent dir (if any) 105 | dirName := filepath.Dir(fileName) 106 | if dirName == "." { 107 | dirName = "" 108 | } 109 | if fileName != "" { // don't make box root dir a child of itself 110 | if dir := box.Files[dirName]; dir != nil { 111 | dir.children = append(dir.children, af) 112 | } 113 | } 114 | } 115 | } 116 | 117 | // implements os.FileInfo. 118 | // used for Readdir() 119 | type appendedDirInfo struct { 120 | name string 121 | time time.Time 122 | } 123 | 124 | func (adi *appendedDirInfo) Name() string { 125 | return adi.name 126 | } 127 | func (adi *appendedDirInfo) Size() int64 { 128 | return 0 129 | } 130 | func (adi *appendedDirInfo) Mode() os.FileMode { 131 | return os.ModeDir 132 | } 133 | func (adi *appendedDirInfo) ModTime() time.Time { 134 | return adi.time 135 | } 136 | func (adi *appendedDirInfo) IsDir() bool { 137 | return true 138 | } 139 | func (adi *appendedDirInfo) Sys() interface{} { 140 | return nil 141 | } 142 | -------------------------------------------------------------------------------- /vendor/github.com/GeertJohan/go.rice/config.go: -------------------------------------------------------------------------------- 1 | package rice 2 | 3 | // LocateMethod defines how a box is located. 4 | type LocateMethod int 5 | 6 | const ( 7 | LocateFS = LocateMethod(iota) // Locate on the filesystem according to package path. 8 | LocateAppended // Locate boxes appended to the executable. 9 | LocateEmbedded // Locate embedded boxes. 10 | LocateWorkingDirectory // Locate on the binary working directory 11 | ) 12 | 13 | // Config allows customizing the box lookup behavior. 14 | type Config struct { 15 | // LocateOrder defines the priority order that boxes are searched for. By 16 | // default, the package global FindBox searches for embedded boxes first, 17 | // then appended boxes, and then finally boxes on the filesystem. That 18 | // search order may be customized by provided the ordered list here. Leaving 19 | // out a particular method will omit that from the search space. For 20 | // example, []LocateMethod{LocateEmbedded, LocateAppended} will never search 21 | // the filesystem for boxes. 22 | LocateOrder []LocateMethod 23 | } 24 | 25 | // FindBox searches for boxes using the LocateOrder of the config. 26 | func (c *Config) FindBox(boxName string) (*Box, error) { 27 | return findBox(boxName, c.LocateOrder) 28 | } 29 | 30 | // MustFindBox searches for boxes using the LocateOrder of the config, like 31 | // FindBox does. It does not return an error, instead it panics when an error 32 | // occurs. 33 | func (c *Config) MustFindBox(boxName string) *Box { 34 | box, err := findBox(boxName, c.LocateOrder) 35 | if err != nil { 36 | panic(err) 37 | } 38 | return box 39 | } 40 | -------------------------------------------------------------------------------- /vendor/github.com/GeertJohan/go.rice/debug.go: -------------------------------------------------------------------------------- 1 | package rice 2 | 3 | // Debug can be set to true to enable debugging. 4 | var Debug = false 5 | -------------------------------------------------------------------------------- /vendor/github.com/GeertJohan/go.rice/embedded.go: -------------------------------------------------------------------------------- 1 | package rice 2 | 3 | import ( 4 | "os" 5 | "time" 6 | 7 | "github.com/GeertJohan/go.rice/embedded" 8 | ) 9 | 10 | // re-type to make exported methods invisible to user (godoc) 11 | // they're not required for the user 12 | // embeddedDirInfo implements os.FileInfo 13 | type embeddedDirInfo embedded.EmbeddedDir 14 | 15 | // Name returns the base name of the directory 16 | // (implementing os.FileInfo) 17 | func (ed *embeddedDirInfo) Name() string { 18 | return ed.Filename 19 | } 20 | 21 | // Size always returns 0 22 | // (implementing os.FileInfo) 23 | func (ed *embeddedDirInfo) Size() int64 { 24 | return 0 25 | } 26 | 27 | // Mode returns the file mode bits 28 | // (implementing os.FileInfo) 29 | func (ed *embeddedDirInfo) Mode() os.FileMode { 30 | return os.FileMode(0555 | os.ModeDir) // dr-xr-xr-x 31 | } 32 | 33 | // ModTime returns the modification time 34 | // (implementing os.FileInfo) 35 | func (ed *embeddedDirInfo) ModTime() time.Time { 36 | return ed.DirModTime 37 | } 38 | 39 | // IsDir returns the abbreviation for Mode().IsDir() (always true) 40 | // (implementing os.FileInfo) 41 | func (ed *embeddedDirInfo) IsDir() bool { 42 | return true 43 | } 44 | 45 | // Sys returns the underlying data source (always nil) 46 | // (implementing os.FileInfo) 47 | func (ed *embeddedDirInfo) Sys() interface{} { 48 | return nil 49 | } 50 | 51 | // re-type to make exported methods invisible to user (godoc) 52 | // they're not required for the user 53 | // embeddedFileInfo implements os.FileInfo 54 | type embeddedFileInfo embedded.EmbeddedFile 55 | 56 | // Name returns the base name of the file 57 | // (implementing os.FileInfo) 58 | func (ef *embeddedFileInfo) Name() string { 59 | return ef.Filename 60 | } 61 | 62 | // Size returns the length in bytes for regular files; system-dependent for others 63 | // (implementing os.FileInfo) 64 | func (ef *embeddedFileInfo) Size() int64 { 65 | return int64(len(ef.Content)) 66 | } 67 | 68 | // Mode returns the file mode bits 69 | // (implementing os.FileInfo) 70 | func (ef *embeddedFileInfo) Mode() os.FileMode { 71 | return os.FileMode(0555) // r-xr-xr-x 72 | } 73 | 74 | // ModTime returns the modification time 75 | // (implementing os.FileInfo) 76 | func (ef *embeddedFileInfo) ModTime() time.Time { 77 | return ef.FileModTime 78 | } 79 | 80 | // IsDir returns the abbreviation for Mode().IsDir() (always false) 81 | // (implementing os.FileInfo) 82 | func (ef *embeddedFileInfo) IsDir() bool { 83 | return false 84 | } 85 | 86 | // Sys returns the underlying data source (always nil) 87 | // (implementing os.FileInfo) 88 | func (ef *embeddedFileInfo) Sys() interface{} { 89 | return nil 90 | } 91 | -------------------------------------------------------------------------------- /vendor/github.com/GeertJohan/go.rice/embedded/embedded.go: -------------------------------------------------------------------------------- 1 | // Package embedded defines embedded data types that are shared between the go.rice package and generated code. 2 | package embedded 3 | 4 | import ( 5 | "fmt" 6 | "path/filepath" 7 | "strings" 8 | "time" 9 | ) 10 | 11 | const ( 12 | EmbedTypeGo = 0 13 | EmbedTypeSyso = 1 14 | ) 15 | 16 | // EmbeddedBox defines an embedded box 17 | type EmbeddedBox struct { 18 | Name string // box name 19 | Time time.Time // embed time 20 | EmbedType int // kind of embedding 21 | Files map[string]*EmbeddedFile // ALL embedded files by full path 22 | Dirs map[string]*EmbeddedDir // ALL embedded dirs by full path 23 | } 24 | 25 | // Link creates the ChildDirs and ChildFiles links in all EmbeddedDir's 26 | func (e *EmbeddedBox) Link() { 27 | for _, ed := range e.Dirs { 28 | ed.ChildDirs = make([]*EmbeddedDir, 0) 29 | ed.ChildFiles = make([]*EmbeddedFile, 0) 30 | } 31 | for path, ed := range e.Dirs { 32 | // skip for root, it'll create a recursion 33 | if path == "" { 34 | continue 35 | } 36 | parentDirpath, _ := filepath.Split(path) 37 | if strings.HasSuffix(parentDirpath, "/") { 38 | parentDirpath = parentDirpath[:len(parentDirpath)-1] 39 | } 40 | parentDir := e.Dirs[parentDirpath] 41 | if parentDir == nil { 42 | panic("parentDir `" + parentDirpath + "` is missing in embedded box") 43 | } 44 | parentDir.ChildDirs = append(parentDir.ChildDirs, ed) 45 | } 46 | for path, ef := range e.Files { 47 | dirpath, _ := filepath.Split(path) 48 | if strings.HasSuffix(dirpath, "/") { 49 | dirpath = dirpath[:len(dirpath)-1] 50 | } 51 | dir := e.Dirs[dirpath] 52 | if dir == nil { 53 | panic("dir `" + dirpath + "` is missing in embedded box") 54 | } 55 | dir.ChildFiles = append(dir.ChildFiles, ef) 56 | } 57 | } 58 | 59 | // EmbeddedDir is instanced in the code generated by the rice tool and contains all necicary information about an embedded file 60 | type EmbeddedDir struct { 61 | Filename string 62 | DirModTime time.Time 63 | ChildDirs []*EmbeddedDir // direct childs, as returned by virtualDir.Readdir() 64 | ChildFiles []*EmbeddedFile // direct childs, as returned by virtualDir.Readdir() 65 | } 66 | 67 | // EmbeddedFile is instanced in the code generated by the rice tool and contains all necicary information about an embedded file 68 | type EmbeddedFile struct { 69 | Filename string // filename 70 | FileModTime time.Time 71 | Content string 72 | } 73 | 74 | // EmbeddedBoxes is a public register of embedded boxes 75 | var EmbeddedBoxes = make(map[string]*EmbeddedBox) 76 | 77 | // RegisterEmbeddedBox registers an EmbeddedBox 78 | func RegisterEmbeddedBox(name string, box *EmbeddedBox) { 79 | if _, exists := EmbeddedBoxes[name]; exists { 80 | panic(fmt.Sprintf("EmbeddedBox with name `%s` exists already", name)) 81 | } 82 | EmbeddedBoxes[name] = box 83 | } 84 | -------------------------------------------------------------------------------- /vendor/github.com/GeertJohan/go.rice/go.mod: -------------------------------------------------------------------------------- 1 | module github.com/GeertJohan/go.rice 2 | 3 | go 1.12 4 | 5 | require ( 6 | github.com/GeertJohan/go.incremental v1.0.0 7 | github.com/akavel/rsrc v0.8.0 8 | github.com/daaku/go.zipexe v1.0.0 9 | github.com/davecgh/go-spew v1.1.1 10 | github.com/jessevdk/go-flags v1.4.0 11 | github.com/nkovacs/streamquote v0.0.0-20170412213628-49af9bddb229 12 | github.com/valyala/fasttemplate v1.0.1 13 | ) 14 | -------------------------------------------------------------------------------- /vendor/github.com/GeertJohan/go.rice/go.sum: -------------------------------------------------------------------------------- 1 | github.com/GeertJohan/go.incremental v1.0.0 h1:7AH+pY1XUgQE4Y1HcXYaMqAI0m9yrFqo/jt0CW30vsg= 2 | github.com/GeertJohan/go.incremental v1.0.0/go.mod h1:6fAjUhbVuX1KcMD3c8TEgVUqmo4seqhv0i0kdATSkM0= 3 | github.com/akavel/rsrc v0.8.0 h1:zjWn7ukO9Kc5Q62DOJCcxGpXC18RawVtYAGdz2aLlfw= 4 | github.com/akavel/rsrc v0.8.0/go.mod h1:uLoCtb9J+EyAqh+26kdrTgmzRBFPGOolLWKpdxkKq+c= 5 | github.com/daaku/go.zipexe v1.0.0 h1:VSOgZtH418pH9L16hC/JrgSNJbbAL26pj7lmD1+CGdY= 6 | github.com/daaku/go.zipexe v1.0.0/go.mod h1:z8IiR6TsVLEYKwXAoE/I+8ys/sDkgTzSL0CLnGVd57E= 7 | github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= 8 | github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 9 | github.com/jessevdk/go-flags v1.4.0 h1:4IU2WS7AumrZ/40jfhf4QVDMsQwqA7VEHozFRrGARJA= 10 | github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= 11 | github.com/nkovacs/streamquote v0.0.0-20170412213628-49af9bddb229 h1:E2B8qYyeSgv5MXpmzZXRNp8IAQ4vjxIjhpAf5hv/tAg= 12 | github.com/nkovacs/streamquote v0.0.0-20170412213628-49af9bddb229/go.mod h1:0aYXnNPJ8l7uZxf45rWW1a/uME32OF0rhiYGNQ2oF2E= 13 | github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw= 14 | github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc= 15 | github.com/valyala/fasttemplate v1.0.1 h1:tY9CJiPnMXf1ERmG2EyK7gNUd+c6RKGD0IfU8WdUSz8= 16 | github.com/valyala/fasttemplate v1.0.1/go.mod h1:UQGH1tvbgY+Nz5t2n7tXsz52dQxojPUpymEIMZ47gx8= 17 | -------------------------------------------------------------------------------- /vendor/github.com/GeertJohan/go.rice/http.go: -------------------------------------------------------------------------------- 1 | package rice 2 | 3 | import ( 4 | "net/http" 5 | ) 6 | 7 | // HTTPBox implements http.FileSystem which allows the use of Box with a http.FileServer. 8 | // e.g.: http.Handle("/", http.FileServer(rice.MustFindBox("http-files").HTTPBox())) 9 | type HTTPBox struct { 10 | *Box 11 | } 12 | 13 | // HTTPBox creates a new HTTPBox from an existing Box 14 | func (b *Box) HTTPBox() *HTTPBox { 15 | return &HTTPBox{b} 16 | } 17 | 18 | // Open returns a File using the http.File interface 19 | func (hb *HTTPBox) Open(name string) (http.File, error) { 20 | return hb.Box.Open(name) 21 | } 22 | -------------------------------------------------------------------------------- /vendor/github.com/GeertJohan/go.rice/sort.go: -------------------------------------------------------------------------------- 1 | package rice 2 | 3 | import "os" 4 | 5 | // SortByName allows an array of os.FileInfo objects 6 | // to be easily sorted by filename using sort.Sort(SortByName(array)) 7 | type SortByName []os.FileInfo 8 | 9 | func (f SortByName) Len() int { return len(f) } 10 | func (f SortByName) Less(i, j int) bool { return f[i].Name() < f[j].Name() } 11 | func (f SortByName) Swap(i, j int) { f[i], f[j] = f[j], f[i] } 12 | 13 | // SortByModified allows an array of os.FileInfo objects 14 | // to be easily sorted by modified date using sort.Sort(SortByModified(array)) 15 | type SortByModified []os.FileInfo 16 | 17 | func (f SortByModified) Len() int { return len(f) } 18 | func (f SortByModified) Less(i, j int) bool { return f[i].ModTime().Unix() > f[j].ModTime().Unix() } 19 | func (f SortByModified) Swap(i, j int) { f[i], f[j] = f[j], f[i] } 20 | -------------------------------------------------------------------------------- /vendor/github.com/GeertJohan/go.rice/walk.go: -------------------------------------------------------------------------------- 1 | package rice 2 | 3 | import ( 4 | "os" 5 | "path/filepath" 6 | "sort" 7 | "strings" 8 | ) 9 | 10 | // Walk is like filepath.Walk() 11 | // Visit http://golang.org/pkg/path/filepath/#Walk for more information 12 | func (b *Box) Walk(path string, walkFn filepath.WalkFunc) error { 13 | 14 | pathFile, err := b.Open(path) 15 | if err != nil { 16 | return err 17 | } 18 | defer pathFile.Close() 19 | 20 | pathInfo, err := pathFile.Stat() 21 | if err != nil { 22 | return err 23 | } 24 | 25 | if b.IsAppended() || b.IsEmbedded() { 26 | return b.walk(path, pathInfo, walkFn) 27 | } 28 | 29 | // We don't have any embedded or appended box so use live filesystem mode 30 | return filepath.Walk(filepath.Join(b.absolutePath, path), func(path string, info os.FileInfo, err error) error { 31 | 32 | // Strip out the box name from the returned paths 33 | path = strings.TrimPrefix(path, b.absolutePath+string(os.PathSeparator)) 34 | return walkFn(path, info, err) 35 | 36 | }) 37 | 38 | } 39 | 40 | // walk recursively descends path. 41 | // See walk() in $GOROOT/src/pkg/path/filepath/path.go 42 | func (b *Box) walk(path string, info os.FileInfo, walkFn filepath.WalkFunc) error { 43 | 44 | err := walkFn(path, info, nil) 45 | if err != nil { 46 | if info.IsDir() && err == filepath.SkipDir { 47 | return nil 48 | } 49 | return err 50 | } 51 | 52 | if !info.IsDir() { 53 | return nil 54 | } 55 | 56 | names, err := b.readDirNames(path) 57 | if err != nil { 58 | return walkFn(path, info, err) 59 | } 60 | 61 | for _, name := range names { 62 | 63 | filename := filepath.ToSlash(filepath.Join(path, name)) 64 | fileObject, err := b.Open(filename) 65 | if err != nil { 66 | return err 67 | } 68 | defer fileObject.Close() 69 | 70 | fileInfo, err := fileObject.Stat() 71 | if err != nil { 72 | if err := walkFn(filename, fileInfo, err); err != nil && err != filepath.SkipDir { 73 | return err 74 | } 75 | } else { 76 | err = b.walk(filename, fileInfo, walkFn) 77 | if err != nil { 78 | if !fileInfo.IsDir() || err != filepath.SkipDir { 79 | return err 80 | } 81 | } 82 | } 83 | } 84 | 85 | return nil 86 | 87 | } 88 | 89 | // readDirNames reads the directory named by path and returns a sorted list of directory entries. 90 | // See readDirNames() in $GOROOT/pkg/path/filepath/path.go 91 | func (b *Box) readDirNames(path string) ([]string, error) { 92 | 93 | f, err := b.Open(path) 94 | if err != nil { 95 | return nil, err 96 | } 97 | defer f.Close() 98 | 99 | stat, err := f.Stat() 100 | if err != nil { 101 | return nil, err 102 | } 103 | 104 | if !stat.IsDir() { 105 | return nil, nil 106 | } 107 | 108 | infos, err := f.Readdir(0) 109 | if err != nil { 110 | return nil, err 111 | } 112 | 113 | var names []string 114 | 115 | for _, info := range infos { 116 | names = append(names, info.Name()) 117 | } 118 | 119 | sort.Strings(names) 120 | return names, nil 121 | 122 | } 123 | -------------------------------------------------------------------------------- /vendor/github.com/daaku/go.zipexe/go.mod: -------------------------------------------------------------------------------- 1 | module github.com/daaku/go.zipexe 2 | 3 | go 1.12 4 | -------------------------------------------------------------------------------- /vendor/github.com/daaku/go.zipexe/license: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright © 2012-2015 Carlos Castillo 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of 6 | this software and associated documentation files (the “Software”), to deal in 7 | the Software without restriction, including without limitation the rights to 8 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 9 | of the Software, and to permit persons to whom the Software is furnished to do 10 | so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /vendor/github.com/daaku/go.zipexe/readme.md: -------------------------------------------------------------------------------- 1 | go.zipexe 2 | ========= 3 | 4 | This module was taken as-is from https://github.com/cookieo9/resources-go. 5 | Documentation: https://godoc.org/github.com/daaku/go.zipexe 6 | -------------------------------------------------------------------------------- /vendor/github.com/daaku/go.zipexe/zipexe.go: -------------------------------------------------------------------------------- 1 | // Package zipexe attempts to open an executable binary file as a zip file. 2 | package zipexe 3 | 4 | import ( 5 | "archive/zip" 6 | "debug/elf" 7 | "debug/macho" 8 | "debug/pe" 9 | "errors" 10 | "io" 11 | "os" 12 | ) 13 | 14 | // Opens a zip file by path. 15 | func Open(path string) (*zip.Reader, error) { 16 | _, rd, err := OpenCloser(path) 17 | return rd, err 18 | } 19 | 20 | // OpenCloser is like Open but returns an additional Closer to avoid leaking open files. 21 | func OpenCloser(path string) (io.Closer, *zip.Reader, error) { 22 | file, err := os.Open(path) 23 | if err != nil { 24 | return nil, nil, err 25 | } 26 | finfo, err := file.Stat() 27 | if err != nil { 28 | return nil, nil, err 29 | } 30 | zr, err := NewReader(file, finfo.Size()) 31 | if err != nil { 32 | return nil, nil, err 33 | } 34 | return file, zr, nil 35 | } 36 | 37 | // Open a zip file, specially handling various binaries that may have been 38 | // augmented with zip data. 39 | func NewReader(rda io.ReaderAt, size int64) (*zip.Reader, error) { 40 | handlers := []func(io.ReaderAt, int64) (*zip.Reader, error){ 41 | zip.NewReader, 42 | zipExeReaderMacho, 43 | zipExeReaderElf, 44 | zipExeReaderPe, 45 | } 46 | 47 | for _, handler := range handlers { 48 | zfile, err := handler(rda, size) 49 | if err == nil { 50 | return zfile, nil 51 | } 52 | } 53 | return nil, errors.New("Couldn't Open As Executable") 54 | } 55 | 56 | // zipExeReaderMacho treats the file as a Mach-O binary 57 | // (Mac OS X / Darwin executable) and attempts to find a zip archive. 58 | func zipExeReaderMacho(rda io.ReaderAt, size int64) (*zip.Reader, error) { 59 | file, err := macho.NewFile(rda) 60 | if err != nil { 61 | return nil, err 62 | } 63 | 64 | var max int64 65 | for _, load := range file.Loads { 66 | seg, ok := load.(*macho.Segment) 67 | if ok { 68 | // Check if the segment contains a zip file 69 | if zfile, err := zip.NewReader(seg, int64(seg.Filesz)); err == nil { 70 | return zfile, nil 71 | } 72 | 73 | // Otherwise move end of file pointer 74 | end := int64(seg.Offset + seg.Filesz) 75 | if end > max { 76 | max = end 77 | } 78 | } 79 | } 80 | 81 | // No zip file within binary, try appended to end 82 | section := io.NewSectionReader(rda, max, size-max) 83 | return zip.NewReader(section, section.Size()) 84 | } 85 | 86 | // zipExeReaderPe treats the file as a Portable Exectuable binary 87 | // (Windows executable) and attempts to find a zip archive. 88 | func zipExeReaderPe(rda io.ReaderAt, size int64) (*zip.Reader, error) { 89 | file, err := pe.NewFile(rda) 90 | if err != nil { 91 | return nil, err 92 | } 93 | 94 | var max int64 95 | for _, sec := range file.Sections { 96 | // Check if this section has a zip file 97 | if zfile, err := zip.NewReader(sec, int64(sec.Size)); err == nil { 98 | return zfile, nil 99 | } 100 | 101 | // Otherwise move end of file pointer 102 | end := int64(sec.Offset + sec.Size) 103 | if end > max { 104 | max = end 105 | } 106 | } 107 | 108 | // No zip file within binary, try appended to end 109 | section := io.NewSectionReader(rda, max, size-max) 110 | return zip.NewReader(section, section.Size()) 111 | } 112 | 113 | // zipExeReaderElf treats the file as a ELF binary 114 | // (linux/BSD/etc... executable) and attempts to find a zip archive. 115 | func zipExeReaderElf(rda io.ReaderAt, size int64) (*zip.Reader, error) { 116 | file, err := elf.NewFile(rda) 117 | if err != nil { 118 | return nil, err 119 | } 120 | 121 | var max int64 122 | for _, sect := range file.Sections { 123 | if sect.Type == elf.SHT_NOBITS { 124 | continue 125 | } 126 | 127 | // Check if this section has a zip file 128 | if zfile, err := zip.NewReader(sect, int64(sect.Size)); err == nil { 129 | return zfile, nil 130 | } 131 | 132 | // Otherwise move end of file pointer 133 | end := int64(sect.Offset + sect.Size) 134 | if end > max { 135 | max = end 136 | } 137 | } 138 | 139 | // No zip file within binary, try appended to end 140 | section := io.NewSectionReader(rda, max, size-max) 141 | return zip.NewReader(section, section.Size()) 142 | } 143 | -------------------------------------------------------------------------------- /vendor/github.com/disintegration/imaging/.travis.yml: -------------------------------------------------------------------------------- 1 | language: go 2 | go: 3 | - "1.10.x" 4 | - "1.11.x" 5 | - "1.12.x" 6 | 7 | before_install: 8 | - go get github.com/mattn/goveralls 9 | 10 | script: 11 | - go test -v -race -cover 12 | - $GOPATH/bin/goveralls -service=travis-ci 13 | -------------------------------------------------------------------------------- /vendor/github.com/disintegration/imaging/LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2012 Grigory Dryapak 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /vendor/github.com/disintegration/imaging/convolution.go: -------------------------------------------------------------------------------- 1 | package imaging 2 | 3 | import ( 4 | "image" 5 | ) 6 | 7 | // ConvolveOptions are convolution parameters. 8 | type ConvolveOptions struct { 9 | // If Normalize is true the kernel is normalized before convolution. 10 | Normalize bool 11 | 12 | // If Abs is true the absolute value of each color channel is taken after convolution. 13 | Abs bool 14 | 15 | // Bias is added to each color channel value after convolution. 16 | Bias int 17 | } 18 | 19 | // Convolve3x3 convolves the image with the specified 3x3 convolution kernel. 20 | // Default parameters are used if a nil *ConvolveOptions is passed. 21 | func Convolve3x3(img image.Image, kernel [9]float64, options *ConvolveOptions) *image.NRGBA { 22 | return convolve(img, kernel[:], options) 23 | } 24 | 25 | // Convolve5x5 convolves the image with the specified 5x5 convolution kernel. 26 | // Default parameters are used if a nil *ConvolveOptions is passed. 27 | func Convolve5x5(img image.Image, kernel [25]float64, options *ConvolveOptions) *image.NRGBA { 28 | return convolve(img, kernel[:], options) 29 | } 30 | 31 | func convolve(img image.Image, kernel []float64, options *ConvolveOptions) *image.NRGBA { 32 | src := toNRGBA(img) 33 | w := src.Bounds().Max.X 34 | h := src.Bounds().Max.Y 35 | dst := image.NewNRGBA(image.Rect(0, 0, w, h)) 36 | 37 | if w < 1 || h < 1 { 38 | return dst 39 | } 40 | 41 | if options == nil { 42 | options = &ConvolveOptions{} 43 | } 44 | 45 | if options.Normalize { 46 | normalizeKernel(kernel) 47 | } 48 | 49 | type coef struct { 50 | x, y int 51 | k float64 52 | } 53 | var coefs []coef 54 | var m int 55 | 56 | switch len(kernel) { 57 | case 9: 58 | m = 1 59 | case 25: 60 | m = 2 61 | } 62 | 63 | i := 0 64 | for y := -m; y <= m; y++ { 65 | for x := -m; x <= m; x++ { 66 | if kernel[i] != 0 { 67 | coefs = append(coefs, coef{x: x, y: y, k: kernel[i]}) 68 | } 69 | i++ 70 | } 71 | } 72 | 73 | parallel(0, h, func(ys <-chan int) { 74 | for y := range ys { 75 | for x := 0; x < w; x++ { 76 | var r, g, b float64 77 | for _, c := range coefs { 78 | ix := x + c.x 79 | if ix < 0 { 80 | ix = 0 81 | } else if ix >= w { 82 | ix = w - 1 83 | } 84 | 85 | iy := y + c.y 86 | if iy < 0 { 87 | iy = 0 88 | } else if iy >= h { 89 | iy = h - 1 90 | } 91 | 92 | off := iy*src.Stride + ix*4 93 | s := src.Pix[off : off+3 : off+3] 94 | r += float64(s[0]) * c.k 95 | g += float64(s[1]) * c.k 96 | b += float64(s[2]) * c.k 97 | } 98 | 99 | if options.Abs { 100 | if r < 0 { 101 | r = -r 102 | } 103 | if g < 0 { 104 | g = -g 105 | } 106 | if b < 0 { 107 | b = -b 108 | } 109 | } 110 | 111 | if options.Bias != 0 { 112 | r += float64(options.Bias) 113 | g += float64(options.Bias) 114 | b += float64(options.Bias) 115 | } 116 | 117 | srcOff := y*src.Stride + x*4 118 | dstOff := y*dst.Stride + x*4 119 | d := dst.Pix[dstOff : dstOff+4 : dstOff+4] 120 | d[0] = clamp(r) 121 | d[1] = clamp(g) 122 | d[2] = clamp(b) 123 | d[3] = src.Pix[srcOff+3] 124 | } 125 | } 126 | }) 127 | 128 | return dst 129 | } 130 | 131 | func normalizeKernel(kernel []float64) { 132 | var sum, sumpos float64 133 | for i := range kernel { 134 | sum += kernel[i] 135 | if kernel[i] > 0 { 136 | sumpos += kernel[i] 137 | } 138 | } 139 | if sum != 0 { 140 | for i := range kernel { 141 | kernel[i] /= sum 142 | } 143 | } else if sumpos != 0 { 144 | for i := range kernel { 145 | kernel[i] /= sumpos 146 | } 147 | } 148 | } 149 | -------------------------------------------------------------------------------- /vendor/github.com/disintegration/imaging/doc.go: -------------------------------------------------------------------------------- 1 | /* 2 | Package imaging provides basic image processing functions (resize, rotate, crop, brightness/contrast adjustments, etc.). 3 | 4 | All the image processing functions provided by the package accept any image type that implements image.Image interface 5 | as an input, and return a new image of *image.NRGBA type (32bit RGBA colors, non-premultiplied alpha). 6 | */ 7 | package imaging 8 | -------------------------------------------------------------------------------- /vendor/github.com/disintegration/imaging/effects.go: -------------------------------------------------------------------------------- 1 | package imaging 2 | 3 | import ( 4 | "image" 5 | "math" 6 | ) 7 | 8 | func gaussianBlurKernel(x, sigma float64) float64 { 9 | return math.Exp(-(x*x)/(2*sigma*sigma)) / (sigma * math.Sqrt(2*math.Pi)) 10 | } 11 | 12 | // Blur produces a blurred version of the image using a Gaussian function. 13 | // Sigma parameter must be positive and indicates how much the image will be blurred. 14 | // 15 | // Example: 16 | // 17 | // dstImage := imaging.Blur(srcImage, 3.5) 18 | // 19 | func Blur(img image.Image, sigma float64) *image.NRGBA { 20 | if sigma <= 0 { 21 | return Clone(img) 22 | } 23 | 24 | radius := int(math.Ceil(sigma * 3.0)) 25 | kernel := make([]float64, radius+1) 26 | 27 | for i := 0; i <= radius; i++ { 28 | kernel[i] = gaussianBlurKernel(float64(i), sigma) 29 | } 30 | 31 | return blurVertical(blurHorizontal(img, kernel), kernel) 32 | } 33 | 34 | func blurHorizontal(img image.Image, kernel []float64) *image.NRGBA { 35 | src := newScanner(img) 36 | dst := image.NewNRGBA(image.Rect(0, 0, src.w, src.h)) 37 | radius := len(kernel) - 1 38 | 39 | parallel(0, src.h, func(ys <-chan int) { 40 | scanLine := make([]uint8, src.w*4) 41 | scanLineF := make([]float64, len(scanLine)) 42 | for y := range ys { 43 | src.scan(0, y, src.w, y+1, scanLine) 44 | for i, v := range scanLine { 45 | scanLineF[i] = float64(v) 46 | } 47 | for x := 0; x < src.w; x++ { 48 | min := x - radius 49 | if min < 0 { 50 | min = 0 51 | } 52 | max := x + radius 53 | if max > src.w-1 { 54 | max = src.w - 1 55 | } 56 | var r, g, b, a, wsum float64 57 | for ix := min; ix <= max; ix++ { 58 | i := ix * 4 59 | weight := kernel[absint(x-ix)] 60 | wsum += weight 61 | s := scanLineF[i : i+4 : i+4] 62 | wa := s[3] * weight 63 | r += s[0] * wa 64 | g += s[1] * wa 65 | b += s[2] * wa 66 | a += wa 67 | } 68 | if a != 0 { 69 | aInv := 1 / a 70 | j := y*dst.Stride + x*4 71 | d := dst.Pix[j : j+4 : j+4] 72 | d[0] = clamp(r * aInv) 73 | d[1] = clamp(g * aInv) 74 | d[2] = clamp(b * aInv) 75 | d[3] = clamp(a / wsum) 76 | } 77 | } 78 | } 79 | }) 80 | 81 | return dst 82 | } 83 | 84 | func blurVertical(img image.Image, kernel []float64) *image.NRGBA { 85 | src := newScanner(img) 86 | dst := image.NewNRGBA(image.Rect(0, 0, src.w, src.h)) 87 | radius := len(kernel) - 1 88 | 89 | parallel(0, src.w, func(xs <-chan int) { 90 | scanLine := make([]uint8, src.h*4) 91 | scanLineF := make([]float64, len(scanLine)) 92 | for x := range xs { 93 | src.scan(x, 0, x+1, src.h, scanLine) 94 | for i, v := range scanLine { 95 | scanLineF[i] = float64(v) 96 | } 97 | for y := 0; y < src.h; y++ { 98 | min := y - radius 99 | if min < 0 { 100 | min = 0 101 | } 102 | max := y + radius 103 | if max > src.h-1 { 104 | max = src.h - 1 105 | } 106 | var r, g, b, a, wsum float64 107 | for iy := min; iy <= max; iy++ { 108 | i := iy * 4 109 | weight := kernel[absint(y-iy)] 110 | wsum += weight 111 | s := scanLineF[i : i+4 : i+4] 112 | wa := s[3] * weight 113 | r += s[0] * wa 114 | g += s[1] * wa 115 | b += s[2] * wa 116 | a += wa 117 | } 118 | if a != 0 { 119 | aInv := 1 / a 120 | j := y*dst.Stride + x*4 121 | d := dst.Pix[j : j+4 : j+4] 122 | d[0] = clamp(r * aInv) 123 | d[1] = clamp(g * aInv) 124 | d[2] = clamp(b * aInv) 125 | d[3] = clamp(a / wsum) 126 | } 127 | } 128 | } 129 | }) 130 | 131 | return dst 132 | } 133 | 134 | // Sharpen produces a sharpened version of the image. 135 | // Sigma parameter must be positive and indicates how much the image will be sharpened. 136 | // 137 | // Example: 138 | // 139 | // dstImage := imaging.Sharpen(srcImage, 3.5) 140 | // 141 | func Sharpen(img image.Image, sigma float64) *image.NRGBA { 142 | if sigma <= 0 { 143 | return Clone(img) 144 | } 145 | 146 | src := newScanner(img) 147 | dst := image.NewNRGBA(image.Rect(0, 0, src.w, src.h)) 148 | blurred := Blur(img, sigma) 149 | 150 | parallel(0, src.h, func(ys <-chan int) { 151 | scanLine := make([]uint8, src.w*4) 152 | for y := range ys { 153 | src.scan(0, y, src.w, y+1, scanLine) 154 | j := y * dst.Stride 155 | for i := 0; i < src.w*4; i++ { 156 | val := int(scanLine[i])<<1 - int(blurred.Pix[j]) 157 | if val < 0 { 158 | val = 0 159 | } else if val > 0xff { 160 | val = 0xff 161 | } 162 | dst.Pix[j] = uint8(val) 163 | j++ 164 | } 165 | } 166 | }) 167 | 168 | return dst 169 | } 170 | -------------------------------------------------------------------------------- /vendor/github.com/disintegration/imaging/go.mod: -------------------------------------------------------------------------------- 1 | module github.com/disintegration/imaging 2 | 3 | require golang.org/x/image v0.0.0-20191009234506-e7c1f5e7dbb8 4 | -------------------------------------------------------------------------------- /vendor/github.com/disintegration/imaging/go.sum: -------------------------------------------------------------------------------- 1 | golang.org/x/image v0.0.0-20191009234506-e7c1f5e7dbb8 h1:hVwzHzIUGRjiF7EcUjqNxk3NCfkPxbDKRdnNE1Rpg0U= 2 | golang.org/x/image v0.0.0-20191009234506-e7c1f5e7dbb8/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= 3 | golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= 4 | -------------------------------------------------------------------------------- /vendor/github.com/disintegration/imaging/histogram.go: -------------------------------------------------------------------------------- 1 | package imaging 2 | 3 | import ( 4 | "image" 5 | "sync" 6 | ) 7 | 8 | // Histogram returns a normalized histogram of an image. 9 | // 10 | // Resulting histogram is represented as an array of 256 floats, where 11 | // histogram[i] is a probability of a pixel being of a particular luminance i. 12 | func Histogram(img image.Image) [256]float64 { 13 | var mu sync.Mutex 14 | var histogram [256]float64 15 | var total float64 16 | 17 | src := newScanner(img) 18 | if src.w == 0 || src.h == 0 { 19 | return histogram 20 | } 21 | 22 | parallel(0, src.h, func(ys <-chan int) { 23 | var tmpHistogram [256]float64 24 | var tmpTotal float64 25 | scanLine := make([]uint8, src.w*4) 26 | for y := range ys { 27 | src.scan(0, y, src.w, y+1, scanLine) 28 | i := 0 29 | for x := 0; x < src.w; x++ { 30 | s := scanLine[i : i+3 : i+3] 31 | r := s[0] 32 | g := s[1] 33 | b := s[2] 34 | y := 0.299*float32(r) + 0.587*float32(g) + 0.114*float32(b) 35 | tmpHistogram[int(y+0.5)]++ 36 | tmpTotal++ 37 | i += 4 38 | } 39 | } 40 | mu.Lock() 41 | for i := 0; i < 256; i++ { 42 | histogram[i] += tmpHistogram[i] 43 | } 44 | total += tmpTotal 45 | mu.Unlock() 46 | }) 47 | 48 | for i := 0; i < 256; i++ { 49 | histogram[i] = histogram[i] / total 50 | } 51 | return histogram 52 | } 53 | -------------------------------------------------------------------------------- /vendor/github.com/disintegration/imaging/utils.go: -------------------------------------------------------------------------------- 1 | package imaging 2 | 3 | import ( 4 | "image" 5 | "math" 6 | "runtime" 7 | "sync" 8 | ) 9 | 10 | // parallel processes the data in separate goroutines. 11 | func parallel(start, stop int, fn func(<-chan int)) { 12 | count := stop - start 13 | if count < 1 { 14 | return 15 | } 16 | 17 | procs := runtime.GOMAXPROCS(0) 18 | if procs > count { 19 | procs = count 20 | } 21 | 22 | c := make(chan int, count) 23 | for i := start; i < stop; i++ { 24 | c <- i 25 | } 26 | close(c) 27 | 28 | var wg sync.WaitGroup 29 | for i := 0; i < procs; i++ { 30 | wg.Add(1) 31 | go func() { 32 | defer wg.Done() 33 | fn(c) 34 | }() 35 | } 36 | wg.Wait() 37 | } 38 | 39 | // absint returns the absolute value of i. 40 | func absint(i int) int { 41 | if i < 0 { 42 | return -i 43 | } 44 | return i 45 | } 46 | 47 | // clamp rounds and clamps float64 value to fit into uint8. 48 | func clamp(x float64) uint8 { 49 | v := int64(x + 0.5) 50 | if v > 255 { 51 | return 255 52 | } 53 | if v > 0 { 54 | return uint8(v) 55 | } 56 | return 0 57 | } 58 | 59 | func reverse(pix []uint8) { 60 | if len(pix) <= 4 { 61 | return 62 | } 63 | i := 0 64 | j := len(pix) - 4 65 | for i < j { 66 | pi := pix[i : i+4 : i+4] 67 | pj := pix[j : j+4 : j+4] 68 | pi[0], pj[0] = pj[0], pi[0] 69 | pi[1], pj[1] = pj[1], pi[1] 70 | pi[2], pj[2] = pj[2], pi[2] 71 | pi[3], pj[3] = pj[3], pi[3] 72 | i += 4 73 | j -= 4 74 | } 75 | } 76 | 77 | func toNRGBA(img image.Image) *image.NRGBA { 78 | if img, ok := img.(*image.NRGBA); ok { 79 | return &image.NRGBA{ 80 | Pix: img.Pix, 81 | Stride: img.Stride, 82 | Rect: img.Rect.Sub(img.Rect.Min), 83 | } 84 | } 85 | return Clone(img) 86 | } 87 | 88 | // rgbToHSL converts a color from RGB to HSL. 89 | func rgbToHSL(r, g, b uint8) (float64, float64, float64) { 90 | rr := float64(r) / 255 91 | gg := float64(g) / 255 92 | bb := float64(b) / 255 93 | 94 | max := math.Max(rr, math.Max(gg, bb)) 95 | min := math.Min(rr, math.Min(gg, bb)) 96 | 97 | l := (max + min) / 2 98 | 99 | if max == min { 100 | return 0, 0, l 101 | } 102 | 103 | var h, s float64 104 | d := max - min 105 | if l > 0.5 { 106 | s = d / (2 - max - min) 107 | } else { 108 | s = d / (max + min) 109 | } 110 | 111 | switch max { 112 | case rr: 113 | h = (gg - bb) / d 114 | if g < b { 115 | h += 6 116 | } 117 | case gg: 118 | h = (bb-rr)/d + 2 119 | case bb: 120 | h = (rr-gg)/d + 4 121 | } 122 | h /= 6 123 | 124 | return h, s, l 125 | } 126 | 127 | // hslToRGB converts a color from HSL to RGB. 128 | func hslToRGB(h, s, l float64) (uint8, uint8, uint8) { 129 | var r, g, b float64 130 | if s == 0 { 131 | v := clamp(l * 255) 132 | return v, v, v 133 | } 134 | 135 | var q float64 136 | if l < 0.5 { 137 | q = l * (1 + s) 138 | } else { 139 | q = l + s - l*s 140 | } 141 | p := 2*l - q 142 | 143 | r = hueToRGB(p, q, h+1/3.0) 144 | g = hueToRGB(p, q, h) 145 | b = hueToRGB(p, q, h-1/3.0) 146 | 147 | return clamp(r * 255), clamp(g * 255), clamp(b * 255) 148 | } 149 | 150 | func hueToRGB(p, q, t float64) float64 { 151 | if t < 0 { 152 | t++ 153 | } 154 | if t > 1 { 155 | t-- 156 | } 157 | if t < 1/6.0 { 158 | return p + (q-p)*6*t 159 | } 160 | if t < 1/2.0 { 161 | return q 162 | } 163 | if t < 2/3.0 { 164 | return p + (q-p)*(2/3.0-t)*6 165 | } 166 | return p 167 | } 168 | -------------------------------------------------------------------------------- /vendor/github.com/dustin/go-humanize/.travis.yml: -------------------------------------------------------------------------------- 1 | sudo: false 2 | language: go 3 | go: 4 | - 1.3.x 5 | - 1.5.x 6 | - 1.6.x 7 | - 1.7.x 8 | - 1.8.x 9 | - 1.9.x 10 | - master 11 | matrix: 12 | allow_failures: 13 | - go: master 14 | fast_finish: true 15 | install: 16 | - # Do nothing. This is needed to prevent default install action "go get -t -v ./..." from happening here (we want it to happen inside script step). 17 | script: 18 | - go get -t -v ./... 19 | - diff -u <(echo -n) <(gofmt -d -s .) 20 | - go tool vet . 21 | - go test -v -race ./... 22 | -------------------------------------------------------------------------------- /vendor/github.com/dustin/go-humanize/LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2005-2008 Dustin Sallings 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in 11 | all copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | SOFTWARE. 20 | 21 | 22 | -------------------------------------------------------------------------------- /vendor/github.com/dustin/go-humanize/README.markdown: -------------------------------------------------------------------------------- 1 | # Humane Units [![Build Status](https://travis-ci.org/dustin/go-humanize.svg?branch=master)](https://travis-ci.org/dustin/go-humanize) [![GoDoc](https://godoc.org/github.com/dustin/go-humanize?status.svg)](https://godoc.org/github.com/dustin/go-humanize) 2 | 3 | Just a few functions for helping humanize times and sizes. 4 | 5 | `go get` it as `github.com/dustin/go-humanize`, import it as 6 | `"github.com/dustin/go-humanize"`, use it as `humanize`. 7 | 8 | See [godoc](https://godoc.org/github.com/dustin/go-humanize) for 9 | complete documentation. 10 | 11 | ## Sizes 12 | 13 | This lets you take numbers like `82854982` and convert them to useful 14 | strings like, `83 MB` or `79 MiB` (whichever you prefer). 15 | 16 | Example: 17 | 18 | ```go 19 | fmt.Printf("That file is %s.", humanize.Bytes(82854982)) // That file is 83 MB. 20 | ``` 21 | 22 | ## Times 23 | 24 | This lets you take a `time.Time` and spit it out in relative terms. 25 | For example, `12 seconds ago` or `3 days from now`. 26 | 27 | Example: 28 | 29 | ```go 30 | fmt.Printf("This was touched %s.", humanize.Time(someTimeInstance)) // This was touched 7 hours ago. 31 | ``` 32 | 33 | Thanks to Kyle Lemons for the time implementation from an IRC 34 | conversation one day. It's pretty neat. 35 | 36 | ## Ordinals 37 | 38 | From a [mailing list discussion][odisc] where a user wanted to be able 39 | to label ordinals. 40 | 41 | 0 -> 0th 42 | 1 -> 1st 43 | 2 -> 2nd 44 | 3 -> 3rd 45 | 4 -> 4th 46 | [...] 47 | 48 | Example: 49 | 50 | ```go 51 | fmt.Printf("You're my %s best friend.", humanize.Ordinal(193)) // You are my 193rd best friend. 52 | ``` 53 | 54 | ## Commas 55 | 56 | Want to shove commas into numbers? Be my guest. 57 | 58 | 0 -> 0 59 | 100 -> 100 60 | 1000 -> 1,000 61 | 1000000000 -> 1,000,000,000 62 | -100000 -> -100,000 63 | 64 | Example: 65 | 66 | ```go 67 | fmt.Printf("You owe $%s.\n", humanize.Comma(6582491)) // You owe $6,582,491. 68 | ``` 69 | 70 | ## Ftoa 71 | 72 | Nicer float64 formatter that removes trailing zeros. 73 | 74 | ```go 75 | fmt.Printf("%f", 2.24) // 2.240000 76 | fmt.Printf("%s", humanize.Ftoa(2.24)) // 2.24 77 | fmt.Printf("%f", 2.0) // 2.000000 78 | fmt.Printf("%s", humanize.Ftoa(2.0)) // 2 79 | ``` 80 | 81 | ## SI notation 82 | 83 | Format numbers with [SI notation][sinotation]. 84 | 85 | Example: 86 | 87 | ```go 88 | humanize.SI(0.00000000223, "M") // 2.23 nM 89 | ``` 90 | 91 | ## English-specific functions 92 | 93 | The following functions are in the `humanize/english` subpackage. 94 | 95 | ### Plurals 96 | 97 | Simple English pluralization 98 | 99 | ```go 100 | english.PluralWord(1, "object", "") // object 101 | english.PluralWord(42, "object", "") // objects 102 | english.PluralWord(2, "bus", "") // buses 103 | english.PluralWord(99, "locus", "loci") // loci 104 | 105 | english.Plural(1, "object", "") // 1 object 106 | english.Plural(42, "object", "") // 42 objects 107 | english.Plural(2, "bus", "") // 2 buses 108 | english.Plural(99, "locus", "loci") // 99 loci 109 | ``` 110 | 111 | ### Word series 112 | 113 | Format comma-separated words lists with conjuctions: 114 | 115 | ```go 116 | english.WordSeries([]string{"foo"}, "and") // foo 117 | english.WordSeries([]string{"foo", "bar"}, "and") // foo and bar 118 | english.WordSeries([]string{"foo", "bar", "baz"}, "and") // foo, bar and baz 119 | 120 | english.OxfordWordSeries([]string{"foo", "bar", "baz"}, "and") // foo, bar, and baz 121 | ``` 122 | 123 | [odisc]: https://groups.google.com/d/topic/golang-nuts/l8NhI74jl-4/discussion 124 | [sinotation]: http://en.wikipedia.org/wiki/Metric_prefix 125 | -------------------------------------------------------------------------------- /vendor/github.com/dustin/go-humanize/big.go: -------------------------------------------------------------------------------- 1 | package humanize 2 | 3 | import ( 4 | "math/big" 5 | ) 6 | 7 | // order of magnitude (to a max order) 8 | func oomm(n, b *big.Int, maxmag int) (float64, int) { 9 | mag := 0 10 | m := &big.Int{} 11 | for n.Cmp(b) >= 0 { 12 | n.DivMod(n, b, m) 13 | mag++ 14 | if mag == maxmag && maxmag >= 0 { 15 | break 16 | } 17 | } 18 | return float64(n.Int64()) + (float64(m.Int64()) / float64(b.Int64())), mag 19 | } 20 | 21 | // total order of magnitude 22 | // (same as above, but with no upper limit) 23 | func oom(n, b *big.Int) (float64, int) { 24 | mag := 0 25 | m := &big.Int{} 26 | for n.Cmp(b) >= 0 { 27 | n.DivMod(n, b, m) 28 | mag++ 29 | } 30 | return float64(n.Int64()) + (float64(m.Int64()) / float64(b.Int64())), mag 31 | } 32 | -------------------------------------------------------------------------------- /vendor/github.com/dustin/go-humanize/bigbytes.go: -------------------------------------------------------------------------------- 1 | package humanize 2 | 3 | import ( 4 | "fmt" 5 | "math/big" 6 | "strings" 7 | "unicode" 8 | ) 9 | 10 | var ( 11 | bigIECExp = big.NewInt(1024) 12 | 13 | // BigByte is one byte in bit.Ints 14 | BigByte = big.NewInt(1) 15 | // BigKiByte is 1,024 bytes in bit.Ints 16 | BigKiByte = (&big.Int{}).Mul(BigByte, bigIECExp) 17 | // BigMiByte is 1,024 k bytes in bit.Ints 18 | BigMiByte = (&big.Int{}).Mul(BigKiByte, bigIECExp) 19 | // BigGiByte is 1,024 m bytes in bit.Ints 20 | BigGiByte = (&big.Int{}).Mul(BigMiByte, bigIECExp) 21 | // BigTiByte is 1,024 g bytes in bit.Ints 22 | BigTiByte = (&big.Int{}).Mul(BigGiByte, bigIECExp) 23 | // BigPiByte is 1,024 t bytes in bit.Ints 24 | BigPiByte = (&big.Int{}).Mul(BigTiByte, bigIECExp) 25 | // BigEiByte is 1,024 p bytes in bit.Ints 26 | BigEiByte = (&big.Int{}).Mul(BigPiByte, bigIECExp) 27 | // BigZiByte is 1,024 e bytes in bit.Ints 28 | BigZiByte = (&big.Int{}).Mul(BigEiByte, bigIECExp) 29 | // BigYiByte is 1,024 z bytes in bit.Ints 30 | BigYiByte = (&big.Int{}).Mul(BigZiByte, bigIECExp) 31 | ) 32 | 33 | var ( 34 | bigSIExp = big.NewInt(1000) 35 | 36 | // BigSIByte is one SI byte in big.Ints 37 | BigSIByte = big.NewInt(1) 38 | // BigKByte is 1,000 SI bytes in big.Ints 39 | BigKByte = (&big.Int{}).Mul(BigSIByte, bigSIExp) 40 | // BigMByte is 1,000 SI k bytes in big.Ints 41 | BigMByte = (&big.Int{}).Mul(BigKByte, bigSIExp) 42 | // BigGByte is 1,000 SI m bytes in big.Ints 43 | BigGByte = (&big.Int{}).Mul(BigMByte, bigSIExp) 44 | // BigTByte is 1,000 SI g bytes in big.Ints 45 | BigTByte = (&big.Int{}).Mul(BigGByte, bigSIExp) 46 | // BigPByte is 1,000 SI t bytes in big.Ints 47 | BigPByte = (&big.Int{}).Mul(BigTByte, bigSIExp) 48 | // BigEByte is 1,000 SI p bytes in big.Ints 49 | BigEByte = (&big.Int{}).Mul(BigPByte, bigSIExp) 50 | // BigZByte is 1,000 SI e bytes in big.Ints 51 | BigZByte = (&big.Int{}).Mul(BigEByte, bigSIExp) 52 | // BigYByte is 1,000 SI z bytes in big.Ints 53 | BigYByte = (&big.Int{}).Mul(BigZByte, bigSIExp) 54 | ) 55 | 56 | var bigBytesSizeTable = map[string]*big.Int{ 57 | "b": BigByte, 58 | "kib": BigKiByte, 59 | "kb": BigKByte, 60 | "mib": BigMiByte, 61 | "mb": BigMByte, 62 | "gib": BigGiByte, 63 | "gb": BigGByte, 64 | "tib": BigTiByte, 65 | "tb": BigTByte, 66 | "pib": BigPiByte, 67 | "pb": BigPByte, 68 | "eib": BigEiByte, 69 | "eb": BigEByte, 70 | "zib": BigZiByte, 71 | "zb": BigZByte, 72 | "yib": BigYiByte, 73 | "yb": BigYByte, 74 | // Without suffix 75 | "": BigByte, 76 | "ki": BigKiByte, 77 | "k": BigKByte, 78 | "mi": BigMiByte, 79 | "m": BigMByte, 80 | "gi": BigGiByte, 81 | "g": BigGByte, 82 | "ti": BigTiByte, 83 | "t": BigTByte, 84 | "pi": BigPiByte, 85 | "p": BigPByte, 86 | "ei": BigEiByte, 87 | "e": BigEByte, 88 | "z": BigZByte, 89 | "zi": BigZiByte, 90 | "y": BigYByte, 91 | "yi": BigYiByte, 92 | } 93 | 94 | var ten = big.NewInt(10) 95 | 96 | func humanateBigBytes(s, base *big.Int, sizes []string) string { 97 | if s.Cmp(ten) < 0 { 98 | return fmt.Sprintf("%d B", s) 99 | } 100 | c := (&big.Int{}).Set(s) 101 | val, mag := oomm(c, base, len(sizes)-1) 102 | suffix := sizes[mag] 103 | f := "%.0f %s" 104 | if val < 10 { 105 | f = "%.1f %s" 106 | } 107 | 108 | return fmt.Sprintf(f, val, suffix) 109 | 110 | } 111 | 112 | // BigBytes produces a human readable representation of an SI size. 113 | // 114 | // See also: ParseBigBytes. 115 | // 116 | // BigBytes(82854982) -> 83 MB 117 | func BigBytes(s *big.Int) string { 118 | sizes := []string{"B", "kB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB"} 119 | return humanateBigBytes(s, bigSIExp, sizes) 120 | } 121 | 122 | // BigIBytes produces a human readable representation of an IEC size. 123 | // 124 | // See also: ParseBigBytes. 125 | // 126 | // BigIBytes(82854982) -> 79 MiB 127 | func BigIBytes(s *big.Int) string { 128 | sizes := []string{"B", "KiB", "MiB", "GiB", "TiB", "PiB", "EiB", "ZiB", "YiB"} 129 | return humanateBigBytes(s, bigIECExp, sizes) 130 | } 131 | 132 | // ParseBigBytes parses a string representation of bytes into the number 133 | // of bytes it represents. 134 | // 135 | // See also: BigBytes, BigIBytes. 136 | // 137 | // ParseBigBytes("42 MB") -> 42000000, nil 138 | // ParseBigBytes("42 mib") -> 44040192, nil 139 | func ParseBigBytes(s string) (*big.Int, error) { 140 | lastDigit := 0 141 | hasComma := false 142 | for _, r := range s { 143 | if !(unicode.IsDigit(r) || r == '.' || r == ',') { 144 | break 145 | } 146 | if r == ',' { 147 | hasComma = true 148 | } 149 | lastDigit++ 150 | } 151 | 152 | num := s[:lastDigit] 153 | if hasComma { 154 | num = strings.Replace(num, ",", "", -1) 155 | } 156 | 157 | val := &big.Rat{} 158 | _, err := fmt.Sscanf(num, "%f", val) 159 | if err != nil { 160 | return nil, err 161 | } 162 | 163 | extra := strings.ToLower(strings.TrimSpace(s[lastDigit:])) 164 | if m, ok := bigBytesSizeTable[extra]; ok { 165 | mv := (&big.Rat{}).SetInt(m) 166 | val.Mul(val, mv) 167 | rv := &big.Int{} 168 | rv.Div(val.Num(), val.Denom()) 169 | return rv, nil 170 | } 171 | 172 | return nil, fmt.Errorf("unhandled size name: %v", extra) 173 | } 174 | -------------------------------------------------------------------------------- /vendor/github.com/dustin/go-humanize/bytes.go: -------------------------------------------------------------------------------- 1 | package humanize 2 | 3 | import ( 4 | "fmt" 5 | "math" 6 | "strconv" 7 | "strings" 8 | "unicode" 9 | ) 10 | 11 | // IEC Sizes. 12 | // kibis of bits 13 | const ( 14 | Byte = 1 << (iota * 10) 15 | KiByte 16 | MiByte 17 | GiByte 18 | TiByte 19 | PiByte 20 | EiByte 21 | ) 22 | 23 | // SI Sizes. 24 | const ( 25 | IByte = 1 26 | KByte = IByte * 1000 27 | MByte = KByte * 1000 28 | GByte = MByte * 1000 29 | TByte = GByte * 1000 30 | PByte = TByte * 1000 31 | EByte = PByte * 1000 32 | ) 33 | 34 | var bytesSizeTable = map[string]uint64{ 35 | "b": Byte, 36 | "kib": KiByte, 37 | "kb": KByte, 38 | "mib": MiByte, 39 | "mb": MByte, 40 | "gib": GiByte, 41 | "gb": GByte, 42 | "tib": TiByte, 43 | "tb": TByte, 44 | "pib": PiByte, 45 | "pb": PByte, 46 | "eib": EiByte, 47 | "eb": EByte, 48 | // Without suffix 49 | "": Byte, 50 | "ki": KiByte, 51 | "k": KByte, 52 | "mi": MiByte, 53 | "m": MByte, 54 | "gi": GiByte, 55 | "g": GByte, 56 | "ti": TiByte, 57 | "t": TByte, 58 | "pi": PiByte, 59 | "p": PByte, 60 | "ei": EiByte, 61 | "e": EByte, 62 | } 63 | 64 | func logn(n, b float64) float64 { 65 | return math.Log(n) / math.Log(b) 66 | } 67 | 68 | func humanateBytes(s uint64, base float64, sizes []string) string { 69 | if s < 10 { 70 | return fmt.Sprintf("%d B", s) 71 | } 72 | e := math.Floor(logn(float64(s), base)) 73 | suffix := sizes[int(e)] 74 | val := math.Floor(float64(s)/math.Pow(base, e)*10+0.5) / 10 75 | f := "%.0f %s" 76 | if val < 10 { 77 | f = "%.1f %s" 78 | } 79 | 80 | return fmt.Sprintf(f, val, suffix) 81 | } 82 | 83 | // Bytes produces a human readable representation of an SI size. 84 | // 85 | // See also: ParseBytes. 86 | // 87 | // Bytes(82854982) -> 83 MB 88 | func Bytes(s uint64) string { 89 | sizes := []string{"B", "kB", "MB", "GB", "TB", "PB", "EB"} 90 | return humanateBytes(s, 1000, sizes) 91 | } 92 | 93 | // IBytes produces a human readable representation of an IEC size. 94 | // 95 | // See also: ParseBytes. 96 | // 97 | // IBytes(82854982) -> 79 MiB 98 | func IBytes(s uint64) string { 99 | sizes := []string{"B", "KiB", "MiB", "GiB", "TiB", "PiB", "EiB"} 100 | return humanateBytes(s, 1024, sizes) 101 | } 102 | 103 | // ParseBytes parses a string representation of bytes into the number 104 | // of bytes it represents. 105 | // 106 | // See Also: Bytes, IBytes. 107 | // 108 | // ParseBytes("42 MB") -> 42000000, nil 109 | // ParseBytes("42 mib") -> 44040192, nil 110 | func ParseBytes(s string) (uint64, error) { 111 | lastDigit := 0 112 | hasComma := false 113 | for _, r := range s { 114 | if !(unicode.IsDigit(r) || r == '.' || r == ',') { 115 | break 116 | } 117 | if r == ',' { 118 | hasComma = true 119 | } 120 | lastDigit++ 121 | } 122 | 123 | num := s[:lastDigit] 124 | if hasComma { 125 | num = strings.Replace(num, ",", "", -1) 126 | } 127 | 128 | f, err := strconv.ParseFloat(num, 64) 129 | if err != nil { 130 | return 0, err 131 | } 132 | 133 | extra := strings.ToLower(strings.TrimSpace(s[lastDigit:])) 134 | if m, ok := bytesSizeTable[extra]; ok { 135 | f *= float64(m) 136 | if f >= math.MaxUint64 { 137 | return 0, fmt.Errorf("too large: %v", s) 138 | } 139 | return uint64(f), nil 140 | } 141 | 142 | return 0, fmt.Errorf("unhandled size name: %v", extra) 143 | } 144 | -------------------------------------------------------------------------------- /vendor/github.com/dustin/go-humanize/comma.go: -------------------------------------------------------------------------------- 1 | package humanize 2 | 3 | import ( 4 | "bytes" 5 | "math" 6 | "math/big" 7 | "strconv" 8 | "strings" 9 | ) 10 | 11 | // Comma produces a string form of the given number in base 10 with 12 | // commas after every three orders of magnitude. 13 | // 14 | // e.g. Comma(834142) -> 834,142 15 | func Comma(v int64) string { 16 | sign := "" 17 | 18 | // Min int64 can't be negated to a usable value, so it has to be special cased. 19 | if v == math.MinInt64 { 20 | return "-9,223,372,036,854,775,808" 21 | } 22 | 23 | if v < 0 { 24 | sign = "-" 25 | v = 0 - v 26 | } 27 | 28 | parts := []string{"", "", "", "", "", "", ""} 29 | j := len(parts) - 1 30 | 31 | for v > 999 { 32 | parts[j] = strconv.FormatInt(v%1000, 10) 33 | switch len(parts[j]) { 34 | case 2: 35 | parts[j] = "0" + parts[j] 36 | case 1: 37 | parts[j] = "00" + parts[j] 38 | } 39 | v = v / 1000 40 | j-- 41 | } 42 | parts[j] = strconv.Itoa(int(v)) 43 | return sign + strings.Join(parts[j:], ",") 44 | } 45 | 46 | // Commaf produces a string form of the given number in base 10 with 47 | // commas after every three orders of magnitude. 48 | // 49 | // e.g. Commaf(834142.32) -> 834,142.32 50 | func Commaf(v float64) string { 51 | buf := &bytes.Buffer{} 52 | if v < 0 { 53 | buf.Write([]byte{'-'}) 54 | v = 0 - v 55 | } 56 | 57 | comma := []byte{','} 58 | 59 | parts := strings.Split(strconv.FormatFloat(v, 'f', -1, 64), ".") 60 | pos := 0 61 | if len(parts[0])%3 != 0 { 62 | pos += len(parts[0]) % 3 63 | buf.WriteString(parts[0][:pos]) 64 | buf.Write(comma) 65 | } 66 | for ; pos < len(parts[0]); pos += 3 { 67 | buf.WriteString(parts[0][pos : pos+3]) 68 | buf.Write(comma) 69 | } 70 | buf.Truncate(buf.Len() - 1) 71 | 72 | if len(parts) > 1 { 73 | buf.Write([]byte{'.'}) 74 | buf.WriteString(parts[1]) 75 | } 76 | return buf.String() 77 | } 78 | 79 | // CommafWithDigits works like the Commaf but limits the resulting 80 | // string to the given number of decimal places. 81 | // 82 | // e.g. CommafWithDigits(834142.32, 1) -> 834,142.3 83 | func CommafWithDigits(f float64, decimals int) string { 84 | return stripTrailingDigits(Commaf(f), decimals) 85 | } 86 | 87 | // BigComma produces a string form of the given big.Int in base 10 88 | // with commas after every three orders of magnitude. 89 | func BigComma(b *big.Int) string { 90 | sign := "" 91 | if b.Sign() < 0 { 92 | sign = "-" 93 | b.Abs(b) 94 | } 95 | 96 | athousand := big.NewInt(1000) 97 | c := (&big.Int{}).Set(b) 98 | _, m := oom(c, athousand) 99 | parts := make([]string, m+1) 100 | j := len(parts) - 1 101 | 102 | mod := &big.Int{} 103 | for b.Cmp(athousand) >= 0 { 104 | b.DivMod(b, athousand, mod) 105 | parts[j] = strconv.FormatInt(mod.Int64(), 10) 106 | switch len(parts[j]) { 107 | case 2: 108 | parts[j] = "0" + parts[j] 109 | case 1: 110 | parts[j] = "00" + parts[j] 111 | } 112 | j-- 113 | } 114 | parts[j] = strconv.Itoa(int(b.Int64())) 115 | return sign + strings.Join(parts[j:], ",") 116 | } 117 | -------------------------------------------------------------------------------- /vendor/github.com/dustin/go-humanize/commaf.go: -------------------------------------------------------------------------------- 1 | // +build go1.6 2 | 3 | package humanize 4 | 5 | import ( 6 | "bytes" 7 | "math/big" 8 | "strings" 9 | ) 10 | 11 | // BigCommaf produces a string form of the given big.Float in base 10 12 | // with commas after every three orders of magnitude. 13 | func BigCommaf(v *big.Float) string { 14 | buf := &bytes.Buffer{} 15 | if v.Sign() < 0 { 16 | buf.Write([]byte{'-'}) 17 | v.Abs(v) 18 | } 19 | 20 | comma := []byte{','} 21 | 22 | parts := strings.Split(v.Text('f', -1), ".") 23 | pos := 0 24 | if len(parts[0])%3 != 0 { 25 | pos += len(parts[0]) % 3 26 | buf.WriteString(parts[0][:pos]) 27 | buf.Write(comma) 28 | } 29 | for ; pos < len(parts[0]); pos += 3 { 30 | buf.WriteString(parts[0][pos : pos+3]) 31 | buf.Write(comma) 32 | } 33 | buf.Truncate(buf.Len() - 1) 34 | 35 | if len(parts) > 1 { 36 | buf.Write([]byte{'.'}) 37 | buf.WriteString(parts[1]) 38 | } 39 | return buf.String() 40 | } 41 | -------------------------------------------------------------------------------- /vendor/github.com/dustin/go-humanize/ftoa.go: -------------------------------------------------------------------------------- 1 | package humanize 2 | 3 | import ( 4 | "strconv" 5 | "strings" 6 | ) 7 | 8 | func stripTrailingZeros(s string) string { 9 | offset := len(s) - 1 10 | for offset > 0 { 11 | if s[offset] == '.' { 12 | offset-- 13 | break 14 | } 15 | if s[offset] != '0' { 16 | break 17 | } 18 | offset-- 19 | } 20 | return s[:offset+1] 21 | } 22 | 23 | func stripTrailingDigits(s string, digits int) string { 24 | if i := strings.Index(s, "."); i >= 0 { 25 | if digits <= 0 { 26 | return s[:i] 27 | } 28 | i++ 29 | if i+digits >= len(s) { 30 | return s 31 | } 32 | return s[:i+digits] 33 | } 34 | return s 35 | } 36 | 37 | // Ftoa converts a float to a string with no trailing zeros. 38 | func Ftoa(num float64) string { 39 | return stripTrailingZeros(strconv.FormatFloat(num, 'f', 6, 64)) 40 | } 41 | 42 | // FtoaWithDigits converts a float to a string but limits the resulting string 43 | // to the given number of decimal places, and no trailing zeros. 44 | func FtoaWithDigits(num float64, digits int) string { 45 | return stripTrailingZeros(stripTrailingDigits(strconv.FormatFloat(num, 'f', 6, 64), digits)) 46 | } 47 | -------------------------------------------------------------------------------- /vendor/github.com/dustin/go-humanize/humanize.go: -------------------------------------------------------------------------------- 1 | /* 2 | Package humanize converts boring ugly numbers to human-friendly strings and back. 3 | 4 | Durations can be turned into strings such as "3 days ago", numbers 5 | representing sizes like 82854982 into useful strings like, "83 MB" or 6 | "79 MiB" (whichever you prefer). 7 | */ 8 | package humanize 9 | -------------------------------------------------------------------------------- /vendor/github.com/dustin/go-humanize/number.go: -------------------------------------------------------------------------------- 1 | package humanize 2 | 3 | /* 4 | Slightly adapted from the source to fit go-humanize. 5 | 6 | Author: https://github.com/gorhill 7 | Source: https://gist.github.com/gorhill/5285193 8 | 9 | */ 10 | 11 | import ( 12 | "math" 13 | "strconv" 14 | ) 15 | 16 | var ( 17 | renderFloatPrecisionMultipliers = [...]float64{ 18 | 1, 19 | 10, 20 | 100, 21 | 1000, 22 | 10000, 23 | 100000, 24 | 1000000, 25 | 10000000, 26 | 100000000, 27 | 1000000000, 28 | } 29 | 30 | renderFloatPrecisionRounders = [...]float64{ 31 | 0.5, 32 | 0.05, 33 | 0.005, 34 | 0.0005, 35 | 0.00005, 36 | 0.000005, 37 | 0.0000005, 38 | 0.00000005, 39 | 0.000000005, 40 | 0.0000000005, 41 | } 42 | ) 43 | 44 | // FormatFloat produces a formatted number as string based on the following user-specified criteria: 45 | // * thousands separator 46 | // * decimal separator 47 | // * decimal precision 48 | // 49 | // Usage: s := RenderFloat(format, n) 50 | // The format parameter tells how to render the number n. 51 | // 52 | // See examples: http://play.golang.org/p/LXc1Ddm1lJ 53 | // 54 | // Examples of format strings, given n = 12345.6789: 55 | // "#,###.##" => "12,345.67" 56 | // "#,###." => "12,345" 57 | // "#,###" => "12345,678" 58 | // "#\u202F###,##" => "12 345,68" 59 | // "#.###,###### => 12.345,678900 60 | // "" (aka default format) => 12,345.67 61 | // 62 | // The highest precision allowed is 9 digits after the decimal symbol. 63 | // There is also a version for integer number, FormatInteger(), 64 | // which is convenient for calls within template. 65 | func FormatFloat(format string, n float64) string { 66 | // Special cases: 67 | // NaN = "NaN" 68 | // +Inf = "+Infinity" 69 | // -Inf = "-Infinity" 70 | if math.IsNaN(n) { 71 | return "NaN" 72 | } 73 | if n > math.MaxFloat64 { 74 | return "Infinity" 75 | } 76 | if n < -math.MaxFloat64 { 77 | return "-Infinity" 78 | } 79 | 80 | // default format 81 | precision := 2 82 | decimalStr := "." 83 | thousandStr := "," 84 | positiveStr := "" 85 | negativeStr := "-" 86 | 87 | if len(format) > 0 { 88 | format := []rune(format) 89 | 90 | // If there is an explicit format directive, 91 | // then default values are these: 92 | precision = 9 93 | thousandStr = "" 94 | 95 | // collect indices of meaningful formatting directives 96 | formatIndx := []int{} 97 | for i, char := range format { 98 | if char != '#' && char != '0' { 99 | formatIndx = append(formatIndx, i) 100 | } 101 | } 102 | 103 | if len(formatIndx) > 0 { 104 | // Directive at index 0: 105 | // Must be a '+' 106 | // Raise an error if not the case 107 | // index: 0123456789 108 | // +0.000,000 109 | // +000,000.0 110 | // +0000.00 111 | // +0000 112 | if formatIndx[0] == 0 { 113 | if format[formatIndx[0]] != '+' { 114 | panic("RenderFloat(): invalid positive sign directive") 115 | } 116 | positiveStr = "+" 117 | formatIndx = formatIndx[1:] 118 | } 119 | 120 | // Two directives: 121 | // First is thousands separator 122 | // Raise an error if not followed by 3-digit 123 | // 0123456789 124 | // 0.000,000 125 | // 000,000.00 126 | if len(formatIndx) == 2 { 127 | if (formatIndx[1] - formatIndx[0]) != 4 { 128 | panic("RenderFloat(): thousands separator directive must be followed by 3 digit-specifiers") 129 | } 130 | thousandStr = string(format[formatIndx[0]]) 131 | formatIndx = formatIndx[1:] 132 | } 133 | 134 | // One directive: 135 | // Directive is decimal separator 136 | // The number of digit-specifier following the separator indicates wanted precision 137 | // 0123456789 138 | // 0.00 139 | // 000,0000 140 | if len(formatIndx) == 1 { 141 | decimalStr = string(format[formatIndx[0]]) 142 | precision = len(format) - formatIndx[0] - 1 143 | } 144 | } 145 | } 146 | 147 | // generate sign part 148 | var signStr string 149 | if n >= 0.000000001 { 150 | signStr = positiveStr 151 | } else if n <= -0.000000001 { 152 | signStr = negativeStr 153 | n = -n 154 | } else { 155 | signStr = "" 156 | n = 0.0 157 | } 158 | 159 | // split number into integer and fractional parts 160 | intf, fracf := math.Modf(n + renderFloatPrecisionRounders[precision]) 161 | 162 | // generate integer part string 163 | intStr := strconv.FormatInt(int64(intf), 10) 164 | 165 | // add thousand separator if required 166 | if len(thousandStr) > 0 { 167 | for i := len(intStr); i > 3; { 168 | i -= 3 169 | intStr = intStr[:i] + thousandStr + intStr[i:] 170 | } 171 | } 172 | 173 | // no fractional part, we can leave now 174 | if precision == 0 { 175 | return signStr + intStr 176 | } 177 | 178 | // generate fractional part 179 | fracStr := strconv.Itoa(int(fracf * renderFloatPrecisionMultipliers[precision])) 180 | // may need padding 181 | if len(fracStr) < precision { 182 | fracStr = "000000000000000"[:precision-len(fracStr)] + fracStr 183 | } 184 | 185 | return signStr + intStr + decimalStr + fracStr 186 | } 187 | 188 | // FormatInteger produces a formatted number as string. 189 | // See FormatFloat. 190 | func FormatInteger(format string, n int) string { 191 | return FormatFloat(format, float64(n)) 192 | } 193 | -------------------------------------------------------------------------------- /vendor/github.com/dustin/go-humanize/ordinals.go: -------------------------------------------------------------------------------- 1 | package humanize 2 | 3 | import "strconv" 4 | 5 | // Ordinal gives you the input number in a rank/ordinal format. 6 | // 7 | // Ordinal(3) -> 3rd 8 | func Ordinal(x int) string { 9 | suffix := "th" 10 | switch x % 10 { 11 | case 1: 12 | if x%100 != 11 { 13 | suffix = "st" 14 | } 15 | case 2: 16 | if x%100 != 12 { 17 | suffix = "nd" 18 | } 19 | case 3: 20 | if x%100 != 13 { 21 | suffix = "rd" 22 | } 23 | } 24 | return strconv.Itoa(x) + suffix 25 | } 26 | -------------------------------------------------------------------------------- /vendor/github.com/dustin/go-humanize/si.go: -------------------------------------------------------------------------------- 1 | package humanize 2 | 3 | import ( 4 | "errors" 5 | "math" 6 | "regexp" 7 | "strconv" 8 | ) 9 | 10 | var siPrefixTable = map[float64]string{ 11 | -24: "y", // yocto 12 | -21: "z", // zepto 13 | -18: "a", // atto 14 | -15: "f", // femto 15 | -12: "p", // pico 16 | -9: "n", // nano 17 | -6: "µ", // micro 18 | -3: "m", // milli 19 | 0: "", 20 | 3: "k", // kilo 21 | 6: "M", // mega 22 | 9: "G", // giga 23 | 12: "T", // tera 24 | 15: "P", // peta 25 | 18: "E", // exa 26 | 21: "Z", // zetta 27 | 24: "Y", // yotta 28 | } 29 | 30 | var revSIPrefixTable = revfmap(siPrefixTable) 31 | 32 | // revfmap reverses the map and precomputes the power multiplier 33 | func revfmap(in map[float64]string) map[string]float64 { 34 | rv := map[string]float64{} 35 | for k, v := range in { 36 | rv[v] = math.Pow(10, k) 37 | } 38 | return rv 39 | } 40 | 41 | var riParseRegex *regexp.Regexp 42 | 43 | func init() { 44 | ri := `^([\-0-9.]+)\s?([` 45 | for _, v := range siPrefixTable { 46 | ri += v 47 | } 48 | ri += `]?)(.*)` 49 | 50 | riParseRegex = regexp.MustCompile(ri) 51 | } 52 | 53 | // ComputeSI finds the most appropriate SI prefix for the given number 54 | // and returns the prefix along with the value adjusted to be within 55 | // that prefix. 56 | // 57 | // See also: SI, ParseSI. 58 | // 59 | // e.g. ComputeSI(2.2345e-12) -> (2.2345, "p") 60 | func ComputeSI(input float64) (float64, string) { 61 | if input == 0 { 62 | return 0, "" 63 | } 64 | mag := math.Abs(input) 65 | exponent := math.Floor(logn(mag, 10)) 66 | exponent = math.Floor(exponent/3) * 3 67 | 68 | value := mag / math.Pow(10, exponent) 69 | 70 | // Handle special case where value is exactly 1000.0 71 | // Should return 1 M instead of 1000 k 72 | if value == 1000.0 { 73 | exponent += 3 74 | value = mag / math.Pow(10, exponent) 75 | } 76 | 77 | value = math.Copysign(value, input) 78 | 79 | prefix := siPrefixTable[exponent] 80 | return value, prefix 81 | } 82 | 83 | // SI returns a string with default formatting. 84 | // 85 | // SI uses Ftoa to format float value, removing trailing zeros. 86 | // 87 | // See also: ComputeSI, ParseSI. 88 | // 89 | // e.g. SI(1000000, "B") -> 1 MB 90 | // e.g. SI(2.2345e-12, "F") -> 2.2345 pF 91 | func SI(input float64, unit string) string { 92 | value, prefix := ComputeSI(input) 93 | return Ftoa(value) + " " + prefix + unit 94 | } 95 | 96 | // SIWithDigits works like SI but limits the resulting string to the 97 | // given number of decimal places. 98 | // 99 | // e.g. SIWithDigits(1000000, 0, "B") -> 1 MB 100 | // e.g. SIWithDigits(2.2345e-12, 2, "F") -> 2.23 pF 101 | func SIWithDigits(input float64, decimals int, unit string) string { 102 | value, prefix := ComputeSI(input) 103 | return FtoaWithDigits(value, decimals) + " " + prefix + unit 104 | } 105 | 106 | var errInvalid = errors.New("invalid input") 107 | 108 | // ParseSI parses an SI string back into the number and unit. 109 | // 110 | // See also: SI, ComputeSI. 111 | // 112 | // e.g. ParseSI("2.2345 pF") -> (2.2345e-12, "F", nil) 113 | func ParseSI(input string) (float64, string, error) { 114 | found := riParseRegex.FindStringSubmatch(input) 115 | if len(found) != 4 { 116 | return 0, "", errInvalid 117 | } 118 | mag := revSIPrefixTable[found[2]] 119 | unit := found[3] 120 | 121 | base, err := strconv.ParseFloat(found[1], 64) 122 | return base * mag, unit, err 123 | } 124 | -------------------------------------------------------------------------------- /vendor/github.com/dustin/go-humanize/times.go: -------------------------------------------------------------------------------- 1 | package humanize 2 | 3 | import ( 4 | "fmt" 5 | "math" 6 | "sort" 7 | "time" 8 | ) 9 | 10 | // Seconds-based time units 11 | const ( 12 | Day = 24 * time.Hour 13 | Week = 7 * Day 14 | Month = 30 * Day 15 | Year = 12 * Month 16 | LongTime = 37 * Year 17 | ) 18 | 19 | // Time formats a time into a relative string. 20 | // 21 | // Time(someT) -> "3 weeks ago" 22 | func Time(then time.Time) string { 23 | return RelTime(then, time.Now(), "ago", "from now") 24 | } 25 | 26 | // A RelTimeMagnitude struct contains a relative time point at which 27 | // the relative format of time will switch to a new format string. A 28 | // slice of these in ascending order by their "D" field is passed to 29 | // CustomRelTime to format durations. 30 | // 31 | // The Format field is a string that may contain a "%s" which will be 32 | // replaced with the appropriate signed label (e.g. "ago" or "from 33 | // now") and a "%d" that will be replaced by the quantity. 34 | // 35 | // The DivBy field is the amount of time the time difference must be 36 | // divided by in order to display correctly. 37 | // 38 | // e.g. if D is 2*time.Minute and you want to display "%d minutes %s" 39 | // DivBy should be time.Minute so whatever the duration is will be 40 | // expressed in minutes. 41 | type RelTimeMagnitude struct { 42 | D time.Duration 43 | Format string 44 | DivBy time.Duration 45 | } 46 | 47 | var defaultMagnitudes = []RelTimeMagnitude{ 48 | {time.Second, "now", time.Second}, 49 | {2 * time.Second, "1 second %s", 1}, 50 | {time.Minute, "%d seconds %s", time.Second}, 51 | {2 * time.Minute, "1 minute %s", 1}, 52 | {time.Hour, "%d minutes %s", time.Minute}, 53 | {2 * time.Hour, "1 hour %s", 1}, 54 | {Day, "%d hours %s", time.Hour}, 55 | {2 * Day, "1 day %s", 1}, 56 | {Week, "%d days %s", Day}, 57 | {2 * Week, "1 week %s", 1}, 58 | {Month, "%d weeks %s", Week}, 59 | {2 * Month, "1 month %s", 1}, 60 | {Year, "%d months %s", Month}, 61 | {18 * Month, "1 year %s", 1}, 62 | {2 * Year, "2 years %s", 1}, 63 | {LongTime, "%d years %s", Year}, 64 | {math.MaxInt64, "a long while %s", 1}, 65 | } 66 | 67 | // RelTime formats a time into a relative string. 68 | // 69 | // It takes two times and two labels. In addition to the generic time 70 | // delta string (e.g. 5 minutes), the labels are used applied so that 71 | // the label corresponding to the smaller time is applied. 72 | // 73 | // RelTime(timeInPast, timeInFuture, "earlier", "later") -> "3 weeks earlier" 74 | func RelTime(a, b time.Time, albl, blbl string) string { 75 | return CustomRelTime(a, b, albl, blbl, defaultMagnitudes) 76 | } 77 | 78 | // CustomRelTime formats a time into a relative string. 79 | // 80 | // It takes two times two labels and a table of relative time formats. 81 | // In addition to the generic time delta string (e.g. 5 minutes), the 82 | // labels are used applied so that the label corresponding to the 83 | // smaller time is applied. 84 | func CustomRelTime(a, b time.Time, albl, blbl string, magnitudes []RelTimeMagnitude) string { 85 | lbl := albl 86 | diff := b.Sub(a) 87 | 88 | if a.After(b) { 89 | lbl = blbl 90 | diff = a.Sub(b) 91 | } 92 | 93 | n := sort.Search(len(magnitudes), func(i int) bool { 94 | return magnitudes[i].D > diff 95 | }) 96 | 97 | if n >= len(magnitudes) { 98 | n = len(magnitudes) - 1 99 | } 100 | mag := magnitudes[n] 101 | args := []interface{}{} 102 | escaped := false 103 | for _, ch := range mag.Format { 104 | if escaped { 105 | switch ch { 106 | case 's': 107 | args = append(args, lbl) 108 | case 'd': 109 | args = append(args, diff/mag.DivBy) 110 | } 111 | escaped = false 112 | } else { 113 | escaped = ch == '%' 114 | } 115 | } 116 | return fmt.Sprintf(mag.Format, args...) 117 | } 118 | -------------------------------------------------------------------------------- /vendor/github.com/espebra/filebin/LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2015-2016, Espen Braastad 2 | All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without 5 | modification, are permitted provided that the following conditions are met: 6 | * Redistributions of source code must retain the above copyright 7 | notice, this list of conditions and the following disclaimer. 8 | * Redistributions in binary form must reproduce the above copyright 9 | notice, this list of conditions and the following disclaimer in the 10 | documentation and/or other materials provided with the distribution. 11 | * Neither the name of the nor the 12 | names of its contributors may be used to endorse or promote products 13 | derived from this software without specific prior written permission. 14 | 15 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 16 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 17 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 18 | DISCLAIMED. IN NO EVENT SHALL BE LIABLE FOR ANY 19 | DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 20 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 21 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 22 | ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 23 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 24 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 25 | -------------------------------------------------------------------------------- /vendor/github.com/espebra/filebin/app/config/config.go: -------------------------------------------------------------------------------- 1 | package config 2 | 3 | type filters []string 4 | 5 | func (i *filters) String() string { 6 | return "string representation" 7 | } 8 | 9 | func (i *filters) Set(value string) error { 10 | *i = append(*i, value) 11 | return nil 12 | } 13 | 14 | type Configuration struct { 15 | Host string 16 | Port int 17 | ReadTimeout int 18 | WriteTimeout int 19 | MaxHeaderBytes int 20 | Filedir string 21 | Baseurl string 22 | Tempdir string 23 | Expiration int64 24 | TriggerNewBin string 25 | TriggerUploadFile string 26 | TriggerDownloadBin string 27 | TriggerDownloadFile string 28 | TriggerDeleteBin string 29 | TriggerDeleteFile string 30 | TriggerExpireBin string 31 | ClientAddrHeader string 32 | DefaultBinLength int 33 | Workers int 34 | Version bool 35 | CacheInvalidation bool 36 | AdminUsername string 37 | AdminPassword string 38 | AccessLog string 39 | Filters filters 40 | HotLinking bool 41 | } 42 | 43 | var Global Configuration 44 | 45 | func init() { 46 | Global = Configuration{ 47 | Host: "127.0.0.1", 48 | Port: 31337, 49 | ReadTimeout: 3600, 50 | WriteTimeout: 3600, 51 | MaxHeaderBytes: 1 << 20, 52 | // 7776000 = 3 months 53 | Expiration: 7776000, 54 | Baseurl: "http://localhost:31337", 55 | Filedir: "/srv/filebin/files", 56 | Tempdir: "/tmp", 57 | DefaultBinLength: 16, 58 | Workers: 1, 59 | CacheInvalidation: false, 60 | AdminUsername: "admin", 61 | ClientAddrHeader: "", 62 | AccessLog: "/var/log/filebin/access.log", 63 | Filters: []string{}, 64 | HotLinking: true, 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /vendor/github.com/espebra/filebin/app/metrics/metrics.go: -------------------------------------------------------------------------------- 1 | package metrics 2 | 3 | import ( 4 | "sync" 5 | "time" 6 | ) 7 | 8 | type Metrics struct { 9 | sync.RWMutex 10 | startTime time.Time 11 | stats map[string]int64 12 | } 13 | 14 | func Init() Metrics { 15 | m := Metrics{} 16 | m.Lock() 17 | m.startTime = time.Now().UTC() 18 | m.stats = make(map[string]int64) 19 | m.Unlock() 20 | return m 21 | } 22 | 23 | func (m *Metrics) Uptime() time.Duration { 24 | m.RLock() 25 | defer m.RUnlock() 26 | return time.Since(m.startTime) 27 | } 28 | 29 | func (m *Metrics) StartTime() time.Time { 30 | m.RLock() 31 | defer m.RUnlock() 32 | return m.startTime 33 | } 34 | 35 | func (m *Metrics) GetStats() map[string]int64 { 36 | m.RLock() 37 | defer m.RUnlock() 38 | return m.stats 39 | } 40 | 41 | func (m *Metrics) Get(key string) (int64, bool) { 42 | m.RLock() 43 | defer m.RUnlock() 44 | value, found := m.stats[key] 45 | return value, found 46 | } 47 | 48 | func (m *Metrics) IncrSet(key string, i int64) int64 { 49 | newValue := i 50 | m.Lock() 51 | defer m.Unlock() 52 | currentValue, found := m.stats[key] 53 | if found { 54 | newValue = currentValue + i 55 | } 56 | m.stats[key] = newValue 57 | return newValue 58 | } 59 | 60 | func (m *Metrics) Incr(key string) int64 { 61 | m.Lock() 62 | defer m.Unlock() 63 | currentValue, _ := m.stats[key] 64 | newValue := currentValue + 1 65 | m.stats[key] = newValue 66 | return newValue 67 | } 68 | 69 | func (m *Metrics) Decr(key string) int64 { 70 | m.Lock() 71 | defer m.Unlock() 72 | currentValue, _ := m.stats[key] 73 | newValue := currentValue - 1 74 | m.stats[key] = newValue 75 | return newValue 76 | } 77 | -------------------------------------------------------------------------------- /vendor/github.com/espebra/filebin/app/model/batch.go: -------------------------------------------------------------------------------- 1 | package model 2 | 3 | import ( 4 | "github.com/espebra/filebin/app/backend/fs" 5 | "github.com/espebra/filebin/app/shared" 6 | "math/rand" 7 | "time" 8 | ) 9 | 10 | // Dispatcher function to spawn a number of workers 11 | func StartDispatcher(nworkers int, WorkQueue chan Job, backend *fs.Backend) { 12 | for i := 0; i < nworkers; i++ { 13 | go StartWorker(WorkQueue, backend) 14 | } 15 | } 16 | 17 | func StartWorker(WorkQueue chan Job, backend *fs.Backend) { 18 | for { 19 | select { 20 | case j := <-WorkQueue: 21 | startTime := time.Now().UTC() 22 | 23 | if err := backend.GenerateThumbnail(j.Bin, j.Filename, 115, 115, true); err != nil { 24 | j.Log.Println(err.Error()) 25 | break 26 | } 27 | 28 | if err := backend.GenerateThumbnail(j.Bin, j.Filename, 1140, 0, false); err != nil { 29 | j.Log.Println(err.Error()) 30 | break 31 | } 32 | 33 | if j.Cfg.CacheInvalidation { 34 | links := backend.GenerateLinks(j.Bin, j.Filename) 35 | for _, l := range links { 36 | if err := shared.PurgeURL(l.Href, j.Log); err != nil { 37 | j.Log.Println(err) 38 | } 39 | } 40 | } 41 | 42 | finishTime := time.Now().UTC() 43 | elapsedTime := finishTime.Sub(startTime) 44 | j.Log.Println("Batch job completed: " + j.Bin + "/" + j.Filename + " (" + elapsedTime.String() + ")") 45 | } 46 | } 47 | } 48 | 49 | func randomString(n int) string { 50 | var letters = []rune("abcdefghijklmnopqrstuvwxyz0123456789") 51 | b := make([]rune, n) 52 | for i := range b { 53 | b[i] = letters[rand.Intn(len(letters))] 54 | } 55 | return string(b) 56 | } 57 | -------------------------------------------------------------------------------- /vendor/github.com/espebra/filebin/app/model/context.go: -------------------------------------------------------------------------------- 1 | package model 2 | 3 | import ( 4 | "github.com/GeertJohan/go.rice" 5 | "github.com/espebra/filebin/app/backend/fs" 6 | "github.com/espebra/filebin/app/config" 7 | "github.com/espebra/filebin/app/events" 8 | "github.com/espebra/filebin/app/metrics" 9 | "github.com/espebra/filebin/app/tokens" 10 | "log" 11 | ) 12 | 13 | type Job struct { 14 | Bin string 15 | Filename string 16 | Log *log.Logger 17 | Cfg *config.Configuration 18 | } 19 | 20 | type Context struct { 21 | TemplateBox *rice.Box 22 | StaticBox *rice.Box 23 | Baseurl string 24 | Log *log.Logger 25 | WorkQueue chan Job 26 | Backend *fs.Backend 27 | Metrics *metrics.Metrics 28 | Events *events.Events 29 | Tokens *tokens.Tokens 30 | Token string 31 | RemoteAddr string 32 | } 33 | -------------------------------------------------------------------------------- /vendor/github.com/espebra/filebin/app/model/link.go: -------------------------------------------------------------------------------- 1 | package model 2 | 3 | type Link struct { 4 | Rel string 5 | Href string 6 | } 7 | -------------------------------------------------------------------------------- /vendor/github.com/espebra/filebin/app/output/output.go: -------------------------------------------------------------------------------- 1 | package output 2 | 3 | import ( 4 | "encoding/json" 5 | "html/template" 6 | "io" 7 | "net/http" 8 | "strconv" 9 | 10 | "github.com/espebra/filebin/app/model" 11 | ) 12 | 13 | func JSONresponse(w http.ResponseWriter, status int, d interface{}, ctx model.Context) { 14 | dj, err := json.MarshalIndent(d, "", " ") 15 | if err != nil { 16 | ctx.Log.Println("Unable to convert response to json: ", err) 17 | http.Error(w, "Failed while generating a response", http.StatusInternalServerError) 18 | return 19 | } 20 | 21 | w.WriteHeader(status) 22 | ctx.Log.Println("Response status: " + strconv.Itoa(status)) 23 | io.WriteString(w, string(dj)) 24 | } 25 | 26 | // This function is a hack. Need to figure out a better way to do this. 27 | func HTMLresponse(w http.ResponseWriter, tpl string, status int, d interface{}, ctx model.Context) { 28 | box := ctx.TemplateBox 29 | t := template.New(tpl) 30 | 31 | var templateString string 32 | var err error 33 | 34 | templateString, err = box.String(tpl + ".html") 35 | if err != nil { 36 | ctx.Log.Fatalln(err) 37 | } 38 | t, err = t.Parse(templateString) 39 | if err != nil { 40 | ctx.Log.Fatalln(err) 41 | } 42 | 43 | w.WriteHeader(status) 44 | ctx.Log.Println("Response status: " + strconv.Itoa(status)) 45 | 46 | // To send multiple structs to the template 47 | err = t.Execute(w, map[string]interface{}{ 48 | "Data": d, 49 | "Ctx": ctx, 50 | }) 51 | if err != nil { 52 | ctx.Log.Panicln(err) 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /vendor/github.com/espebra/filebin/app/shared/shared.go: -------------------------------------------------------------------------------- 1 | package shared 2 | 3 | import ( 4 | "log" 5 | "net/http" 6 | "time" 7 | ) 8 | 9 | func PurgeURL(url string, log *log.Logger) error { 10 | timeout := time.Duration(2 * time.Second) 11 | client := &http.Client{ 12 | Timeout: timeout, 13 | } 14 | 15 | // Invalidate the file 16 | req, err := http.NewRequest("PURGE", url, nil) 17 | if err != nil { 18 | log.Println("Unable to purge " + url + ": " + err.Error()) 19 | return err 20 | } 21 | 22 | resp, err := client.Do(req) 23 | if err == nil { 24 | defer resp.Body.Close() 25 | } 26 | 27 | if err != nil { 28 | log.Println("Unable to purge " + url + ": " + err.Error()) 29 | return err 30 | } 31 | 32 | log.Println("Purging " + url + ": " + resp.Status) 33 | 34 | return nil 35 | } 36 | -------------------------------------------------------------------------------- /vendor/github.com/espebra/filebin/app/tokens/token.go: -------------------------------------------------------------------------------- 1 | package tokens 2 | 3 | import ( 4 | "github.com/dustin/go-humanize" 5 | "math/rand" 6 | "sync" 7 | "time" 8 | "log" 9 | ) 10 | 11 | type Token struct { 12 | ValidTo time.Time 13 | ExpiresReadable string 14 | Id string 15 | VerifiedCount int 16 | } 17 | 18 | type Tokens struct { 19 | sync.RWMutex 20 | tokens []Token 21 | } 22 | 23 | func Init() Tokens { 24 | t := Tokens{} 25 | return t 26 | } 27 | 28 | func (t *Tokens) Generate() string { 29 | t.Cleanup() 30 | 31 | var token Token 32 | token.Id = RandomString(8) 33 | now := time.Now().UTC() 34 | token.ValidTo = now.Add(5 * time.Minute) 35 | 36 | t.Lock() 37 | t.tokens = append([]Token{token}, t.tokens...) 38 | t.Unlock() 39 | return token.Id 40 | } 41 | 42 | func (t *Tokens) Verify(token string) bool { 43 | t.Lock() 44 | found := false 45 | now := time.Now().UTC() 46 | for i, data := range t.tokens { 47 | if data.Id == token { 48 | if now.Before(data.ValidTo) { 49 | t.tokens[i].VerifiedCount = t.tokens[i].VerifiedCount + 1 50 | found = true 51 | } 52 | } 53 | } 54 | t.Unlock() 55 | return found 56 | } 57 | 58 | func (t *Tokens) Cleanup() { 59 | var valid []Token 60 | t.Lock() 61 | if len(t.tokens) > 500 { 62 | now := time.Now().UTC() 63 | for _, data := range t.tokens { 64 | if now.Before(data.ValidTo) { 65 | valid = append(valid, data) 66 | } 67 | } 68 | before := len(t.tokens) 69 | t.tokens = valid 70 | after := len(t.tokens) 71 | log.Println("Token clean up:", before-after, "tokens have been removed.") 72 | } 73 | t.Unlock() 74 | 75 | } 76 | 77 | func (t *Tokens) GetAllTokens() []Token { 78 | t.RLock() 79 | defer t.RUnlock() 80 | 81 | var r []Token 82 | for _, data := range t.tokens { 83 | data.ExpiresReadable = humanize.Time(data.ValidTo) 84 | r = append(r, data) 85 | } 86 | return r 87 | } 88 | 89 | func RandomString(n int) string { 90 | var letters = []rune("abcdefghijklmnopqrstuvwxyz0123456789") 91 | b := make([]rune, n) 92 | for i := range b { 93 | b[i] = letters[rand.Intn(len(letters))] 94 | } 95 | return string(b) 96 | } 97 | -------------------------------------------------------------------------------- /vendor/github.com/gorilla/handlers/LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2013 The Gorilla Handlers Authors. All rights reserved. 2 | 3 | Redistribution and use in source and binary forms, with or without 4 | modification, are permitted provided that the following conditions are met: 5 | 6 | Redistributions of source code must retain the above copyright notice, this 7 | list of conditions and the following disclaimer. 8 | 9 | Redistributions in binary form must reproduce the above copyright notice, 10 | this list of conditions and the following disclaimer in the documentation 11 | and/or other materials provided with the distribution. 12 | 13 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 14 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 15 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 16 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 17 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 18 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 19 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 20 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 21 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 22 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 23 | -------------------------------------------------------------------------------- /vendor/github.com/gorilla/handlers/README.md: -------------------------------------------------------------------------------- 1 | gorilla/handlers 2 | ================ 3 | [![GoDoc](https://godoc.org/github.com/gorilla/handlers?status.svg)](https://godoc.org/github.com/gorilla/handlers) 4 | [![CircleCI](https://circleci.com/gh/gorilla/handlers.svg?style=svg)](https://circleci.com/gh/gorilla/handlers) 5 | [![Sourcegraph](https://sourcegraph.com/github.com/gorilla/handlers/-/badge.svg)](https://sourcegraph.com/github.com/gorilla/handlers?badge) 6 | 7 | 8 | Package handlers is a collection of handlers (aka "HTTP middleware") for use 9 | with Go's `net/http` package (or any framework supporting `http.Handler`), including: 10 | 11 | * [**LoggingHandler**](https://godoc.org/github.com/gorilla/handlers#LoggingHandler) for logging HTTP requests in the Apache [Common Log 12 | Format](http://httpd.apache.org/docs/2.2/logs.html#common). 13 | * [**CombinedLoggingHandler**](https://godoc.org/github.com/gorilla/handlers#CombinedLoggingHandler) for logging HTTP requests in the Apache [Combined Log 14 | Format](http://httpd.apache.org/docs/2.2/logs.html#combined) commonly used by 15 | both Apache and nginx. 16 | * [**CompressHandler**](https://godoc.org/github.com/gorilla/handlers#CompressHandler) for gzipping responses. 17 | * [**ContentTypeHandler**](https://godoc.org/github.com/gorilla/handlers#ContentTypeHandler) for validating requests against a list of accepted 18 | content types. 19 | * [**MethodHandler**](https://godoc.org/github.com/gorilla/handlers#MethodHandler) for matching HTTP methods against handlers in a 20 | `map[string]http.Handler` 21 | * [**ProxyHeaders**](https://godoc.org/github.com/gorilla/handlers#ProxyHeaders) for populating `r.RemoteAddr` and `r.URL.Scheme` based on the 22 | `X-Forwarded-For`, `X-Real-IP`, `X-Forwarded-Proto` and RFC7239 `Forwarded` 23 | headers when running a Go server behind a HTTP reverse proxy. 24 | * [**CanonicalHost**](https://godoc.org/github.com/gorilla/handlers#CanonicalHost) for re-directing to the preferred host when handling multiple 25 | domains (i.e. multiple CNAME aliases). 26 | * [**RecoveryHandler**](https://godoc.org/github.com/gorilla/handlers#RecoveryHandler) for recovering from unexpected panics. 27 | 28 | Other handlers are documented [on the Gorilla 29 | website](https://www.gorillatoolkit.org/pkg/handlers). 30 | 31 | ## Example 32 | 33 | A simple example using `handlers.LoggingHandler` and `handlers.CompressHandler`: 34 | 35 | ```go 36 | import ( 37 | "net/http" 38 | "github.com/gorilla/handlers" 39 | ) 40 | 41 | func main() { 42 | r := http.NewServeMux() 43 | 44 | // Only log requests to our admin dashboard to stdout 45 | r.Handle("/admin", handlers.LoggingHandler(os.Stdout, http.HandlerFunc(ShowAdminDashboard))) 46 | r.HandleFunc("/", ShowIndex) 47 | 48 | // Wrap our server with our gzip handler to gzip compress all responses. 49 | http.ListenAndServe(":8000", handlers.CompressHandler(r)) 50 | } 51 | ``` 52 | 53 | ## License 54 | 55 | BSD licensed. See the included LICENSE file for details. 56 | 57 | -------------------------------------------------------------------------------- /vendor/github.com/gorilla/handlers/canonical.go: -------------------------------------------------------------------------------- 1 | package handlers 2 | 3 | import ( 4 | "net/http" 5 | "net/url" 6 | "strings" 7 | ) 8 | 9 | type canonical struct { 10 | h http.Handler 11 | domain string 12 | code int 13 | } 14 | 15 | // CanonicalHost is HTTP middleware that re-directs requests to the canonical 16 | // domain. It accepts a domain and a status code (e.g. 301 or 302) and 17 | // re-directs clients to this domain. The existing request path is maintained. 18 | // 19 | // Note: If the provided domain is considered invalid by url.Parse or otherwise 20 | // returns an empty scheme or host, clients are not re-directed. 21 | // 22 | // Example: 23 | // 24 | // r := mux.NewRouter() 25 | // canonical := handlers.CanonicalHost("http://www.gorillatoolkit.org", 302) 26 | // r.HandleFunc("/route", YourHandler) 27 | // 28 | // log.Fatal(http.ListenAndServe(":7000", canonical(r))) 29 | // 30 | func CanonicalHost(domain string, code int) func(h http.Handler) http.Handler { 31 | fn := func(h http.Handler) http.Handler { 32 | return canonical{h, domain, code} 33 | } 34 | 35 | return fn 36 | } 37 | 38 | func (c canonical) ServeHTTP(w http.ResponseWriter, r *http.Request) { 39 | dest, err := url.Parse(c.domain) 40 | if err != nil { 41 | // Call the next handler if the provided domain fails to parse. 42 | c.h.ServeHTTP(w, r) 43 | return 44 | } 45 | 46 | if dest.Scheme == "" || dest.Host == "" { 47 | // Call the next handler if the scheme or host are empty. 48 | // Note that url.Parse won't fail on in this case. 49 | c.h.ServeHTTP(w, r) 50 | return 51 | } 52 | 53 | if !strings.EqualFold(cleanHost(r.Host), dest.Host) { 54 | // Re-build the destination URL 55 | dest := dest.Scheme + "://" + dest.Host + r.URL.Path 56 | if r.URL.RawQuery != "" { 57 | dest += "?" + r.URL.RawQuery 58 | } 59 | http.Redirect(w, r, dest, c.code) 60 | return 61 | } 62 | 63 | c.h.ServeHTTP(w, r) 64 | } 65 | 66 | // cleanHost cleans invalid Host headers by stripping anything after '/' or ' '. 67 | // This is backported from Go 1.5 (in response to issue #11206) and attempts to 68 | // mitigate malformed Host headers that do not match the format in RFC7230. 69 | func cleanHost(in string) string { 70 | if i := strings.IndexAny(in, " /"); i != -1 { 71 | return in[:i] 72 | } 73 | return in 74 | } 75 | -------------------------------------------------------------------------------- /vendor/github.com/gorilla/handlers/compress.go: -------------------------------------------------------------------------------- 1 | // Copyright 2013 The Gorilla Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package handlers 6 | 7 | import ( 8 | "compress/flate" 9 | "compress/gzip" 10 | "io" 11 | "net/http" 12 | "strings" 13 | ) 14 | 15 | type compressResponseWriter struct { 16 | io.Writer 17 | http.ResponseWriter 18 | http.Hijacker 19 | http.Flusher 20 | http.CloseNotifier 21 | } 22 | 23 | func (w *compressResponseWriter) WriteHeader(c int) { 24 | w.ResponseWriter.Header().Del("Content-Length") 25 | w.ResponseWriter.WriteHeader(c) 26 | } 27 | 28 | func (w *compressResponseWriter) Header() http.Header { 29 | return w.ResponseWriter.Header() 30 | } 31 | 32 | func (w *compressResponseWriter) Write(b []byte) (int, error) { 33 | h := w.ResponseWriter.Header() 34 | if h.Get("Content-Type") == "" { 35 | h.Set("Content-Type", http.DetectContentType(b)) 36 | } 37 | h.Del("Content-Length") 38 | 39 | return w.Writer.Write(b) 40 | } 41 | 42 | type flusher interface { 43 | Flush() error 44 | } 45 | 46 | func (w *compressResponseWriter) Flush() { 47 | // Flush compressed data if compressor supports it. 48 | if f, ok := w.Writer.(flusher); ok { 49 | f.Flush() 50 | } 51 | // Flush HTTP response. 52 | if w.Flusher != nil { 53 | w.Flusher.Flush() 54 | } 55 | } 56 | 57 | // CompressHandler gzip compresses HTTP responses for clients that support it 58 | // via the 'Accept-Encoding' header. 59 | // 60 | // Compressing TLS traffic may leak the page contents to an attacker if the 61 | // page contains user input: http://security.stackexchange.com/a/102015/12208 62 | func CompressHandler(h http.Handler) http.Handler { 63 | return CompressHandlerLevel(h, gzip.DefaultCompression) 64 | } 65 | 66 | // CompressHandlerLevel gzip compresses HTTP responses with specified compression level 67 | // for clients that support it via the 'Accept-Encoding' header. 68 | // 69 | // The compression level should be gzip.DefaultCompression, gzip.NoCompression, 70 | // or any integer value between gzip.BestSpeed and gzip.BestCompression inclusive. 71 | // gzip.DefaultCompression is used in case of invalid compression level. 72 | func CompressHandlerLevel(h http.Handler, level int) http.Handler { 73 | if level < gzip.DefaultCompression || level > gzip.BestCompression { 74 | level = gzip.DefaultCompression 75 | } 76 | 77 | return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 78 | L: 79 | for _, enc := range strings.Split(r.Header.Get("Accept-Encoding"), ",") { 80 | switch strings.TrimSpace(enc) { 81 | case "gzip": 82 | w.Header().Set("Content-Encoding", "gzip") 83 | r.Header.Del("Accept-Encoding") 84 | w.Header().Add("Vary", "Accept-Encoding") 85 | 86 | gw, _ := gzip.NewWriterLevel(w, level) 87 | defer gw.Close() 88 | 89 | h, hok := w.(http.Hijacker) 90 | if !hok { /* w is not Hijacker... oh well... */ 91 | h = nil 92 | } 93 | 94 | f, fok := w.(http.Flusher) 95 | if !fok { 96 | f = nil 97 | } 98 | 99 | cn, cnok := w.(http.CloseNotifier) 100 | if !cnok { 101 | cn = nil 102 | } 103 | 104 | w = &compressResponseWriter{ 105 | Writer: gw, 106 | ResponseWriter: w, 107 | Hijacker: h, 108 | Flusher: f, 109 | CloseNotifier: cn, 110 | } 111 | 112 | break L 113 | case "deflate": 114 | w.Header().Set("Content-Encoding", "deflate") 115 | r.Header.Del("Accept-Encoding") 116 | w.Header().Add("Vary", "Accept-Encoding") 117 | 118 | fw, _ := flate.NewWriter(w, level) 119 | defer fw.Close() 120 | 121 | h, hok := w.(http.Hijacker) 122 | if !hok { /* w is not Hijacker... oh well... */ 123 | h = nil 124 | } 125 | 126 | f, fok := w.(http.Flusher) 127 | if !fok { 128 | f = nil 129 | } 130 | 131 | cn, cnok := w.(http.CloseNotifier) 132 | if !cnok { 133 | cn = nil 134 | } 135 | 136 | w = &compressResponseWriter{ 137 | Writer: fw, 138 | ResponseWriter: w, 139 | Hijacker: h, 140 | Flusher: f, 141 | CloseNotifier: cn, 142 | } 143 | 144 | break L 145 | } 146 | } 147 | 148 | h.ServeHTTP(w, r) 149 | }) 150 | } 151 | -------------------------------------------------------------------------------- /vendor/github.com/gorilla/handlers/doc.go: -------------------------------------------------------------------------------- 1 | /* 2 | Package handlers is a collection of handlers (aka "HTTP middleware") for use 3 | with Go's net/http package (or any framework supporting http.Handler). 4 | 5 | The package includes handlers for logging in standardised formats, compressing 6 | HTTP responses, validating content types and other useful tools for manipulating 7 | requests and responses. 8 | */ 9 | package handlers 10 | -------------------------------------------------------------------------------- /vendor/github.com/gorilla/handlers/go.mod: -------------------------------------------------------------------------------- 1 | module github.com/gorilla/handlers 2 | -------------------------------------------------------------------------------- /vendor/github.com/gorilla/handlers/handlers_go18.go: -------------------------------------------------------------------------------- 1 | // +build go1.8 2 | 3 | package handlers 4 | 5 | import ( 6 | "fmt" 7 | "net/http" 8 | ) 9 | 10 | type loggingResponseWriter interface { 11 | commonLoggingResponseWriter 12 | http.Pusher 13 | } 14 | 15 | func (l *responseLogger) Push(target string, opts *http.PushOptions) error { 16 | p, ok := l.w.(http.Pusher) 17 | if !ok { 18 | return fmt.Errorf("responseLogger does not implement http.Pusher") 19 | } 20 | return p.Push(target, opts) 21 | } 22 | 23 | func (c *compressResponseWriter) Push(target string, opts *http.PushOptions) error { 24 | p, ok := c.ResponseWriter.(http.Pusher) 25 | if !ok { 26 | return fmt.Errorf("compressResponseWriter does not implement http.Pusher") 27 | } 28 | return p.Push(target, opts) 29 | } 30 | -------------------------------------------------------------------------------- /vendor/github.com/gorilla/handlers/handlers_pre18.go: -------------------------------------------------------------------------------- 1 | // +build !go1.8 2 | 3 | package handlers 4 | 5 | type loggingResponseWriter interface { 6 | commonLoggingResponseWriter 7 | } 8 | -------------------------------------------------------------------------------- /vendor/github.com/gorilla/handlers/proxy_headers.go: -------------------------------------------------------------------------------- 1 | package handlers 2 | 3 | import ( 4 | "net/http" 5 | "regexp" 6 | "strings" 7 | ) 8 | 9 | var ( 10 | // De-facto standard header keys. 11 | xForwardedFor = http.CanonicalHeaderKey("X-Forwarded-For") 12 | xForwardedHost = http.CanonicalHeaderKey("X-Forwarded-Host") 13 | xForwardedProto = http.CanonicalHeaderKey("X-Forwarded-Proto") 14 | xForwardedScheme = http.CanonicalHeaderKey("X-Forwarded-Scheme") 15 | xRealIP = http.CanonicalHeaderKey("X-Real-IP") 16 | ) 17 | 18 | var ( 19 | // RFC7239 defines a new "Forwarded: " header designed to replace the 20 | // existing use of X-Forwarded-* headers. 21 | // e.g. Forwarded: for=192.0.2.60;proto=https;by=203.0.113.43 22 | forwarded = http.CanonicalHeaderKey("Forwarded") 23 | // Allows for a sub-match of the first value after 'for=' to the next 24 | // comma, semi-colon or space. The match is case-insensitive. 25 | forRegex = regexp.MustCompile(`(?i)(?:for=)([^(;|,| )]+)`) 26 | // Allows for a sub-match for the first instance of scheme (http|https) 27 | // prefixed by 'proto='. The match is case-insensitive. 28 | protoRegex = regexp.MustCompile(`(?i)(?:proto=)(https|http)`) 29 | ) 30 | 31 | // ProxyHeaders inspects common reverse proxy headers and sets the corresponding 32 | // fields in the HTTP request struct. These are X-Forwarded-For and X-Real-IP 33 | // for the remote (client) IP address, X-Forwarded-Proto or X-Forwarded-Scheme 34 | // for the scheme (http|https), X-Forwarded-Host for the host and the RFC7239 35 | // Forwarded header, which may include both client IPs and schemes. 36 | // 37 | // NOTE: This middleware should only be used when behind a reverse 38 | // proxy like nginx, HAProxy or Apache. Reverse proxies that don't (or are 39 | // configured not to) strip these headers from client requests, or where these 40 | // headers are accepted "as is" from a remote client (e.g. when Go is not behind 41 | // a proxy), can manifest as a vulnerability if your application uses these 42 | // headers for validating the 'trustworthiness' of a request. 43 | func ProxyHeaders(h http.Handler) http.Handler { 44 | fn := func(w http.ResponseWriter, r *http.Request) { 45 | // Set the remote IP with the value passed from the proxy. 46 | if fwd := getIP(r); fwd != "" { 47 | r.RemoteAddr = fwd 48 | } 49 | 50 | // Set the scheme (proto) with the value passed from the proxy. 51 | if scheme := getScheme(r); scheme != "" { 52 | r.URL.Scheme = scheme 53 | } 54 | // Set the host with the value passed by the proxy 55 | if r.Header.Get(xForwardedHost) != "" { 56 | r.Host = r.Header.Get(xForwardedHost) 57 | } 58 | // Call the next handler in the chain. 59 | h.ServeHTTP(w, r) 60 | } 61 | 62 | return http.HandlerFunc(fn) 63 | } 64 | 65 | // getIP retrieves the IP from the X-Forwarded-For, X-Real-IP and RFC7239 66 | // Forwarded headers (in that order). 67 | func getIP(r *http.Request) string { 68 | var addr string 69 | 70 | if fwd := r.Header.Get(xForwardedFor); fwd != "" { 71 | // Only grab the first (client) address. Note that '192.168.0.1, 72 | // 10.1.1.1' is a valid key for X-Forwarded-For where addresses after 73 | // the first may represent forwarding proxies earlier in the chain. 74 | s := strings.Index(fwd, ", ") 75 | if s == -1 { 76 | s = len(fwd) 77 | } 78 | addr = fwd[:s] 79 | } else if fwd := r.Header.Get(xRealIP); fwd != "" { 80 | // X-Real-IP should only contain one IP address (the client making the 81 | // request). 82 | addr = fwd 83 | } else if fwd := r.Header.Get(forwarded); fwd != "" { 84 | // match should contain at least two elements if the protocol was 85 | // specified in the Forwarded header. The first element will always be 86 | // the 'for=' capture, which we ignore. In the case of multiple IP 87 | // addresses (for=8.8.8.8, 8.8.4.4,172.16.1.20 is valid) we only 88 | // extract the first, which should be the client IP. 89 | if match := forRegex.FindStringSubmatch(fwd); len(match) > 1 { 90 | // IPv6 addresses in Forwarded headers are quoted-strings. We strip 91 | // these quotes. 92 | addr = strings.Trim(match[1], `"`) 93 | } 94 | } 95 | 96 | return addr 97 | } 98 | 99 | // getScheme retrieves the scheme from the X-Forwarded-Proto and RFC7239 100 | // Forwarded headers (in that order). 101 | func getScheme(r *http.Request) string { 102 | var scheme string 103 | 104 | // Retrieve the scheme from X-Forwarded-Proto. 105 | if proto := r.Header.Get(xForwardedProto); proto != "" { 106 | scheme = strings.ToLower(proto) 107 | } else if proto = r.Header.Get(xForwardedScheme); proto != "" { 108 | scheme = strings.ToLower(proto) 109 | } else if proto = r.Header.Get(forwarded); proto != "" { 110 | // match should contain at least two elements if the protocol was 111 | // specified in the Forwarded header. The first element will always be 112 | // the 'proto=' capture, which we ignore. In the case of multiple proto 113 | // parameters (invalid) we only extract the first. 114 | if match := protoRegex.FindStringSubmatch(proto); len(match) > 1 { 115 | scheme = strings.ToLower(match[1]) 116 | } 117 | } 118 | 119 | return scheme 120 | } 121 | -------------------------------------------------------------------------------- /vendor/github.com/gorilla/handlers/recovery.go: -------------------------------------------------------------------------------- 1 | package handlers 2 | 3 | import ( 4 | "log" 5 | "net/http" 6 | "runtime/debug" 7 | ) 8 | 9 | // RecoveryHandlerLogger is an interface used by the recovering handler to print logs. 10 | type RecoveryHandlerLogger interface { 11 | Println(...interface{}) 12 | } 13 | 14 | type recoveryHandler struct { 15 | handler http.Handler 16 | logger RecoveryHandlerLogger 17 | printStack bool 18 | } 19 | 20 | // RecoveryOption provides a functional approach to define 21 | // configuration for a handler; such as setting the logging 22 | // whether or not to print strack traces on panic. 23 | type RecoveryOption func(http.Handler) 24 | 25 | func parseRecoveryOptions(h http.Handler, opts ...RecoveryOption) http.Handler { 26 | for _, option := range opts { 27 | option(h) 28 | } 29 | 30 | return h 31 | } 32 | 33 | // RecoveryHandler is HTTP middleware that recovers from a panic, 34 | // logs the panic, writes http.StatusInternalServerError, and 35 | // continues to the next handler. 36 | // 37 | // Example: 38 | // 39 | // r := mux.NewRouter() 40 | // r.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { 41 | // panic("Unexpected error!") 42 | // }) 43 | // 44 | // http.ListenAndServe(":1123", handlers.RecoveryHandler()(r)) 45 | func RecoveryHandler(opts ...RecoveryOption) func(h http.Handler) http.Handler { 46 | return func(h http.Handler) http.Handler { 47 | r := &recoveryHandler{handler: h} 48 | return parseRecoveryOptions(r, opts...) 49 | } 50 | } 51 | 52 | // RecoveryLogger is a functional option to override 53 | // the default logger 54 | func RecoveryLogger(logger RecoveryHandlerLogger) RecoveryOption { 55 | return func(h http.Handler) { 56 | r := h.(*recoveryHandler) 57 | r.logger = logger 58 | } 59 | } 60 | 61 | // PrintRecoveryStack is a functional option to enable 62 | // or disable printing stack traces on panic. 63 | func PrintRecoveryStack(print bool) RecoveryOption { 64 | return func(h http.Handler) { 65 | r := h.(*recoveryHandler) 66 | r.printStack = print 67 | } 68 | } 69 | 70 | func (h recoveryHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) { 71 | defer func() { 72 | if err := recover(); err != nil { 73 | w.WriteHeader(http.StatusInternalServerError) 74 | h.log(err) 75 | } 76 | }() 77 | 78 | h.handler.ServeHTTP(w, req) 79 | } 80 | 81 | func (h recoveryHandler) log(v ...interface{}) { 82 | if h.logger != nil { 83 | h.logger.Println(v...) 84 | } else { 85 | log.Println(v...) 86 | } 87 | 88 | if h.printStack { 89 | debug.PrintStack() 90 | } 91 | } 92 | -------------------------------------------------------------------------------- /vendor/github.com/gorilla/mux/AUTHORS: -------------------------------------------------------------------------------- 1 | # This is the official list of gorilla/mux authors for copyright purposes. 2 | # 3 | # Please keep the list sorted. 4 | 5 | Google LLC (https://opensource.google.com/) 6 | Kamil Kisielk 7 | Matt Silverlock 8 | Rodrigo Moraes (https://github.com/moraes) 9 | -------------------------------------------------------------------------------- /vendor/github.com/gorilla/mux/LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2012-2018 The Gorilla Authors. All rights reserved. 2 | 3 | Redistribution and use in source and binary forms, with or without 4 | modification, are permitted provided that the following conditions are 5 | met: 6 | 7 | * Redistributions of source code must retain the above copyright 8 | notice, this list of conditions and the following disclaimer. 9 | * Redistributions in binary form must reproduce the above 10 | copyright notice, this list of conditions and the following disclaimer 11 | in the documentation and/or other materials provided with the 12 | distribution. 13 | * Neither the name of Google Inc. nor the names of its 14 | contributors may be used to endorse or promote products derived from 15 | this software without specific prior written permission. 16 | 17 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 18 | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 19 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 20 | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 21 | OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 22 | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 23 | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 24 | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 25 | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 26 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 27 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 | -------------------------------------------------------------------------------- /vendor/github.com/gorilla/mux/context.go: -------------------------------------------------------------------------------- 1 | package mux 2 | 3 | import ( 4 | "context" 5 | "net/http" 6 | ) 7 | 8 | func contextGet(r *http.Request, key interface{}) interface{} { 9 | return r.Context().Value(key) 10 | } 11 | 12 | func contextSet(r *http.Request, key, val interface{}) *http.Request { 13 | if val == nil { 14 | return r 15 | } 16 | 17 | return r.WithContext(context.WithValue(r.Context(), key, val)) 18 | } 19 | -------------------------------------------------------------------------------- /vendor/github.com/gorilla/mux/go.mod: -------------------------------------------------------------------------------- 1 | module github.com/gorilla/mux 2 | -------------------------------------------------------------------------------- /vendor/github.com/gorilla/mux/middleware.go: -------------------------------------------------------------------------------- 1 | package mux 2 | 3 | import ( 4 | "net/http" 5 | "strings" 6 | ) 7 | 8 | // MiddlewareFunc is a function which receives an http.Handler and returns another http.Handler. 9 | // Typically, the returned handler is a closure which does something with the http.ResponseWriter and http.Request passed 10 | // to it, and then calls the handler passed as parameter to the MiddlewareFunc. 11 | type MiddlewareFunc func(http.Handler) http.Handler 12 | 13 | // middleware interface is anything which implements a MiddlewareFunc named Middleware. 14 | type middleware interface { 15 | Middleware(handler http.Handler) http.Handler 16 | } 17 | 18 | // Middleware allows MiddlewareFunc to implement the middleware interface. 19 | func (mw MiddlewareFunc) Middleware(handler http.Handler) http.Handler { 20 | return mw(handler) 21 | } 22 | 23 | // Use appends a MiddlewareFunc to the chain. Middleware can be used to intercept or otherwise modify requests and/or responses, and are executed in the order that they are applied to the Router. 24 | func (r *Router) Use(mwf ...MiddlewareFunc) { 25 | for _, fn := range mwf { 26 | r.middlewares = append(r.middlewares, fn) 27 | } 28 | } 29 | 30 | // useInterface appends a middleware to the chain. Middleware can be used to intercept or otherwise modify requests and/or responses, and are executed in the order that they are applied to the Router. 31 | func (r *Router) useInterface(mw middleware) { 32 | r.middlewares = append(r.middlewares, mw) 33 | } 34 | 35 | // CORSMethodMiddleware automatically sets the Access-Control-Allow-Methods response header 36 | // on requests for routes that have an OPTIONS method matcher to all the method matchers on 37 | // the route. Routes that do not explicitly handle OPTIONS requests will not be processed 38 | // by the middleware. See examples for usage. 39 | func CORSMethodMiddleware(r *Router) MiddlewareFunc { 40 | return func(next http.Handler) http.Handler { 41 | return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) { 42 | allMethods, err := getAllMethodsForRoute(r, req) 43 | if err == nil { 44 | for _, v := range allMethods { 45 | if v == http.MethodOptions { 46 | w.Header().Set("Access-Control-Allow-Methods", strings.Join(allMethods, ",")) 47 | } 48 | } 49 | } 50 | 51 | next.ServeHTTP(w, req) 52 | }) 53 | } 54 | } 55 | 56 | // getAllMethodsForRoute returns all the methods from method matchers matching a given 57 | // request. 58 | func getAllMethodsForRoute(r *Router, req *http.Request) ([]string, error) { 59 | var allMethods []string 60 | 61 | err := r.Walk(func(route *Route, _ *Router, _ []*Route) error { 62 | for _, m := range route.matchers { 63 | if _, ok := m.(*routeRegexp); ok { 64 | if m.Match(req, &RouteMatch{}) { 65 | methods, err := route.GetMethods() 66 | if err != nil { 67 | return err 68 | } 69 | 70 | allMethods = append(allMethods, methods...) 71 | } 72 | break 73 | } 74 | } 75 | return nil 76 | }) 77 | 78 | return allMethods, err 79 | } 80 | -------------------------------------------------------------------------------- /vendor/github.com/gorilla/mux/test_helpers.go: -------------------------------------------------------------------------------- 1 | // Copyright 2012 The Gorilla Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package mux 6 | 7 | import "net/http" 8 | 9 | // SetURLVars sets the URL variables for the given request, to be accessed via 10 | // mux.Vars for testing route behaviour. Arguments are not modified, a shallow 11 | // copy is returned. 12 | // 13 | // This API should only be used for testing purposes; it provides a way to 14 | // inject variables into the request context. Alternatively, URL variables 15 | // can be set by making a route that captures the required variables, 16 | // starting a server and sending the request to that server. 17 | func SetURLVars(r *http.Request, val map[string]string) *http.Request { 18 | return setVars(r, val) 19 | } 20 | -------------------------------------------------------------------------------- /vendor/github.com/rwcarlsen/goexif/LICENSE: -------------------------------------------------------------------------------- 1 | 2 | Copyright (c) 2012, Robert Carlsen & Contributors 3 | All rights reserved. 4 | 5 | Redistribution and use in source and binary forms, with or without 6 | modification, are permitted provided that the following conditions are met: 7 | 8 | * Redistributions of source code must retain the above copyright notice, this 9 | list of conditions and the following disclaimer. 10 | 11 | * Redistributions in binary form must reproduce the above copyright notice, 12 | this list of conditions and the following disclaimer in the documentation 13 | and/or other materials provided with the distribution. 14 | 15 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 16 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 17 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 18 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 19 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 20 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 21 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 22 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 23 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 24 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 25 | -------------------------------------------------------------------------------- /vendor/github.com/rwcarlsen/goexif/exif/README.md: -------------------------------------------------------------------------------- 1 | 2 | To regenerate the regression test data, run `go generate` inside the exif 3 | package directory and commit the changes to *regress_expected_test.go*. 4 | 5 | -------------------------------------------------------------------------------- /vendor/github.com/rwcarlsen/goexif/exif/sample1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/espebra/filebin/a152fedd40a578eae20f91ac313852bb61ed4462/vendor/github.com/rwcarlsen/goexif/exif/sample1.jpg -------------------------------------------------------------------------------- /vendor/github.com/rwcarlsen/goexif/tiff/sample1.tif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/espebra/filebin/a152fedd40a578eae20f91ac313852bb61ed4462/vendor/github.com/rwcarlsen/goexif/tiff/sample1.tif -------------------------------------------------------------------------------- /vendor/github.com/rwcarlsen/goexif/tiff/tiff.go: -------------------------------------------------------------------------------- 1 | // Package tiff implements TIFF decoding as defined in TIFF 6.0 specification at 2 | // http://partners.adobe.com/public/developer/en/tiff/TIFF6.pdf 3 | package tiff 4 | 5 | import ( 6 | "bytes" 7 | "encoding/binary" 8 | "errors" 9 | "fmt" 10 | "io" 11 | "io/ioutil" 12 | ) 13 | 14 | // ReadAtReader is used when decoding Tiff tags and directories 15 | type ReadAtReader interface { 16 | io.Reader 17 | io.ReaderAt 18 | } 19 | 20 | // Tiff provides access to a decoded tiff data structure. 21 | type Tiff struct { 22 | // Dirs is an ordered slice of the tiff's Image File Directories (IFDs). 23 | // The IFD at index 0 is IFD0. 24 | Dirs []*Dir 25 | // The tiff's byte-encoding (i.e. big/little endian). 26 | Order binary.ByteOrder 27 | } 28 | 29 | // Decode parses tiff-encoded data from r and returns a Tiff struct that 30 | // reflects the structure and content of the tiff data. The first read from r 31 | // should be the first byte of the tiff-encoded data and not necessarily the 32 | // first byte of an os.File object. 33 | func Decode(r io.Reader) (*Tiff, error) { 34 | data, err := ioutil.ReadAll(r) 35 | if err != nil { 36 | return nil, errors.New("tiff: could not read data") 37 | } 38 | buf := bytes.NewReader(data) 39 | 40 | t := new(Tiff) 41 | 42 | // read byte order 43 | bo := make([]byte, 2) 44 | if _, err = io.ReadFull(buf, bo); err != nil { 45 | return nil, errors.New("tiff: could not read tiff byte order") 46 | } 47 | if string(bo) == "II" { 48 | t.Order = binary.LittleEndian 49 | } else if string(bo) == "MM" { 50 | t.Order = binary.BigEndian 51 | } else { 52 | return nil, errors.New("tiff: could not read tiff byte order") 53 | } 54 | 55 | // check for special tiff marker 56 | var sp int16 57 | err = binary.Read(buf, t.Order, &sp) 58 | if err != nil || 42 != sp { 59 | return nil, errors.New("tiff: could not find special tiff marker") 60 | } 61 | 62 | // load offset to first IFD 63 | var offset int32 64 | err = binary.Read(buf, t.Order, &offset) 65 | if err != nil { 66 | return nil, errors.New("tiff: could not read offset to first IFD") 67 | } 68 | 69 | // load IFD's 70 | var d *Dir 71 | prev := offset 72 | for offset != 0 { 73 | // seek to offset 74 | _, err := buf.Seek(int64(offset), 0) 75 | if err != nil { 76 | return nil, errors.New("tiff: seek to IFD failed") 77 | } 78 | 79 | if buf.Len() == 0 { 80 | return nil, errors.New("tiff: seek offset after EOF") 81 | } 82 | 83 | // load the dir 84 | d, offset, err = DecodeDir(buf, t.Order) 85 | if err != nil { 86 | return nil, err 87 | } 88 | 89 | if offset == prev { 90 | return nil, errors.New("tiff: recursive IFD") 91 | } 92 | prev = offset 93 | 94 | t.Dirs = append(t.Dirs, d) 95 | } 96 | 97 | return t, nil 98 | } 99 | 100 | func (tf *Tiff) String() string { 101 | var buf bytes.Buffer 102 | fmt.Fprint(&buf, "Tiff{") 103 | for _, d := range tf.Dirs { 104 | fmt.Fprintf(&buf, "%s, ", d.String()) 105 | } 106 | fmt.Fprintf(&buf, "}") 107 | return buf.String() 108 | } 109 | 110 | // Dir provides access to the parsed content of a tiff Image File Directory (IFD). 111 | type Dir struct { 112 | Tags []*Tag 113 | } 114 | 115 | // DecodeDir parses a tiff-encoded IFD from r and returns a Dir object. offset 116 | // is the offset to the next IFD. The first read from r should be at the first 117 | // byte of the IFD. ReadAt offsets should generally be relative to the 118 | // beginning of the tiff structure (not relative to the beginning of the IFD). 119 | func DecodeDir(r ReadAtReader, order binary.ByteOrder) (d *Dir, offset int32, err error) { 120 | d = new(Dir) 121 | 122 | // get num of tags in ifd 123 | var nTags int16 124 | err = binary.Read(r, order, &nTags) 125 | if err != nil { 126 | return nil, 0, errors.New("tiff: failed to read IFD tag count: " + err.Error()) 127 | } 128 | 129 | // load tags 130 | for n := 0; n < int(nTags); n++ { 131 | t, err := DecodeTag(r, order) 132 | if err != nil { 133 | return nil, 0, err 134 | } 135 | d.Tags = append(d.Tags, t) 136 | } 137 | 138 | // get offset to next ifd 139 | err = binary.Read(r, order, &offset) 140 | if err != nil { 141 | return nil, 0, errors.New("tiff: falied to read offset to next IFD: " + err.Error()) 142 | } 143 | 144 | return d, offset, nil 145 | } 146 | 147 | func (d *Dir) String() string { 148 | s := "Dir{" 149 | for _, t := range d.Tags { 150 | s += t.String() + ", " 151 | } 152 | return s + "}" 153 | } 154 | -------------------------------------------------------------------------------- /vendor/golang.org/x/image/AUTHORS: -------------------------------------------------------------------------------- 1 | # This source code refers to The Go Authors for copyright purposes. 2 | # The master list of authors is in the main Go distribution, 3 | # visible at http://tip.golang.org/AUTHORS. 4 | -------------------------------------------------------------------------------- /vendor/golang.org/x/image/CONTRIBUTORS: -------------------------------------------------------------------------------- 1 | # This source code was written by the Go contributors. 2 | # The master list of contributors is in the main Go distribution, 3 | # visible at http://tip.golang.org/CONTRIBUTORS. 4 | -------------------------------------------------------------------------------- /vendor/golang.org/x/image/LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2009 The Go Authors. All rights reserved. 2 | 3 | Redistribution and use in source and binary forms, with or without 4 | modification, are permitted provided that the following conditions are 5 | met: 6 | 7 | * Redistributions of source code must retain the above copyright 8 | notice, this list of conditions and the following disclaimer. 9 | * Redistributions in binary form must reproduce the above 10 | copyright notice, this list of conditions and the following disclaimer 11 | in the documentation and/or other materials provided with the 12 | distribution. 13 | * Neither the name of Google Inc. nor the names of its 14 | contributors may be used to endorse or promote products derived from 15 | this software without specific prior written permission. 16 | 17 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 18 | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 19 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 20 | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 21 | OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 22 | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 23 | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 24 | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 25 | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 26 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 27 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 | -------------------------------------------------------------------------------- /vendor/golang.org/x/image/PATENTS: -------------------------------------------------------------------------------- 1 | Additional IP Rights Grant (Patents) 2 | 3 | "This implementation" means the copyrightable works distributed by 4 | Google as part of the Go project. 5 | 6 | Google hereby grants to You a perpetual, worldwide, non-exclusive, 7 | no-charge, royalty-free, irrevocable (except as stated in this section) 8 | patent license to make, have made, use, offer to sell, sell, import, 9 | transfer and otherwise run, modify and propagate the contents of this 10 | implementation of Go, where such license applies only to those patent 11 | claims, both currently owned or controlled by Google and acquired in 12 | the future, licensable by Google that are necessarily infringed by this 13 | implementation of Go. This grant does not include claims that would be 14 | infringed only as a consequence of further modification of this 15 | implementation. If you or your agent or exclusive licensee institute or 16 | order or agree to the institution of patent litigation against any 17 | entity (including a cross-claim or counterclaim in a lawsuit) alleging 18 | that this implementation of Go or any code incorporated within this 19 | implementation of Go constitutes direct or contributory patent 20 | infringement, or inducement of patent infringement, then any patent 21 | rights granted to you under this License for this implementation of Go 22 | shall terminate as of the date such litigation is filed. 23 | -------------------------------------------------------------------------------- /vendor/golang.org/x/image/ccitt/writer.go: -------------------------------------------------------------------------------- 1 | // Copyright 2019 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package ccitt 6 | 7 | import ( 8 | "encoding/binary" 9 | "io" 10 | ) 11 | 12 | type bitWriter struct { 13 | w io.Writer 14 | 15 | // order is whether to process w's bytes LSB first or MSB first. 16 | order Order 17 | 18 | // The high nBits bits of the bits field hold encoded bits to be written to w. 19 | bits uint64 20 | nBits uint32 21 | 22 | // bytes[:bw] holds encoded bytes not yet written to w. 23 | // Overflow protection is ensured by using a multiple of 8 as bytes length. 24 | bw uint32 25 | bytes [1024]uint8 26 | } 27 | 28 | // flushBits copies 64 bits from b.bits to b.bytes. If b.bytes is then full, it 29 | // is written to b.w. 30 | func (b *bitWriter) flushBits() error { 31 | binary.BigEndian.PutUint64(b.bytes[b.bw:], b.bits) 32 | b.bits = 0 33 | b.nBits = 0 34 | b.bw += 8 35 | if b.bw < uint32(len(b.bytes)) { 36 | return nil 37 | } 38 | b.bw = 0 39 | if b.order != MSB { 40 | reverseBitsWithinBytes(b.bytes[:]) 41 | } 42 | _, err := b.w.Write(b.bytes[:]) 43 | return err 44 | } 45 | 46 | // close finalizes a bitcode stream by writing any 47 | // pending bits to bitWriter's underlying io.Writer. 48 | func (b *bitWriter) close() error { 49 | // Write any encoded bits to bytes. 50 | if b.nBits > 0 { 51 | binary.BigEndian.PutUint64(b.bytes[b.bw:], b.bits) 52 | b.bw += (b.nBits + 7) >> 3 53 | } 54 | 55 | if b.order != MSB { 56 | reverseBitsWithinBytes(b.bytes[:b.bw]) 57 | } 58 | 59 | // Write b.bw bytes to b.w. 60 | _, err := b.w.Write(b.bytes[:b.bw]) 61 | return err 62 | } 63 | 64 | // alignToByteBoundary rounds b.nBits up to a multiple of 8. 65 | // If all 64 bits are used, flush them to bitWriter's bytes. 66 | func (b *bitWriter) alignToByteBoundary() error { 67 | if b.nBits = (b.nBits + 7) &^ 7; b.nBits == 64 { 68 | return b.flushBits() 69 | } 70 | return nil 71 | } 72 | 73 | // writeCode writes a variable length bitcode to b's underlying io.Writer. 74 | func (b *bitWriter) writeCode(bs bitString) error { 75 | bits := bs.bits 76 | nBits := bs.nBits 77 | if 64-b.nBits >= nBits { 78 | // b.bits has sufficient room for storing nBits bits. 79 | b.bits |= uint64(bits) << (64 - nBits - b.nBits) 80 | b.nBits += nBits 81 | if b.nBits == 64 { 82 | return b.flushBits() 83 | } 84 | return nil 85 | } 86 | 87 | // Number of leading bits that fill b.bits. 88 | i := 64 - b.nBits 89 | 90 | // Fill b.bits then flush and write remaining bits. 91 | b.bits |= uint64(bits) >> (nBits - i) 92 | b.nBits = 64 93 | 94 | if err := b.flushBits(); err != nil { 95 | return err 96 | } 97 | 98 | nBits -= i 99 | b.bits = uint64(bits) << (64 - nBits) 100 | b.nBits = nBits 101 | return nil 102 | } 103 | -------------------------------------------------------------------------------- /vendor/golang.org/x/image/tiff/buffer.go: -------------------------------------------------------------------------------- 1 | // Copyright 2011 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package tiff 6 | 7 | import "io" 8 | 9 | // buffer buffers an io.Reader to satisfy io.ReaderAt. 10 | type buffer struct { 11 | r io.Reader 12 | buf []byte 13 | } 14 | 15 | // fill reads data from b.r until the buffer contains at least end bytes. 16 | func (b *buffer) fill(end int) error { 17 | m := len(b.buf) 18 | if end > m { 19 | if end > cap(b.buf) { 20 | newcap := 1024 21 | for newcap < end { 22 | newcap *= 2 23 | } 24 | newbuf := make([]byte, end, newcap) 25 | copy(newbuf, b.buf) 26 | b.buf = newbuf 27 | } else { 28 | b.buf = b.buf[:end] 29 | } 30 | if n, err := io.ReadFull(b.r, b.buf[m:end]); err != nil { 31 | end = m + n 32 | b.buf = b.buf[:end] 33 | return err 34 | } 35 | } 36 | return nil 37 | } 38 | 39 | func (b *buffer) ReadAt(p []byte, off int64) (int, error) { 40 | o := int(off) 41 | end := o + len(p) 42 | if int64(end) != off+int64(len(p)) { 43 | return 0, io.ErrUnexpectedEOF 44 | } 45 | 46 | err := b.fill(end) 47 | return copy(p, b.buf[o:end]), err 48 | } 49 | 50 | // Slice returns a slice of the underlying buffer. The slice contains 51 | // n bytes starting at offset off. 52 | func (b *buffer) Slice(off, n int) ([]byte, error) { 53 | end := off + n 54 | if err := b.fill(end); err != nil { 55 | return nil, err 56 | } 57 | return b.buf[off:end], nil 58 | } 59 | 60 | // newReaderAt converts an io.Reader into an io.ReaderAt. 61 | func newReaderAt(r io.Reader) io.ReaderAt { 62 | if ra, ok := r.(io.ReaderAt); ok { 63 | return ra 64 | } 65 | return &buffer{ 66 | r: r, 67 | buf: make([]byte, 0, 1024), 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /vendor/golang.org/x/image/tiff/compress.go: -------------------------------------------------------------------------------- 1 | // Copyright 2011 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package tiff 6 | 7 | import ( 8 | "bufio" 9 | "io" 10 | ) 11 | 12 | type byteReader interface { 13 | io.Reader 14 | io.ByteReader 15 | } 16 | 17 | // unpackBits decodes the PackBits-compressed data in src and returns the 18 | // uncompressed data. 19 | // 20 | // The PackBits compression format is described in section 9 (p. 42) 21 | // of the TIFF spec. 22 | func unpackBits(r io.Reader) ([]byte, error) { 23 | buf := make([]byte, 128) 24 | dst := make([]byte, 0, 1024) 25 | br, ok := r.(byteReader) 26 | if !ok { 27 | br = bufio.NewReader(r) 28 | } 29 | 30 | for { 31 | b, err := br.ReadByte() 32 | if err != nil { 33 | if err == io.EOF { 34 | return dst, nil 35 | } 36 | return nil, err 37 | } 38 | code := int(int8(b)) 39 | switch { 40 | case code >= 0: 41 | n, err := io.ReadFull(br, buf[:code+1]) 42 | if err != nil { 43 | return nil, err 44 | } 45 | dst = append(dst, buf[:n]...) 46 | case code == -128: 47 | // No-op. 48 | default: 49 | if b, err = br.ReadByte(); err != nil { 50 | return nil, err 51 | } 52 | for j := 0; j < 1-code; j++ { 53 | buf[j] = b 54 | } 55 | dst = append(dst, buf[:1-code]...) 56 | } 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /vendor/golang.org/x/image/tiff/consts.go: -------------------------------------------------------------------------------- 1 | // Copyright 2011 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package tiff 6 | 7 | // A tiff image file contains one or more images. The metadata 8 | // of each image is contained in an Image File Directory (IFD), 9 | // which contains entries of 12 bytes each and is described 10 | // on page 14-16 of the specification. An IFD entry consists of 11 | // 12 | // - a tag, which describes the signification of the entry, 13 | // - the data type and length of the entry, 14 | // - the data itself or a pointer to it if it is more than 4 bytes. 15 | // 16 | // The presence of a length means that each IFD is effectively an array. 17 | 18 | const ( 19 | leHeader = "II\x2A\x00" // Header for little-endian files. 20 | beHeader = "MM\x00\x2A" // Header for big-endian files. 21 | 22 | ifdLen = 12 // Length of an IFD entry in bytes. 23 | ) 24 | 25 | // Data types (p. 14-16 of the spec). 26 | const ( 27 | dtByte = 1 28 | dtASCII = 2 29 | dtShort = 3 30 | dtLong = 4 31 | dtRational = 5 32 | ) 33 | 34 | // The length of one instance of each data type in bytes. 35 | var lengths = [...]uint32{0, 1, 1, 2, 4, 8} 36 | 37 | // Tags (see p. 28-41 of the spec). 38 | const ( 39 | tImageWidth = 256 40 | tImageLength = 257 41 | tBitsPerSample = 258 42 | tCompression = 259 43 | tPhotometricInterpretation = 262 44 | 45 | tFillOrder = 266 46 | 47 | tStripOffsets = 273 48 | tSamplesPerPixel = 277 49 | tRowsPerStrip = 278 50 | tStripByteCounts = 279 51 | 52 | tT4Options = 292 // CCITT Group 3 options, a set of 32 flag bits. 53 | tT6Options = 293 // CCITT Group 4 options, a set of 32 flag bits. 54 | 55 | tTileWidth = 322 56 | tTileLength = 323 57 | tTileOffsets = 324 58 | tTileByteCounts = 325 59 | 60 | tXResolution = 282 61 | tYResolution = 283 62 | tResolutionUnit = 296 63 | 64 | tPredictor = 317 65 | tColorMap = 320 66 | tExtraSamples = 338 67 | tSampleFormat = 339 68 | ) 69 | 70 | // Compression types (defined in various places in the spec and supplements). 71 | const ( 72 | cNone = 1 73 | cCCITT = 2 74 | cG3 = 3 // Group 3 Fax. 75 | cG4 = 4 // Group 4 Fax. 76 | cLZW = 5 77 | cJPEGOld = 6 // Superseded by cJPEG. 78 | cJPEG = 7 79 | cDeflate = 8 // zlib compression. 80 | cPackBits = 32773 81 | cDeflateOld = 32946 // Superseded by cDeflate. 82 | ) 83 | 84 | // Photometric interpretation values (see p. 37 of the spec). 85 | const ( 86 | pWhiteIsZero = 0 87 | pBlackIsZero = 1 88 | pRGB = 2 89 | pPaletted = 3 90 | pTransMask = 4 // transparency mask 91 | pCMYK = 5 92 | pYCbCr = 6 93 | pCIELab = 8 94 | ) 95 | 96 | // Values for the tPredictor tag (page 64-65 of the spec). 97 | const ( 98 | prNone = 1 99 | prHorizontal = 2 100 | ) 101 | 102 | // Values for the tResolutionUnit tag (page 18). 103 | const ( 104 | resNone = 1 105 | resPerInch = 2 // Dots per inch. 106 | resPerCM = 3 // Dots per centimeter. 107 | ) 108 | 109 | // imageMode represents the mode of the image. 110 | type imageMode int 111 | 112 | const ( 113 | mBilevel imageMode = iota 114 | mPaletted 115 | mGray 116 | mGrayInvert 117 | mRGB 118 | mRGBA 119 | mNRGBA 120 | mCMYK 121 | ) 122 | 123 | // CompressionType describes the type of compression used in Options. 124 | type CompressionType int 125 | 126 | // Constants for supported compression types. 127 | const ( 128 | Uncompressed CompressionType = iota 129 | Deflate 130 | LZW 131 | CCITTGroup3 132 | CCITTGroup4 133 | ) 134 | 135 | // specValue returns the compression type constant from the TIFF spec that 136 | // is equivalent to c. 137 | func (c CompressionType) specValue() uint32 { 138 | switch c { 139 | case LZW: 140 | return cLZW 141 | case Deflate: 142 | return cDeflate 143 | case CCITTGroup3: 144 | return cG3 145 | case CCITTGroup4: 146 | return cG4 147 | } 148 | return cNone 149 | } 150 | -------------------------------------------------------------------------------- /vendor/golang.org/x/image/tiff/fuzz.go: -------------------------------------------------------------------------------- 1 | // Copyright 2019 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | // +build gofuzz 6 | 7 | package tiff 8 | 9 | import "bytes" 10 | 11 | func Fuzz(data []byte) int { 12 | cfg, err := DecodeConfig(bytes.NewReader(data)) 13 | if err != nil { 14 | return 0 15 | } 16 | if cfg.Width*cfg.Height > 1e6 { 17 | return 0 18 | } 19 | img, err := Decode(bytes.NewReader(data)) 20 | if err != nil { 21 | return 0 22 | } 23 | var w bytes.Buffer 24 | err = Encode(&w, img, nil) 25 | if err != nil { 26 | panic(err) 27 | } 28 | return 1 29 | } 30 | -------------------------------------------------------------------------------- /vendor/modules.txt: -------------------------------------------------------------------------------- 1 | # github.com/GeertJohan/go.rice v1.0.0 2 | github.com/GeertJohan/go.rice 3 | github.com/GeertJohan/go.rice/embedded 4 | # github.com/daaku/go.zipexe v1.0.0 5 | github.com/daaku/go.zipexe 6 | # github.com/disintegration/imaging v1.6.2 7 | github.com/disintegration/imaging 8 | # github.com/dustin/go-humanize v1.0.0 9 | github.com/dustin/go-humanize 10 | # github.com/espebra/filebin v0.0.0-20190808211437-aa2884848e3f 11 | github.com/espebra/filebin/app/api 12 | github.com/espebra/filebin/app/backend/fs 13 | github.com/espebra/filebin/app/config 14 | github.com/espebra/filebin/app/events 15 | github.com/espebra/filebin/app/metrics 16 | github.com/espebra/filebin/app/model 17 | github.com/espebra/filebin/app/output 18 | github.com/espebra/filebin/app/shared 19 | github.com/espebra/filebin/app/tokens 20 | # github.com/gorilla/handlers v1.4.2 21 | github.com/gorilla/handlers 22 | # github.com/gorilla/mux v1.7.3 23 | github.com/gorilla/mux 24 | # github.com/rwcarlsen/goexif v0.0.0-20190401172101-9e8deecbddbd 25 | github.com/rwcarlsen/goexif/exif 26 | github.com/rwcarlsen/goexif/tiff 27 | # golang.org/x/image v0.0.0-20191009234506-e7c1f5e7dbb8 28 | golang.org/x/image/bmp 29 | golang.org/x/image/ccitt 30 | golang.org/x/image/tiff 31 | golang.org/x/image/tiff/lzw 32 | --------------------------------------------------------------------------------