1 package gmitohtml
2
3 import (
4 "bufio"
5 "bytes"
6 "errors"
7 "fmt"
8 "html"
9 "net/url"
10 "path"
11 "strings"
12 "sync"
13 )
14
15
16 var ErrInvalidURL = errors.New("invalid URL")
17
18 var daemonAddress string
19
20 var assetLock sync.Mutex
21
22 var imageExtensions = []string{"png", "jpg", "jpeg", "gif", "svg", "webp"}
23
24 func rewriteURL(u string, loc *url.URL) string {
25 if daemonAddress == "" {
26 return u
27 }
28
29 if loc.Path == "" {
30 loc.Path = "/"
31 }
32
33 scheme := "gemini"
34 if strings.HasPrefix(loc.Path, "/file/") {
35 scheme = "file"
36 }
37
38 if strings.HasPrefix(u, "file://") {
39 if !allowFileAccess {
40 return "http://" + daemonAddress + "/?FileAccessNotAllowed"
41 }
42 return "http://" + daemonAddress + "/file/" + u[7:]
43 }
44
45 offset := 0
46 if strings.HasPrefix(u, "gemini://") {
47 offset = 9
48 }
49 firstSlash := strings.IndexRune(u[offset:], '/')
50 if firstSlash != -1 {
51 u = strings.ToLower(u[:firstSlash+offset]) + u[firstSlash+offset:]
52 }
53
54 if strings.HasPrefix(u, "gemini://") {
55 return "http://" + daemonAddress + "/gemini/" + u[9:]
56 } else if strings.Contains(u, "://") {
57 return u
58 } else if loc != nil && len(u) > 0 && !strings.HasPrefix(u, "//") {
59 if u[0] != '/' {
60 if loc.Path[len(loc.Path)-1] == '/' {
61 u = path.Join("/", loc.Path, u)
62 } else {
63 u = path.Join("/", path.Dir(loc.Path), u)
64 }
65 }
66 return "http://" + daemonAddress + "/" + scheme + "/" + strings.ToLower(loc.Host) + u
67 }
68 return "http://" + daemonAddress + "/" + scheme + "/" + u
69 }
70
71 func newPage() []byte {
72 data := []byte(pageHeader)
73 if daemonAddress != "" {
74 data = append(data, navHeader...)
75 }
76 return append(data, contentHeader...)
77 }
78
79
80 func Convert(page []byte, u string) []byte {
81 var result []byte
82
83 var preformatted bool
84
85 parsedURL, err := url.Parse(u)
86 if err != nil {
87 parsedURL = nil
88 err = nil
89 }
90
91 scanner := bufio.NewScanner(bytes.NewReader(page))
92 for scanner.Scan() {
93 line := scanner.Bytes()
94 l := len(line)
95 if l >= 3 && string(line[0:3]) == "```" {
96 preformatted = !preformatted
97 if preformatted {
98 result = append(result, []byte("<pre>\n")...)
99 } else {
100 result = append(result, []byte("</pre>\n")...)
101 }
102 continue
103 }
104
105 if preformatted {
106 result = append(result, html.EscapeString(string(line))...)
107 result = append(result, []byte("\n")...)
108 continue
109 }
110
111 if l >= 6 && bytes.HasPrefix(line, []byte("=>")) {
112 splitStart := 2
113 if line[splitStart] == ' ' || line[splitStart] == '\t' {
114 splitStart++
115 }
116
117 var split [][]byte
118 firstSpace := bytes.IndexRune(line[splitStart:], ' ')
119 firstTab := bytes.IndexRune(line[splitStart:], '\t')
120 if firstSpace != -1 && (firstTab == -1 || firstSpace < firstTab) {
121 split = bytes.SplitN(line[splitStart:], []byte(" "), 2)
122 } else if firstTab != -1 {
123 split = bytes.SplitN(line[splitStart:], []byte("\t"), 2)
124 }
125
126 var linkURL []byte
127 var linkLabel []byte
128 if len(split) == 2 {
129 linkURL = split[0]
130 linkLabel = split[1]
131 } else {
132 linkURL = line[splitStart:]
133 linkLabel = line[splitStart:]
134 }
135
136 parts := strings.Split(string(linkURL), ".")
137 extension := parts[len(parts)-1]
138 isImage := false
139 for _, ext := range imageExtensions {
140 if extension == ext {
141 isImage = true
142 }
143 }
144
145 uri := html.EscapeString(rewriteURL(string(linkURL), parsedURL))
146 title := html.EscapeString(string(linkLabel))
147
148
149 if isImage && Config.ConvertImages {
150 result = append(result, []byte("<img src=\""+uri+"\" alt=\""+title+"\">")...)
151 } else {
152 result = append(result, []byte("<a href=\""+uri+"\">"+title+"</a><br>")...)
153 }
154
155 continue
156 }
157
158 heading := 0
159 for i := 0; i < l; i++ {
160 if line[i] == '#' {
161 heading++
162 } else {
163 break
164 }
165 }
166 if heading > 0 {
167 result = append(result, []byte(fmt.Sprintf("<h%d>%s</h%d>", heading, html.EscapeString(string(line[heading:])), heading))...)
168 continue
169 }
170
171 result = append(result, html.EscapeString(string(line))...)
172 result = append(result, []byte("<br>")...)
173 }
174
175 if preformatted {
176 result = append(result, []byte("</pre>\n")...)
177 }
178
179 data := newPage()
180 data = append(data, result...)
181 data = append(data, []byte(pageFooter)...)
182 return fillTemplateVariables(data, u, false)
183 }
184
View as plain text