@@ -3,10 +3,10 @@
import ( "database/sql" "fmt" + "git.sophuwu.com/gophuwu/flags" _ "github.com/glebarez/go-sqlite" "os" "path/filepath" - "git.sophuwu.com/gophuwu/flags" ) var DBPATH, INBOX, SAVEPATH string@@ -37,15 +37,18 @@ if _, err = os.Stat(mailbox); os.IsNotExist(err) {
os.MkdirAll(mailbox, 0700) } - INBOX = filepath.Join(mailbox, "inbox", "new") + INBOX, err = filepath.Abs(filepath.Join(mailbox, "inbox", "new")) + ChkErr(err) if _, err = os.Stat(INBOX); os.IsNotExist(err) { os.MkdirAll(INBOX, 0700) } - SAVEPATH = filepath.Join(mailbox, "saved") + SAVEPATH, err = filepath.Abs(filepath.Join(mailbox, "saved")) + ChkErr(err) if _, err = os.Stat(SAVEPATH); os.IsNotExist(err) { os.MkdirAll(SAVEPATH, 0700) } - DBPATH = filepath.Join(mailbox, "mailbox.sqlite") + DBPATH, err = filepath.Abs(filepath.Join(mailbox, "mailbox.sqlite")) + ChkErr(err) } func readRows(rows *sql.Rows) ([]EmailMeta, error) {
@@ -11,6 +11,18 @@ <body>
<div class="container"> <div class="header sidebyside"> <div class="nomargin nopadding sidebyside" style="width: 200px;"><h1>Samael</h1><h6>some<br>email</h6></div> + <div class="nomargin nopadding sidebyside" style="width: 200px;"> <form action="." class="pageform" method="get"> + <div class="sidebyside"> + <button onclick="pageUpOrDown(this);"><</button> + <span>Page {{ .Page }} of {{ .TotalPages }}</span> + <button onclick="pageUpOrDown(this);">></button> + </div> + <input type="hidden" id="pageform" name="page" value="{{ .Page }}" /> + <input type="hidden" name="to" value="{{ .ToAddr }}" /> + <input type="hidden" name="from" value="{{ .FromAddr }}" /> + <input type="hidden" name="subject" value="{{ .Subject }}" /> + <input type="hidden" name="date" value="{{ .Date }}"/> + </form></div> <div class="nomargin nopadding" style="width: calc(100% - 400px);"> <center><input autocomplete="off" style="width: 100%; max-width: 800px;" id="q" name="q" type="text" onchange="SearchInbox()" placeholder="Search Mail"></center> </div>@@ -21,37 +33,37 @@ </div>
<div class="main sidebyside" style="width: 100%; height: 100%;"> <div id="inboxframe" class="divframe"> {{ range .HtmlMetas }} - <div className="inbox-entry" onClick="OpenMail('{{ .Id }}')"> + <div class="inbox-entry" onClick="OpenMail('{{ .Id }}')"> - <div className="sidebyside"> - <p className="inbox-from" title="{{ .FromAddr }}"> + <div class="sidebyside"> + <p class="inbox-from" title="{{ .FromAddr }}"> {{ if .FromName }} {{ .FromName }} {{ else }} {{ .FromAddr }} {{ end }} </p> - <p className="inbox-to" title="{{ .ToAddr }}"> + <p class="inbox-to" title="{{ .ToAddr }}"> {{ if .ToName }} {{ .ToName }} {{ else }} {{ .ToAddr }} {{ end }} </p> - <p className="inbox-date"> + <p class="inbox-date"> {{ .Date }} </p> </div> - <p className="inbox-subject"> + <p class="inbox-subject"> {{ .Subject }} </p> - <hr/> </div> + <hr> {{ end }} </div> <iframe referrerpolicy="no-referrer" loading="lazy" sandbox id="contentframe" src="" style="visibility: hidden; width: 0;"></iframe> </div> - <iframe sandbox="" id="sendframe" src="/send.html"></iframe> + <iframe id="sendframe" src="/send.html"></iframe> </div> </body> </html>
@@ -75,4 +75,17 @@ console.log(err);
if (err.code === 404) document.getElementById("inboxframe").innerHTML = "<p>No results found.</p>"; else document.getElementById("inboxframe").innerHTML = "<p>An error occurred.</p>"; } +} + +function pageUpOrDown(e) { + let pf = document.getElementById("pageform"); + let v = parseInt(pf.value); + if (e.innerHTML === "<" || e.innerText === "<") { + pf.value = (v-1).toString(); + } else if (e.innerHTML === ">" || e.innerText === ">") { + pf.value = (v+1).toString(); + } else { + return; + } + pf.form.submit(); }
@@ -0,0 +1,34 @@
+<!DOCTYPE html> +<html lang="en" style="background-color: transparent;"> +<head> + <link rel="stylesheet" href="style.css"> + <link rel="stylesheet" href="font.css"> + <title>Samael</title> + <script> + function ToggleDomain() { + let dom = document.getElementById("domain").value; + if (dom === "@sophuwu.site") { + document.getElementById("domain").value = "@skisiel.com"; + } else { + document.getElementById("domain").value = "@sophuwu.site"; + } + } + </script> +</head> +<body style="background-color: transparent;"> +<form style="height: 100%; width: 100%; background-color: transparent;" action="/send" method="post"> + <div style="height: calc(100% - 45px); background-color: transparent; border: none;" class="coldiv"> + <span style="background: #242424; " class="inputlike topin">Compose</span> + <input class="botin topin" type="text" name="recipient" autocomplete="off" placeholder="Recipient" /> + <input class="topin botin" type="text" name="subject" autocomplete="off" placeholder="Subject" /> + <textarea class="inputlike topin botin" style="height: 100%;" autocomplete="off" name="body" wrap="soft"></textarea> + </div> + <div class="sidebyside" style="background-color: transparent; padding: 0 2px;"> + <input class="botin leftin" style="width: 140px;" autocomplete="off" type="text" name="from" placeholder="From" /> + <input type="text" readonly class="rightin botin leftin" id="domain" style="width: 110px; cursor: pointer;" onclick="ToggleDomain()" value="@sophuwu.site"/> + <span class="inputlike leftin botin rightin"><br></span> + <input type="submit" class="inputlike rightin botin" style="width: 80px; cursor: pointer;" value="Send" /> + </div> +</form> +</body> +</html>
@@ -4,13 +4,16 @@ left: 50%;
transform: translate(-50%, 0%); display: flex; flex-direction: column; - height: calc( 100% - 20px ); - width: calc( 100% - 50px ); + height: calc( 100% - 5lh ); + width: calc( 100% - 1lh ); min-width: 550px; } +#contentframe { + zoom: 1.33; +} iframe, .divframe { border: 0; - margin: 5px 0 0; + margin: 0 0; padding: 0; width: 100%; height: 100%;@@ -39,9 +42,6 @@ padding: 0 10px;
} .inbox-subject { color: #999999; -} -hr { - color: #333333; } .nomargin { margin: 0!important;@@ -103,8 +103,9 @@ padding: 0;
background-color: #262833; } .header { - padding: 10px 0; - border-bottom: #444444 solid 1px; + padding: 0.5lh 0; + margin: 0 0 -1ch 0; + border-bottom: #666 solid 1px; } .sidebyside { display: flex;@@ -127,9 +128,9 @@ justify-content: center;
align-items: center; } .main { - margin: 0; - padding: 20px 0 0 0; - height: calc(100% - 50px); + margin: 0.5lh 0; + padding: 0; + height: calc(100% - 2lh); } li { margin: 5px;@@ -150,8 +151,9 @@ font-size: small;
} *{ color: white; +} +body, html, .container, .divframe, .main{ background-color: #262833; - background: #262833; } ::-webkit-scrollbar { width: 5px;@@ -216,3 +218,42 @@ -webkit-user-select: none;
touch-action: manipulation; will-change: transform; } + +.sidebyside { + display: flex; + flex-direction: row; + width: 100%; + align-items: center; + align-content: space-between; +} +.inbox-entry p { + padding: 0; + margin: 0.3lh 0; +} +.inbox-entry, hr { + margin: 0.5lh 0 0.25lh; + padding: 0.5lh 1ch; + width: calc(100% - 2lh ); + border-radius: 0.5lh; +} +/* #inboxframe, { + padding: 0; + margin: 0; + height: 100%; + width: 100%; +} */ +.inbox-entry:hover { + background-color: #0004; + +} +hr{ + margin-top: 0; + margin-bottom: 0; + padding-top: 0; + padding-bottom: 0; + color: #666; + height: 0; +} +#inboxframe > hr:last-of-type { + display: none; +}
@@ -9,8 +9,10 @@ "github.com/microcosm-cc/bluemonday"
"golang.org/x/sys/unix" "html" "html/template" + "mime" "net/http" "net/mail" + "net/url" "os" "os/signal" "path/filepath"@@ -21,6 +23,9 @@ )
//go:embed templates/index.html var htmlTemplate string + +//go:embed templates/send.html +var sendTemplate string //go:embed templates/style.css var cssText string@@ -145,6 +150,11 @@ w.Header().Set("Content-Type", "application/javascript; charset=utf-8")
fmt.Fprint(w, jsText) return } + if r.URL.Path == "/send.html" { + w.Header().Set("Content-Type", "text/html; charset=utf-8") + fmt.Fprint(w, sendTemplate) + return + } h := NewHandle(w, r) r.ParseForm() if r.URL.Path == "/" || r.URL.Path == "/api" {@@ -167,7 +177,7 @@ if err != nil {
TempErr(w, 404) return } - html = fmt.Sprintf(`<!DOCTYPE html><html><head><meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1"></head><body style="background-color: #262855; color: white;"><pre>%s</pre></body></html>`, string(b)) + html = fmt.Sprintf(`<!DOCTYPE html><html><head><meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1"></head><body style="background-color: #262855; color: white;"><pre style="text-wrap: wrap;">%s</pre></body></html>`, string(b)) } else { html = string(b)@@ -190,6 +200,12 @@ p.AllowElements("table", "thead", "tbody", "tfoot", "th", "tr", "td", "ul", "ol", "li", "dl", "dt", "dd", "style", "a", "img", "iframe", "video", "audio")
// p.AllowAttrs("type").OnElements("style") p.AllowElements("div", "section", "span", "p", "br", "hr", "b", "i", "u", "strong", "em", "h1", "h2", "h3", "h4", "h5", "h6", "pre", "code", "blockquote") // p.SkipElementsContent("script") + p.AllowRelativeURLs(true) + p.RewriteSrc(func(src *url.URL) { + if _, err = os.Stat(filepath.Join(db.SAVEPATH, id, src.Path)); err == nil && strings.HasPrefix(mime.TypeByExtension(filepath.Ext(src.Path)), "image") { + src.Path = filepath.Join("/" + id + "/" + src.Path) + } + }) html = p.Sanitize(html) if !strings.Contains(html, "</html>") && !strings.Contains(html, "</body>") {