diff --git a/.gitignore b/.gitignore
index a1338d6..ddfc70b 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,3 +1,5 @@
+code/
+
# Binaries for programs and plugins
*.exe
*.dll
@@ -12,3 +14,119 @@
# Project-local glide cache, RE: https://github.com/Masterminds/glide/issues/736
.glide/
+
+### JetBrains template
+# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio and Webstorm
+# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839
+
+# User-specific stuff:
+.idea/**/workspace.xml
+.idea/**/tasks.xml
+.idea/dictionaries
+
+# Sensitive or high-churn files:
+.idea/**/dataSources/
+.idea/**/dataSources.ids
+.idea/**/dataSources.xml
+.idea/**/dataSources.local.xml
+.idea/**/sqlDataSources.xml
+.idea/**/dynamic.xml
+.idea/**/uiDesigner.xml
+
+# Gradle:
+.idea/**/gradle.xml
+.idea/**/libraries
+
+# CMake
+cmake-build-debug/
+
+# Mongo Explorer plugin:
+.idea/**/mongoSettings.xml
+
+## File-based project format:
+*.iws
+
+## Plugin-specific files:
+
+# IntelliJ
+out/
+
+# mpeltonen/sbt-idea plugin
+.idea_modules/
+
+# JIRA plugin
+atlassian-ide-plugin.xml
+
+# Cursive Clojure plugin
+.idea/replstate.xml
+
+# Crashlytics plugin (for Android Studio and IntelliJ)
+com_crashlytics_export_strings.xml
+crashlytics.properties
+crashlytics-build.properties
+fabric.properties
+### Linux template
+*~
+
+# temporary files which can be created if a process still has a handle open of a deleted file
+.fuse_hidden*
+
+# KDE directory preferences
+.directory
+
+# Linux trash folder which might appear on any partition or disk
+.Trash-*
+
+# .nfs files are created when an open file is removed but is still being accessed
+.nfs*
+### macOS template
+# General
+.DS_Store
+.AppleDouble
+.LSOverride
+
+# Icon must end with two \r
+Icon
+
+# Thumbnails
+._*
+
+# Files that might appear in the root of a volume
+.DocumentRevisions-V100
+.fseventsd
+.Spotlight-V100
+.TemporaryItems
+.Trashes
+.VolumeIcon.icns
+.com.apple.timemachine.donotpresent
+
+# Directories potentially created on remote AFP share
+.AppleDB
+.AppleDesktop
+Network Trash Folder
+Temporary Items
+.apdisk
+### Windows template
+# Windows thumbnail cache files
+Thumbs.db
+ehthumbs.db
+ehthumbs_vista.db
+
+# Dump file
+*.stackdump
+
+# Folder config file
+[Dd]esktop.ini
+
+# Recycle Bin used on file shares
+$RECYCLE.BIN/
+
+# Windows Installer files
+*.cab
+*.msi
+*.msm
+*.msp
+
+# Windows shortcuts
+*.lnk
+
diff --git a/.idea/go-fische.iml b/.idea/go-fische.iml
new file mode 100644
index 0000000..c956989
--- /dev/null
+++ b/.idea/go-fische.iml
@@ -0,0 +1,8 @@
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/go.imports.xml b/.idea/go.imports.xml
new file mode 100644
index 0000000..e39c1bd
--- /dev/null
+++ b/.idea/go.imports.xml
@@ -0,0 +1,9 @@
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/modules.xml b/.idea/modules.xml
new file mode 100644
index 0000000..64e6bba
--- /dev/null
+++ b/.idea/modules.xml
@@ -0,0 +1,8 @@
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/vcs.xml b/.idea/vcs.xml
new file mode 100644
index 0000000..94a25f7
--- /dev/null
+++ b/.idea/vcs.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/watcherTasks.xml b/.idea/watcherTasks.xml
new file mode 100644
index 0000000..500f3b7
--- /dev/null
+++ b/.idea/watcherTasks.xml
@@ -0,0 +1,29 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/VERSION b/VERSION
new file mode 100644
index 0000000..0e8428a
--- /dev/null
+++ b/VERSION
@@ -0,0 +1 @@
+0.0.1
diff --git a/README.md b/docs/README.md
similarity index 100%
rename from README.md
rename to docs/README.md
diff --git a/_config.yml b/docs/_config.yml
similarity index 100%
rename from _config.yml
rename to docs/_config.yml
diff --git a/docs/logo.png b/docs/logo.png
new file mode 100644
index 0000000..d2739e4
Binary files /dev/null and b/docs/logo.png differ
diff --git a/main.go b/main.go
new file mode 100644
index 0000000..e660295
--- /dev/null
+++ b/main.go
@@ -0,0 +1,129 @@
+package main
+
+import (
+ "bufio"
+ "fmt"
+ "log"
+ "net"
+ "os"
+ "strconv"
+ "time"
+
+ "github.com/spf13/pflag"
+ "github.com/spf13/viper"
+)
+
+const AppName = "Go-Fiche"
+
+const slugMap = "AaBbCcDdEeFfGgHhIiJjKkLlMmNnOoPpQqRrSsTtUuVvWwXxYyZz0123456789"
+
+var (
+ GitCommit,
+ GitBranch,
+ GitState,
+ BuildDate,
+ Version string
+)
+
+func init() {
+ pflag.BoolP("help", "h", false, "Prints this help message")
+ pflag.StringP("output", "o", "./code", "Relative or absolute path to the directory where you want to store user-posted pastes.")
+ pflag.StringP("domain", "d", "localhost", "This will be used as a prefix for an output received by the client. Value will be prepended with http[s].")
+ pflag.IntP("port", "p", 9999, "Port in which the service should listen on.")
+ pflag.BoolP("https", "S", false, fmt.Sprintf("If set, %s returns url with https prefix instead of http.", AppName))
+ pflag.StringP("username", "u", "", fmt.Sprintf("%s will try to switch to the requested user on startup if any is provided.", AppName))
+ pflag.IntP("buffer", "B", 32768, "This parameter defines size of the buffer used for getting data from the user. Maximum size (in bytes) of all input files is defined by this value.")
+ pflag.StringP("log", "l", "", "Log file. This file has to be user-writable.")
+ pflag.Parse()
+ viper.BindPFlags(pflag.CommandLine)
+
+ if viper.GetBool("help") {
+ fmt.Printf("%s!\n Version %s, Built: %s [%s:%s-%s)\n\n", AppName, Version, BuildDate, GitBranch, GitCommit, GitState)
+ pflag.Usage()
+ os.Exit(2)
+ }
+
+ if viper.GetBool("https") {
+ viper.Set("uriprefix", "https")
+ } else {
+ viper.Set("uriprefix", "http")
+ }
+}
+
+func main() {
+ log.Printf("Starting %s on %s...", AppName, time.Now().Format(time.RFC822Z))
+ listener, err := net.Listen("tcp", fmt.Sprintf(":%d", viper.Get("port")))
+ if err != nil {
+ // handle error
+ log.Fatalf("Could not bind to port: %d!", viper.Get("port"))
+ os.Exit(-1)
+ }
+ log.Printf("Server started listening on port: %d.", viper.Get("port"))
+ for {
+ conn, err := listener.Accept()
+ if err != nil {
+ log.Printf("Error on accepting connection!")
+ continue
+ }
+ go handleConnection(conn)
+ }
+ time.Sleep(5)
+ os.Exit(0)
+}
+
+func generateSlug(seed int64) string {
+ stringSeed := fmt.Sprintf("%d", seed)
+ evenLength := (len(stringSeed) / 2) * 2
+ digitHold := int64(0)
+ out := ""
+ for i := 0; i < evenLength; i += 2 {
+ digitHold, _ = strconv.ParseInt(stringSeed[i:i+2], 10, 8)
+ digitHold = digitHold % int64(len(slugMap))
+ out += slugMap[digitHold : digitHold+1]
+ }
+ return out
+}
+
+func handleConnection(conn net.Conn) {
+ remoteHost, err := net.LookupCNAME(conn.RemoteAddr().String())
+ if err != nil {
+ remoteHost = conn.RemoteAddr().String()
+ }
+ defer func() {
+ log.Printf("Closing connection to: %s (%s).\n", remoteHost, conn.RemoteAddr())
+ conn.Close()
+ }()
+ log.Printf("Incoming connection from: %s (%s).\n", remoteHost, conn.RemoteAddr())
+ conn.SetReadDeadline(time.Now().Add(time.Second * 5))
+
+ slug := generateSlug(time.Now().UnixNano())
+ slugFullpath := fmt.Sprintf("%s/%s", viper.GetString("output"), slug)
+ log.Printf("Writing file to %s", slugFullpath)
+
+ file, err := os.Create(slugFullpath)
+ if err != nil {
+ log.Fatalf("Unable to create slug file: %s", slugFullpath)
+ return
+ }
+
+ scanner := bufio.NewScanner(conn)
+ for scanner.Scan() {
+ file.WriteString(scanner.Text())
+ file.Write([]byte("\n"))
+ conn.SetReadDeadline(time.Now().Add(time.Second * 5))
+ }
+
+ stat, _ := file.Stat()
+ if stat.Size() <= 0 {
+ file.Close()
+ log.Printf("0 bytes received, aborting...")
+ os.Remove(slugFullpath)
+ return
+ } else {
+ file.Close()
+ }
+
+ log.Printf("Wrote %d bytes to %s", stat.Size(), slugFullpath)
+
+ conn.Write([]byte(fmt.Sprintf("%s://%s/%s\r\n", viper.GetString("uriprefix"), viper.GetString("domain"), slug)))
+}