Browse Source

Major rewrite

* use dep for vendoring
* lets encrypt
* moved web to transfer.sh-web repo
* single command install
* added first tests
tags/v1.0.0
Remco 7 years ago
parent
commit
cb6e5cb0c7
100 changed files with 3134 additions and 15935 deletions
  1. +0
    -3
      .travis.yml
  2. +0
    -308
      Gruntfile.js
  3. +1
    -1
      README.md
  4. +227
    -0
      cmd/cmd.go
  5. +204
    -0
      lock.json
  6. +8
    -0
      main.go
  7. +7
    -0
      manifest.json
  8. +76
    -0
      server/clamav.go
  9. +65
    -0
      server/codec.go
  10. +0
    -0
      server/codec.go.bak
  11. +586
    -0
      server/handlers.go
  12. +620
    -0
      server/handlers.go.bak
  13. +110
    -0
      server/handlers_test.go
  14. +371
    -0
      server/server.go
  15. +239
    -0
      server/server.go.bak
  16. +278
    -0
      server/storage.go
  17. +0
    -0
      server/storage.go.bak
  18. +276
    -0
      server/utils.go
  19. +0
    -0
      server/utils.go.bak
  20. +66
    -0
      server/virustotal.go
  21. +0
    -0
      server/virustotal.go.bak
  22. +0
    -609
      transfersh-server/handlers.go
  23. +0
    -201
      transfersh-server/main.go
  24. +0
    -10
      transfersh-server/run.sh.sample
  25. +0
    -157
      transfersh-server/static/404.html
  26. +0
    -1
      transfersh-server/static/404.txt
  27. +0
    -130
      transfersh-server/static/download.audio.html
  28. +0
    -134
      transfersh-server/static/download.code.html
  29. +0
    -117
      transfersh-server/static/download.html
  30. +0
    -127
      transfersh-server/static/download.image.html
  31. +0
    -125
      transfersh-server/static/download.markdown.html
  32. +0
    -116
      transfersh-server/static/download.sandbox.html
  33. +0
    -130
      transfersh-server/static/download.video.html
  34. BIN
     
  35. BIN
     
  36. BIN
     
  37. +0
    -414
      transfersh-server/static/fonts/font-awesome/fontawesome-webfont.svg
  38. BIN
     
  39. BIN
     
  40. BIN
     
  41. +0
    -229
      transfersh-server/static/fonts/glyphicons/glyphicons-halflings-regular.svg
  42. BIN
     
  43. BIN
     
  44. BIN
     
  45. +0
    -25
      transfersh-server/static/fonts/transfersh.svg
  46. BIN
     
  47. BIN
     
  48. BIN
     
  49. BIN
     
  50. BIN
     
  51. BIN
     
  52. BIN
     
  53. BIN
     
  54. BIN
     
  55. +0
    -133
      transfersh-server/static/images/terminal-top.svg
  56. +0
    -189
      transfersh-server/static/images/terminal.svg
  57. +0
    -6
      transfersh-server/static/images/tor.svg
  58. +0
    -5
      transfersh-server/static/includes/download-bottom.html
  59. +0
    -10
      transfersh-server/static/includes/download-btn.html
  60. +0
    -6
      transfersh-server/static/includes/download-top.html
  61. +0
    -37
      transfersh-server/static/includes/footer.html
  62. +0
    -15
      transfersh-server/static/includes/ga.html
  63. +0
    -12
      transfersh-server/static/includes/head.html
  64. +0
    -2
      transfersh-server/static/includes/js.html
  65. +0
    -15
      transfersh-server/static/includes/navigation.html
  66. +0
    -479
      transfersh-server/static/index.html
  67. +0
    -40
      transfersh-server/static/index.txt
  68. +0
    -5
      transfersh-server/static/robots.txt
  69. +0
    -5
      transfersh-server/static/scripts/main.js
  70. +0
    -1
      transfersh-server/static/scripts/vendor/modernizr.js
  71. +0
    -1
      transfersh-server/static/styles/main.css
  72. +0
    -27
      transfersh-server/vendor/github.com/bmizerany/pat/example/hello.go
  73. +0
    -29
      transfersh-server/vendor/github.com/bmizerany/pat/example/patexample/hello_appengine.go
  74. +0
    -310
      transfersh-server/vendor/github.com/bmizerany/pat/mux.go
  75. +0
    -48
      transfersh-server/vendor/github.com/eknkc/amber/amberc/cli.go
  76. +0
    -781
      transfersh-server/vendor/github.com/eknkc/amber/compiler.go
  77. +0
    -257
      transfersh-server/vendor/github.com/eknkc/amber/doc.go
  78. +0
    -281
      transfersh-server/vendor/github.com/eknkc/amber/parser/nodes.go
  79. +0
    -454
      transfersh-server/vendor/github.com/eknkc/amber/parser/parser.go
  80. +0
    -501
      transfersh-server/vendor/github.com/eknkc/amber/parser/scanner.go
  81. +0
    -287
      transfersh-server/vendor/github.com/eknkc/amber/runtime.go
  82. +0
    -175
      transfersh-server/vendor/github.com/garyburd/redigo/redis/LICENSE
  83. +0
    -168
      transfersh-server/vendor/github.com/garyburd/redigo/redis/doc.go
  84. +0
    -433
      transfersh-server/vendor/github.com/goamz/goamz/aws/aws.go
  85. +0
    -254
      transfersh-server/vendor/github.com/goamz/goamz/aws/regions.go
  86. +0
    -185
      transfersh-server/vendor/github.com/goamz/goamz/s3/LICENSE
  87. +0
    -1161
      transfersh-server/vendor/github.com/goamz/goamz/s3/s3.go
  88. +0
    -640
      transfersh-server/vendor/github.com/goamz/goamz/s3/s3test/server.go
  89. +0
    -185
      transfersh-server/vendor/github.com/goamz/goamz/testutil/LICENSE
  90. +0
    -206
      transfersh-server/vendor/github.com/gorilla/mux/doc.go
  91. +0
    -495
      transfersh-server/vendor/github.com/gorilla/mux/mux.go
  92. +0
    -312
      transfersh-server/vendor/github.com/gorilla/mux/regexp.go
  93. +0
    -634
      transfersh-server/vendor/github.com/gorilla/mux/route.go
  94. +0
    -384
      transfersh-server/vendor/github.com/kennygrant/sanitize/sanitize.go
  95. +0
    -1420
      transfersh-server/vendor/github.com/russross/blackfriday/block.go
  96. +0
    -1148
      transfersh-server/vendor/github.com/russross/blackfriday/inline.go
  97. +0
    -926
      transfersh-server/vendor/github.com/russross/blackfriday/markdown.go
  98. +0
    -129
      transfersh-server/vendor/golang.org/x/text/cases/cases.go
  99. +0
    -281
      transfersh-server/vendor/golang.org/x/text/cases/context.go
  100. +0
    -26
      transfersh-server/vendor/golang.org/x/text/cases/fold.go

+ 0
- 3
.travis.yml View File

@@ -1,8 +1,5 @@
language: go
go:
- 1.1
- 1.2
- 1.3
- release
- tip



+ 0
- 308
Gruntfile.js View File

@@ -1,308 +0,0 @@
'use strict';

// # Globbing
// for performance reasons we're only matching one level down:
// 'test/spec/{,*/}*.js'
// use this if you want to match all subfolders:
// 'test/spec/**/*.js'

module.exports = function (grunt) {
// load all grunt tasks
require('load-grunt-tasks')(grunt);
// show elapsed time at the end
require('time-grunt')(grunt);

// configurable paths
var yeomanConfig = {
app: require('./bower.json').appPath || 'transfersh-web',
dist: 'transfersh-server/static/'
};

grunt.initConfig({
yeoman: yeomanConfig,
watch: {
less: {
files: ['<%= yeoman.app %>/styles/{,*/}*.less'],
tasks: ['less']
},
gruntfile: {
files: ['Gruntfile.js']
},
includes: {
files: ['<%= yeoman.app %>/*.html', '.tmp/*.html'],
tasks: ['includes:server']
},
livereload: {
options: {
livereload: '<%= connect.options.livereload %>'
},
files: [
'<%= yeoman.app %>/*.html',
'{.tmp,<%= yeoman.app %>}/styles/{,*/}*.css',
'{.tmp,<%= yeoman.app %>}/scripts/{,*/}*.js',
'<%= yeoman.app %>/images/{,*/}*.{png,jpg,jpeg,gif,webp,svg}'
],
tasks: ['includes:server']
}
},
connect: {
options: {
port: 9000,
// change this to '0.0.0.0' to access the server from outside
hostname: 'localhost',
livereload: 35729
},
livereload: {
options: {
open: true,
base: [
'.tmp',
'<%= yeoman.app %>'
]
}
},
test: {
options: {
port: 9001,
base: [
'.tmp',
'test',
'<%= yeoman.app %>'
]
}
},
dist: {
options: {
base: '<%= yeoman.dist %>'
}
}
},
clean: {
dist: {
files: [{
dot: true,
src: [
'.tmp',
'<%= yeoman.dist %>/*',
'!<%= yeoman.dist %>/.git*'
]
}]
},
server: '.tmp'
},
jshint: {
options: {
jshintrc: '.jshintrc',
reporter: require('jshint-stylish')
},
all: [
'Gruntfile.js',
'<%= yeoman.app %>/scripts/{,*/}*.js',
'!<%= yeoman.app %>/scripts/vendor/*',
'test/spec/{,*/}*.js'
]
},


less: {
dist: {
files: {
'<%= yeoman.app %>/styles/main.css': ['<%= yeoman.app %>/styles/main.less']
},
options: {
sourceMap: true,
sourceMapFilename: '<%= yeoman.app %>/styles/main.css.map',
sourceMapBasepath: '<%= yeoman.app %>/',
sourceMapRootpath: '/'
}
}
},

includes: {
build: {
cwd: '<%= yeoman.app %>',
src: ['*.html', 'includes/*.html'],
dest: '<%= yeoman.dist %>',
options: {
flatten: true,
banner: ''
}
},
server: {
cwd: '<%= yeoman.app %>',
src: ['*.html', 'includes/*.html'],
dest: '.tmp/',
options: {
flatten: true,
banner: ''
}
}
},
// not used since Uglify task does concat,
// but still available if needed
/*concat: {
dist: {}
},*/
// not enabled since usemin task does concat and uglify
// check index.html to edit your build targets
// enable this task if you prefer defining your build targets here
/*uglify: {
dist: {}
},*/
rev: {
dist: {
files: {
src: [
'<%= yeoman.dist %>/scripts/{,*/}*.js',
'<%= yeoman.dist %>/styles/{,*/}*.css',
'<%= yeoman.dist %>/images/{,*/}*.{png,jpg,jpeg,gif,webp,svg}',
'<%= yeoman.dist %>/fonts/{,*/}*.*'
]
}
}
},
useminPrepare: {
html: '<%= yeoman.app %>/*.html',
options: {
dest: '<%= yeoman.dist %>'
}
},
usemin: {
html: ['<%= yeoman.dist %>/{,*/}*.html'],
css: ['<%= yeoman.dist %>/styles/{,*/}*.css'],
options: {
dirs: ['<%= yeoman.dist %>']
}
},
imagemin: {
dist: {
files: [{
expand: true,
cwd: '<%= yeoman.app %>/images',
src: '{,*/}*.{png,jpg,jpeg}',
dest: '<%= yeoman.dist %>/images'
}]
}
},

cssmin: {
dist: {
files: {
'<%= yeoman.dist %>/styles/main.css': [
'.tmp/styles/{,*/}*.css',
'<%= yeoman.app %>/styles/{,*/}*.css'
]
}
}
},
htmlmin: {
dist: {
options: {
/*removeCommentsFromCDATA: true,
// https://github.com/yeoman/grunt-usemin/issues/44
//collapseWhitespace: true,
collapseBooleanAttributes: true,
removeAttributeQuotes: true,
removeRedundantAttributes: true,
useShortDoctype: true,
removeEmptyAttributes: true,
removeOptionalTags: true*/
},
files: [{
expand: true,
cwd: '<%= yeoman.app %>',
src: '*.html',
dest: '<%= yeoman.dist %>'
}]
}
},
copy: {
dist: {
files: [{
expand: true,
dot: true,
cwd: '<%= yeoman.app %>',
dest: '<%= yeoman.dist %>',
src: [
'*.{ico,png,txt}',
'fonts/{,*/}*.*',
'.htaccess',
'index.txt',
'404.txt',
'images/{,*/}*.{webp,gif,svg}'
]
}]
},
server: {
files: [{
expand: true,
dot: true,
cwd: '<%= yeoman.app %>/bower_components/font-awesome/fonts/',
dest: '<%= yeoman.app %>/fonts/font-awesome',
src: ['*']
}, {
expand: true,
dot: true,
cwd: '<%= yeoman.app %>/bower_components/bootstrap/dist/fonts/',
dest: '<%= yeoman.app %>/fonts/glyphicons',
src: ['*']
}]
}
},
concurrent: {
dist: [
'less',
'imagemin',
'htmlmin'
]
}
});

grunt.registerTask('serve', function (target) {
if (target === 'dist') {
return grunt.task.run(['build', 'connect:dist:keepalive']);
}

grunt.task.run([
'clean:server',
'less',
'includes:server',
'copy:server',
'connect:livereload',
'watch'
]);
});

grunt.registerTask('server', function () {
grunt.log.warn('The `server` task has been deprecated. Use `grunt serve` to start a server.');
grunt.task.run(['serve']);
});

grunt.registerTask('test', [
'clean:server',
'less',
'copy:server',
'connect:test',
]);

grunt.registerTask('build', [
'clean:dist',

'copy:server',
'useminPrepare',
'concurrent',
'cssmin',
'concat',
'includes:build',
'uglify',
'copy',
'usemin',

]);

grunt.registerTask('default', [
'jshint',
'test',
'build'
]);
};

+ 1
- 1
README.md View File

@@ -1,4 +1,4 @@
# transfer.sh [![Gitter](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/dutchcoders/transfer.sh?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) [![Build Status](https://travis-ci.org/dutchcoders/transfer.sh.svg?branch=master)](https://travis-ci.org/dutchcoders/transfer.sh)
# transfer.sh [![Gitter](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/dutchcoders/transfer.sh?utm_source=badge&utm_medium=badge&utm_campaign=&utm_campaign=pr-badge&utm_content=badge) [![Go Report Card](https://goreportcard.com/badge/dutchcoders/transfer.sh)](https://goreportcard.com/report/dutchcoders/transfer.sh) [![Docker pulls](https://img.shields.io/docker/pulls/transfer.sh/transfer.sh.svg)](https://hub.docker.com/r/transfer.sh/transfer.sh/) [![Build Status](https://travis-ci.org/dutchcoders/transfer.sh.svg?branch=master)](https://travis-ci.org/dutchcoders/transfer.sh)

Easy and fast file sharing from the command-line. This code contains the server with everything you need to create your own instance.



+ 227
- 0
cmd/cmd.go View File

@@ -0,0 +1,227 @@
package cmd

import (
"fmt"

"os"

"strings"

"github.com/dutchcoders/transfer.sh/server"
"github.com/fatih/color"
"github.com/minio/cli"
)

var Version = "0.1"
var helpTemplate = `NAME:
{{.Name}} - {{.Usage}}

DESCRIPTION:
{{.Description}}

USAGE:
{{.Name}} {{if .Flags}}[flags] {{end}}command{{if .Flags}}{{end}} [arguments...]

COMMANDS:
{{range .Commands}}{{join .Names ", "}}{{ "\t" }}{{.Usage}}
{{end}}{{if .Flags}}
FLAGS:
{{range .Flags}}{{.}}
{{end}}{{end}}
VERSION:
` + Version +
`{{ "\n"}}`

var globalFlags = []cli.Flag{
cli.StringFlag{
Name: "listener",
Usage: "127.0.0.1:8080",
Value: "127.0.0.1:8080",
},
// redirect to https?
// hostnames
cli.StringFlag{
Name: "profile-listener",
Usage: "127.0.0.1:6060",
Value: "",
},
cli.BoolFlag{
Name: "force-https",
Usage: "",
},
cli.StringFlag{
Name: "tls-listener",
Usage: "127.0.0.1:8443",
Value: "",
},
cli.StringFlag{
Name: "tls-cert-file",
Value: "",
},
cli.StringFlag{
Name: "tls-private-key",
Value: "",
},
cli.StringFlag{
Name: "temp-path",
Usage: "path to temp files",
Value: os.TempDir(),
},
cli.StringFlag{
Name: "web-path",
Usage: "path to static web files",
Value: "",
},
cli.StringFlag{
Name: "provider",
Usage: "s3|local",
Value: "",
},
cli.StringFlag{
Name: "aws-access-key",
Usage: "",
Value: "",
EnvVar: "AWS_ACCESS_KEY",
},
cli.StringFlag{
Name: "aws-secret-key",
Usage: "",
Value: "",
EnvVar: "AWS_SECRET_KEY",
},
cli.StringFlag{
Name: "bucket",
Usage: "",
Value: "",
EnvVar: "BUCKET",
},
cli.StringFlag{
Name: "lets-encrypt-hosts",
Usage: "host1, host2",
Value: "",
EnvVar: "HOSTS",
},
cli.StringFlag{
Name: "log",
Usage: "/var/log/transfersh.log",
Value: "",
},
cli.StringFlag{
Name: "basedir",
Usage: "path to storage",
Value: "",
},
cli.BoolFlag{
Name: "profiler",
Usage: "enable profiling",
},
}

type Cmd struct {
*cli.App
}

func VersionAction(c *cli.Context) {
fmt.Println(color.YellowString(fmt.Sprintf("transfer.sh: Easy file sharing from the command line")))
}

func New() *Cmd {
app := cli.NewApp()
app.Name = "transfer.sh"
app.Author = ""
app.Usage = "transfer.sh"
app.Description = `Easy file sharing from the command line`
app.Flags = globalFlags
app.CustomAppHelpTemplate = helpTemplate
app.Commands = []cli.Command{
{
Name: "version",
Action: VersionAction,
},
}

app.Before = func(c *cli.Context) error {
return nil
}

app.Action = func(c *cli.Context) {
options := []server.OptionFn{}
if v := c.String("listener"); v != "" {
options = append(options, server.Listener(v))
}

if v := c.String("tls-listener"); v != "" {
options = append(options, server.TLSListener(v))
}

if v := c.String("profile-listener"); v != "" {
options = append(options, server.ProfileListener(v))
}

if v := c.String("web-path"); v != "" {
options = append(options, server.WebPath(v))
}

if v := c.String("temp-path"); v != "" {
options = append(options, server.TempPath(v))
}

if v := c.String("lets-encrypt-hosts"); v != "" {
options = append(options, server.UseLetsEncrypt(strings.Split(v, ",")))
}

if cert := c.String("tls-cert-file"); cert == "" {
} else if pk := c.String("tls-private-key"); pk == "" {
} else {
options = append(options, server.TLSConfig(cert, pk))
}

if c.Bool("profiler") {
options = append(options, server.EnableProfiler())
}

if c.Bool("force-https") {
options = append(options, server.ForceHTTPs())
}

switch provider := c.String("provider"); provider {
case "s3":
if accessKey := c.String("aws-access-key"); accessKey == "" {
panic("access-key not set.")
} else if secretKey := c.String("aws-secret-key"); secretKey == "" {
panic("secret-key not set.")
} else if bucket := c.String("bucket"); bucket == "" {
panic("bucket not set.")
} else if storage, err := server.NewS3Storage(accessKey, secretKey, bucket); err != nil {
panic(err)
} else {
options = append(options, server.UseStorage(storage))
}
case "local":
if v := c.String("basedir"); v == "" {
panic("basedir not set.")
} else if storage, err := server.NewLocalStorage(v); err != nil {
panic(err)
} else {
options = append(options, server.UseStorage(storage))
}
default:
panic("Provider not set or invalid.")
}

srvr, err := server.New(
options...,
)

if err != nil {
fmt.Println(color.RedString("Error starting server: %s", err.Error()))
return
}

srvr.Run()
}

return &Cmd{
App: app,
}
}

+ 204
- 0
lock.json View File

@@ -0,0 +1,204 @@
{
"memo": "07876113f39e289dbd1d493a6ba955bad81664a6f5291a4daa554700d5d536f3",
"projects": [
{
"name": "github.com/PuerkitoBio/ghost",
"branch": "master",
"revision": "206e6e460e14a42d1d811c970b30248db058e9b2",
"packages": [
".",
"handlers"
]
},
{
"name": "github.com/dutchcoders/go-clamd",
"branch": "master",
"revision": "a9a81beaffff0392094052913ec45fa140eb8511",
"packages": [
"."
]
},
{
"name": "github.com/dutchcoders/go-virustotal",
"branch": "master",
"revision": "24cc8e6fa329f020c70a3b32330b5743f1ba7971",
"packages": [
"."
]
},
{
"name": "github.com/dutchcoders/transfer.sh-web",
"branch": "master",
"revision": "648a3f436b4772ca979e5d272010fd2719a7c5e2",
"packages": [
"."
]
},
{
"name": "github.com/elazarl/go-bindata-assetfs",
"branch": "master",
"revision": "30f82fa23fd844bd5bb1e5f216db87fd77b5eb43",
"packages": [
"."
]
},
{
"name": "github.com/fatih/color",
"version": "v1.4.1",
"revision": "9131ab34cf20d2f6d83fdc67168a5430d1c7dc23",
"packages": [
"."
]
},
{
"name": "github.com/garyburd/redigo",
"version": "v1.0.0",
"revision": "8873b2f1995f59d4bcdd2b0dc9858e2cb9bf0c13",
"packages": [
"internal",
"redis"
]
},
{
"name": "github.com/goamz/goamz",
"branch": "master",
"revision": "c35091c30f44b7f151ec9028b895465a191d1ea7",
"packages": [
"aws",
"s3"
]
},
{
"name": "github.com/golang/gddo",
"branch": "master",
"revision": "72302b972abba39585150723aea3cf343e99437c",
"packages": [
"httputil/header"
]
},
{
"name": "github.com/gorilla/context",
"version": "v1.1",
"revision": "1ea25387ff6f684839d82767c1733ff4d4d15d0a",
"packages": [
"."
]
},
{
"name": "github.com/gorilla/mux",
"version": "v1.3.0",
"revision": "392c28fe23e1c45ddba891b0320b3b5df220beea",
"packages": [
"."
]
},
{
"name": "github.com/gorilla/securecookie",
"version": "v1.1",
"revision": "667fe4e3466a040b780561fe9b51a83a3753eefc",
"packages": [
"."
]
},
{
"name": "github.com/kennygrant/sanitize",
"version": "v1.2",
"revision": "6a0bfdde8629a3a3a7418a7eae45c54154692514",
"packages": [
"."
]
},
{
"name": "github.com/mattn/go-colorable",
"version": "v0.0.7",
"revision": "d228849504861217f796da67fae4f6e347643f15",
"packages": [
"."
]
},
{
"name": "github.com/mattn/go-isatty",
"version": "v0.0.1",
"revision": "3a115632dcd687f9c8cd01679c83a06a0e21c1f3",
"packages": [
"."
]
},
{
"name": "github.com/minio/cli",
"version": "v1.3.0",
"revision": "8683fa7fef37cc8cb092f47bdb6b403e0049f9ee",
"packages": [
"."
]
},
{
"name": "github.com/nu7hatch/gouuid",
"branch": "master",
"revision": "179d4d0c4d8d407a32af483c2354df1d2c91e6c3",
"packages": [
"."
]
},
{
"name": "github.com/russross/blackfriday",
"version": "v1.4",
"revision": "0b647d0506a698cca42caca173e55559b12a69f2",
"packages": [
"."
]
},
{
"name": "github.com/shurcooL/sanitized_anchor_name",
"branch": "master",
"revision": "1dba4b3954bc059efc3991ec364f9f9a35f597d2",
"packages": [
"."
]
},
{
"name": "github.com/vaughan0/go-ini",
"branch": "master",
"revision": "a98ad7ee00ec53921f08832bc06ecf7fd600e6a1",
"packages": [
"."
]
},
{
"name": "golang.org/x/crypto",
"branch": "master",
"revision": "459e26527287adbc2adcc5d0d49abff9a5f315a7",
"packages": [
"acme",
"acme/autocert"
]
},
{
"name": "golang.org/x/net",
"branch": "master",
"revision": "a6577fac2d73be281a500b310739095313165611",
"packages": [
"context",
"context/ctxhttp",
"html",
"html/atom"
]
},
{
"name": "golang.org/x/sys",
"branch": "master",
"revision": "99f16d856c9836c42d24e7ab64ea72916925fa97",
"packages": [
"unix"
]
},
{
"name": "gopkg.in/check.v1",
"branch": "v1",
"revision": "20d25e2804050c1cd24a7eea1e7a6447dd0e74ec",
"packages": [
"."
]
}
]
}

+ 8
- 0
main.go View File

@@ -0,0 +1,8 @@
package main

import "github.com/dutchcoders/transfer.sh/cmd"

func main() {
app := cmd.New()
app.RunAndExitOnError()
}

+ 7
- 0
manifest.json View File

@@ -0,0 +1,7 @@
{
"dependencies": {
"github.com/dutchcoders/transfer.sh-web": {
"branch": "master"
}
}
}

+ 76
- 0
server/clamav.go View File

@@ -0,0 +1,76 @@
/*
The MIT License (MIT)

Copyright (c) 2014 DutchCoders [https://github.com/dutchcoders/]

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/

package server

import (
// _ "transfer.sh/app/handlers"
// _ "transfer.sh/app/utils"

"fmt"
"io"
"log"
"net/http"
"path/filepath"
"time"

clamd "github.com/dutchcoders/go-clamd"

"github.com/gorilla/mux"
"github.com/kennygrant/sanitize"
)

func (s *Server) scanHandler(w http.ResponseWriter, r *http.Request) {
vars := mux.Vars(r)

filename := sanitize.Path(filepath.Base(vars["filename"]))

contentLength := r.ContentLength
contentType := r.Header.Get("Content-Type")

log.Printf("Scanning %s %d %s", filename, contentLength, contentType)

var reader io.Reader

reader = r.Body

c := clamd.NewClamd(s.ClamAVDaemonHost)

abort := make(chan bool)
response, err := c.ScanStream(reader, abort)
if err != nil {
log.Printf("%s", err.Error())
http.Error(w, err.Error(), 500)
return
}

select {
case s := <-response:
w.Write([]byte(fmt.Sprintf("%v\n", s.Status)))
case <-time.After(time.Second * 60):
abort <- true
}

close(abort)
}

+ 65
- 0
server/codec.go View File

@@ -0,0 +1,65 @@
/*
https://github.com/fs111/kurz.go/blob/master/src/codec.go

Originally written and Copyright (c) 2011 Andrรฉ Kelpe
Modifications Copyright (c) 2015 John Ko

Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
the Software, and to permit persons to whom the Software is furnished to do so,
subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/

package server

import (
"math"
"strings"
)

const (
// characters used for short-urls
SYMBOLS = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"

// someone set us up the bomb !!
BASE = int64(len(SYMBOLS))
)

// encodes a number into our *base* representation
// TODO can this be made better with some bitshifting?
func Encode(number int64) string {
rest := number % BASE
// strings are a bit weird in go...
result := string(SYMBOLS[rest])
if number-rest != 0 {
newnumber := (number - rest) / BASE
result = Encode(newnumber) + result
}
return result
}

// Decodes a string given in our encoding and returns the decimal
// integer.
func Decode(input string) int64 {
const floatbase = float64(BASE)
l := len(input)
var sum int = 0
for index := l - 1; index > -1; index -= 1 {
current := string(input[index])
pos := strings.Index(SYMBOLS, current)
sum = sum + (pos * int(math.Pow(floatbase, float64((l-index-1)))))
}
return int64(sum)
}

transfersh-server/codec.go → server/codec.go.bak View File


+ 586
- 0
server/handlers.go View File

@@ -0,0 +1,586 @@
/*
The MIT License (MIT)

Copyright (c) 2014 DutchCoders [https://github.com/dutchcoders/]

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/

package server

import (
// _ "transfer.sh/app/handlers"
// _ "transfer.sh/app/utils"

"archive/tar"
"archive/zip"
"bytes"
"compress/gzip"
"errors"
"fmt"
"html"
html_template "html/template"
"io"
"io/ioutil"
"log"
"math/rand"
"mime"
"net/http"
"os"
"path/filepath"
"strconv"
"strings"
text_template "text/template"
"time"

web "github.com/dutchcoders/transfer.sh-web"
"github.com/gorilla/mux"
"github.com/kennygrant/sanitize"
"github.com/russross/blackfriday"
)

var (
html_templates = initHTMLTemplates()
text_templates = initTextTemplates()
)

func stripPrefix(path string) string {
return strings.Replace(path, web.Prefix+"/", "", -1)
}

func initTextTemplates() *text_template.Template {
templateMap := text_template.FuncMap{"format": formatNumber}

// Templates with functions available to them
var templates = text_template.New("").Funcs(templateMap)
return templates
}

func initHTMLTemplates() *html_template.Template {
templateMap := html_template.FuncMap{"format": formatNumber}

// Templates with functions available to them
var templates = html_template.New("").Funcs(templateMap)

return templates
}

func healthHandler(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, "Approaching Neutral Zone, all systems normal and functioning.")
}

/* The preview handler will show a preview of the content for browsers (accept type text/html), and referer is not transfer.sh */
func (s *Server) previewHandler(w http.ResponseWriter, r *http.Request) {

vars := mux.Vars(r)

token := vars["token"]
filename := vars["filename"]

contentType, contentLength, err := storage.Head(token, filename)
if err != nil {
http.Error(w, http.StatusText(404), 404)
return
}

var templatePath string
var content html_template.HTML

switch {
case strings.HasPrefix(contentType, "image/"):
templatePath = "download.image.html"
case strings.HasPrefix(contentType, "video/"):
templatePath = "download.video.html"
case strings.HasPrefix(contentType, "audio/"):
templatePath = "download.audio.html"
case strings.HasPrefix(contentType, "text/"):
templatePath = "download.markdown.html"

var reader io.ReadCloser
if reader, _, _, err = storage.Get(token, filename); err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}

var data []byte
if data, err = ioutil.ReadAll(reader); err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}

if strings.HasPrefix(contentType, "text/x-markdown") || strings.HasPrefix(contentType, "text/markdown") {
output := blackfriday.MarkdownCommon(data)
content = html_template.HTML(output)
} else if strings.HasPrefix(contentType, "text/plain") {
content = html_template.HTML(fmt.Sprintf("<pre>%s</pre>", html.EscapeString(string(data))))
} else {
templatePath = "download.sandbox.html"
}

default:
templatePath = "download.html"
}

if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}

data := struct {
ContentType string
Content html_template.HTML
Filename string
Url string
ContentLength uint64
}{
contentType,
content,
filename,
r.URL.String(),
contentLength,
}

if err := html_templates.ExecuteTemplate(w, templatePath, data); err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}

}

// this handler will output html or text, depending on the
// support of the client (Accept header).

func (s *Server) viewHandler(w http.ResponseWriter, r *http.Request) {
// vars := mux.Vars(r)

if acceptsHtml(r.Header) {
if err := html_templates.ExecuteTemplate(w, "index.html", nil); err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
} else {
if err := text_templates.ExecuteTemplate(w, "index.txt", nil); err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
}
}

func (s *Server) notFoundHandler(w http.ResponseWriter, r *http.Request) {
http.Error(w, http.StatusText(404), 404)
}

func (s *Server) postHandler(w http.ResponseWriter, r *http.Request) {
if err := r.ParseMultipartForm(_24K); nil != err {
log.Printf("%s", err.Error())
http.Error(w, "Error occurred copying to output stream", 500)
return
}

token := Encode(10000000 + int64(rand.Intn(1000000000)))

w.Header().Set("Content-Type", "text/plain")

for _, fheaders := range r.MultipartForm.File {
for _, fheader := range fheaders {
filename := sanitize.Path(filepath.Base(fheader.Filename))
contentType := fheader.Header.Get("Content-Type")

if contentType == "" {
contentType = mime.TypeByExtension(filepath.Ext(fheader.Filename))
}

var f io.Reader
var err error

if f, err = fheader.Open(); err != nil {
log.Printf("%s", err.Error())
http.Error(w, err.Error(), 500)
return
}

var b bytes.Buffer

n, err := io.CopyN(&b, f, _24K+1)
if err != nil && err != io.EOF {
log.Printf("%s", err.Error())
http.Error(w, err.Error(), 500)
return
}

var reader io.Reader

if n > _24K {
file, err := ioutil.TempFile(s.tempPath, "transfer-")
if err != nil {
log.Fatal(err)
}
defer file.Close()

n, err = io.Copy(file, io.MultiReader(&b, f))
if err != nil {
os.Remove(file.Name())

log.Printf("%s", err.Error())
http.Error(w, err.Error(), 500)
return
}

reader, err = os.Open(file.Name())
} else {
reader = bytes.NewReader(b.Bytes())
}

contentLength := n

log.Printf("Uploading %s %s %d %s", token, filename, contentLength, contentType)

if err = storage.Put(token, filename, reader, contentType, uint64(contentLength)); err != nil {
log.Printf("%s", err.Error())
http.Error(w, err.Error(), 500)
return

}

fmt.Fprintf(w, "https://%s/%s/%s\n", ipAddrFromRemoteAddr(r.Host), token, filename)
}
}
}

func (s *Server) putHandler(w http.ResponseWriter, r *http.Request) {
vars := mux.Vars(r)

filename := sanitize.Path(filepath.Base(vars["filename"]))

contentLength := r.ContentLength

var reader io.Reader

reader = r.Body

if contentLength == -1 {
// queue file to disk, because s3 needs content length
var err error
var f io.Reader

f = reader

var b bytes.Buffer

n, err := io.CopyN(&b, f, _24K+1)
if err != nil && err != io.EOF {
log.Printf("%s", err.Error())
http.Error(w, err.Error(), 500)
return
}

if n > _24K {
file, err := ioutil.TempFile(s.tempPath, "transfer-")
if err != nil {
log.Printf("%s", err.Error())
http.Error(w, err.Error(), 500)
return
}

defer file.Close()

n, err = io.Copy(file, io.MultiReader(&b, f))
if err != nil {
os.Remove(file.Name())
log.Printf("%s", err.Error())
http.Error(w, err.Error(), 500)
return
}

reader, err = os.Open(file.Name())
} else {
reader = bytes.NewReader(b.Bytes())
}

contentLength = n
}

contentType := r.Header.Get("Content-Type")

if contentType == "" {
contentType = mime.TypeByExtension(filepath.Ext(vars["filename"]))
}

token := Encode(10000000 + int64(rand.Intn(1000000000)))

log.Printf("Uploading %s %s %d %s", token, filename, contentLength, contentType)

var err error

if err = storage.Put(token, filename, reader, contentType, uint64(contentLength)); err != nil {
log.Printf("%s", err.Error())
http.Error(w, errors.New("Could not save file").Error(), 500)
return
}

// w.Statuscode = 200

w.Header().Set("Content-Type", "text/plain")

fmt.Fprintf(w, "https://%s/%s/%s\n", ipAddrFromRemoteAddr(r.Host), token, filename)
}

func (s *Server) zipHandler(w http.ResponseWriter, r *http.Request) {
vars := mux.Vars(r)

files := vars["files"]

zipfilename := fmt.Sprintf("transfersh-%d.zip", uint16(time.Now().UnixNano()))

w.Header().Set("Content-Type", "application/zip")
w.Header().Set("Content-Disposition", fmt.Sprintf("attachment; filename=\"%s\"", zipfilename))
w.Header().Set("Connection", "close")

zw := zip.NewWriter(w)

for _, key := range strings.Split(files, ",") {
if strings.HasPrefix(key, "/") {
key = key[1:]
}

key = strings.Replace(key, "\\", "/", -1)

token := strings.Split(key, "/")[0]
filename := sanitize.Path(strings.Split(key, "/")[1])

reader, _, _, err := storage.Get(token, filename)

if err != nil {
if storage.IsNotExist(err) {
http.Error(w, "File not found", 404)
return
} else {
log.Printf("%s", err.Error())
http.Error(w, "Could not retrieve file.", 500)
return
}
}

defer reader.Close()

header := &zip.FileHeader{
Name: strings.Split(key, "/")[1],
Method: zip.Store,
ModifiedTime: uint16(time.Now().UnixNano()),
ModifiedDate: uint16(time.Now().UnixNano()),
}

fw, err := zw.CreateHeader(header)

if err != nil {
log.Printf("%s", err.Error())
http.Error(w, "Internal server error.", 500)
return
}

if _, err = io.Copy(fw, reader); err != nil {
log.Printf("%s", err.Error())
http.Error(w, "Internal server error.", 500)
return
}
}

if err := zw.Close(); err != nil {
log.Printf("%s", err.Error())
http.Error(w, "Internal server error.", 500)
return
}
}

func (s *Server) tarGzHandler(w http.ResponseWriter, r *http.Request) {
vars := mux.Vars(r)

files := vars["files"]

tarfilename := fmt.Sprintf("transfersh-%d.tar.gz", uint16(time.Now().UnixNano()))

w.Header().Set("Content-Type", "application/x-gzip")
w.Header().Set("Content-Disposition", fmt.Sprintf("attachment; filename=\"%s\"", tarfilename))
w.Header().Set("Connection", "close")

os := gzip.NewWriter(w)
defer os.Close()

zw := tar.NewWriter(os)
defer zw.Close()

for _, key := range strings.Split(files, ",") {
if strings.HasPrefix(key, "/") {
key = key[1:]
}

key = strings.Replace(key, "\\", "/", -1)

token := strings.Split(key, "/")[0]
filename := sanitize.Path(strings.Split(key, "/")[1])

reader, _, contentLength, err := storage.Get(token, filename)
if err != nil {
if storage.IsNotExist(err) {
http.Error(w, "File not found", 404)
return
} else {
log.Printf("%s", err.Error())
http.Error(w, "Could not retrieve file.", 500)
return
}
}

defer reader.Close()

header := &tar.Header{
Name: strings.Split(key, "/")[1],
Size: int64(contentLength),
}

err = zw.WriteHeader(header)
if err != nil {
log.Printf("%s", err.Error())
http.Error(w, "Internal server error.", 500)
return
}

if _, err = io.Copy(zw, reader); err != nil {
log.Printf("%s", err.Error())
http.Error(w, "Internal server error.", 500)
return
}
}
}

func (s *Server) tarHandler(w http.ResponseWriter, r *http.Request) {
vars := mux.Vars(r)

files := vars["files"]

tarfilename := fmt.Sprintf("transfersh-%d.tar", uint16(time.Now().UnixNano()))

w.Header().Set("Content-Type", "application/x-tar")
w.Header().Set("Content-Disposition", fmt.Sprintf("attachment; filename=\"%s\"", tarfilename))
w.Header().Set("Connection", "close")

zw := tar.NewWriter(w)
defer zw.Close()

for _, key := range strings.Split(files, ",") {
token := strings.Split(key, "/")[0]
filename := strings.Split(key, "/")[1]

reader, _, contentLength, err := storage.Get(token, filename)
if err != nil {
if storage.IsNotExist(err) {
http.Error(w, "File not found", 404)
return
} else {
log.Printf("%s", err.Error())
http.Error(w, "Could not retrieve file.", 500)
return
}
}

defer reader.Close()

header := &tar.Header{
Name: strings.Split(key, "/")[1],
Size: int64(contentLength),
}

err = zw.WriteHeader(header)
if err != nil {
log.Printf("%s", err.Error())
http.Error(w, "Internal server error.", 500)
return
}

if _, err = io.Copy(zw, reader); err != nil {
log.Printf("%s", err.Error())
http.Error(w, "Internal server error.", 500)
return
}
}
}

func (s *Server) getHandler(w http.ResponseWriter, r *http.Request) {
vars := mux.Vars(r)

token := vars["token"]
filename := vars["filename"]

reader, contentType, contentLength, err := storage.Get(token, filename)
if err != nil {
if storage.IsNotExist(err) {
http.Error(w, "File not found", 404)
return
} else {
log.Printf("%s", err.Error())
http.Error(w, "Could not retrieve file.", 500)
return
}
}

defer reader.Close()

w.Header().Set("Content-Type", contentType)
w.Header().Set("Content-Length", strconv.FormatUint(contentLength, 10))
w.Header().Set("Content-Disposition", fmt.Sprintf("attachment; filename=\"%s\"", filename))
w.Header().Set("Connection", "close")

if _, err = io.Copy(w, reader); err != nil {
log.Printf("%s", err.Error())
http.Error(w, "Error occurred copying to output stream", 500)
return
}
}

func (s *Server) RedirectHandler(h http.Handler) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
if !s.forceHTTPs {
// we don't want to enforce https
} else if r.URL.Path == "/health.html" {
// health check url won't redirect
} else if strings.HasSuffix(ipAddrFromRemoteAddr(r.Host), ".onion") {
// .onion addresses cannot get a valid certificate, so don't redirect
} else if r.Header.Get("X-Forwarded-Proto") == "https" {
} else if r.URL.Scheme == "https" {
} else {
u := *r.URL
u.Scheme = "https"

http.Redirect(w, r, u.String(), http.StatusPermanentRedirect)
return
}

h.ServeHTTP(w, r)
}
}

// Create a log handler for every request it receives.
func LoveHandler(h http.Handler) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("x-made-with", "<3 by DutchCoders")
w.Header().Set("x-served-by", "Proudly served by DutchCoders")
w.Header().Set("Server", "Transfer.sh HTTP Server 1.0")
h.ServeHTTP(w, r)
}
}

+ 620
- 0
server/handlers.go.bak View File

@@ -0,0 +1,620 @@
/*
The MIT License (MIT)

Copyright (c) 2014 DutchCoders [https://github.com/dutchcoders/]

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/

package main

import (
// _ "transfer.sh/app/handlers"
// _ "transfer.sh/app/utils"

"archive/tar"
"archive/zip"
"bytes"
"compress/gzip"
"errors"
"fmt"
"html"
html_template "html/template"
"io"
"io/ioutil"
"log"
"math/rand"
"mime"
"net/http"
"os"
"path/filepath"
"strconv"
"strings"
text_template "text/template"
"time"

clamd "github.com/dutchcoders/go-clamd"

web "github.com/dutchcoders/transfer.sh-web"
"github.com/gorilla/mux"
"github.com/kennygrant/sanitize"
"github.com/russross/blackfriday"
)

var (
html_templates = initHTMLTemplates()
text_templates = initTextTemplates()
)

func stripPrefix(path string) string {
return strings.Replace(path, web.Prefix+"/", "", -1)
}

func initTextTemplates() *text_template.Template {
templateMap := text_template.FuncMap{"format": formatNumber}

// Templates with functions available to them
var templates = text_template.New("").Funcs(templateMap)
return templates
}

func initHTMLTemplates() *html_template.Template {
templateMap := html_template.FuncMap{"format": formatNumber}

// Templates with functions available to them
var templates = html_template.New("").Funcs(templateMap)

return templates
}

func healthHandler(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, "Approaching Neutral Zone, all systems normal and functioning.")
}

/* The preview handler will show a preview of the content for browsers (accept type text/html), and referer is not transfer.sh */
func previewHandler(w http.ResponseWriter, r *http.Request) {

vars := mux.Vars(r)

token := vars["token"]
filename := vars["filename"]

contentType, contentLength, err := storage.Head(token, filename)
if err != nil {
http.Error(w, http.StatusText(404), 404)
return
}

var templatePath string
var content html_template.HTML

switch {
case strings.HasPrefix(contentType, "image/"):
templatePath = "download.image.html"
case strings.HasPrefix(contentType, "video/"):
templatePath = "download.video.html"
case strings.HasPrefix(contentType, "audio/"):
templatePath = "download.audio.html"
case strings.HasPrefix(contentType, "text/"):
templatePath = "download.markdown.html"

var reader io.ReadCloser
if reader, _, _, err = storage.Get(token, filename); err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}

var data []byte
if data, err = ioutil.ReadAll(reader); err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}

if strings.HasPrefix(contentType, "text/x-markdown") || strings.HasPrefix(contentType, "text/markdown") {
output := blackfriday.MarkdownCommon(data)
content = html_template.HTML(output)
} else if strings.HasPrefix(contentType, "text/plain") {
content = html_template.HTML(fmt.Sprintf("<pre>%s</pre>", html.EscapeString(string(data))))
} else {
templatePath = "download.sandbox.html"
}

default:
templatePath = "download.html"
}

if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}

data := struct {
ContentType string
Content html_template.HTML
Filename string
Url string
ContentLength uint64
}{
contentType,
content,
filename,
r.URL.String(),
contentLength,
}

if err := html_templates.ExecuteTemplate(w, templatePath, data); err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}

}

// this handler will output html or text, depending on the
// support of the client (Accept header).

func viewHandler(w http.ResponseWriter, r *http.Request) {
// vars := mux.Vars(r)

if acceptsHtml(r.Header) {
if err := html_templates.ExecuteTemplate(w, "index.html", nil); err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
} else {
if err := text_templates.ExecuteTemplate(w, "index.txt", nil); err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
}
}

func notFoundHandler(w http.ResponseWriter, r *http.Request) {
http.Error(w, http.StatusText(404), 404)
}

func postHandler(w http.ResponseWriter, r *http.Request) {
if err := r.ParseMultipartForm(_24K); nil != err {
log.Printf("%s", err.Error())
http.Error(w, "Error occurred copying to output stream", 500)
return
}

token := Encode(10000000 + int64(rand.Intn(1000000000)))

w.Header().Set("Content-Type", "text/plain")

for _, fheaders := range r.MultipartForm.File {
for _, fheader := range fheaders {
filename := sanitize.Path(filepath.Base(fheader.Filename))
contentType := fheader.Header.Get("Content-Type")

if contentType == "" {
contentType = mime.TypeByExtension(filepath.Ext(fheader.Filename))
}

var f io.Reader
var err error

if f, err = fheader.Open(); err != nil {
log.Printf("%s", err.Error())
http.Error(w, err.Error(), 500)
return
}

var b bytes.Buffer

n, err := io.CopyN(&b, f, _24K+1)
if err != nil && err != io.EOF {
log.Printf("%s", err.Error())
http.Error(w, err.Error(), 500)
return
}

var reader io.Reader

if n > _24K {
file, err := ioutil.TempFile(config.Temp, "transfer-")
if err != nil {
log.Fatal(err)
}
defer file.Close()

n, err = io.Copy(file, io.MultiReader(&b, f))
if err != nil {
os.Remove(file.Name())

log.Printf("%s", err.Error())
http.Error(w, err.Error(), 500)
return
}

reader, err = os.Open(file.Name())
} else {
reader = bytes.NewReader(b.Bytes())
}

contentLength := n

log.Printf("Uploading %s %s %d %s", token, filename, contentLength, contentType)

if err = storage.Put(token, filename, reader, contentType, uint64(contentLength)); err != nil {
log.Printf("%s", err.Error())
http.Error(w, err.Error(), 500)
return

}

fmt.Fprintf(w, "https://%s/%s/%s\n", ipAddrFromRemoteAddr(r.Host), token, filename)
}
}
}

func scanHandler(w http.ResponseWriter, r *http.Request) {
vars := mux.Vars(r)

filename := sanitize.Path(filepath.Base(vars["filename"]))

contentLength := r.ContentLength
contentType := r.Header.Get("Content-Type")

log.Printf("Scanning %s %d %s", filename, contentLength, contentType)

var reader io.Reader

reader = r.Body

c := clamd.NewClamd(config.CLAMAV_DAEMON_HOST)

abort := make(chan bool)
response, err := c.ScanStream(reader, abort)
if err != nil {
log.Printf("%s", err.Error())
http.Error(w, err.Error(), 500)
return
}

select {
case s := <-response:
w.Write([]byte(fmt.Sprintf("%v\n", s.Status)))
case <-time.After(time.Second * 60):
abort <- true
}

close(abort)
}

func putHandler(w http.ResponseWriter, r *http.Request) {
vars := mux.Vars(r)

filename := sanitize.Path(filepath.Base(vars["filename"]))

contentLength := r.ContentLength

var reader io.Reader

reader = r.Body

if contentLength == -1 {
// queue file to disk, because s3 needs content length
var err error
var f io.Reader

f = reader

var b bytes.Buffer

n, err := io.CopyN(&b, f, _24K+1)
if err != nil && err != io.EOF {
log.Printf("%s", err.Error())
http.Error(w, err.Error(), 500)
return
}

if n > _24K {
file, err := ioutil.TempFile(config.Temp, "transfer-")
if err != nil {
log.Printf("%s", err.Error())
http.Error(w, err.Error(), 500)
return
}

defer file.Close()

n, err = io.Copy(file, io.MultiReader(&b, f))
if err != nil {
os.Remove(file.Name())
log.Printf("%s", err.Error())
http.Error(w, err.Error(), 500)
return
}

reader, err = os.Open(file.Name())
} else {
reader = bytes.NewReader(b.Bytes())
}

contentLength = n
}

contentType := r.Header.Get("Content-Type")

if contentType == "" {
contentType = mime.TypeByExtension(filepath.Ext(vars["filename"]))
}

token := Encode(10000000 + int64(rand.Intn(1000000000)))

log.Printf("Uploading %s %s %d %s", token, filename, contentLength, contentType)

var err error

if err = storage.Put(token, filename, reader, contentType, uint64(contentLength)); err != nil {
log.Printf("%s", err.Error())
http.Error(w, errors.New("Could not save file").Error(), 500)
return
}

// w.Statuscode = 200

w.Header().Set("Content-Type", "text/plain")

fmt.Fprintf(w, "https://%s/%s/%s\n", ipAddrFromRemoteAddr(r.Host), token, filename)
}

func zipHandler(w http.ResponseWriter, r *http.Request) {
vars := mux.Vars(r)

files := vars["files"]

zipfilename := fmt.Sprintf("transfersh-%d.zip", uint16(time.Now().UnixNano()))

w.Header().Set("Content-Type", "application/zip")
w.Header().Set("Content-Disposition", fmt.Sprintf("attachment; filename=\"%s\"", zipfilename))
w.Header().Set("Connection", "close")

zw := zip.NewWriter(w)

for _, key := range strings.Split(files, ",") {
if strings.HasPrefix(key, "/") {
key = key[1:]
}

key = strings.Replace(key, "\\", "/", -1)

token := strings.Split(key, "/")[0]
filename := sanitize.Path(strings.Split(key, "/")[1])

reader, _, _, err := storage.Get(token, filename)

if err != nil {
if storage.IsNotExist(err) {
http.Error(w, "File not found", 404)
return
} else {
log.Printf("%s", err.Error())
http.Error(w, "Could not retrieve file.", 500)
return
}
}

defer reader.Close()

header := &zip.FileHeader{
Name: strings.Split(key, "/")[1],
Method: zip.Store,
ModifiedTime: uint16(time.Now().UnixNano()),
ModifiedDate: uint16(time.Now().UnixNano()),
}

fw, err := zw.CreateHeader(header)

if err != nil {
log.Printf("%s", err.Error())
http.Error(w, "Internal server error.", 500)
return
}

if _, err = io.Copy(fw, reader); err != nil {
log.Printf("%s", err.Error())
http.Error(w, "Internal server error.", 500)
return
}
}

if err := zw.Close(); err != nil {
log.Printf("%s", err.Error())
http.Error(w, "Internal server error.", 500)
return
}
}

func tarGzHandler(w http.ResponseWriter, r *http.Request) {
vars := mux.Vars(r)

files := vars["files"]

tarfilename := fmt.Sprintf("transfersh-%d.tar.gz", uint16(time.Now().UnixNano()))

w.Header().Set("Content-Type", "application/x-gzip")
w.Header().Set("Content-Disposition", fmt.Sprintf("attachment; filename=\"%s\"", tarfilename))
w.Header().Set("Connection", "close")

os := gzip.NewWriter(w)
defer os.Close()

zw := tar.NewWriter(os)
defer zw.Close()

for _, key := range strings.Split(files, ",") {
if strings.HasPrefix(key, "/") {
key = key[1:]
}

key = strings.Replace(key, "\\", "/", -1)

token := strings.Split(key, "/")[0]
filename := sanitize.Path(strings.Split(key, "/")[1])

reader, _, contentLength, err := storage.Get(token, filename)
if err != nil {
if storage.IsNotExist(err) {
http.Error(w, "File not found", 404)
return
} else {
log.Printf("%s", err.Error())
http.Error(w, "Could not retrieve file.", 500)
return
}
}

defer reader.Close()

header := &tar.Header{
Name: strings.Split(key, "/")[1],
Size: int64(contentLength),
}

err = zw.WriteHeader(header)
if err != nil {
log.Printf("%s", err.Error())
http.Error(w, "Internal server error.", 500)
return
}

if _, err = io.Copy(zw, reader); err != nil {
log.Printf("%s", err.Error())
http.Error(w, "Internal server error.", 500)
return
}
}
}

func tarHandler(w http.ResponseWriter, r *http.Request) {
vars := mux.Vars(r)

files := vars["files"]

tarfilename := fmt.Sprintf("transfersh-%d.tar", uint16(time.Now().UnixNano()))

w.Header().Set("Content-Type", "application/x-tar")
w.Header().Set("Content-Disposition", fmt.Sprintf("attachment; filename=\"%s\"", tarfilename))
w.Header().Set("Connection", "close")

zw := tar.NewWriter(w)
defer zw.Close()

for _, key := range strings.Split(files, ",") {
token := strings.Split(key, "/")[0]
filename := strings.Split(key, "/")[1]

reader, _, contentLength, err := storage.Get(token, filename)
if err != nil {
if storage.IsNotExist(err) {
http.Error(w, "File not found", 404)
return
} else {
log.Printf("%s", err.Error())
http.Error(w, "Could not retrieve file.", 500)
return
}
}

defer reader.Close()

header := &tar.Header{
Name: strings.Split(key, "/")[1],
Size: int64(contentLength),
}

err = zw.WriteHeader(header)
if err != nil {
log.Printf("%s", err.Error())
http.Error(w, "Internal server error.", 500)
return
}

if _, err = io.Copy(zw, reader); err != nil {
log.Printf("%s", err.Error())
http.Error(w, "Internal server error.", 500)
return
}
}
}

func getHandler(w http.ResponseWriter, r *http.Request) {
vars := mux.Vars(r)

token := vars["token"]
filename := vars["file