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