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
49func isGemini(filename string) bool {
50 extension := path.Ext(filename)
51 return extension == ".gmi" || extension == ".gemini"
52}
53
54func timeago(t *time.Time) string {
55 d := time.Since(*t)
56 if d.Seconds() < 60 {
57 seconds := int(d.Seconds())
58 if seconds == 1 {
59 return "1 second ago"
60 }
61 return fmt.Sprintf("%d seconds ago", seconds)
62 } else if d.Minutes() < 60 {
63 minutes := int(d.Minutes())
64 if minutes == 1 {
65 return "1 minute ago"
66 }
67 return fmt.Sprintf("%d minutes ago", minutes)
68 } else if d.Hours() < 24 {
69 hours := int(d.Hours())
70 if hours == 1 {
71 return "1 hour ago"
72 }
73 return fmt.Sprintf("%d hours ago", hours)
74 } else {
75 days := int(d.Hours()) / 24
76 if days == 1 {
77 return "1 day ago"
78 }
79 return fmt.Sprintf("%d days ago", days)
80 }
81}
82
83// safe
84func getUserDirectory(username string) string {
85 // extra filepath.clean just to be safe
86 userFolder := path.Join(c.FilesDirectory, filepath.Clean(username))
87 return userFolder
88}
89
90// ugh idk
91func safeGetFilePath(username string, filename string) string {
92 return path.Join(getUserDirectory(username), filepath.Clean(filename))
93}
94
95// TODO move into checkIfValidFile. rename it
96func userHasSpace(user string, newBytes int) bool {
97 userPath := path.Join(c.FilesDirectory, user)
98 size, err := dirSize(userPath)
99 if err != nil || size+int64(newBytes) > c.MaxUserBytes {
100 return false
101 }
102 return true
103}
104
105func dirSize(path string) (int64, error) {
106 var size int64
107 err := filepath.Walk(path, func(_ string, info os.FileInfo, err error) error {
108 if err != nil {
109 return err
110 }
111 if !info.IsDir() {
112 size += info.Size()
113 }
114 return err
115 })
116 return size, err
117}
118
119/// Perform some checks to make sure the file is OK
120func checkIfValidFile(filename string, fileBytes []byte) error {
121 if len(filename) == 0 {
122 return fmt.Errorf("Please enter a filename")
123 }
124 if len(filename) > 256 { // arbitrarily chosen
125 return fmt.Errorf("Filename is too long")
126 }
127 ext := strings.ToLower(path.Ext(filename))
128 found := false
129 for _, mimetype := range c.OkExtensions {
130 if ext == mimetype {
131 found = true
132 }
133 }
134 if !found {
135 return fmt.Errorf("Invalid file extension: %s", ext)
136 }
137 if len(fileBytes) > c.MaxFileBytes {
138 return fmt.Errorf("File too large. File was %d bytes, Max file size is %d", len(fileBytes), c.MaxFileBytes)
139 }
140 //
141 return nil
142}
143
144func zipit(source string, target io.Writer) error {
145 archive := zip.NewWriter(target)
146
147 info, err := os.Stat(source)
148 if err != nil {
149 return nil
150 }
151
152 var baseDir string
153 if info.IsDir() {
154 baseDir = filepath.Base(source)
155 }
156
157 filepath.Walk(source, func(path string, info os.FileInfo, err error) error {
158 if err != nil {
159 return err
160 }
161
162 header, err := zip.FileInfoHeader(info)
163 if err != nil {
164 return err
165 }
166
167 if baseDir != "" {
168 header.Name = filepath.Join(baseDir, strings.TrimPrefix(path, source))
169 }
170
171 if info.IsDir() {
172 header.Name += "/"
173 } else {
174 header.Method = zip.Deflate
175 }
176
177 writer, err := archive.CreateHeader(header)
178 if err != nil {
179 return err
180 }
181
182 if info.IsDir() {
183 return nil
184 }
185
186 file, err := os.Open(path)
187 if err != nil {
188 return err
189 }
190 defer file.Close()
191 _, err = io.Copy(writer, file)
192 return err
193 })
194
195 archive.Close()
196
197 return err
198}