123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260 |
- package main
- import (
- "errors"
- "fmt"
- "log"
- "math/rand"
- "os"
- "flag"
- "gopkg.in/yaml.v2"
- "net/http"
- "io/ioutil"
- "bytes"
- "mime/multipart"
- "encoding/json"
- )
- /**
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see http://www.gnu.org/licenses/gpl-3.0.
- *
- * 2019 - 2023 https://://www.bananas-playground.net/projekt/selfpaste
- */
- /**
- * !WARNING!
- * This is a very simple, with limited experience written, go program.
- * Use at own risk and feel free to improve
- */
- const website = "https://www.bananas-playground.net/projekt/selfpaste"
- const version = "1.0"
- // used for non-existing default config
- const letters = "abcdefghijklmnopqrstuvwxyz0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ-_"
- // command line parameters
- var optsVerbose bool
- var optsCreateConfig bool
- var optsDebug bool
- // config
- var cfg Config
- // config file struct
- type Config struct {
- Endpoint struct {
- Host string `yaml:"host"`
- Secret string `yaml:"secret"`
- } `yaml:"endpoint"`
- }
- // response json struct
- type Response struct {
- Message string `json:"message"`
- Status int `json:"status"`
- }
- /**
- * Main
- */
- func main() {
- // parse commandline parameters
- flag.BoolVar(&optsVerbose, "verbose", false, "Produce verbose output")
- flag.BoolVar(&optsCreateConfig, "create-config-file", false, "Create default config file")
- flag.BoolVar(&optsDebug, "debug", false, "Print debug infos")
- flag.Parse()
- if optsDebug {
- fmt.Println("verbose:", optsVerbose)
- fmt.Println("create-config-file:", optsCreateConfig)
- fmt.Println("debug:", optsDebug)
- }
- // load the config and populate Config
- loadConfig()
- // get the paylout
- payload := getInput()
- if optsDebug { log.Println(payload) }
- // do the upload and get the response
- responseString := uploadCall(payload)
- response := Response{}
- json.Unmarshal([]byte(responseString), &response)
- // print the result and link to the pasty
- fmt.Printf("Status: %d\n", response.Status)
- fmt.Printf("Message: %s\n", response.Message)
- }
- /**
- * Check and display error with additional message
- */
- func errorCheck(e error, msg string) {
- if e != nil {
- log.Fatal(msg,e)
- }
- }
- /**
- * just a random string
- */
- func randStringBytes(n int) string {
- b := make([]byte, n)
- for i := range b {
- b[i] = letters[rand.Intn(len(letters))]
- }
- return string(b)
- }
- /**
- * load or even create a default config
- * $HOME/.selfpaste.yaml
- */
- func loadConfig() {
- homeDir, err := os.UserHomeDir()
- errorCheck(err, "No $HOME directory available?")
- if optsVerbose { log.Printf("Your $HOME: %s \n", homeDir) }
- var configFile = homeDir + "/.selfpaste.yaml"
- if _, err := os.Stat(configFile); errors.Is(err, os.ErrNotExist) {
- log.Printf("Config file not found: %s \n", configFile)
- if optsCreateConfig {
- log.Printf("Creating new default config file: %s \n", configFile)
- newConfig, err := os.Create(configFile)
- errorCheck(err, "Can not create config file!")
- defer newConfig.Close()
- _, err = fmt.Fprintf(newConfig, "# selfpaste go client config file.\n")
- errorCheck(err, "Can not write to new config file")
- fmt.Fprintf(newConfig, "# See %s for more details.\n", website)
- fmt.Fprintf(newConfig, "# Version: %s\n", version)
- fmt.Fprintf(newConfig, "endpoint:\n")
- fmt.Fprintf(newConfig, " host: http://your-seflpaste-endpoi.nt\n")
- fmt.Fprintf(newConfig, " secret: %s\n", randStringBytes(50))
- log.Fatalf("New default config file created: - %s - Edit and launch again!",configFile)
- }
- }
- existingConfigFile, err := os.Open(configFile)
- errorCheck(err, "Can not open config file")
- defer existingConfigFile.Close()
- if optsVerbose { log.Printf("Reading config file: %s \n", configFile) }
- var decoder = yaml.NewDecoder(existingConfigFile)
- err = decoder.Decode(&cfg)
- errorCheck(err, "Can not decode config file")
- if cfg.Endpoint.Host == "" || cfg.Endpoint.Secret == "" {
- log.Fatal("Empty config?")
- }
- if optsDebug {
- log.Println(cfg.Endpoint.Host)
- log.Println(cfg.Endpoint.Secret)
- }
- }
- /**
- * Do a http POST call to the defined endpoint
- * and upload the payload
- * Return response body as string
- */
- func uploadCall(payload string) string {
- if optsVerbose { log.Println("Starting to upload data") }
- if optsDebug { log.Println(payload) }
- if len(payload) == 0 {
- log.Fatal("Nothing provided to upload")
- }
- // Buffer to store our request body as bytes
- var requestBody bytes.Buffer
- // Create a multipart writer
- multiPartWriter := multipart.NewWriter(&requestBody)
- // file field
- fileWriter, err := multiPartWriter.CreateFormFile("pasty", "pastyfile")
- errorCheck(err, "Can not create form file field")
- fileWriter.Write([]byte(payload))
- dlField, err := multiPartWriter.CreateFormField("dl")
- errorCheck(err, "Can not create form dl field")
- dlField.Write([]byte(cfg.Endpoint.Secret))
- multiPartWriter.Close()
- req, err := http.NewRequest(http.MethodPost, cfg.Endpoint.Host, &requestBody)
- errorCheck(err, "Can not create http request")
- // We need to set the content type from the writer, it includes necessary boundary as well
- req.Header.Set("Content-Type", multiPartWriter.FormDataContentType())
- req.Header.Set("User-Agent", "selfpasteAgent/1.0");
- // Do the request
- client := &http.Client{}
- response, err := client.Do(req)
- errorCheck(err, "POST request failed")
- responseBody, err := ioutil.ReadAll(response.Body)
- errorCheck(err, "Can not read response body")
- if optsVerbose { log.Println("Request done") }
- if optsDebug {
- log.Printf("Response status code: %d\n",response.StatusCode)
- log.Printf("Response headers: %#v\n", response.Header)
- log.Println(string(responseBody))
- }
- return string(responseBody)
- }
- /**
- * check if file is provided as commandline argument
- * or piped into
- * return the read data as string
- */
- func getInput() string {
- if optsVerbose { log.Println("Getting input") }
- var inputString string
- if filename := flag.Arg(0); filename != "" {
- if optsVerbose { log.Println("Read from file argument") }
- bytes, err := os.ReadFile(filename)
- errorCheck(err, "Error opening file")
- inputString = string(bytes)
- } else {
- stat, _ := os.Stdin.Stat()
- if (stat.Mode() & os.ModeCharDevice) == 0 {
- if optsVerbose { log.Println("data is being piped") }
- bytes, _ := ioutil.ReadAll(os.Stdin)
- inputString = string(bytes)
- }
- }
- if len(inputString) == 0 {
- log.Fatal("Nothing provided to upload")
- }
- return inputString
- }
|