Skip to content

Commit 1c6502e

Browse files
authored
Merge pull request #1 from criticalsession/main
2 parents ad5067f + 2e5e983 commit 1c6502e

File tree

4 files changed

+128
-22
lines changed

4 files changed

+128
-22
lines changed

README.md

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,10 +10,16 @@ When executed, the program reads the specified file and creates a multipart/form
1010

1111
Upon a successful upload, the program outputs the URL to the uploaded file to the console and also **writes it to the clipboard**.
1212

13+
### Uploading multiple files
14+
15+
When using `-dir` mode, the program will loop through all files in the directory provided and upload each one. Upon completing all uploads, the program will output a table with the files uploaded and the URL generated. This output will also be saved to a file in the source directory naned `upload.xxxxx.log`.
16+
17+
The same functionality is available if more than one arg is provided (e.g. `npu file1.jpg file2.jpg`) but the log file will be stored in the executable's root directory.
18+
1319
## To do
1420

15-
- [ ] Add support for multiple files
16-
- [ ] Add support for directories
21+
- [x] Add support for multiple files
22+
- [x] Add support for directories
1723
- [ ] Add support for archiving files (with encryption)
1824
- [ ] Add configuration file
1925

src/npu/go.mod

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ go 1.20
55
require golang.design/x/clipboard v0.6.3
66

77
require (
8+
github.com/rodaine/table v1.1.0 // indirect
89
golang.org/x/exp v0.0.0-20190731235908-ec7cb31e5a56 // indirect
910
golang.org/x/image v0.0.0-20211028202545-6944b10bf410 // indirect
1011
golang.org/x/mobile v0.0.0-20210716004757-34ab1303b554 // indirect

src/npu/go.sum

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,12 @@
11
github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
2+
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
3+
github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
4+
github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI=
5+
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
6+
github.com/rodaine/table v1.1.0 h1:/fUlCSdjamMY8VifdQRIu3VWZXYLY7QHFkVorS8NTr4=
7+
github.com/rodaine/table v1.1.0/go.mod h1:Qu3q5wi1jTQD6B6HsP6szie/S4w1QUQ8pq22pz9iL8g=
8+
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
9+
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
210
github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
311
golang.design/x/clipboard v0.6.3 h1:qIAjOL1yYLzfEclnPjaoUF4FTqmk4C57LB9MBz2QwCM=
412
golang.design/x/clipboard v0.6.3/go.mod h1:kqBSweBP0/im4SZGGjLrppH0D400Hnfo5WbFKSNK8N4=
@@ -39,3 +47,5 @@ golang.org/x/tools v0.1.2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
3947
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
4048
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
4149
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
50+
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
51+
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=

src/npu/npu.go

Lines changed: 109 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -2,25 +2,34 @@ package main
22

33
import (
44
"bytes"
5+
"errors"
56
"flag"
67
"fmt"
78
"io"
8-
"io/ioutil"
99
"mime/multipart"
1010
"net/http"
1111
"os"
1212
"path/filepath"
1313
"strconv"
14+
"strings"
1415
"time"
1516

17+
"github.com/rodaine/table"
1618
"golang.design/x/clipboard"
1719
)
1820

21+
type uploadedFile struct {
22+
fileName string
23+
upUrl string
24+
}
25+
1926
func main() {
2027
expires := flag.Int("expires", 0, "set expiration time for the uploaded file in hours")
2128
secret := flag.Bool("secret", false, "enable secret mode")
29+
dirMode := flag.Bool("dir", false, "enable directory mode (non-recursive)")
30+
2231
flag.Usage = func() {
23-
fmt.Fprintf(os.Stderr, "Usage: [-expires <hours>] [-secret] <filePath>\n")
32+
fmt.Fprintf(os.Stderr, "Usage: [-expires <hours>] [-secret] [-dir] <directory/filePath>\n")
2433
flag.PrintDefaults()
2534
fmt.Print("\n")
2635
os.Exit(1)
@@ -31,21 +40,108 @@ func main() {
3140
if len(args) < 1 {
3241
flag.Usage()
3342
}
34-
filePath := args[0]
3543

44+
// construct list of files to upload
45+
filePaths := []string{}
46+
dir := "./"
47+
if *dirMode {
48+
dir = strings.TrimSuffix(strings.TrimSuffix(args[0], "/"), "\\") // trim trailing slashes
49+
50+
dirEntries, err := os.ReadDir(dir)
51+
if err != nil {
52+
fmt.Println("Failed to read files in directory:", err)
53+
os.Exit(1)
54+
}
55+
56+
for _, entry := range dirEntries {
57+
if !entry.IsDir() {
58+
filePaths = append(filePaths, fmt.Sprintf("%s/%s", dir, entry.Name()))
59+
}
60+
}
61+
} else {
62+
for _, argPath := range args {
63+
filePaths = append(filePaths, argPath)
64+
}
65+
}
66+
67+
// upload files
68+
uploadedFiles := []uploadedFile{}
69+
for _, filePath := range filePaths {
70+
responseBody, err := uploadFile(filePath, expires, secret)
71+
if err != nil {
72+
fmt.Println(err)
73+
os.Exit(1)
74+
}
75+
76+
if responseBody != nil {
77+
upUrl := string(responseBody)
78+
upUrl = strings.TrimSuffix(strings.TrimSuffix(upUrl, "\n"), "\r")
79+
80+
uploadedFiles = append(uploadedFiles, uploadedFile{
81+
fileName: filepath.Base(filePath),
82+
upUrl: upUrl,
83+
})
84+
}
85+
}
86+
87+
if len(uploadedFiles) == 1 {
88+
// original, single file upload
89+
upUrl := uploadedFiles[0].upUrl + "\n"
90+
fmt.Println(string(upUrl))
91+
clipboard.Write(clipboard.FmtText, []byte(upUrl))
92+
} else {
93+
// for multiple file upload
94+
printTblOutput(&uploadedFiles)
95+
logFile, err := writeOutputToFile(dir, &uploadedFiles)
96+
if err != nil {
97+
fmt.Println(err)
98+
os.Exit(1)
99+
}
100+
101+
fmt.Println("\nLog file saved:", logFile)
102+
}
103+
}
104+
105+
func printTblOutput(uploadedFiles *[]uploadedFile) {
106+
tbl := table.New("File", "| 0x0 Url")
107+
for _, upFile := range *uploadedFiles {
108+
tbl.AddRow(upFile.fileName, "| "+upFile.upUrl)
109+
}
110+
tbl.Print()
111+
}
112+
113+
func writeOutputToFile(dir string, uploadedFiles *[]uploadedFile) (logFile string, err error) {
114+
logFile = fmt.Sprintf("%s/%s.%d.log", dir, "upload", time.Now().Unix())
115+
f, err := os.Create(logFile)
116+
if err != nil {
117+
return "", errors.New(fmt.Sprintf("Failed to save output log file: %v", err))
118+
}
119+
defer f.Close()
120+
121+
for _, upFile := range *uploadedFiles {
122+
_, err = f.WriteString(fmt.Sprintf("%s: %s\r\n", upFile.fileName, upFile.upUrl))
123+
if err != nil {
124+
return "", errors.New(fmt.Sprintf("Failed to write to log file: %v", err))
125+
}
126+
}
127+
128+
f.Sync()
129+
130+
return logFile, nil
131+
}
132+
133+
func uploadFile(filePath string, expires *int, secret *bool) (responseBody []byte, err error) {
36134
file, err := os.Open(filePath)
37135
if err != nil {
38-
fmt.Println("Failed to open file:", err)
39-
os.Exit(1)
136+
return nil, errors.New(fmt.Sprintf("Failed to open file: %v", err))
40137
}
41138
defer file.Close()
42139

43140
body := &bytes.Buffer{}
44141
writer := multipart.NewWriter(body)
45142
part, err := writer.CreateFormFile("file", filepath.Base(filePath))
46143
if err != nil {
47-
fmt.Println("Failed to create form file:", err)
48-
os.Exit(1)
144+
return nil, errors.New(fmt.Sprintf("Failed to create form file: %v", err))
49145
}
50146
io.Copy(part, file)
51147

@@ -62,33 +158,26 @@ func main() {
62158

63159
err = writer.Close()
64160
if err != nil {
65-
fmt.Println("Failed to close multipart writer:", err)
66-
os.Exit(1)
161+
return nil, errors.New(fmt.Sprintf("Failed to close multipart writer: %v", err))
67162
}
68163

69164
request, err := http.NewRequest("POST", "https://0x0.st", body)
70165
if err != nil {
71-
fmt.Println("Failed to create HTTP request:", err)
72-
os.Exit(1)
166+
return nil, errors.New(fmt.Sprintf("Failed to create HTTP request: %v", err))
73167
}
74168
request.Header.Set("Content-Type", writer.FormDataContentType())
75169

76170
client := &http.Client{}
77171
response, err := client.Do(request)
78172
if err != nil {
79-
fmt.Println("Failed to send HTTP request:", err)
80-
os.Exit(1)
173+
return nil, errors.New(fmt.Sprintf("Failed to send HTTP request: %v", err))
81174
}
82175
defer response.Body.Close()
83176

84-
responseBody, err := ioutil.ReadAll(response.Body)
177+
responseBody, err = io.ReadAll(response.Body)
85178
if err != nil {
86-
fmt.Println("Failed to read response body:", err)
87-
os.Exit(1)
179+
return nil, errors.New(fmt.Sprintf("Failed to read response body: %v", err))
88180
}
89181

90-
if responseBody != nil {
91-
fmt.Println(string(responseBody))
92-
clipboard.Write(clipboard.FmtText, []byte(string(responseBody)))
93-
}
182+
return responseBody, nil
94183
}

0 commit comments

Comments
 (0)