Compare commits

...

16 commits

Author SHA1 Message Date
Ted Unangst
50524cade2 just be explicit about match checking 2023-02-03 22:48:19 -05:00
Ted Unangst
91db81b44d more flexible regex for quick mentions, reported by dirk 2023-02-03 22:37:19 -05:00
Ted Unangst
59e07ff522 csp fix from tim 2023-01-27 15:42:50 -05:00
Ted Unangst
84312b409c need to add this 2023-01-27 12:39:17 -05:00
Ted Unangst
6957a4d950 i failed to add 2023-01-27 00:21:16 -05:00
Ted Unangst
a1574411aa fixes for peekses 2023-01-27 00:20:27 -05:00
Ted Unangst
6883c5b6b9 mention new features 2023-01-26 16:40:03 -05:00
Ted Unangst
a45e27038f make quote inlining an option 2023-01-26 16:38:52 -05:00
Ted Unangst
455989f85b add an emu peeker to the honkform.
from petersanchez, adapted to new csp rules
2023-01-26 16:27:11 -05:00
Ted Unangst
2983156e4b apply CSP patches. no more inline script or css.
from timkuijsten
2023-01-26 15:56:48 -05:00
Ted Unangst
78a58539ea allow subs to resub, from xwatt 2023-01-24 19:10:17 -05:00
Ted Unangst
f4dfdcea25 allow . to match all summaries 2023-01-24 19:08:26 -05:00
Ted Unangst
9539575678 more masto stompage 2023-01-24 17:19:25 -05:00
Ted Unangst
ebf1000f1c the mastodon made me do it; i am blameless 2023-01-24 17:13:49 -05:00
Ted Unangst
32ed638147 some basic qt support 2023-01-24 14:46:55 -05:00
Ted Unangst
be858a970a fix http sigs for gets and adjacent mentions 2023-01-23 12:52:27 -05:00
25 changed files with 461 additions and 136 deletions

View file

@ -27,6 +27,7 @@ import (
"net/http" "net/http"
"net/url" "net/url"
"os" "os"
"regexp"
"strings" "strings"
"time" "time"
@ -506,6 +507,39 @@ func firstofmany(obj junk.Junk, key string) string {
return "" return ""
} }
var re_mast0link = regexp.MustCompile(`https://[[:alnum:].]+/users/[[:alnum:]]+/statuses/[[:digit:]]+`)
var re_masto1ink = regexp.MustCompile(`https://[[:alnum:].]+/@[[:alnum:]]+/[[:digit:]]+`)
var re_misslink = regexp.MustCompile(`https://[[:alnum:].]+/notes/[[:alnum:]]+`)
var re_honklink = regexp.MustCompile(`https://[[:alnum:].]+/u/[[:alnum:]]+/h/[[:alnum:]]+`)
var re_romalink = regexp.MustCompile(`https://[[:alnum:].]+/objects/[[:alnum:]-]+`)
var re_qtlinks = regexp.MustCompile(`>https://[^\s<]+<`)
func qutify(user *WhatAbout, content string) string {
// well this is gross
malcontent := strings.ReplaceAll(content, `</span><span class="ellipsis">`, "")
malcontent = strings.ReplaceAll(malcontent, `</span><span class="invisible">`, "")
mlinks := re_qtlinks.FindAllString(malcontent, -1)
for _, m := range mlinks {
m = m[1 : len(m)-1]
dlog.Printf("consider qt: %s", m)
if re_mast0link.MatchString(m) ||
re_masto1ink.MatchString(m) ||
re_misslink.MatchString(m) ||
re_honklink.MatchString(m) ||
re_romalink.MatchString(m) {
j, err := GetJunk(user.ID, m)
dlog.Printf("fetched %s: %s", m, err)
if err == nil {
q, ok := j.GetString("content")
if ok {
content = fmt.Sprintf("%s<blockquote>%s</blockquote>", content, q)
}
}
}
}
return content
}
func xonksaver(user *WhatAbout, item junk.Junk, origin string) *Honk { func xonksaver(user *WhatAbout, item junk.Junk, origin string) *Honk {
depth := 0 depth := 0
maxdepth := 10 maxdepth := 10
@ -690,9 +724,11 @@ func xonksaver(user *WhatAbout, item junk.Junk, origin string) *Honk {
return nil return nil
} }
if originate(xid) != origin { if originate(xid) != origin {
ilog.Printf("original sin: %s not from %s", xid, origin) if !develMode && origin != "" {
item.Write(ilog.Writer()) ilog.Printf("original sin: %s not from %s", xid, origin)
return nil item.Write(ilog.Writer())
return nil
}
} }
var xonk Honk var xonk Honk
@ -742,6 +778,9 @@ func xonksaver(user *WhatAbout, item junk.Junk, origin string) *Honk {
content += fmt.Sprintf(`<p><a href="%s">%s</a>`, url, url) content += fmt.Sprintf(`<p><a href="%s">%s</a>`, url, url)
url = xid url = xid
} }
if user.Options.InlineQuotes {
content = qutify(user, content)
}
rid, ok = obj.GetString("inReplyTo") rid, ok = obj.GetString("inReplyTo")
if !ok { if !ok {
if robj, ok := obj.GetMap("inReplyTo"); ok { if robj, ok := obj.GetMap("inReplyTo"); ok {
@ -1904,7 +1943,7 @@ func followyou2(user *WhatAbout, j junk.Junk) {
ilog.Printf("updating honker accept: %s", who) ilog.Printf("updating honker accept: %s", who)
db := opendatabase() db := opendatabase()
row := db.QueryRow("select name, folxid from honkers where userid = ? and xid = ? and flavor in ('presub')", row := db.QueryRow("select name, folxid from honkers where userid = ? and xid = ? and flavor in ('presub', 'sub')",
user.ID, who) user.ID, who)
var name, folxid string var name, folxid string
err := row.Scan(&name, &folxid) err := row.Scan(&name, &folxid)

View file

@ -2,6 +2,18 @@ changelog
=== next === next
+ Emu peeker
+ CSP compliance
+ Filter to match anything with summary/warning.
+ Start collecting quties.
+ Fix http signatures for GET requests.
+ Fix adjacent mentions.
+ Fix argv for chpass. + Fix argv for chpass.
+ Avoid self mention in reply all. + Avoid self mention in reply all.

View file

@ -51,6 +51,7 @@ fields as well.
.It Ar text .It Ar text
Regular expression match against the post Regular expression match against the post
.Fa content . .Fa content .
The special value of "." will match any post with a summary only.
.It Ar is announce .It Ar is announce
Is announced (shared). Is announced (shared).
.It Ar announce of .It Ar announce of

View file

@ -139,6 +139,9 @@ A 24 hour clock is assumed, unless am or pm are specified.
The duration is optional and may be specified as XdYhZm for X days, Y hours, The duration is optional and may be specified as XdYhZm for X days, Y hours,
and Z minutes (1d12h would be a 36 hour event). and Z minutes (1d12h would be a 36 hour event).
.Pp .Pp
Clicking the pretty circle face will open the emu peeker to add in the
selection of emus.
.Pp
When everything is at last ready to go, press the When everything is at last ready to go, press the
.Dq it's gonna be honked .Dq it's gonna be honked
button. button.

5
fun.go
View file

@ -457,7 +457,7 @@ func memetize(honk *Honk) {
honk.Noise = re_memes.ReplaceAllStringFunc(honk.Noise, repl) honk.Noise = re_memes.ReplaceAllStringFunc(honk.Noise, repl)
} }
var re_quickmention = regexp.MustCompile("(^|[ \n])@[[:alnum:]]+([ \n.]|$)") var re_quickmention = regexp.MustCompile("(^|[ \n])@[[:alnum:]]+([ \n.,']|$)")
func quickrename(s string, userid int64) string { func quickrename(s string, userid int64) string {
nonstop := true nonstop := true
@ -472,7 +472,8 @@ func quickrename(s string, userid int64) string {
prefix += "@" prefix += "@"
m = m[1:] m = m[1:]
tail := "" tail := ""
if last := m[len(m)-1]; last == ' ' || last == '\n' || last == '.' { if last := m[len(m)-1]; last == ' ' || last == '\n' ||
last == '.' || last == ',' || last == '\'' {
tail = m[len(m)-1:] tail = m[len(m)-1:]
m = m[:len(m)-1] m = m[:len(m)-1]
} }

2
go.mod
View file

@ -9,5 +9,5 @@ require (
golang.org/x/crypto v0.0.0-20220411220226-7b82a4e95df4 golang.org/x/crypto v0.0.0-20220411220226-7b82a4e95df4
golang.org/x/net v0.0.0-20220425223048-2871e0cb64e4 golang.org/x/net v0.0.0-20220425223048-2871e0cb64e4
humungus.tedunangst.com/r/go-sqlite3 v1.1.3 humungus.tedunangst.com/r/go-sqlite3 v1.1.3
humungus.tedunangst.com/r/webs v0.6.59 humungus.tedunangst.com/r/webs v0.6.60
) )

4
go.sum
View file

@ -25,5 +25,5 @@ golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
humungus.tedunangst.com/r/go-sqlite3 v1.1.3 h1:G2N4wzDS0NbuvrZtQJhh4F+3X+s7BF8b9ga8k38geUI= humungus.tedunangst.com/r/go-sqlite3 v1.1.3 h1:G2N4wzDS0NbuvrZtQJhh4F+3X+s7BF8b9ga8k38geUI=
humungus.tedunangst.com/r/go-sqlite3 v1.1.3/go.mod h1:FtEEmQM7U2Ey1TuEEOyY1BmphTZnmiEjPsNLEAkpf/M= humungus.tedunangst.com/r/go-sqlite3 v1.1.3/go.mod h1:FtEEmQM7U2Ey1TuEEOyY1BmphTZnmiEjPsNLEAkpf/M=
humungus.tedunangst.com/r/webs v0.6.59 h1:zOlBGZqrRo22QND1CN8HbhEv3JwoiZkspi7TgVuJS0s= humungus.tedunangst.com/r/webs v0.6.60 h1:2PjVTVH3js4PXv8lrEw7nxtRmwwt1COl7t7tZMPxBPs=
humungus.tedunangst.com/r/webs v0.6.59/go.mod h1:03R0N9BcT49HB4TDd1YmarpbiPvPzVDm74Mk4h1hYPc= humungus.tedunangst.com/r/webs v0.6.60/go.mod h1:03R0N9BcT49HB4TDd1YmarpbiPvPzVDm74Mk4h1hYPc=

11
hfcs.go
View file

@ -107,7 +107,7 @@ func filtcachefiller(userid int64) (afiltermap, bool) {
expflush = filt.Expiration expflush = filt.Expiration
} }
} }
if t := filt.Text; t != "" { if t := filt.Text; t != "" && t != "." {
wordfront := t[0] != '#' wordfront := t[0] != '#'
wordtail := true wordtail := true
t = "(?i:" + t + ")" t = "(?i:" + t + ")"
@ -314,7 +314,7 @@ func matchfilterX(h *Honk, f *Filter) string {
rv += " announce" rv += " announce"
} }
} }
if match && f.Text != "" { if match && f.Text != "" && f.Text != "." {
match = false match = false
re := f.re_text re := f.re_text
m := re.FindString(h.Precis) m := re.FindString(h.Precis)
@ -334,6 +334,13 @@ func matchfilterX(h *Honk, f *Filter) string {
rv = m rv = m
} }
} }
if match && f.Text == "." {
match = false
if h.Precis != "" {
match = true
rv = h.Precis
}
}
if match { if match {
return rv return rv
} }

19
honk.go
View file

@ -51,15 +51,16 @@ type WhatAbout struct {
} }
type UserOptions struct { type UserOptions struct {
SkinnyCSS bool `json:",omitempty"` SkinnyCSS bool `json:",omitempty"`
OmitImages bool `json:",omitempty"` OmitImages bool `json:",omitempty"`
MentionAll bool `json:",omitempty"` MentionAll bool `json:",omitempty"`
Avatar string `json:",omitempty"` InlineQuotes bool `json:",omitempty"`
Banner string `json:",omitempty"` Avatar string `json:",omitempty"`
MapLink string `json:",omitempty"` Banner string `json:",omitempty"`
Reaction string `json:",omitempty"` MapLink string `json:",omitempty"`
MeCount int64 Reaction string `json:",omitempty"`
ChatCount int64 MeCount int64
ChatCount int64
} }
type KeyInfo struct { type KeyInfo struct {

View file

@ -3,12 +3,12 @@
<div class="info"> <div class="info">
{{ .AboutMsg }} {{ .AboutMsg }}
<p> <p>
<table style="font-size:0.8em"> <table class="font08em">
<tbody> <tbody>
<tr><td>version:<td style="text-align:right">{{ .HonkVersion }} <tr><td>version:<td class="textright">{{ .HonkVersion }}
<tr><td>memory:<td style="text-align:right">{{ printf "%.02f" .Sensors.Memory }}MB <tr><td>memory:<td class="textright">{{ printf "%.02f" .Sensors.Memory }}MB
<tr><td>uptime:<td style="text-align:right">{{ printf "%.02f" .Sensors.Uptime }}s <tr><td>uptime:<td class="textright">{{ printf "%.02f" .Sensors.Uptime }}s
<tr><td>cputime:<td style="text-align:right">{{ printf "%.02f" .Sensors.CPU }}s <tr><td>cputime:<td class="textright">{{ printf "%.02f" .Sensors.CPU }}s
</table> </table>
<p> <p>
</div> </div>

View file

@ -14,7 +14,8 @@
<input tabindex=1 type="checkbox" id="omitimages" name="omitimages" value="omitimages" {{ if .User.Options.OmitImages }}checked{{ end }}><span></span> <input tabindex=1 type="checkbox" id="omitimages" name="omitimages" value="omitimages" {{ if .User.Options.OmitImages }}checked{{ end }}><span></span>
<p><label class="button" for="mentionall">mention all:</label> <p><label class="button" for="mentionall">mention all:</label>
<input tabindex=1 type="checkbox" id="mentionall" name="mentionall" value="mentionall" {{ if .User.Options.MentionAll }}checked{{ end }}><span></span> <input tabindex=1 type="checkbox" id="mentionall" name="mentionall" value="mentionall" {{ if .User.Options.MentionAll }}checked{{ end }}><span></span>
<p><label class="button" for="inlineqts">inline quotes:</label>
<input tabindex=1 type="checkbox" id="inlineqts" name="inlineqts" value="inlineqts" {{ if .User.Options.InlineQuotes }}checked{{ end }}><span></span>
<p><label class="button" for="maps">apple map links:</label> <p><label class="button" for="maps">apple map links:</label>
<input tabindex=1 type="checkbox" id="maps" name="maps" value="apple" {{ if eq "apple" .User.Options.MapLink }}checked{{ end }}><span></span> <input tabindex=1 type="checkbox" id="maps" name="maps" value="apple" {{ if eq "apple" .User.Options.MapLink }}checked{{ end }}><span></span>
<p><label class="button" for="reaction">reaction:</label> <p><label class="button" for="reaction">reaction:</label>

View file

@ -1,4 +1,5 @@
{{ template "header.html" . }} {{ template "header.html" . }}
<script src="/misc.js{{ .MiscJSParam }}" defer></script>
<main> <main>
<div class="info"> <div class="info">
<p> <p>
@ -10,14 +11,8 @@
<p><label for=noise>noise:</label><br> <p><label for=noise>noise:</label><br>
<textarea name="noise" id="noise"></textarea> <textarea name="noise" id="noise"></textarea>
<p><button name="chonk" value="chonk">chonk</button> <p><button name="chonk" value="chonk">chonk</button>
<label class=button id="donker">attach: <input onchange="updatedonker(this);" type="file" name="donk"><span></span></label> <label class=button id="donker">attach: <input type="file" name="donk"><span></span></label>
</form> </form>
<script>
function updatedonker(el) {
el = el.parentElement
el.children[1].textContent = el.children[0].value.slice(-20)
}
</script>
</div> </div>
{{ $chonkcsrf := .ChonkCSRF }} {{ $chonkcsrf := .ChonkCSRF }}
{{ range .Chatter }} {{ range .Chatter }}
@ -59,7 +54,7 @@ chatter: {{ .Target }}
<p><label for=noise>noise:</label><br> <p><label for=noise>noise:</label><br>
<textarea name="noise" id="noise"></textarea> <textarea name="noise" id="noise"></textarea>
<p><button name="chonk" value="chonk">chonk</button> <p><button name="chonk" value="chonk">chonk</button>
<label class=button id="donker">attach: <input onchange="updatedonker(this);" type="file" name="donk"><span></span></label> <label class=button id="donker">attach: <input type="file" name="donk"><span></span></label>
</form> </form>
</section> </section>
{{ end }} {{ end }}

View file

@ -6,7 +6,7 @@
{{ range .Combos }} {{ range .Combos }}
<section class="honk"> <section class="honk">
<header> <header>
<p style="font-size: 1.8em"><a href="/c/{{ . }}">{{ . }}</a> <p class="font18em"><a href="/c/{{ . }}">{{ . }}</a>
</header> </header>
</section> </section>
{{ end }} {{ end }}

1
views/emus.html Normal file
View file

@ -0,0 +1 @@
{{ range .Emus }}<img class="emu" alt=":{{.Name}}:" src="{{ .ID }}">{{ end }}

View file

@ -6,14 +6,11 @@
{{ if .LocalStyleParam }} {{ if .LocalStyleParam }}
<link href="/local.css{{ .LocalStyleParam }}" rel="stylesheet"> <link href="/local.css{{ .LocalStyleParam }}" rel="stylesheet">
{{ end }} {{ end }}
<style>
{{ .UserStyle }}
</style>
<link href="/icon.png" rel="icon"> <link href="/icon.png" rel="icon">
<meta name="theme-color" content="#305"> <meta name="theme-color" content="#305">
<meta name="viewport" content="width=device-width"> <meta name="viewport" content="width=device-width">
</head> </head>
<body> <body{{ if .UserStyle}} {{ .UserStyle }}{{ end }}>
<header> <header>
{{ if .UserInfo }} {{ if .UserInfo }}
<details id="topmenu"> <details id="topmenu">
@ -22,7 +19,7 @@
<li><a id="homelink" href="/">home</a> <li><a id="homelink" href="/">home</a>
<li><a id="atmelink" href="/atme">@me<span id=mecount>{{ if .UserInfo.Options.MeCount }}({{ .UserInfo.Options.MeCount }}){{ end }}</span></a> <li><a id="atmelink" href="/atme">@me<span id=mecount>{{ if .UserInfo.Options.MeCount }}({{ .UserInfo.Options.MeCount }}){{ end }}</span></a>
<li><a id="firstlink" href="/first">first</a> <li><a id="firstlink" href="/first">first</a>
<li style="list-style-type:none; margin-left:-1em"> <li class="details">
<details> <details>
<summary>combos</summary> <summary>combos</summary>
<ul> <ul>
@ -39,7 +36,7 @@
<li><a href="/honkers">honkers</a> <li><a href="/honkers">honkers</a>
<li><a href="/hfcs">filters</a> <li><a href="/hfcs">filters</a>
<li><a href="/account">account</a> <li><a href="/account">account</a>
<li style="list-style-type:none; margin-left:-1em"> <li class="details">
<details> <details>
<summary>more stuff</summary> <summary>more stuff</summary>
<ul> <ul>
@ -59,7 +56,7 @@
</details> </details>
<div id="topspacer"> <div id="topspacer">
<p> <p>
<p class="nophone" onclick="window.scrollTo(0,0)">top <p class="nophone">top
</div> </div>
{{ else }} {{ else }}
<div id="topmenu"> <div id="topmenu">

View file

@ -1,4 +1,4 @@
<article class="honk {{ .Honk.Style }}" data-convoy="{{ .Honk.Convoy }}"> <article class="honk {{ .Honk.Style }}" data-convoy="{{ .Honk.Convoy }}" data-hname="{{ .Honk.Handles }}" data-xid="{{ .Honk.XID }}" data-id="{{ .Honk.ID }}">
{{ $bonkcsrf := .BonkCSRF }} {{ $bonkcsrf := .BonkCSRF }}
{{ $IsPreview := .IsPreview }} {{ $IsPreview := .IsPreview }}
{{ $maplink := .MapLink }} {{ $maplink := .MapLink }}
@ -30,7 +30,7 @@
<span class="clip"><a href="{{ .URL }}" rel=noreferrer>{{ .What }}</a> {{ .Date.Local.Format "02 Jan 2006 15:04 -0700" }}</span> <span class="clip"><a href="{{ .URL }}" rel=noreferrer>{{ .What }}</a> {{ .Date.Local.Format "02 Jan 2006 15:04 -0700" }}</span>
{{ if .Oonker }} {{ if .Oonker }}
<br> <br>
<span style="margin-left: 1em;" class="clip"> <span class="left1em clip">
{{ if $bonkcsrf }} {{ if $bonkcsrf }}
original: <a class="honkerlink" href="/h?xid={{ .Oonker }}" data-xid="{{ .Oonker }}">{{ .Oondle }}</a> original: <a class="honkerlink" href="/h?xid={{ .Oonker }}" data-xid="{{ .Oonker }}">{{ .Oondle }}</a>
{{ else }} {{ else }}
@ -40,14 +40,14 @@ original: <a href="{{ .Oonker }}" rel=noreferrer>{{ .Oondle }}</a>
{{ else }} {{ else }}
{{ if .RID }} {{ if .RID }}
<br> <br>
<span style="margin-left: 1em;" class="clip"> <span class="left1em clip">
in reply to: <a href="{{ .RID }}" rel=noreferrer>{{ .RID }}</a> in reply to: <a href="{{ .RID }}" rel=noreferrer>{{ .RID }}</a>
</span> </span>
{{ end }} {{ end }}
{{ end }} {{ end }}
<br> <br>
{{ if $bonkcsrf }} {{ if $bonkcsrf }}
<span style="margin-left: 1em;" class="clip">convoy: <a class="convoylink" href="/t?c={{ .Convoy }}">{{ .Convoy }}</a></span> <span class="left1em clip">convoy: <a class="convoylink" href="/t?c={{ .Convoy }}">{{ .Convoy }}</a></span>
{{ end }} {{ end }}
</header> </header>
<p> <p>
@ -95,7 +95,7 @@ in reply to: <a href="{{ .RID }}" rel=noreferrer>{{ .RID }}</a>
{{ .Honk.Guesses }} {{ .Honk.Guesses }}
<p>{{ .Honk.Noise }} <p>{{ .Honk.Noise }}
{{ else }} {{ else }}
<button onclick="return playit(this, '{{ .Honk.Noise }}', '{{ .Honk.Wonkles }}', '{{ .Honk.XID }}')">it's play time!</button> <button class="playit" data-noise="{{ .Honk.Noise }}" data-wonk="{{ .Honk.Wonkles }}">it's play time!</button>
{{ end }} {{ end }}
{{ end }} {{ end }}
{{ if and $bonkcsrf (not $IsPreview) }} {{ if and $bonkcsrf (not $IsPreview) }}
@ -106,41 +106,41 @@ in reply to: <a href="{{ .RID }}" rel=noreferrer>{{ .RID }}</a>
<p> <p>
{{ if .Honk.Public }} {{ if .Honk.Public }}
{{ if .Honk.IsBonked }} {{ if .Honk.IsBonked }}
<button onclick="return unbonk(this, '{{ .Honk.XID }}');">unbonk</button> <button class="unbonk">unbonk</button>
{{ else }} {{ else }}
<button onclick="return bonk(this, '{{ .Honk.XID }}');">bonk</button> <button class="bonk">bonk</button>
{{ end }} {{ end }}
{{ else }} {{ else }}
<button disabled>nope</button> <button disabled>nope</button>
{{ end }} {{ end }}
<button onclick="return showhonkform(this, '{{ .Honk.XID }}', '{{ .Honk.Handles }}');"><a href="/newhonk?rid={{ .Honk.XID }}">honk back</a></button> <button class="honkback"><a href="/newhonk?rid={{ .Honk.XID }}">honk back</a></button>
<button onclick="return muteit(this, '{{ .Honk.Convoy }}');">mute</button> <button class="mute">mute</button>
<button onclick="return showelement('evenmore{{ .Honk.ID }}')">even more</button> <button class="evenmore">even more</button>
</div> </div>
<div id="evenmore{{ .Honk.ID }}" style="display:none"> <div id="evenmore{{ .Honk.ID }}" class="hide">
<p> <p>
<button onclick="return zonkit(this, '{{ .Honk.XID }}');">zonk</button> <button class="zonk">zonk</button>
{{ if .Honk.IsAcked }} {{ if .Honk.IsAcked }}
<button onclick="return flogit(this, 'deack', '{{ .Honk.XID }}');">deack</button> <button class="flogit-deack">deack</button>
{{ else }} {{ else }}
<button onclick="return flogit(this, 'ack', '{{ .Honk.XID }}');">ack</button> <button class="flogit-ack" >ack</button>
{{ end }} {{ end }}
{{ if .Honk.IsSaved }} {{ if .Honk.IsSaved }}
<button onclick="return flogit(this, 'unsave', '{{ .Honk.XID }}');">unsave</button> <button class="flogit-unsave">unsave</button>
{{ else }} {{ else }}
<button onclick="return flogit(this, 'save', '{{ .Honk.XID }}');">save</button> <button class="flogit-save">save</button>
{{ end }} {{ end }}
{{ if .Honk.IsUntagged }} {{ if .Honk.IsUntagged }}
<button disabled>untagged</button> <button disabled>untagged</button>
{{ else }} {{ else }}
<button onclick="return flogit(this, 'untag', '{{ .Honk.XID }}');">untag me</button> <button class="flogit-untag">untag me</button>
{{ end }} {{ end }}
<button><a href="/edit?xid={{ .Honk.XID }}">edit</a></button> <button><a href="/edit?xid={{ .Honk.XID }}">edit</a></button>
{{ if not (eq .Badonk "none") }} {{ if not (eq .Badonk "none") }}
{{ if .Honk.IsReacted }} {{ if .Honk.IsReacted }}
<button disabled>badonked</button> <button disabled>badonked</button>
{{ else }} {{ else }}
<button onclick="return flogit(this, 'react', '{{ .Honk.XID }}');">{{ .Badonk }}</button> <button class="flogit-react" >{{ .Badonk }}</button>
{{ end }} {{ end }}
{{ end }} {{ end }}
</div> </div>

View file

@ -1,4 +1,5 @@
{{ template "header.html" . }} {{ template "header.html" . }}
<script src="/misc.js{{ .MiscJSParam }}" defer></script>
<main> <main>
<div class="info"> <div class="info">
<p> <p>
@ -21,21 +22,13 @@
</div> </div>
{{ $honkercsrf := .HonkerCSRF }} {{ $honkercsrf := .HonkerCSRF }}
<div class="info"> <div class="info">
<script> <p><button class="expand">expand</button>
function expandstuff() {
var els = document.querySelectorAll(".honk details")
for (var i = 0; i < els.length; i++) {
els[i].open = true
}
}
</script>
<p><button onclick="expandstuff()">expand</button>
</div> </div>
{{ range .Honkers }} {{ range .Honkers }}
<section class="honk"> <section class="honk">
<header> <header>
<img alt="avatar" src="/a?a={{ .XID }}"> <img alt="avatar" src="/a?a={{ .XID }}">
<p style="font-size: 1.8em"><a href="/h/{{ .Name }}">{{ .Name }}</a> <p class="font18em"><a href="/h/{{ .Name }}">{{ .Name }}</a>
</header> </header>
<p> <p>
<details> <details>

View file

@ -1,6 +1,6 @@
<p id="honkformhost"> <p id="honkformhost">
<button id="honkingtime" onclick="return showhonkform();" {{ if .IsPreview }}style="display:none"{{ end }}><a href="/newhonk">it's honking time</a></button> <button id="honkingtime" {{ if .IsPreview }}class="hide"{{ end }}><a href="/newhonk">it's honking time</a></button>
<form id="honkform" action="/honk" method="POST" enctype="multipart/form-data" {{ if not .IsPreview }}style="display: none"{{ end }}> <form id="honkform" action="/honk" method="POST" enctype="multipart/form-data" {{ if not .IsPreview }}class="hide"{{ end }}>
<input type="hidden" name="CSRF" value="{{ .HonkCSRF }}"> <input type="hidden" name="CSRF" value="{{ .HonkCSRF }}">
<input type="hidden" name="updatexid" id="updatexidinput" value = "{{ .UpdateXID }}"> <input type="hidden" name="updatexid" id="updatexidinput" value = "{{ .UpdateXID }}">
<input type="hidden" name="rid" id="ridinput" value="{{ .InReplyTo }}"> <input type="hidden" name="rid" id="ridinput" value="{{ .InReplyTo }}">
@ -9,12 +9,12 @@
<details> <details>
<summary>more options</summary> <summary>more options</summary>
<p> <p>
<label class=button id="donker">attach: <input onchange="updatedonker();" type="file" name="donk"><span>{{ .SavedFile }}</span></label> <label class=button id="donker">attach: <input type="file" name="donk"><span>{{ .SavedFile }}</span></label>
<input type="hidden" id="saveddonkxid" name="donkxid" value="{{ .SavedFile }}"> <input type="hidden" id="saveddonkxid" name="donkxid" value="{{ .SavedFile }}">
<p id="donkdescriptor"><label for=donkdesc>description:</label><br> <p id="donkdescriptor"><label for=donkdesc>description:</label><br>
<input type="text" name="donkdesc" value="{{ .DonkDesc }}" autocomplete=off> <input type="text" name="donkdesc" value="{{ .DonkDesc }}" autocomplete=off>
{{ with .SavedPlace }} {{ with .SavedPlace }}
<p><button id=checkinbutton type=button onclick="fillcheckin()">checkin</button> <p><button id=checkinbutton type=button>checkin</button>
<div id=placedescriptor> <div id=placedescriptor>
<p><label>name:</label><br><input type="text" name="placename" id=placenameinput value="{{ .Name }}"> <p><label>name:</label><br><input type="text" name="placename" id=placenameinput value="{{ .Name }}">
<p><label>url:</label><br><input type="text" name="placeurl" id=placeurlinput value="{{ .Url }}"> <p><label>url:</label><br><input type="text" name="placeurl" id=placeurlinput value="{{ .Url }}">
@ -22,26 +22,34 @@
<label>lon: </label><input type="text" size=9 name="placelong" id=placelonginput value="{{ .Longitude }}"> <label>lon: </label><input type="text" size=9 name="placelong" id=placelonginput value="{{ .Longitude }}">
</div> </div>
{{ else }} {{ else }}
<p><button id=checkinbutton type=button onclick="fillcheckin()">checkin</button> <p><button id=checkinbutton type=button>checkin</button>
<div id=placedescriptor style="display: none"> <div id=placedescriptor class="hide">
<p><label>name:</label><br><input type="text" name="placename" id=placenameinput value=""> <p><label>name:</label><br><input type="text" name="placename" id=placenameinput value="">
<p><label>url:</label><br><input type="text" name="placeurl" id=placeurlinput value=""> <p><label>url:</label><br><input type="text" name="placeurl" id=placeurlinput value="">
<p><label>lat: </label><input type="text" size=9 name="placelat" id=placelatinput value=""> <p><label>lat: </label><input type="text" size=9 name="placelat" id=placelatinput value="">
<label>lon: </label><input type="text" size=9 name="placelong" id=placelonginput value=""> <label>lon: </label><input type="text" size=9 name="placelong" id=placelonginput value="">
</div> </div>
{{ end }} {{ end }}
<p><button id=addtimebutton type=button onclick="showelement('timedescriptor')">add time</button> <p><button id=addtimebutton type=button>add time</button>
<div id=timedescriptor style="{{ or .ShowTime "display: none" }}"> <div id=timedescriptor class="{{ or .ShowTime "hide" }}">
<p><label for=timestart>start:</label><br> <p><label for=timestart>start:</label><br>
<input type="text" name="timestart" value="{{ .StartTime }}"> <input type="text" name="timestart" value="{{ .StartTime }}">
<p><label for=timeend>duration:</label><br> <p><label for=timeend>duration:</label><br>
<input type="text" name="timeend" value="{{ .Duration }}"> <input type="text" name="timeend" value="{{ .Duration }}">
</div> </div>
<svg class="emuload" id="emuload" xmlns="http://www.w3.org/2000/svg" class="icon icon-tabler icon-tabler-mood-neutral" width="24" height="24" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round">
<path stroke="none" d="M0 0h24v24H0z" fill="none"></path>
<circle cx="12" cy="12" r="9"></circle>
<line x1="9" y1="10" x2="9.01" y2="10"></line>
<line x1="15" y1="10" x2="15.01" y2="10"></line>
</svg>
<div id="emupicker">
</div>
</details> </details>
<p> <p>
<textarea name="noise" id="honknoise">{{ .Noise }}</textarea> <textarea name="noise" id="honknoise">{{ .Noise }}</textarea>
<p class="buttonarray"> <p class="buttonarray">
<button>it's gonna be honked</button> <button>it's gonna be honked</button>
<button name="preview" value="preview">preview</button> <button name="preview" value="preview">preview</button>
<button type=button name="cancel" value="cancel" onclick="cancelhonking()">cancel</button> <button type=button name="cancel" value="cancel">cancel</button>
</form> </form>

View file

@ -3,42 +3,24 @@
<div class="info" id="infobox"> <div class="info" id="infobox">
<div id="srvmsg"> <div id="srvmsg">
{{ if .Name }} {{ if .Name }}
<p>{{ .Name }} <span style="margin-left:1em;"><a href="/u/{{ .Name }}/rss">rss</a></span> <p>{{ .Name }} <span class="left1em"><a href="/u/{{ .Name }}/rss">rss</a></span>
<p>{{ .WhatAbout }} <p>{{ .WhatAbout }}
{{ end }} {{ end }}
<p>{{ .ServerMessage }} <p>{{ .ServerMessage }}
</div> </div>
{{ if .HonkCSRF }} {{ if .HonkCSRF }}
{{ template "honkform.html" . }} {{ template "honkform.html" . }}
<script> <script src="/honkpage.js{{ .JSParam }}" defer data-csrf="{{ .HonkCSRF }}" data-pagename="{{ .PageName }}" data-pagearg="{{ .PageArg }}" data-tophid="{{ .TopHID }}" data-srvmsg="{{ .ServerMessage }}"></script>
var csrftoken = {{ .HonkCSRF }}
var honksforpage = { }
var curpagestate = { name: "{{ .PageName }}", arg : "{{ .PageArg }}" }
var tophid = { }
tophid[curpagestate.name + ":" + curpagestate.arg] = "{{ .TopHID }}"
var servermsgs = { }
servermsgs[curpagestate.name + ":" + curpagestate.arg] = "{{ .ServerMessage }}"
</script>
<script src="/honkpage.js{{ .JSParam }}"></script>
{{ end }} {{ end }}
<script>
function playit(elem, word, wordlist, xid) {
import('/wonk.js').then(module => {
makeaguess = module.makeaguess
module.addguesscontrols(elem, word, wordlist, xid)
})
}
</script>
{{ if .LocalJSParam }} {{ if .LocalJSParam }}
<script src="/local.js{{ .LocalJSParam }}"></script> <script src="/local.js{{ .LocalJSParam }}" defer></script>
{{ end }} {{ end }}
</div> </div>
{{ if and .HonkCSRF (not .IsPreview) }} {{ if and .HonkCSRF (not .IsPreview) }}
<div class="info" id="refreshbox"> <div class="info" id="refreshbox">
<p><button onclick="refreshhonks(this)">refresh</button><span></span> <p><button class="refresh">refresh</button><span></span>
<button onclick="oldestnewest(this)">scroll down</button> <button class="scrolldown">scroll down</button>
</div> </div>
{{ if eq .ServerMessage "one honk maybe more" }} <script> hideelement("refreshbox")</script> {{ end }}
{{ end }} {{ end }}
<div id="honksonpage"> <div id="honksonpage">
<div> <div>

View file

@ -1,10 +1,16 @@
var csrftoken = ""
var honksforpage = { }
var curpagestate = { name: "", arg : "" }
var tophid = { }
var servermsgs = { }
function encode(hash) { function encode(hash) {
var s = [] var s = []
for (var key in hash) { for (var key in hash) {
var val = hash[key] var val = hash[key]
s.push(encodeURIComponent(key) + "=" + encodeURIComponent(val)) s.push(encodeURIComponent(key) + "=" + encodeURIComponent(val))
} }
return s.join("&") return s.join("&")
} }
function post(url, data) { function post(url, data) {
var x = new XMLHttpRequest() var x = new XMLHttpRequest()
@ -268,25 +274,78 @@ function relinklinks() {
el.onclick = pageswitcher("honker", xid) el.onclick = pageswitcher("honker", xid)
el.classList.remove("honkerlink") el.classList.remove("honkerlink")
} }
els = document.querySelectorAll("#honksonpage article button")
els.forEach(function(el) {
var honk = el.closest("article")
var convoy = honk.dataset.convoy
var hname = honk.dataset.hname
var xid = honk.dataset.xid
var id = Number(honk.dataset.id)
if (!(id > 0)) {
console.error("could not determine honk id")
return
}
if (el.classList.contains("unbonk")) {
el.onclick = function() {
unbonk(el, xid);
}
} else if (el.classList.contains("bonk")) {
el.onclick = function() {
bonk(el, xid)
}
} else if (el.classList.contains("honkback")) {
el.onclick = function() {
return showhonkform(el, xid, hname)
}
} else if (el.classList.contains("mute")) {
el.onclick = function() {
muteit(el, convoy);
}
} else if (el.classList.contains("evenmore")) {
var more = document.querySelector("#evenmore"+id);
el.onclick = function() {
more.classList.toggle("hide");
}
} else if (el.classList.contains("zonk")) {
el.onclick = function() {
zonkit(el, xid);
}
} else if (el.classList.contains("flogit-deack")) {
el.onclick = function() {
flogit(el, "deack", xid);
}
} else if (el.classList.contains("flogit-ack")) {
el.onclick = function() {
flogit(el, "ack", xid);
}
} else if (el.classList.contains("flogit-unsave")) {
el.onclick = function() {
flogit(el, "unsave", xid);
}
} else if (el.classList.contains("flogit-save")) {
el.onclick = function() {
flogit(el, "save", xid);
}
} else if (el.classList.contains("flogit-untag")) {
el.onclick = function() {
flogit(el, "untag", xid);
}
} else if (el.classList.contains("flogit-react")) {
el.onclick = function() {
flogit(el, "react", xid);
}
} else if (el.classList.contains("playit")) {
var noise = el.dataset.noise
var wonk = el.dataset.wonk
el.onclick = function() {
playit(el, noise, wonk, xid)
}
}
})
} }
(function() {
var el = document.getElementById("homelink")
el.onclick = pageswitcher("home", "")
el = document.getElementById("atmelink")
el.onclick = pageswitcher("atme", "")
el = document.getElementById("firstlink")
el.onclick = pageswitcher("first", "")
el = document.getElementById("savedlink")
el.onclick = pageswitcher("saved", "")
el = document.getElementById("longagolink")
el.onclick = pageswitcher("longago", "")
relinklinks()
window.onpopstate = statechanger
history.replaceState(curpagestate, "some title", "")
})();
(function() {
hideelement("donkdescriptor")
})();
function showhonkform(elem, rid, hname) { function showhonkform(elem, rid, hname) {
var form = lehonkform var form = lehonkform
form.style = "display: block" form.style = "display: block"
@ -363,3 +422,98 @@ function fillcheckin() {
}, gpsoptions) }, gpsoptions)
} }
} }
function playit(elem, word, wordlist, xid) {
import('/wonk.js').then(module => {
makeaguess = module.makeaguess
module.addguesscontrols(elem, word, wordlist, xid)
})
}
function addemu(elem) {
const data = elem.alt
const box = document.getElementById("honknoise");
box.value += data;
}
function loademus() {
div = document.getElementById("emupicker")
request = new XMLHttpRequest()
request.open('GET', '/emus')
request.onload = function() {
div.innerHTML = request.responseText
div.querySelectorAll(".emu").forEach(function(el) {
el.onclick = function() {
addemu(el)
}
})
}
if (div.style.display === "none") {
div.style.display = "block";
} else {
div.style.display = "none";
}
request.send()
}
// init
(function() {
var me = document.currentScript;
csrftoken = me.dataset.csrf
curpagestate.name = me.dataset.pagename
curpagestate.arg = me.dataset.pagearg
tophid[curpagestate.name + ":" + curpagestate.arg] = me.dataset.tophid
servermsgs[curpagestate.name + ":" + curpagestate.arg] = me.dataset.srvmsg
var el = document.getElementById("homelink")
el.onclick = pageswitcher("home", "")
el = document.getElementById("atmelink")
el.onclick = pageswitcher("atme", "")
el = document.getElementById("firstlink")
el.onclick = pageswitcher("first", "")
el = document.getElementById("savedlink")
el.onclick = pageswitcher("saved", "")
el = document.getElementById("longagolink")
el.onclick = pageswitcher("longago", "")
var totop = document.querySelector(".nophone")
if (totop) {
totop.onclick = function() {
window.scrollTo(0,0)
}
}
var refreshbox = document.getElementById("refreshbox")
if (refreshbox) {
refreshbox.querySelectorAll("button").forEach(function(el) {
if (el.classList.contains("refresh")) {
el.onclick = function() {
refreshhonks(el)
}
} else if (el.classList.contains("scrolldown")) {
el.onclick = function() {
oldestnewest(el)
}
}
})
if (me.dataset.srvmsg == "one honk maybe more") {
hideelement(refreshbox)
}
}
var td = document.getElementById("timedescriptor")
document.getElementById("addtimebutton").onclick = function() {
td.classList.toggle("hide")
}
document.getElementById("honkingtime").onclick = function() {
return showhonkform()
}
document.getElementById("checkinbutton").onclick = fillcheckin
document.getElementById("emuload").onclick = loademus
document.querySelector("#donker input").onchange = updatedonker
document.querySelector("button[name=cancel]").onclick = cancelhonking
relinklinks()
window.onpopstate = statechanger
history.replaceState(curpagestate, "some title", "")
hideelement("donkdescriptor")
})();

25
views/misc.js Normal file
View file

@ -0,0 +1,25 @@
function expandstuff() {
var els = document.querySelectorAll(".honk details")
for (var i = 0; i < els.length; i++) {
els[i].open = true
}
}
function updatedonker(el) {
el = el.parentElement
el.children[1].textContent = el.children[0].value.slice(-20)
}
(function() {
var expand = document.querySelector("button.expand")
if (expand) {
expand.onclick = expandstuff
}
var donk = document.querySelector("#donker input")
if (donk) {
donk.onchange = function() {
updatedonker(this);
}
}
})()

View file

@ -10,7 +10,7 @@
{{ $letter = (call $firstrune .Name) }} {{ $letter = (call $firstrune .Name) }}
<li><p> <li><p>
{{ end }} {{ end }}
<span style="white-space: nowrap;"><a href="/o/{{ .Name }}">#{{ .Name }}</a> ({{ .Count }})</span> <span class="wsnowrap"><a href="/o/{{ .Name }}">#{{ .Name }}</a> ({{ .Count }})</span>
{{ end }} {{ end }}
</ul> </ul>
</div> </div>

View file

@ -253,6 +253,11 @@ input[type=file] {
.honk details.actions summary { .honk details.actions summary {
color: var(--fg-subtle); color: var(--fg-subtle);
} }
#emupicker{height:300px;overflow-y:scroll;padding:3px;background:var(--bg-dark);border:solid 5px var(--fg-subtle);text-align:center;display:none;}
#emupicker img{margin:0;}
.emuload{background:var(--bg-page);padding:0.5em;}
.subtle .noise { .subtle .noise {
color: var(--fg-subtle); color: var(--fg-subtle);
font-size: 0.8em; font-size: 0.8em;
@ -340,3 +345,44 @@ img.emu {
--fg-limited: #a79; --fg-limited: #a79;
} }
} }
/*
* CSP: style-src: self
*/
li.details {
list-style-type: none;
margin-left: -1em;
}
.left1em {
margin-left: 1em;
}
.hide {
display: none;
}
.textright {
text-align: right;
}
.font08em {
font-size: 0.8em;
}
.font18em {
font-size: 1.8em;
}
.wsnowrap {
white-space: nowrap;
}
.skinny main {
max-width: 700px;
}
.fontmonospace {
font-family: monospace;
}

View file

@ -17,7 +17,10 @@ export function addguesscontrols(elem, word, wordlist, xid) {
} }
host.validGuesses = validguesses host.validGuesses = validguesses
var div = document.createElement( 'div' ); var div = document.createElement( 'div' );
div.innerHTML = "<p><input> <button onclick='return makeaguess(this)'>guess</button>" div.innerHTML = "<p><input> <button>guess</button>"
div.querySelector('button').onclick = function() {
makeaguess(this)
}
host.append(div) host.append(div)
elem.remove() elem.remove()
} }
@ -57,7 +60,7 @@ export function makeaguess(btn) {
} }
var div = document.createElement( 'div' ); var div = document.createElement( 'div' );
div.innerHTML = "<p style='font-family: monospace'>" + res div.innerHTML = "<p class='fontmonospace'>" + res
host.append(div) host.append(div)
host.guesses.push(obfu) host.guesses.push(obfu)
} else { } else {
@ -76,7 +79,10 @@ export function makeaguess(btn) {
if (typeof(csrftoken) != "undefined") if (typeof(csrftoken) != "undefined")
post("/zonkit", encode({"CSRF": csrftoken, "wherefore": "wonk", "guesses": host.guesses.join("<p>"), "what": host.xid})) post("/zonkit", encode({"CSRF": csrftoken, "wherefore": "wonk", "guesses": host.guesses.join("<p>"), "what": host.xid}))
} else { } else {
div.innerHTML = "<p><input> <button onclick='return makeaguess(this)'>guess</button>" div.innerHTML = "<p><input> <button>guess</button>"
div.querySelector('button').onclick = function() {
makeaguess(this)
}
} }
host.append(div) host.append(div)
btn.parentElement.remove() btn.parentElement.remove()

65
web.go
View file

@ -50,16 +50,18 @@ var honkSep = "h"
var develMode = false var develMode = false
func getuserstyle(u *login.UserInfo) template.CSS { var allemus []Emu
func getuserstyle(u *login.UserInfo) template.HTMLAttr {
if u == nil { if u == nil {
return "" return ""
} }
user, _ := butwhatabout(u.Username) user, _ := butwhatabout(u.Username)
css := template.CSS("") class := template.HTMLAttr("")
if user.Options.SkinnyCSS { if user.Options.SkinnyCSS {
css += "main { max-width: 700px; }\n" class += `class="skinny"`
} }
return css return class
} }
func getmaplink(u *login.UserInfo) string { func getmaplink(u *login.UserInfo) string {
@ -80,6 +82,7 @@ func getInfo(r *http.Request) map[string]interface{} {
templinfo["LocalStyleParam"] = getassetparam(dataDir + "/views/local.css") templinfo["LocalStyleParam"] = getassetparam(dataDir + "/views/local.css")
templinfo["JSParam"] = getassetparam(viewDir + "/views/honkpage.js") templinfo["JSParam"] = getassetparam(viewDir + "/views/honkpage.js")
templinfo["LocalJSParam"] = getassetparam(dataDir + "/views/local.js") templinfo["LocalJSParam"] = getassetparam(dataDir + "/views/local.js")
templinfo["MiscJSParam"] = getassetparam(dataDir + "/views/misc.js")
templinfo["ServerName"] = serverName templinfo["ServerName"] = serverName
templinfo["IconName"] = iconName templinfo["IconName"] = iconName
templinfo["UserSep"] = userSep templinfo["UserSep"] = userSep
@ -148,6 +151,15 @@ func homepage(w http.ResponseWriter, r *http.Request) {
honkpage(w, u, honks, templinfo) honkpage(w, u, honks, templinfo)
} }
func showemus(w http.ResponseWriter, r *http.Request) {
templinfo := getInfo(r)
templinfo["Emus"] = allemus
err := readviews.Execute(w, "emus.html", templinfo)
if err != nil {
elog.Print(err)
}
}
func showfunzone(w http.ResponseWriter, r *http.Request) { func showfunzone(w http.ResponseWriter, r *http.Request) {
var emunames, memenames []string var emunames, memenames []string
emuext := make(map[string]string) emuext := make(map[string]string)
@ -1122,6 +1134,11 @@ func saveuser(w http.ResponseWriter, r *http.Request) {
} else { } else {
options.MentionAll = false options.MentionAll = false
} }
if r.FormValue("inlineqts") == "inlineqts" {
options.InlineQuotes = true
} else {
options.InlineQuotes = false
}
if r.FormValue("maps") == "apple" { if r.FormValue("maps") == "apple" {
options.MapLink = "apple" options.MapLink = "apple"
} else { } else {
@ -1429,7 +1446,7 @@ func edithonkpage(w http.ResponseWriter, r *http.Request) {
templinfo["Noise"] = noise templinfo["Noise"] = noise
templinfo["SavedPlace"] = honk.Place templinfo["SavedPlace"] = honk.Place
if tm := honk.Time; tm != nil { if tm := honk.Time; tm != nil {
templinfo["ShowTime"] = ";" templinfo["ShowTime"] = " "
templinfo["StartTime"] = tm.StartTime.Format("2006-01-02 15:04") templinfo["StartTime"] = tm.StartTime.Format("2006-01-02 15:04")
if tm.Duration != 0 { if tm.Duration != 0 {
templinfo["Duration"] = tm.Duration templinfo["Duration"] = tm.Duration
@ -1751,7 +1768,7 @@ func submithonk(w http.ResponseWriter, r *http.Request) *Honk {
templinfo["Noise"] = r.FormValue("noise") templinfo["Noise"] = r.FormValue("noise")
templinfo["SavedFile"] = donkxid templinfo["SavedFile"] = donkxid
if tm := honk.Time; tm != nil { if tm := honk.Time; tm != nil {
templinfo["ShowTime"] = ";" templinfo["ShowTime"] = " "
templinfo["StartTime"] = tm.StartTime.Format("2006-01-02 15:04") templinfo["StartTime"] = tm.StartTime.Format("2006-01-02 15:04")
if tm.Duration != 0 { if tm.Duration != 0 {
templinfo["Duration"] = tm.Duration templinfo["Duration"] = tm.Duration
@ -2414,6 +2431,37 @@ func bgmonitor() {
} }
} }
func addcspheaders(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Security-Policy", "default-src 'none'; script-src 'self'; connect-src 'self'; style-src 'self'; img-src 'self'; report-uri /csp-violation")
next.ServeHTTP(w, r)
})
}
func emuinit() {
var emunames []string
dir, err := os.Open(dataDir + "/emus")
if err == nil {
emunames, _ = dir.Readdirnames(0)
dir.Close()
}
for _, e := range emunames {
if len(e) <= 4 {
continue
}
ext := e[len(e)-4:]
emu := Emu{
ID: fmt.Sprintf("/emu/%s", e),
Name: e[:len(e)-4],
Type: "image/" + ext[1:],
}
allemus = append(allemus, emu)
}
sort.Slice(allemus, func(i, j int) bool {
return allemus[i].Name < allemus[j].Name
})
}
func serve() { func serve() {
db := opendatabase() db := opendatabase()
login.Init(login.InitArgs{Db: db, Logger: ilog, Insecure: develMode, SameSiteStrict: !develMode}) login.Init(login.InitArgs{Db: db, Logger: ilog, Insecure: develMode, SameSiteStrict: !develMode})
@ -2428,6 +2476,7 @@ func serve() {
go tracker() go tracker()
go bgmonitor() go bgmonitor()
loadLingo() loadLingo()
emuinit()
readviews = templates.Load(develMode, readviews = templates.Load(develMode,
viewDir+"/views/honkpage.html", viewDir+"/views/honkpage.html",
@ -2446,6 +2495,7 @@ func serve() {
viewDir+"/views/msg.html", viewDir+"/views/msg.html",
viewDir+"/views/header.html", viewDir+"/views/header.html",
viewDir+"/views/onts.html", viewDir+"/views/onts.html",
viewDir+"/views/emus.html",
viewDir+"/views/honkpage.js", viewDir+"/views/honkpage.js",
) )
if !develMode { if !develMode {
@ -2466,6 +2516,7 @@ func serve() {
} }
mux := mux.NewRouter() mux := mux.NewRouter()
mux.Use(addcspheaders)
mux.Use(login.Checker) mux.Use(login.Checker)
mux.Handle("/api", login.TokenRequired(http.HandlerFunc(apihandler))) mux.Handle("/api", login.TokenRequired(http.HandlerFunc(apihandler)))
@ -2503,6 +2554,7 @@ func serve() {
getters.HandleFunc("/style.css", serveviewasset) getters.HandleFunc("/style.css", serveviewasset)
getters.HandleFunc("/honkpage.js", serveviewasset) getters.HandleFunc("/honkpage.js", serveviewasset)
getters.HandleFunc("/wonk.js", serveviewasset) getters.HandleFunc("/wonk.js", serveviewasset)
getters.HandleFunc("/misc.js", serveviewasset)
getters.HandleFunc("/local.css", servedataasset) getters.HandleFunc("/local.css", servedataasset)
getters.HandleFunc("/local.js", servedataasset) getters.HandleFunc("/local.js", servedataasset)
getters.HandleFunc("/icon.png", servedataasset) getters.HandleFunc("/icon.png", servedataasset)
@ -2545,6 +2597,7 @@ func serve() {
loggedin.HandleFunc("/t", showconvoy) loggedin.HandleFunc("/t", showconvoy)
loggedin.HandleFunc("/q", showsearch) loggedin.HandleFunc("/q", showsearch)
loggedin.HandleFunc("/hydra", webhydra) loggedin.HandleFunc("/hydra", webhydra)
loggedin.HandleFunc("/emus", showemus)
loggedin.Handle("/submithonker", login.CSRFWrap("submithonker", http.HandlerFunc(submithonker))) loggedin.Handle("/submithonker", login.CSRFWrap("submithonker", http.HandlerFunc(submithonker)))
err = http.Serve(listener, mux) err = http.Serve(listener, mux)