avatar.go (view raw)
1//
2// Copyright (c) 2019 Ted Unangst <tedu@tedunangst.com>
3//
4// Permission to use, copy, modify, and distribute this software for any
5// purpose with or without fee is hereby granted, provided that the above
6// copyright notice and this permission notice appear in all copies.
7//
8// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
9// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
10// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
11// ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
12// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
13// ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
14// OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
15
16package main
17
18import (
19 "bufio"
20 "bytes"
21 "crypto/sha512"
22 "fmt"
23 "image"
24 "image/png"
25 "net/http"
26 "net/url"
27 "regexp"
28 "strconv"
29 "strings"
30
31 "github.com/gorilla/mux"
32)
33
34var avatarcolors = [4][4]byte{
35 {80, 156, 147, 255},
36 {80, 156, 147, 255},
37 {57, 106, 101, 255},
38 {96, 169, 161, 255},
39}
40
41func loadAvatarColors() {
42 var colors string
43 getconfig("avatarcolors", &colors)
44 if colors == "" {
45 return
46 }
47 r := bufio.NewReader(strings.NewReader(colors))
48 for i := 0; i < 4; i++ {
49 l, _ := r.ReadString(' ')
50 for l == " " {
51 l, _ = r.ReadString(' ')
52 }
53 l = strings.Trim(l, "# \n")
54 if len(l) == 6 {
55 l = l + "ff"
56 }
57 c, err := strconv.ParseUint(l, 16, 32)
58 if err != nil {
59 elog.Printf("error reading avatar color %d: %s", i, err)
60 continue
61 }
62 avatarcolors[i][0] = byte(c >> 24 & 0xff)
63 avatarcolors[i][1] = byte(c >> 16 & 0xff)
64 avatarcolors[i][2] = byte(c >> 8 & 0xff)
65 avatarcolors[i][3] = byte(c >> 0 & 0xff)
66 }
67}
68
69func genAvatar(name string) []byte {
70 h := sha512.New()
71 h.Write([]byte(name))
72 s := h.Sum(nil)
73 img := image.NewNRGBA(image.Rect(0, 0, 64, 64))
74 for i := 0; i < 64; i++ {
75 for j := 0; j < 64; j++ {
76 p := i*img.Stride + j*4
77 xx := i/16*16 + j/16
78 x := s[xx]
79 if x < 64 {
80 img.Pix[p+0] = avatarcolors[0][0]
81 img.Pix[p+1] = avatarcolors[0][1]
82 img.Pix[p+2] = avatarcolors[0][2]
83 img.Pix[p+3] = avatarcolors[0][3]
84 } else if x < 128 {
85 img.Pix[p+0] = avatarcolors[1][0]
86 img.Pix[p+1] = avatarcolors[1][1]
87 img.Pix[p+2] = avatarcolors[1][2]
88 img.Pix[p+3] = avatarcolors[1][3]
89 } else if x < 192 {
90 img.Pix[p+0] = avatarcolors[2][0]
91 img.Pix[p+1] = avatarcolors[2][1]
92 img.Pix[p+2] = avatarcolors[2][2]
93 img.Pix[p+3] = avatarcolors[2][3]
94 } else {
95 img.Pix[p+0] = avatarcolors[3][0]
96 img.Pix[p+1] = avatarcolors[3][1]
97 img.Pix[p+2] = avatarcolors[3][2]
98 img.Pix[p+3] = avatarcolors[3][3]
99 }
100 }
101 }
102 var buf bytes.Buffer
103 png.Encode(&buf, img)
104 return buf.Bytes()
105}
106
107func avatarURL(user *WhatAbout) string {
108 if ava := user.Options.Avatar; ava != "" {
109 return ava
110 }
111 return serverURL("/a?a=%s", url.QueryEscape(user.URL))
112}
113
114func showflag(writer http.ResponseWriter, req *http.Request) {
115 code := mux.Vars(req)["code"]
116 colors := strings.Split(code, ",")
117 numcolors := len(colors)
118 vert := false
119 if colors[0] == "vert" {
120 vert = true
121 colors = colors[1:]
122 numcolors--
123 if numcolors == 0 {
124 http.Error(writer, "bad flag", 400)
125 return
126 }
127 }
128 pixels := make([][4]byte, numcolors)
129 for i := 0; i < numcolors; i++ {
130 hex := colors[i]
131 if len(hex) == 3 {
132 hex = fmt.Sprintf("%c%c%c%c%c%c",
133 hex[0], hex[0], hex[1], hex[1], hex[2], hex[2])
134 }
135 c, _ := strconv.ParseUint(hex, 16, 32)
136 r := byte(c >> 16 & 0xff)
137 g := byte(c >> 8 & 0xff)
138 b := byte(c >> 0 & 0xff)
139 pixels[i][0] = r
140 pixels[i][1] = g
141 pixels[i][2] = b
142 pixels[i][3] = 255
143 }
144
145 h := 128
146 w := h * 3 / 2
147 img := image.NewRGBA(image.Rect(0, 0, w, h))
148 if vert {
149 for j := 0; j < w; j++ {
150 pix := pixels[j*numcolors/w][:]
151 for i := 0; i < h; i++ {
152 p := i*img.Stride + j*4
153 copy(img.Pix[p:], pix)
154 }
155 }
156 } else {
157 for i := 0; i < h; i++ {
158 pix := pixels[i*numcolors/h][:]
159 for j := 0; j < w; j++ {
160 p := i*img.Stride + j*4
161 copy(img.Pix[p:], pix)
162 }
163 }
164 }
165
166 writer.Header().Set("Cache-Control", "max-age="+somedays())
167 png.Encode(writer, img)
168}
169
170var re_flags = regexp.MustCompile("flag:[[:alnum:],]+")
171
172func fixupflags(h *Honk) []Emu {
173 var emus []Emu
174 count := 0
175 h.Noise = re_flags.ReplaceAllStringFunc(h.Noise, func(m string) string {
176 count++
177 var e Emu
178 e.Name = fmt.Sprintf(":flag%d:", count)
179 e.ID = serverURL("/flag/%s", m[5:])
180 emus = append(emus, e)
181 return e.Name
182 })
183 return emus
184}
185
186func renderflags(h *Honk) {
187 h.Noise = re_flags.ReplaceAllStringFunc(h.Noise, func(m string) string {
188 code := m[5:]
189 src := serverURL("/flag/%s", code)
190 return fmt.Sprintf(`<img class="emu" title="%s" src="%s">`, "flag", src)
191 })
192}