commit 9ba5c5a8a749de16c8ca038d591319f571be922d Author: Remco Date: Thu Oct 16 20:01:43 2014 +0200 Initial diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..15ce9b5 --- /dev/null +++ b/.gitignore @@ -0,0 +1,15 @@ +build/ +pkg/ +dist/ +src/ +bin/ +*.pyc +*.egg-info/ + +.tmp +.vagrant + +bower_components/ +node_modules/ + +transfersh-server/run.sh diff --git a/.jshintrc b/.jshintrc new file mode 100644 index 0000000..7912f95 --- /dev/null +++ b/.jshintrc @@ -0,0 +1,23 @@ +{ + "node": true, + "browser": true, + "esnext": true, + "bitwise": true, + "camelcase": true, + "curly": true, + "eqeqeq": true, + "immed": true, + "indent": 2, + "latedef": true, + "newcap": true, + "noarg": true, + "quotmark": "single", + "regexp": true, + "undef": true, + "unused": true, + "strict": true, + "trailing": true, + "smarttabs": true, + "jquery": true, + "white": true +} diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 0000000..212857d --- /dev/null +++ b/.travis.yml @@ -0,0 +1,10 @@ +language: go +go: + - 1.1 + - 1.2 + - 1.3 + - release + - tip + +script: + - go test -v ./... diff --git a/Gruntfile.js b/Gruntfile.js new file mode 100644 index 0000000..aa176c2 --- /dev/null +++ b/Gruntfile.js @@ -0,0 +1,287 @@ +'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'] + }, + 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}' + ] + } + }, + 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: '/' + } + } + }, + // 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}', + '<%= yeoman.dist %>/fonts/{,*/}*.*' + ] + } + } + }, + useminPrepare: { + html: '<%= yeoman.app %>/index.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' + }] + } + }, + svgmin: { + dist: { + files: [{ + expand: true, + cwd: '<%= yeoman.app %>/images', + src: '{,*/}*.svg', + 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', + 'images/{,*/}*.{webp,gif}' + ] + }] + }, + 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', + 'svgmin', + 'htmlmin' + ] + } + }); + + grunt.registerTask('serve', function (target) { + if (target === 'dist') { + return grunt.task.run(['build', 'connect:dist:keepalive']); + } + + grunt.task.run([ + 'clean:server', + 'less', + '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', + 'uglify', + 'copy', + 'usemin' + ]); + + grunt.registerTask('default', [ + 'jshint', + 'test', + 'build' + ]); +}; diff --git a/README.md b/README.md new file mode 100644 index 0000000..78a9e06 --- /dev/null +++ b/README.md @@ -0,0 +1,8 @@ +# transfer.sh + + +Development + + +- grunt serve +- grunt build diff --git a/Vagrantfile b/Vagrantfile new file mode 100644 index 0000000..f903c45 --- /dev/null +++ b/Vagrantfile @@ -0,0 +1,13 @@ +# -*- mode: ruby -*- +# vi: set ft=ruby : + +# Vagrantfile API/syntax version. Don't touch unless you know what you're doing! +VAGRANTFILE_API_VERSION = "2" + +Vagrant.configure(VAGRANTFILE_API_VERSION) do |config| + # Every Vagrant virtual environment requires a box to build off of. + config.vm.box = "puphpet/ubuntu1404-x64" + config.vm.provider "vmware_fusion" do |v| + v.gui = true + end +end diff --git a/bower.json b/bower.json new file mode 100644 index 0000000..f59ba0c --- /dev/null +++ b/bower.json @@ -0,0 +1,21 @@ +{ + "name": "transfer.sh", + "version": "0.0.0", + "moduleType": [ + "node" + ], + "private": true, + "ignore": [ + "**/.*", + "node_modules", + "bower_components", + "transfersh-web/bower_components", + "test", + "tests" + ], + "dependencies": { + "bootstrap": "~3.0.0", + "modernizr": "~2.6.2", + "typed.js": "https://github.com/mattboldt/typed.js.git" + } +} diff --git a/extras/clamd b/extras/clamd new file mode 100755 index 0000000..ebb45c8 --- /dev/null +++ b/extras/clamd @@ -0,0 +1,159 @@ +#! /bin/sh +### BEGIN INIT INFO +# Provides: skeleton +# Required-Start: $remote_fs $syslog +# Required-Stop: $remote_fs $syslog +# Default-Start: 2 3 4 5 +# Default-Stop: 0 1 6 +# Short-Description: Example initscript +# Description: This file should be used to construct scripts to be +# placed in /etc/init.d. +### END INIT INFO + +# Author: Foo Bar +# +# Please remove the "Author" lines above and replace them +# with your own name if you copy and modify this script. + +# Do NOT "set -e" + +# PATH should only include /usr/* if it runs after the mountnfs.sh script +PATH=/sbin:/usr/sbin:/bin:/usr/bin +DESC="Clam Daemon" +NAME=clamd +DAEMON="/usr/local/sbin/clamd" +DAEMON_ARGS="-c /usr/local/etc/clamd.conf" +PIDFILE=/var/run/$NAME.pid +SCRIPTNAME=/etc/init.d/$NAME + +# Exit if the package is not installed +[ -x "$DAEMON" ] || exit 0 + +# Read configuration variable file if it is present +[ -r /etc/default/$NAME ] && . /etc/default/$NAME + +# Load the VERBOSE setting and other rcS variables +. /lib/init/vars.sh + +# Define LSB log_* functions. +# Depend on lsb-base (>= 3.2-14) to ensure that this file is present +# and status_of_proc is working. +. /lib/lsb/init-functions + +# +# Function that starts the daemon/service +# +do_start() +{ + # Return + # 0 if daemon has been started + # 1 if daemon was already running + # 2 if daemon could not be started + start-stop-daemon --background --start --quiet --pidfile $PIDFILE --exec $DAEMON --test > /dev/null \ + || return 1 + start-stop-daemon --background --start --quiet --pidfile $PIDFILE --exec $DAEMON -- \ + $DAEMON_ARGS \ + || return 2 + # Add code here, if necessary, that waits for the process to be ready + # to handle requests from services started subsequently which depend + # on this one. As a last resort, sleep for some time. +} + +# +# Function that stops the daemon/service +# +do_stop() +{ + # Return + # 0 if daemon has been stopped + # 1 if daemon was already stopped + # 2 if daemon could not be stopped + # other if a failure occurred + start-stop-daemon --stop --quiet --retry=TERM/30/KILL/5 --pidfile $PIDFILE --name $NAME + RETVAL="$?" + [ "$RETVAL" = 2 ] && return 2 + # Wait for children to finish too if this is a daemon that forks + # and if the daemon is only ever run from this initscript. + # If the above conditions are not satisfied then add some other code + # that waits for the process to drop all resources that could be + # needed by services started subsequently. A last resort is to + # sleep for some time. + start-stop-daemon --stop --quiet --oknodo --retry=0/30/KILL/5 --exec $DAEMON + [ "$?" = 2 ] && return 2 + # Many daemons don't delete their pidfiles when they exit. + rm -f $PIDFILE + return "$RETVAL" +} + +# +# Function that sends a SIGHUP to the daemon/service +# +do_reload() { + # + # If the daemon can reload its configuration without + # restarting (for example, when it is sent a SIGHUP), + # then implement that here. + # + start-stop-daemon --stop --signal 1 --quiet --pidfile $PIDFILE --name $NAME + return 0 +} + +case "$1" in + start) + [ "$VERBOSE" != no ] && log_daemon_msg "Starting $DESC" "$NAME" + do_start + case "$?" in + 0|1) [ "$VERBOSE" != no ] && log_end_msg 0 ;; + 2) [ "$VERBOSE" != no ] && log_end_msg 1 ;; + esac + ;; + stop) + [ "$VERBOSE" != no ] && log_daemon_msg "Stopping $DESC" "$NAME" + do_stop + case "$?" in + 0|1) [ "$VERBOSE" != no ] && log_end_msg 0 ;; + 2) [ "$VERBOSE" != no ] && log_end_msg 1 ;; + esac + ;; + status) + status_of_proc "$DAEMON" "$NAME" && exit 0 || exit $? + ;; + #reload|force-reload) + # + # If do_reload() is not implemented then leave this commented out + # and leave 'force-reload' as an alias for 'restart'. + # + #log_daemon_msg "Reloading $DESC" "$NAME" + #do_reload + #log_end_msg $? + #;; + restart|force-reload) + # + # If the "reload" option is implemented then remove the + # 'force-reload' alias + # + log_daemon_msg "Restarting $DESC" "$NAME" + do_stop + case "$?" in + 0|1) + do_start + case "$?" in + 0) log_end_msg 0 ;; + 1) log_end_msg 1 ;; # Old process is still running + *) log_end_msg 1 ;; # Failed to start + esac + ;; + *) + # Failed to stop + log_end_msg 1 + ;; + esac + ;; + *) + #echo "Usage: $SCRIPTNAME {start|stop|restart|reload|force-reload}" >&2 + echo "Usage: $SCRIPTNAME {start|stop|status|restart|force-reload}" >&2 + exit 3 + ;; +esac + +: diff --git a/extras/transfersh b/extras/transfersh new file mode 100755 index 0000000..e436add --- /dev/null +++ b/extras/transfersh @@ -0,0 +1,165 @@ +#! /bin/sh +### BEGIN INIT INFO +# Provides: skeleton +# Required-Start: $remote_fs $syslog +# Required-Stop: $remote_fs $syslog +# Default-Start: 2 3 4 5 +# Default-Stop: 0 1 6 +# Short-Description: Example initscript +# Description: This file should be used to construct scripts to be +# placed in /etc/init.d. +### END INIT INFO + +# Author: Foo Bar +# +# Please remove the "Author" lines above and replace them +# with your own name if you copy and modify this script. + +# Do NOT "set -e" + +# PATH should only include /usr/* if it runs after the mountnfs.sh script +PATH=/sbin:/usr/sbin:/bin:/usr/bin:/usr/local/go/bin +DESC="Transfersh Web server" +NAME=transfersh +DAEMON="/opt/transfer.sh/main" +DAEMON_ARGS="--port 80 --temp /tmp/" +PIDFILE=/var/run/$NAME.pid +SCRIPTNAME=/etc/init.d/$NAME + +export BUCKET={bucket} +export AWS_ACCESS_KEY={aws_access_key} +export AWS_SECRET_KEY={aws_secret_key} +export VIRUSTOTAL_KEY={virustotal_key} +export GOPATH=/opt/go/ + +# Exit if the package is not installed +[ -x "$DAEMON" ] || exit 0 + +# Read configuration variable file if it is present +[ -r /etc/default/$NAME ] && . /etc/default/$NAME + +# Load the VERBOSE setting and other rcS variables +. /lib/init/vars.sh + +# Define LSB log_* functions. +# Depend on lsb-base (>= 3.2-14) to ensure that this file is present +# and status_of_proc is working. +. /lib/lsb/init-functions + +# +# Function that starts the daemon/service +# +do_start() +{ + # Return + # 0 if daemon has been started + # 1 if daemon was already running + # 2 if daemon could not be started + start-stop-daemon --background --start --chdir /opt/transfer.sh --quiet --pidfile $PIDFILE --make-pidfile --exec $DAEMON --test > /dev/null \ + || return 1 + start-stop-daemon --background --start --chdir /opt/transfer.sh --quiet --pidfile $PIDFILE --make-pidfile --exec $DAEMON -- \ + $DAEMON_ARGS \ + || return 2 + # Add code here, if necessary, that waits for the process to be ready + # to handle requests from services started subsequently which depend + # on this one. As a last resort, sleep for some time. +} + +# +# Function that stops the daemon/service +# +do_stop() +{ + # Return + # 0 if daemon has been stopped + # 1 if daemon was already stopped + # 2 if daemon could not be stopped + # other if a failure occurred + start-stop-daemon --stop --quiet --retry=TERM/30/KILL/5 --pidfile $PIDFILE --name $NAME + RETVAL="$?" + [ "$RETVAL" = 2 ] && return 2 + # Wait for children to finish too if this is a daemon that forks + # and if the daemon is only ever run from this initscript. + # If the above conditions are not satisfied then add some other code + # that waits for the process to drop all resources that could be + # needed by services started subsequently. A last resort is to + # sleep for some time. + start-stop-daemon --stop --quiet --oknodo --retry=0/30/KILL/5 --exec $DAEMON + [ "$?" = 2 ] && return 2 + # Many daemons don't delete their pidfiles when they exit. + rm -f $PIDFILE + return "$RETVAL" +} + +# +# Function that sends a SIGHUP to the daemon/service +# +do_reload() { + # + # If the daemon can reload its configuration without + # restarting (for example, when it is sent a SIGHUP), + # then implement that here. + # + start-stop-daemon --stop --signal 1 --quiet --pidfile $PIDFILE --name $NAME + return 0 +} + +case "$1" in + start) + [ "$VERBOSE" != no ] && log_daemon_msg "Starting $DESC" "$NAME" + do_start + case "$?" in + 0|1) [ "$VERBOSE" != no ] && log_end_msg 0 ;; + 2) [ "$VERBOSE" != no ] && log_end_msg 1 ;; + esac + ;; + stop) + [ "$VERBOSE" != no ] && log_daemon_msg "Stopping $DESC" "$NAME" + do_stop + case "$?" in + 0|1) [ "$VERBOSE" != no ] && log_end_msg 0 ;; + 2) [ "$VERBOSE" != no ] && log_end_msg 1 ;; + esac + ;; + status) + status_of_proc "$DAEMON" "$NAME" && exit 0 || exit $? + ;; + #reload|force-reload) + # + # If do_reload() is not implemented then leave this commented out + # and leave 'force-reload' as an alias for 'restart'. + # + #log_daemon_msg "Reloading $DESC" "$NAME" + #do_reload + #log_end_msg $? + #;; + restart|force-reload) + # + # If the "reload" option is implemented then remove the + # 'force-reload' alias + # + log_daemon_msg "Restarting $DESC" "$NAME" + do_stop + case "$?" in + 0|1) + do_start + case "$?" in + 0) log_end_msg 0 ;; + 1) log_end_msg 1 ;; # Old process is still running + *) log_end_msg 1 ;; # Failed to start + esac + ;; + *) + # Failed to stop + log_end_msg 1 + ;; + esac + ;; + *) + #echo "Usage: $SCRIPTNAME {start|stop|restart|reload|force-reload}" >&2 + echo "Usage: $SCRIPTNAME {start|stop|status|restart|force-reload}" >&2 + exit 3 + ;; +esac + +: diff --git a/package.json b/package.json new file mode 100644 index 0000000..435d4d3 --- /dev/null +++ b/package.json @@ -0,0 +1,32 @@ +{ + "name": "transfer.sh", + "version": "0.0.0", + "dependencies": { + "wiredep": "^1.8.6" + }, + "devDependencies": { + "grunt": "~0.4.5", + "grunt-contrib-copy": "~0.6.0", + "grunt-contrib-concat": "~0.5.0", + "grunt-contrib-uglify": "~0.6.0", + "grunt-contrib-jshint": "~0.10.0", + "grunt-contrib-cssmin": "~0.10.0", + "grunt-contrib-connect": "~0.8.0", + "grunt-contrib-clean": "~0.6.0", + "grunt-contrib-htmlmin": "~0.3.0", + "grunt-contrib-less": "~0.11.4", + "grunt-contrib-imagemin": "0.8.1", + "grunt-contrib-watch": "~0.6.1", + "grunt-rev": "~0.1.0", + "grunt-usemin": "~2.4.0", + "grunt-svgmin": "1.0.0", + "grunt-concurrent": "~1.0.0", + "load-grunt-tasks": "~0.6.0", + "matchdep": "~0.3.0", + "time-grunt": "~1.0.0", + "jshint-stylish": "~1.0.0" + }, + "engines": { + "node": ">=0.8.0" + } +} diff --git a/transfersh-server/codec.go b/transfersh-server/codec.go new file mode 100644 index 0000000..bd394e5 --- /dev/null +++ b/transfersh-server/codec.go @@ -0,0 +1,41 @@ +package main + +import ( + "math" + "strings" +) + +const ( + // characters used for short-urls + SYMBOLS = "0123456789abcdefghijklmnopqrsuvwxyzABCDEFGHIJKLMNOPQRSTUVXYZ" + + // 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) +} diff --git a/transfersh-server/handlers.go b/transfersh-server/handlers.go new file mode 100644 index 0000000..b751b54 --- /dev/null +++ b/transfersh-server/handlers.go @@ -0,0 +1,578 @@ +/* +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" + "github.com/dutchcoders/go-clamd" + "github.com/goamz/goamz/s3" + "github.com/golang/gddo/httputil/header" + "github.com/gorilla/mux" + "github.com/kennygrant/sanitize" + "io" + "io/ioutil" + "log" + "math/rand" + "mime" + "net/http" + "os" + "path/filepath" + "strconv" + "strings" + "time" + html_template "html/template" + text_template "text/template" +) + +func healthHandler(w http.ResponseWriter, r *http.Request) { + fmt.Fprintf(w, "Approaching Neutral Zone, all systems normal and functioning.") +} + +// 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) + + actual := header.ParseAccept(r.Header, "Accept") + + html := false + + for _, s := range actual { + if s.Value == "text/html" { + html = true + } + } + + if html { + tmpl, err := html_template.ParseFiles("static/index.html") + + if err != nil { + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } + + if err := tmpl.Execute(w, nil); err != nil { + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } + } else { + tmpl, err := text_template.ParseFiles("static/index.txt") + + if err != nil { + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } + + if err := tmpl.Execute(w, nil); err != nil { + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } + } +} + +func notFoundHandler(w http.ResponseWriter, r *http.Request) { +} + +func postHandler(w http.ResponseWriter, r *http.Request) { + if err := r.ParseMultipartForm(_24K); nil != err { + log.Println(err) + http.Error(w, "Error occured copying to output stream", 500) + return + } + + bucket, err := getBucket() + if err != nil { + log.Println(err) + http.Error(w, "Error occured 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.Print(err) + 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.Print(err) + 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.Print(err) + http.Error(w, err.Error(), 500) + return + } + + reader, err = os.Open(file.Name()) + } else { + reader = bytes.NewReader(b.Bytes()) + } + + contentLength := n + + key := fmt.Sprintf("%s/%s", token, filename) + + log.Printf("Uploading %s %d %s", key, contentLength, contentType) + + if err = bucket.PutReader(key, reader, contentLength, contentType, s3.PublicRead, s3.Options{}); err != nil { + log.Print(err) + http.Error(w, err.Error(), 500) + return + + } + + fmt.Fprintf(w, "https://transfer.sh/%s\n", key) + } + } +} + +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("/tmp/clamd.socket") + + response, err := c.ScanStream(reader) + if err != nil { + http.Error(w, err.Error(), 500) + } + + var b string + + for s := range response { + b += s + + if !strings.HasPrefix(s, "stream: ") { + continue + } + + w.Write([]byte(fmt.Sprintf("%v\n", s[8:]))) + } +} + +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.Print(err) + http.Error(w, err.Error(), 500) + return + } + + if n > _24K { + file, err := ioutil.TempFile(config.Temp, "transfer-") + if err != nil { + log.Print(err) + 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.Print(err) + 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"])) + } + + key := fmt.Sprintf("%s/%s", Encode(10000000+int64(rand.Intn(1000000000))), filename) + + log.Printf("Uploading %s %d %s", key, contentLength, contentType) + + var b *s3.Bucket + var err error + + b, err = getBucket() + if err != nil { + http.Error(w, errors.New("Could not open bucket").Error(), 500) + return + } + + err = b.PutReader(key, reader, contentLength, contentType, s3.PublicRead, s3.Options{}) + + if err != nil { + 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://transfer.sh/%s\n", key) +} + +func zipHandler(w http.ResponseWriter, r *http.Request) { + vars := mux.Vars(r) + + files := vars["files"] + + b, err := getBucket() + if err != nil { + http.Error(w, err.Error(), 500) + return + } + + filename := 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\"", filename)) + w.Header().Set("Connection", "close") + + zw := zip.NewWriter(w) + + for _, key := range strings.Split(files, ",") { + rc, err := b.GetResponse(key) + if err != nil { + if err.Error() == "The specified key does not exist." { + http.Error(w, "File not found", 404) + return + } else { + log.Printf("%s", err.Error()) + http.Error(w, "Could not retrieve file.", 500) + return + } + } + + defer rc.Body.Close() + + header := &zip.FileHeader{ + Name: strings.Split(key, "/")[1], + Method: zip.Store, + ModifiedTime: uint16(time.Now().UnixNano()), + ModifiedDate: uint16(time.Now().UnixNano()), + } + + fi := rc.Body + + 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, fi); 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"] + + b, err := getBucket() + if err != nil { + http.Error(w, err.Error(), 500) + return + } + + filename := 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\"", filename)) + 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, ",") { + rc, err := b.GetResponse(key) + if err != nil { + if err.Error() == "The specified key does not exist." { + http.Error(w, "File not found", 404) + return + } else { + log.Printf("%s", err.Error()) + http.Error(w, "Could not retrieve file.", 500) + return + } + } + + defer rc.Body.Close() + + contentLength, err := strconv.Atoi(rc.Header.Get("Content-Length")) + + 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 + } + + fi := rc.Body + + if _, err = io.Copy(zw, fi); 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"] + + b, err := getBucket() + if err != nil { + http.Error(w, err.Error(), 500) + return + } + + filename := 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\"", filename)) + w.Header().Set("Connection", "close") + + zw := tar.NewWriter(w) + defer zw.Close() + + for _, key := range strings.Split(files, ",") { + rc, err := b.GetResponse(key) + if err != nil { + if err.Error() == "The specified key does not exist." { + http.Error(w, "File not found", 404) + return + } else { + log.Printf("%s", err.Error()) + http.Error(w, "Could not retrieve file.", 500) + return + } + } + + defer rc.Body.Close() + + contentLength, err := strconv.Atoi(rc.Header.Get("Content-Length")) + + 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 + } + + fi := rc.Body + + if _, err = io.Copy(zw, fi); 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["filename"] + + key := fmt.Sprintf("%s/%s", token, filename) + + b, err := getBucket() + if err != nil { + http.Error(w, err.Error(), 500) + return + } + + rc, err := b.GetResponse(key) + if err != nil { + if err.Error() == "The specified key does not exist." { + http.Error(w, "File not found", 404) + return + } else { + log.Printf("%s", err.Error()) + http.Error(w, "Could not retrieve file.", 500) + return + } + } + + defer rc.Body.Close() + + contentType := rc.Header.Get("Content-Type") + w.Header().Set("Content-Type", contentType) + + mediaType, _, _ := mime.ParseMediaType(contentType) + + fmt.Println(mediaType) + + switch { + case mediaType == "text/html": + w.Header().Set("Content-Disposition", fmt.Sprintf("attachment; filename=\"%s\"", filename)) + break + case strings.HasPrefix(mediaType, "text"): + case mediaType == "": + break + default: + w.Header().Set("Content-Disposition", fmt.Sprintf("attachment; filename=\"%s\"", filename)) + } + + w.Header().Set("Connection", "close") + + if _, err = io.Copy(w, rc.Body); err != nil { + http.Error(w, "Error occured copying to output stream", 500) + return + } +} + +func RedirectHandler(h http.Handler) http.HandlerFunc { + return func(w http.ResponseWriter, r *http.Request) { + if ipAddrFromRemoteAddr(r.Host) != "transfer.sh" && ipAddrFromRemoteAddr(r.Host) != "127.0.0.1" && r.URL.Path != "/health.html" { + http.Redirect(w, r, "https://transfer.sh/", 301) + return + } + + if ipAddrFromRemoteAddr(r.Host) == "transfer.sh" && r.Header.Get("X-Forwarded-Proto") != "https" && r.Method == "GET" { + http.Redirect(w, r, "https://transfer.sh/", 301) + 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) + } +} diff --git a/transfersh-server/main.go b/transfersh-server/main.go new file mode 100644 index 0000000..9e95265 --- /dev/null +++ b/transfersh-server/main.go @@ -0,0 +1,139 @@ +/* +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" + "flag" + "fmt" + "github.com/PuerkitoBio/ghost/handlers" + "github.com/gorilla/mux" + "log" + "math/rand" + "net/http" + "os" + "time" +) + +const SERVER_INFO = "transfer.sh" + +// parse request with maximum memory of _24Kilobits +const _24K = (1 << 20) * 24 + +var config struct { + AWS_ACCESS_KEY string + AWS_SECRET_KEY string + BUCKET string + VIRUSTOTAL_KEY string + Temp string +} + +func init() { + config.AWS_ACCESS_KEY = os.Getenv("AWS_ACCESS_KEY") + config.AWS_SECRET_KEY = os.Getenv("AWS_SECRET_KEY") + config.BUCKET = os.Getenv("BUCKET") + config.VIRUSTOTAL_KEY = os.Getenv("VIRUSTOTAL_KEY") + config.Temp = "" +} + +func main() { + rand.Seed(time.Now().UTC().UnixNano()) + + r := mux.NewRouter() + + r.PathPrefix("/scripts/").Handler(http.FileServer(http.Dir("./static/"))) + r.PathPrefix("/styles/").Handler(http.FileServer(http.Dir("./static/"))) + r.PathPrefix("/images/").Handler(http.FileServer(http.Dir("./static/"))) + r.PathPrefix("/fonts/").Handler(http.FileServer(http.Dir("./static/"))) + r.PathPrefix("/ico/").Handler(http.FileServer(http.Dir("./static/"))) + r.PathPrefix("/robots.txt").Handler(http.FileServer(http.Dir("./static/"))) + + r.HandleFunc("/({files:.*}).zip", zipHandler).Methods("GET") + r.HandleFunc("/({files:.*}).tar", tarHandler).Methods("GET") + r.HandleFunc("/({files:.*}).tar.gz", tarGzHandler).Methods("GET") + r.HandleFunc("/download/{token}/{filename}", getHandler).Methods("GET") + + /*r.HandleFunc("/{token}/{filename}", viewHandler).MatcherFunc(func(r *http.Request, rm *mux.RouteMatch) bool { + u, err := url.Parse(r.Referer()) + if err != nil { + log.Fatal(err) + return true + } + + if u.Host == "transfer.sh" { + return false + } + + if u.Host == "" { + return false + } + + return true + }).Methods("GET")*/ + + r.HandleFunc("/{token}/{filename}", getHandler).Methods("GET") + r.HandleFunc("/get/{token}/{filename}", getHandler).Methods("GET") + r.HandleFunc("/{filename}/virustotal", virusTotalHandler).Methods("PUT") + r.HandleFunc("/{filename}/scan", scanHandler).Methods("PUT") + r.HandleFunc("/put/{filename}", putHandler).Methods("PUT") + r.HandleFunc("/upload/{filename}", putHandler).Methods("PUT") + r.HandleFunc("/{filename}", putHandler).Methods("PUT") + r.HandleFunc("/health.html", healthHandler).Methods("GET") + r.HandleFunc("/", postHandler).Methods("POST") + r.HandleFunc("/{page}", viewHandler).Methods("GET") + r.HandleFunc("/", viewHandler).Methods("GET") + + r.NotFoundHandler = http.HandlerFunc(notFoundHandler) + + port := flag.String("port", "8080", "port number, default: 8080") + temp := flag.String("temp", "", "") + logpath := flag.String("log", "", "") + + flag.Parse() + + if *logpath != "" { + f, err := os.OpenFile(*logpath, os.O_RDWR|os.O_CREATE|os.O_APPEND, 0666) + if err != nil { + log.Fatalf("error opening file: %v", err) + } + + defer f.Close() + + log.SetOutput(f) + } + + config.Temp = *temp + log.Printf("Transfer.sh server started. :%v using temp folder: %s", *port, config.Temp) + log.Printf("---------------------------") + + s := &http.Server{ + Addr: fmt.Sprintf(":%s", *port), + Handler: handlers.PanicHandler(LoveHandler(RedirectHandler(handlers.LogHandler(r, handlers.NewLogOptions(log.Printf, "_default_")))), nil), + } + + log.Panic(s.ListenAndServe()) + log.Printf("Server stopped.") +} diff --git a/transfersh-server/run.sh.sample b/transfersh-server/run.sh.sample new file mode 100644 index 0000000..e33a047 --- /dev/null +++ b/transfersh-server/run.sh.sample @@ -0,0 +1,10 @@ +#!/bin/bash + +export BUCKET={bucket} +export AWS_ACCESS_KEY={access_key} +export AWS_SECRET_KEY={secret_key} +export VIRUSTOTAL_KEY={virustotal_key} +export PATH=$PATH:/usr/local/go/bin +export GOPATH=../go/ + +exec go run *.go diff --git a/transfersh-server/static/404.html b/transfersh-server/static/404.html new file mode 100644 index 0000000..73397ed --- /dev/null +++ b/transfersh-server/static/404.html @@ -0,0 +1,157 @@ + + + + + Page Not Found :( + + + +
+

Not found :(

+

Sorry, but the page you were trying to view does not exist.

+

It looks like this was the result of either:

+
    +
  • a mistyped address
  • +
  • an out-of-date link
  • +
+ + +
+ + \ No newline at end of file diff --git a/transfersh-server/static/favicon.ico b/transfersh-server/static/favicon.ico new file mode 100644 index 0000000..6527905 Binary files /dev/null and b/transfersh-server/static/favicon.ico differ diff --git a/transfersh-server/static/fonts/font-awesome/FontAwesome.otf b/transfersh-server/static/fonts/font-awesome/FontAwesome.otf new file mode 100644 index 0000000..8b0f54e Binary files /dev/null and b/transfersh-server/static/fonts/font-awesome/FontAwesome.otf differ diff --git a/transfersh-server/static/fonts/font-awesome/fontawesome-webfont.eot b/transfersh-server/static/fonts/font-awesome/fontawesome-webfont.eot new file mode 100644 index 0000000..7c79c6a Binary files /dev/null and b/transfersh-server/static/fonts/font-awesome/fontawesome-webfont.eot differ diff --git a/transfersh-server/static/fonts/font-awesome/fontawesome-webfont.svg b/transfersh-server/static/fonts/font-awesome/fontawesome-webfont.svg new file mode 100644 index 0000000..45fdf33 --- /dev/null +++ b/transfersh-server/static/fonts/font-awesome/fontawesome-webfont.svg @@ -0,0 +1,414 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/transfersh-server/static/fonts/font-awesome/fontawesome-webfont.ttf b/transfersh-server/static/fonts/font-awesome/fontawesome-webfont.ttf new file mode 100644 index 0000000..e89738d Binary files /dev/null and b/transfersh-server/static/fonts/font-awesome/fontawesome-webfont.ttf differ diff --git a/transfersh-server/static/fonts/font-awesome/fontawesome-webfont.woff b/transfersh-server/static/fonts/font-awesome/fontawesome-webfont.woff new file mode 100644 index 0000000..8c1748a Binary files /dev/null and b/transfersh-server/static/fonts/font-awesome/fontawesome-webfont.woff differ diff --git a/transfersh-server/static/fonts/glyphicons/glyphicons-halflings-regular.eot b/transfersh-server/static/fonts/glyphicons/glyphicons-halflings-regular.eot new file mode 100644 index 0000000..423bd5d Binary files /dev/null and b/transfersh-server/static/fonts/glyphicons/glyphicons-halflings-regular.eot differ diff --git a/transfersh-server/static/fonts/glyphicons/glyphicons-halflings-regular.svg b/transfersh-server/static/fonts/glyphicons/glyphicons-halflings-regular.svg new file mode 100644 index 0000000..4469488 --- /dev/null +++ b/transfersh-server/static/fonts/glyphicons/glyphicons-halflings-regular.svg @@ -0,0 +1,229 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/transfersh-server/static/fonts/glyphicons/glyphicons-halflings-regular.ttf b/transfersh-server/static/fonts/glyphicons/glyphicons-halflings-regular.ttf new file mode 100644 index 0000000..a498ef4 Binary files /dev/null and b/transfersh-server/static/fonts/glyphicons/glyphicons-halflings-regular.ttf differ diff --git a/transfersh-server/static/fonts/glyphicons/glyphicons-halflings-regular.woff b/transfersh-server/static/fonts/glyphicons/glyphicons-halflings-regular.woff new file mode 100644 index 0000000..d83c539 Binary files /dev/null and b/transfersh-server/static/fonts/glyphicons/glyphicons-halflings-regular.woff differ diff --git a/transfersh-server/static/fonts/transfersh.eot b/transfersh-server/static/fonts/transfersh.eot new file mode 100644 index 0000000..e443084 Binary files /dev/null and b/transfersh-server/static/fonts/transfersh.eot differ diff --git a/transfersh-server/static/fonts/transfersh.svg b/transfersh-server/static/fonts/transfersh.svg new file mode 100644 index 0000000..a68680e --- /dev/null +++ b/transfersh-server/static/fonts/transfersh.svg @@ -0,0 +1,25 @@ + + + +Generated by Fontastic.me + + + + + + + + + + + + + + + + + + + + + diff --git a/transfersh-server/static/fonts/transfersh.ttf b/transfersh-server/static/fonts/transfersh.ttf new file mode 100644 index 0000000..7a1382b Binary files /dev/null and b/transfersh-server/static/fonts/transfersh.ttf differ diff --git a/transfersh-server/static/fonts/transfersh.woff b/transfersh-server/static/fonts/transfersh.woff new file mode 100644 index 0000000..58fa54f Binary files /dev/null and b/transfersh-server/static/fonts/transfersh.woff differ diff --git a/transfersh-server/static/images/Logo-orange.png b/transfersh-server/static/images/Logo-orange.png new file mode 100644 index 0000000..e1a4df3 Binary files /dev/null and b/transfersh-server/static/images/Logo-orange.png differ diff --git a/transfersh-server/static/images/terminal.svg b/transfersh-server/static/images/terminal.svg new file mode 100644 index 0000000..b52f413 --- /dev/null +++ b/transfersh-server/static/images/terminal.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/transfersh-server/static/index.html b/transfersh-server/static/index.html new file mode 100644 index 0000000..6e19344 --- /dev/null +++ b/transfersh-server/static/index.html @@ -0,0 +1,242 @@ + + + + + + + + + + + + transfer.sh - Easy and fast file sharing from the command-line. + + + + + + + + + + + + + + + + +
+
+

+ Upload and share your files +

+ +
+
+

Easy sharing from the command-line

+
+

+
+
+
+

From web

+ +
+ + +

Drag your files here to upload or click to browse

+
+ +
    +
  • +
  • +
+
+ download all files link zip tar.gz +
+
+
+ +
+ +
+ learn more +
+
+ +
+
+
+
+ +

Made for us with shell

+
+
+ +

Share files just with a URL

+
+
+ +

Upload up to 5 GB

+
+
+ +

Files are stored for 14 days

+
+
+
+
+ +

For free

+ +
+
+ + +

Encrypt your files

+ +
+ +
+
+
+ + + +
+
+

+ Sample use cases +

+
+
+

Uploading

+

Uploading is easy using curl.

+ curl --upload-file ./hello.txt https://transfer.sh/hello.txt +

Download the file.

+ curl --upload-file ./hello.txt https://transfer.sh/hello.txt +
+
+

Make an alias

+

Create an alias, and add it to .bashrc for faster use

+ transfer() { curl --upload-file $1 https://transfer.sh/$(basename $1); }
+alias transfer=transfer
+

Now you can just use transfer command

+ transfer hello.txt + +
+
+
+
+

Transfer multiple files

+

Upload multiple files at once +

+ curl -i -F filedata=@/tmp/hello.txt -F filedata=@/tmp/hello2.txt https://transfer.sh/ +

Combining downloads as zip or tar archive

+ curl https://transfer.sh/(15HKz/hello.txt,15HKz/hello.txt).tar.gz +
+ curl https://transfer.sh/(15HKz/hello.txt,15HKz/hello.txt).zip + +
+
+

Encrypt your files before the transfer

+

You can encrypt files using gpg. The following command will encrypt the data before it leaves your server using the password you enter and upload it to transfer.sh.

+

+ cat /tmp/hello.txt|gpg -ac -o-|curl -X PUT --upload-file "-" https://transfer.sh/test.txt +

Encrypt and upload

+ curl https://transfer.sh/1lDau/test.txt|gpg -o- > /tmp/hello.txt +
+ +
+
+
+ +
+
+

Share the love

+ +
+
+ +
+
+ +

+ Any questions? +

+ contact us +
+
+ + + + + Fork me on GitHub + + + + + + + + + \ No newline at end of file diff --git a/transfersh-server/static/index.txt b/transfersh-server/static/index.txt new file mode 100644 index 0000000..eaafee8 --- /dev/null +++ b/transfersh-server/static/index.txt @@ -0,0 +1,29 @@ +transfer.sh: Easy file sharing from the command line +=== +made with <3 by DutchCoders + +Upload: +$ curl --upload-file ./hello.txt https://transfer.sh/hello.txt + +Encrypt & upload: +$ cat /tmp/hello.txt|gpg -ac -o-|curl -X PUT --upload-file "-" https://transfer.sh/test.txt + +Download & decrypt: +$ curl https://transfer.sh/1lDau/test.txt|gpg -o- > /tmp/hello.txt + +Upload to virustotal: +$ curl -X PUT --upload-file nhgbhhj https://transfer.sh/test.txt/virustotal + +Add alias to .bashrc or .zshrc: +=== +transfer() { + # write to output to tmpfile because of progress bar + tmpfile=$( mktemp -t transfer ) + curl --progress-bar --upload-file $1 https://transfer.sh/$(basename $1) >> $tmpfile; + cat $tmpfile; + rm -f $tmpfile; +} + +alias transfer=transfer +=== +$ transfer test.txt diff --git a/transfersh-server/static/robots.txt b/transfersh-server/static/robots.txt new file mode 100644 index 0000000..47c4001 --- /dev/null +++ b/transfersh-server/static/robots.txt @@ -0,0 +1,5 @@ +User-agent: * + +Allow: /$ +Disallow: / + diff --git a/transfersh-server/static/scripts/main.js b/transfersh-server/static/scripts/main.js new file mode 100644 index 0000000..44d69b3 --- /dev/null +++ b/transfersh-server/static/scripts/main.js @@ -0,0 +1,3 @@ +!function(a,b){"object"==typeof module&&"object"==typeof module.exports?module.exports=a.document?b(a,!0):function(a){if(!a.document)throw new Error("jQuery requires a window with a document");return b(a)}:b(a)}("undefined"!=typeof window?window:this,function(a,b){function c(a){var b=a.length,c=_.type(a);return"function"===c||_.isWindow(a)?!1:1===a.nodeType&&b?!0:"array"===c||0===b||"number"==typeof b&&b>0&&b-1 in a}function d(a,b,c){if(_.isFunction(b))return _.grep(a,function(a,d){return!!b.call(a,d,a)!==c});if(b.nodeType)return _.grep(a,function(a){return a===b!==c});if("string"==typeof b){if(hb.test(b))return _.filter(b,a,c);b=_.filter(b,a)}return _.grep(a,function(a){return U.call(b,a)>=0!==c})}function e(a,b){for(;(a=a[b])&&1!==a.nodeType;);return a}function f(a){var b=ob[a]={};return _.each(a.match(nb)||[],function(a,c){b[c]=!0}),b}function g(){Z.removeEventListener("DOMContentLoaded",g,!1),a.removeEventListener("load",g,!1),_.ready()}function h(){Object.defineProperty(this.cache={},0,{get:function(){return{}}}),this.expando=_.expando+Math.random()}function i(a,b,c){var d;if(void 0===c&&1===a.nodeType)if(d="data-"+b.replace(ub,"-$1").toLowerCase(),c=a.getAttribute(d),"string"==typeof c){try{c="true"===c?!0:"false"===c?!1:"null"===c?null:+c+""===c?+c:tb.test(c)?_.parseJSON(c):c}catch(e){}sb.set(a,b,c)}else c=void 0;return c}function j(){return!0}function k(){return!1}function l(){try{return Z.activeElement}catch(a){}}function m(a,b){return _.nodeName(a,"table")&&_.nodeName(11!==b.nodeType?b:b.firstChild,"tr")?a.getElementsByTagName("tbody")[0]||a.appendChild(a.ownerDocument.createElement("tbody")):a}function n(a){return a.type=(null!==a.getAttribute("type"))+"/"+a.type,a}function o(a){var b=Kb.exec(a.type);return b?a.type=b[1]:a.removeAttribute("type"),a}function p(a,b){for(var c=0,d=a.length;d>c;c++)rb.set(a[c],"globalEval",!b||rb.get(b[c],"globalEval"))}function q(a,b){var c,d,e,f,g,h,i,j;if(1===b.nodeType){if(rb.hasData(a)&&(f=rb.access(a),g=rb.set(b,f),j=f.events)){delete g.handle,g.events={};for(e in j)for(c=0,d=j[e].length;d>c;c++)_.event.add(b,e,j[e][c])}sb.hasData(a)&&(h=sb.access(a),i=_.extend({},h),sb.set(b,i))}}function r(a,b){var c=a.getElementsByTagName?a.getElementsByTagName(b||"*"):a.querySelectorAll?a.querySelectorAll(b||"*"):[];return void 0===b||b&&_.nodeName(a,b)?_.merge([a],c):c}function s(a,b){var c=b.nodeName.toLowerCase();"input"===c&&yb.test(a.type)?b.checked=a.checked:("input"===c||"textarea"===c)&&(b.defaultValue=a.defaultValue)}function t(b,c){var d,e=_(c.createElement(b)).appendTo(c.body),f=a.getDefaultComputedStyle&&(d=a.getDefaultComputedStyle(e[0]))?d.display:_.css(e[0],"display");return e.detach(),f}function u(a){var b=Z,c=Ob[a];return c||(c=t(a,b),"none"!==c&&c||(Nb=(Nb||_("