utils.go (view raw)
1package main
2
3import (
4 "archive/zip"
5 "fmt"
6 "io"
7 "mime"
8 "os"
9 "path"
10 "path/filepath"
11 "strings"
12 "time"
13 "unicode/utf8"
14)
15
16// Check if it is a text file, first by checking mimetype, then by reading bytes
17// Stolen from https://github.com/golang/tools/blob/master/godoc/util/util.go
18func isTextFile(fullPath string) bool {
19 isText := strings.HasPrefix(mime.TypeByExtension(path.Ext(fullPath)), "text")
20 if isText {
21 return true
22 }
23 const max = 1024 // at least utf8.UTFMax
24 s := make([]byte, 1024)
25 f, err := os.Open(fullPath)
26 if os.IsNotExist(err) {
27 return true // for the purposes of editing, we return true
28 }
29 n, err := f.Read(s)
30 s = s[0:n]
31 if err != nil {
32 return false
33 }
34 f.Close()
35
36 for i, c := range string(s) {
37 if i+utf8.UTFMax > len(s) {
38 // last char may be incomplete - ignore
39 break
40 }
41 if c == 0xFFFD || c < ' ' && c != '\n' && c != '\t' && c != '\f' {
42 // decoding error or control character - not a text file
43 return false
44 }
45 }
46 return true
47}
48
49// get the user-reltaive local path from the filespath
50// NOTE -- dont use on unsafe input ( I think )
51func getLocalPath(filesPath string) string {
52 l := len(strings.Split(c.FilesDirectory, "/"))
53 return strings.Join(strings.Split(filesPath, "/")[l+1:], "/")
54}
55
56func getCreator(filePath string) string {
57 l := len(strings.Split(c.FilesDirectory, "/"))
58 r := strings.Split(filePath, "/")[l]
59 return r
60}
61
62func isGemini(filename string) bool {
63 extension := path.Ext(filename)
64 return extension == ".gmi" || extension == ".gemini"
65}
66
67func timeago(t *time.Time) string {
68 d := time.Since(*t)
69 if d.Seconds() < 60 {
70 seconds := int(d.Seconds())
71 if seconds == 1 {
72 return "1 second ago"
73 }
74 return fmt.Sprintf("%d seconds ago", seconds)
75 } else if d.Minutes() < 60 {
76 minutes := int(d.Minutes())
77 if minutes == 1 {
78 return "1 minute ago"
79 }
80 return fmt.Sprintf("%d minutes ago", minutes)
81 } else if d.Hours() < 24 {
82 hours := int(d.Hours())
83 if hours == 1 {
84 return "1 hour ago"
85 }
86 return fmt.Sprintf("%d hours ago", hours)
87 } else {
88 days := int(d.Hours()) / 24
89 if days == 1 {
90 return "1 day ago"
91 }
92 return fmt.Sprintf("%d days ago", days)
93 }
94}
95
96// safe
97func getUserDirectory(username string) string {
98 // extra filepath.clean just to be safe
99 userFolder := path.Join(c.FilesDirectory, filepath.Clean(username))
100 return userFolder
101}
102
103// ugh idk
104func safeGetFilePath(username string, filename string) string {
105 return path.Join(getUserDirectory(username), filepath.Clean(filename))
106}
107
108// TODO move into checkIfValidFile. rename it
109func userHasSpace(user string, newBytes int) bool {
110 userPath := path.Join(c.FilesDirectory, user)
111 size, err := dirSize(userPath)
112 if err != nil || size+int64(newBytes) > c.MaxUserBytes {
113 return false
114 }
115 return true
116}
117
118func dirSize(path string) (int64, error) {
119 var size int64
120 err := filepath.Walk(path, func(_ string, info os.FileInfo, err error) error {
121 if err != nil {
122 return err
123 }
124 if !info.IsDir() {
125 size += info.Size()
126 }
127 return err
128 })
129 return size, err
130}
131
132/// Perform some checks to make sure the file is OK
133func checkIfValidFile(filename string, fileBytes []byte) error {
134 if len(filename) == 0 {
135 return fmt.Errorf("Please enter a filename")
136 }
137 if len(filename) > 256 { // arbitrarily chosen
138 return fmt.Errorf("Filename is too long")
139 }
140 ext := strings.ToLower(path.Ext(filename))
141 found := false
142 for _, mimetype := range c.OkExtensions {
143 if ext == mimetype {
144 found = true
145 }
146 }
147 if !found {
148 return fmt.Errorf("Invalid file extension: %s", ext)
149 }
150 if len(fileBytes) > c.MaxFileBytes {
151 return fmt.Errorf("File too large. File was %d bytes, Max file size is %d", len(fileBytes), c.MaxFileBytes)
152 }
153 //
154 return nil
155}
156
157func zipit(source string, target io.Writer) error {
158 archive := zip.NewWriter(target)
159
160 info, err := os.Stat(source)
161 if err != nil {
162 return nil
163 }
164
165 var baseDir string
166 if info.IsDir() {
167 baseDir = filepath.Base(source)
168 }
169
170 filepath.Walk(source, func(path string, info os.FileInfo, err error) error {
171 if err != nil {
172 return err
173 }
174
175 header, err := zip.FileInfoHeader(info)
176 if err != nil {
177 return err
178 }
179
180 if baseDir != "" {
181 header.Name = filepath.Join(baseDir, strings.TrimPrefix(path, source))
182 }
183
184 if info.IsDir() {
185 header.Name += "/"
186 } else {
187 header.Method = zip.Deflate
188 }
189
190 writer, err := archive.CreateHeader(header)
191 if err != nil {
192 return err
193 }
194
195 if info.IsDir() {
196 return nil
197 }
198
199 file, err := os.Open(path)
200 if err != nil {
201 return err
202 }
203 defer file.Close()
204 _, err = io.Copy(writer, file)
205 return err
206 })
207
208 archive.Close()
209
210 return err
211}