├── .gitignore ├── README.md ├── testdata ├── html-msg ├── mail-app-with-image ├── plain-text └── with-image ├── ui.html ├── websomtep.go └── websomtep_test.go /.gitignore: -------------------------------------------------------------------------------- 1 | *~ 2 | 3 | # Compiled Object files, Static and Dynamic libs (Shared Objects) 4 | *.o 5 | *.a 6 | *.so 7 | 8 | # Folders 9 | _obj 10 | _test 11 | 12 | # Architecture specific extensions/prefixes 13 | *.[568vq] 14 | [568vq].out 15 | 16 | *.cgo1.go 17 | *.cgo2.c 18 | _cgo_defun.c 19 | _cgo_gotypes.go 20 | _cgo_export.* 21 | 22 | _testmain.go 23 | 24 | *.exe 25 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | websomtep 2 | ========= 3 | 4 | Combination Pizza Hut Taco Bell? 5 | 6 | This is a combination SMTP / WebSocket server. 7 | 8 | This was a quick hack to demo at the inaugural GoSF meetup. 9 | 10 | This is an SMTP server that delivers all incoming email to all 11 | connected browser (websocket) clients. 12 | 13 | *DEMO*: http://websomtep.danga.com/ 14 | 15 | TOTALLY USEFUL. 16 | -------------------------------------------------------------------------------- /testdata/html-msg: -------------------------------------------------------------------------------- 1 | Subject: html subject 2 | From: Brad Fitzpatrick 3 | To: whatever@websomtep.danga.com 4 | Content-Type: multipart/alternative; boundary=e89a8ff1c1e81025a804be640ec8 5 | 6 | --e89a8ff1c1e81025a804be640ec8 7 | Content-Type: text/plain; charset=UTF-8 8 | 9 | html *body* 10 | 11 | --e89a8ff1c1e81025a804be640ec8 12 | Content-Type: text/html; charset=UTF-8 13 | 14 | html body 15 | 16 | --e89a8ff1c1e81025a804be640ec8-- 17 | -------------------------------------------------------------------------------- /testdata/mail-app-with-image: -------------------------------------------------------------------------------- 1 | From: Dustin Sallings 2 | Content-Type: multipart/alternative; 3 | boundary="Apple-Mail=_2B6F20DF-0410-4723-873B-AB9977D4F374" 4 | X-Smtp-Server: mail.west.spy.net:dustin 5 | Subject: always handy design guidelines 6 | X-Universally-Unique-Identifier: a3157352-3983-44cd-b7cd-62185cbb808f 7 | Date: Mon, 23 Apr 2012 23:19:30 -0700 8 | Message-Id: <0829387E-EB32-47C0-8BF9-C2066C4C9E29@spy.net> 9 | To: design@websomtep.danga.com 10 | Mime-Version: 1.0 (Apple Message framework v1257) 11 | 12 | 13 | --Apple-Mail=_2B6F20DF-0410-4723-873B-AB9977D4F374 14 | Content-Transfer-Encoding: 7bit 15 | Content-Type: text/plain; 16 | charset=us-ascii 17 | 18 | 19 | 20 | -- 21 | dustin sallings 22 | 23 | 24 | 25 | 26 | --Apple-Mail=_2B6F20DF-0410-4723-873B-AB9977D4F374 27 | Content-Type: multipart/related; 28 | type="text/html"; 29 | boundary="Apple-Mail=_29AE071D-8F29-40D0-BC41-F97F9919FC45" 30 | 31 | 32 | --Apple-Mail=_29AE071D-8F29-40D0-BC41-F97F9919FC45 33 | Content-Transfer-Encoding: 7bit 34 | Content-Type: text/html; 35 | charset=us-ascii 36 | 37 |

38 |
-- 
dustin sallings


39 |
40 | 41 |
42 | --Apple-Mail=_29AE071D-8F29-40D0-BC41-F97F9919FC45 43 | Content-Transfer-Encoding: base64 44 | Content-Disposition: inline; 45 | filename=1046.jpg 46 | Content-Type: image/jpg; 47 | x-unix-mode=0644; 48 | name="1046.jpg" 49 | Content-Id: 50 | 51 | /9j/4AAQSkZJRgABAAAAAQABAAD//gBASlBFRyBFbmNvZGVyIENvcHlyaWdodCAxOTk4LCBKYW1l 52 | cyBSLiBXZWVrcyBhbmQgQmlvRWxlY3Ryb01lY2j/2wCEAAoHBwgHBgoICAgLCgoLDhgQDg0NDh0V 53 | FhEYIx8lJCIfIiEmKzcvJik0KSEiMEExNDk7Pj4+JS5ESUM8SDc9PjsBCgsLDg0OHBAQHDsoIig7 54 | Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7O//AABEIAPAB 55 | QAMBEQACEQEDEQH/xAGiAAABBQEBAQEBAQAAAAAAAAAAAQIDBAUGBwgJCgsQAAIBAwMCBAMFBQQE 56 | AAABfQECAwAEEQUSITFBBhNRYQcicRQygZGhCCNCscEVUtHwJDNicoIJChYXGBkaJSYnKCkqNDU2 57 | Nzg5OkNERUZHSElKU1RVVldYWVpjZGVmZ2hpanN0dXZ3eHl6g4SFhoeIiYqSk5SVlpeYmZqio6Sl 58 | pqeoqaqys7S1tre4ubrCw8TFxsfIycrS09TV1tfY2drh4uPk5ebn6Onq8fLz9PX29/j5+gEAAwEB 59 | AQEBAQEBAQAAAAAAAAECAwQFBgcICQoLEQACAQIEBAMEBwUEBAABAncAAQIDEQQFITEGEkFRB2Fx 60 | EyIygQgUQpGhscEJIzNS8BVictEKFiQ04SXxFxgZGiYnKCkqNTY3ODk6Q0RFRkdISUpTVFVWV1hZ 61 | WmNkZWZnaGlqc3R1dnd4eXqCg4SFhoeIiYqSk5SVlpeYmZqio6Slpqeoqaqys7S1tre4ubrCw8TF 62 | xsfIycrS09TV1tfY2dri4+Tl5ufo6ery8/T19vf4+fr/2gAMAwEAAhEDEQA/APKo4kMa5UdK1SQD 63 | vKj/ALop2EHkx/3RSsMPJj/uiiwB5Mf90UWAPJj/ALoosAeVH/dFFgDyk/uiiwg8pP7o/KnYBfLT 64 | +6KVkAeUn90UWQB5Sf3RRYA8pP7oosgDyk/uiiyAPLT+6KLAHlJ/dFFgDyk/uiiwB5Sf3RRYA8tf 65 | 7ooshh5a/wB0UWQB5a/3RRYQeWv90U7AHlp/dFKwB5af3RRYA8tP7oosAeWv90UWANi+gosAeWv9 66 | 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 276 | 1Laal4d8axJp3i+9hg1eNNlrqsa+TvHUBx90HOTg8cnGDVNOLOiE+Y4vxP4S1bwnfeRqEJMDsRBc 277 | r9yYD+R9j+vWqTuWYtUIKAGTf6pvpSYwh/1S/ShAPpiCgAoAKACgAoAKACgAoAKACgAoAKACgAoA 278 | KACgAoAKACgAoAKACgAoAKACgAoAKACgCW1uriwvIby0laG4gcPHIvVWH+elJq4Hq+k/E/w/rdnC 279 | niz7TZXduD+9tnmWKbPciM5z7MCPQ81m0xOKZPc6/wDCq6QrLfzHPcxXJP6rVJtByIovdfCBoyJb 280 | uab0zDPkfT5RTcmHKjOfVfhachbPVQF6bUHP0yf50KUkHKieDxd8N7TBj0HVXZejAKp/SQUm5MLI 281 | xvHXj5PFlra6dZ2DWtjasHQzNulYgFR34GD6n60RQzjasAoAZL/qm+lJjH0xBQAUAFABQAUAFABQ 282 | AUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAYoAMUAFABQAUAFABQAy 283 | X/VN9KTGPpiCgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACg 284 | AoAKACgAoAKACgAoAKACgAoAZN/qm+lJjH0xBQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUA 285 | FABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAMl/1TfSkxj6YgoAKACgAoAKACgA 286 | oAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKAGT 287 | f6pvpSYx9MQUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQA0yIpwWANFxiebH/f 288 | H50rgHmx/wB8fnRcA82P++PzouAebH/fH50XAPNj/vj86LgHmx/3x+dFwDzY/wC+PzouAebH/fH5 289 | 0XAPNj/vj86LgHmx/wB8fnRcA82P++PzouAedH/fFFwDzo/74ouAedH/AHhRcA86P+8KLgHnR/3h 290 | RcBksqGJgGHShsD/2Q== 291 | 292 | --Apple-Mail=_29AE071D-8F29-40D0-BC41-F97F9919FC45-- 293 | 294 | --Apple-Mail=_2B6F20DF-0410-4723-873B-AB9977D4F374-- 295 | -------------------------------------------------------------------------------- /testdata/plain-text: -------------------------------------------------------------------------------- 1 | MIME-Version: 1.0 2 | Subject: plain text subject 3 | From: Brad Fitzpatrick 4 | To: whatever@websomtep.danga.com 5 | Content-Type: text/plain; charset=UTF-8 6 | 7 | plain text body 8 | -------------------------------------------------------------------------------- /testdata/with-image: -------------------------------------------------------------------------------- 1 | Subject: subjecto 2 | From: Brad Fitzpatrick 3 | To: whatever@websomtep.danga.com 4 | Content-Type: multipart/mixed; boundary=e89a8ff1c1e83553e304be640612 5 | 6 | --e89a8ff1c1e83553e304be640612 7 | Content-Type: multipart/alternative; boundary=e89a8ff1c1e83553e004be640610 8 | 9 | --e89a8ff1c1e83553e004be640610 10 | Content-Type: text/plain; charset=UTF-8 11 | 12 | bodyo 13 | 14 | --e89a8ff1c1e83553e004be640610 15 | Content-Type: text/html; charset=UTF-8 16 | 17 | bodyo


18 | 19 | --e89a8ff1c1e83553e004be640610-- 20 | --e89a8ff1c1e83553e304be640612 21 | Content-Type: image/png; name="Screen Shot 2012-04-10 at 7.28.45 PM.png" 22 | Content-Disposition: attachment; 23 | filename="Screen Shot 2012-04-10 at 7.28.45 PM.png" 24 | Content-Transfer-Encoding: base64 25 | X-Attachment-Id: f_h1edgigu0 26 | 27 | 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 57 | 31fPN9m31XfKz95vhd8pf7R/kP82/xsBWgHcgOaAqUDHwJWBfUGkoAVB1UEPgs2CRcE9IXBIYMj2 58 | kLvzDecL53eFgtCA0O2h98KMw5aFfR+OCQ8Lrwl/GGETURDRv4C6YMmClgWvIr0iyyLvRJlESaJ6 59 | oxWjE6Kbo1/HeMeUx0hjrWJXxl6K04gTxHXHY+Oj45vipxf6LNy5cDzBPqE44foi40V5iy4s1lic 60 | vvj4EsUlnCVHEtGJMYktie85oZwGzvTSgKW1S6e4bO4u7hOeB28Hb5Lvyi/nTyS5JpUnPUp2Td6e 61 | PJninlKR8lTAFlQLnqf6p9alvk4LTduf9ik9Jr09A5eRmHFUSBGmCfsytTPzMoezzLOKs6TLnJft 62 | XDYlChI1ZUPZi7K7xTTZz9SAxESyXjKa45ZTk/MmNzr3SJ5ynjBvYLnZ8k3LJ/J9879egVrBXdFb 63 | oFuwtmB0pefK+lXQqqWrelfrry5aPb7Gb82BtYS1aWt/KLQuLC98uS5mXU+RVtGaorH1futbixWK 64 | RcU3NrhsqNuI2ijYOLhp7qaqTR9LeCUXS61LK0rfb+ZuvviVzVeVX33akrRlsMyhbM9WzFbh1uvb 65 | 3LcdKFcuzy8f2x6yvXMHY0fJjpc7l+y8UGFXUbeLsEuyS1oZXNldZVC1tep9dUr1SI1XTXutZu2m 66 | 2te7ebuv7PHY01anVVda926vYO/Ner/6zgajhop9mH05+x42Rjf2f836urlJo6m06cN+4X7pgYgD 67 | fc2Ozc0tmi1lrXCrpHXyYMLBy994f9Pdxmyrb6e3lx4ChySHHn+b+O31w0GHe4+wjrR9Z/hdbQe1 68 | o6QT6lzeOdWV0iXtjusePhp4tLfHpafje8vv9x/TPVZzXOV42QnCiaITn07mn5w+lXXq6enk02O9 69 | S3rvnIk9c60vvG/wbNDZ8+d8z53p9+w/ed71/LELzheOXmRd7LrkcKlzwH6g4wf7HzoGHQY7hxyH 70 | ui87Xe4Znjd84or7ldNXva+euxZw7dLI/JHh61HXb95IuCG9ybv56Fb6ree3c27P3FlzF3235J7S 71 | vYr7mvcbfjT9sV3qID0+6j068GDBgztj3LEnP2X/9H686CH5YcWEzkTzI9tHxyZ9Jy8/Xvh4/EnW 72 | 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 420 | --e89a8ff1c1e83553e304be640612-- 421 | -------------------------------------------------------------------------------- /ui.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 58 | 81 | 82 | 83 |
84 |

This is an SMTP server written in Go which streams incoming mail to your (and everybody else's) active WebSocket connection.

85 |

Test it! Email whatever@websomtep.danga.com (protip: images work too)

86 |

You'll need Chrome 19 or 20 or Firefox or something for the WebSocket stuff.

87 |

Source code is here: https://github.com/bradfitz/websomtep.

88 |
89 |

websomtep == websockets + SMTP

90 |
91 |
92 | 93 | 94 | -------------------------------------------------------------------------------- /websomtep.go: -------------------------------------------------------------------------------- 1 | // This was quick & sloppy demo code to have a little fun at the 2 | // inaugural GoSF meetup (http://www.meetup.com/golangsf/). 3 | // 4 | // I release it under the public domain. It comes with no warranty whatsoever. 5 | // Have fun. 6 | // 7 | // Author: Brad Fitzpatrick 8 | 9 | package main 10 | 11 | import ( 12 | "bytes" 13 | "encoding/base64" 14 | "errors" 15 | "flag" 16 | "fmt" 17 | "html" 18 | "html/template" 19 | "io" 20 | "io/ioutil" 21 | "log" 22 | "mime" 23 | "mime/multipart" 24 | "net" 25 | "net/http" 26 | "net/mail" 27 | "strings" 28 | "sync" 29 | 30 | "code.google.com/p/go-smtpd/smtpd" 31 | "code.google.com/p/go.net/websocket" 32 | ) 33 | 34 | var ( 35 | webListen = flag.String("listen", ":8081", "address to listen for HTTP/WebSockets on") 36 | smtpListen = flag.String("smtp", ":2500", "address to listen for SMTP on") 37 | domain = flag.String("domain", "websomtep.danga.com", "required domain name in RCPT lines") 38 | wsAddr = flag.String("ws", "websomtep.danga.com", "websocket host[:port], as seen by JavaScript") 39 | debug = flag.Bool("debug", false, "enable debug features") 40 | ) 41 | 42 | // Message implements smtpd.Envelope by streaming the message to all 43 | // connected websocket clients. 44 | type Message struct { 45 | // HTML-escaped fields sent to the client 46 | From, To string 47 | Subject string 48 | Body string // includes images (via data URLs) 49 | 50 | // internal state 51 | images []image 52 | bodies []string 53 | buf bytes.Buffer // for accumulating email as it comes in 54 | msg interface{} // alternate message to send 55 | } 56 | 57 | // Stat is a JSON status message sent to clients when the number 58 | // of connected WebSocket clients change. 59 | type Stat struct { 60 | NumClients int 61 | } 62 | 63 | // SMTPStat is a JSON status message sent to clients when the number 64 | // of connected SMTP clients change. 65 | type SMTPStat struct { 66 | NumSenders int 67 | } 68 | 69 | type image struct { 70 | Type string 71 | Data []byte 72 | } 73 | 74 | func (m *Message) parse(r io.Reader) error { 75 | msg, err := mail.ReadMessage(r) 76 | if err != nil { 77 | return err 78 | } 79 | m.Subject = msg.Header.Get("Subject") 80 | m.To = msg.Header.Get("To") 81 | 82 | mediaType, params, err := mime.ParseMediaType(msg.Header.Get("Content-Type")) 83 | if err != nil || !strings.HasPrefix(mediaType, "multipart/") { 84 | slurp, _ := ioutil.ReadAll(msg.Body) 85 | m.Body = string(slurp) 86 | return nil 87 | } 88 | if err := m.parseMultipart(msg.Body, params["boundary"]); err != nil { 89 | return err 90 | } 91 | // If we didn't find a text/plain body, pick the first body we did find. 92 | if m.Body == "" { 93 | for _, body := range m.bodies { 94 | if body != "" { 95 | m.Body = body 96 | break 97 | } 98 | } 99 | } 100 | return nil 101 | } 102 | 103 | // parseMultipart populates Body (preferring text/plain) and images, 104 | // and may call itself recursively, walking through multipart/mixed 105 | // and multipart/alternative innards. 106 | func (m *Message) parseMultipart(r io.Reader, boundary string) error { 107 | mr := multipart.NewReader(r, boundary) 108 | for { 109 | part, err := mr.NextPart() 110 | if err == io.EOF { 111 | break 112 | } 113 | if err != nil { 114 | return err 115 | } 116 | partType, partParams, _ := mime.ParseMediaType(part.Header.Get("Content-Type")) 117 | if strings.HasPrefix(partType, "multipart/") { 118 | err = m.parseMultipart(part, partParams["boundary"]) 119 | if err != nil { 120 | log.Printf("in boundary %q, returning error for multipart child %q: %v", boundary, partParams["boundary"], err) 121 | return err 122 | } 123 | continue 124 | } 125 | if strings.HasPrefix(partType, "image/") { 126 | switch partType { 127 | case "image/gif", "image/png", "image/jpeg": 128 | default: 129 | // Probably http://golang.org/issue/3562 130 | continue 131 | } 132 | contentDis := part.Header.Get("Content-Disposition") 133 | if !(strings.HasPrefix(contentDis, "attachment") || strings.HasPrefix(contentDis, "inline")) { 134 | continue 135 | } 136 | if part.Header.Get("Content-Transfer-Encoding") != "base64" { 137 | continue 138 | } 139 | slurp, _ := ioutil.ReadAll(part) 140 | imdata, err := ioutil.ReadAll(base64.NewDecoder(base64.StdEncoding, bytes.NewReader(removeNewlines(slurp)))) 141 | if err != nil { 142 | log.Printf("image base64 decode error: %v", err) 143 | continue 144 | } 145 | m.images = append(m.images, image{ 146 | Type: partType, 147 | Data: imdata, 148 | }) 149 | continue 150 | } 151 | if !strings.HasPrefix(partType, "text/") { 152 | continue 153 | } 154 | slurp, _ := ioutil.ReadAll(part) 155 | if partType == "text/plain" { 156 | m.Body = string(slurp) 157 | } else { 158 | m.bodies = append(m.bodies, string(slurp)) 159 | } 160 | } 161 | return nil 162 | } 163 | 164 | func removeNewlines(p []byte) []byte { 165 | return bytes.Map(func(r rune) rune { 166 | switch r { 167 | case '\n', '\r': 168 | return -1 169 | } 170 | return r 171 | }, p) 172 | } 173 | 174 | func (m *Message) AddRecipient(rcpt smtpd.MailAddress) error { 175 | m.To = strings.ToLower(rcpt.Email()) 176 | if !strings.HasSuffix(m.To, "@"+*domain) { 177 | return errors.New("Invalid recipient domain") 178 | } 179 | return nil 180 | } 181 | 182 | func (m *Message) BeginData() error { return nil } 183 | 184 | const maxMessageSize = 5 << 20 185 | 186 | func (m *Message) Write(line []byte) error { 187 | m.buf.Write(line) 188 | if m.buf.Len() > maxMessageSize { 189 | return errors.New("too big, yo") 190 | } 191 | return nil 192 | } 193 | 194 | func (m *Message) Close() error { 195 | if err := m.parse(&m.buf); err != nil { 196 | return err 197 | } 198 | 199 | // This is a lame place to do this, but this is a dumb hack, 200 | // so whatever. 201 | for _, field := range []*string{&m.From, &m.To, &m.Subject, &m.Body} { 202 | *field = html.EscapeString(*field) 203 | } 204 | m.Body = strings.Replace(m.Body, "\n", "
\n", -1) 205 | 206 | for _, im := range m.images { 207 | m.Body = m.Body + fmt.Sprintf("

", im.Type, base64.StdEncoding.EncodeToString(im.Data)) 208 | } 209 | broadcast(m) 210 | if *debug { 211 | backlog = append(backlog, m) 212 | } 213 | return nil 214 | } 215 | 216 | var backlog []*Message 217 | 218 | func resend(w http.ResponseWriter, r *http.Request) { 219 | l := len(backlog) 220 | if l == 0 { 221 | return 222 | } 223 | m := backlog[l-1] 224 | for _, c := range clients() { 225 | c.Deliver(m) 226 | } 227 | } 228 | 229 | type Client chan *Message 230 | 231 | func (c Client) Deliver(m *Message) { 232 | select { 233 | case c <- m: 234 | default: 235 | // Client is too backlogged. They don't get this message. 236 | } 237 | } 238 | 239 | var ( 240 | mu sync.Mutex // guards clientMap 241 | clientMap = map[Client]bool{} 242 | ) 243 | 244 | func register(c Client) { 245 | mu.Lock() 246 | clientMap[c] = true 247 | n := len(clientMap) 248 | mu.Unlock() 249 | broadcast(&Message{msg: &Stat{NumClients: n}}) 250 | } 251 | 252 | func unregister(c Client) { 253 | mu.Lock() 254 | delete(clientMap, c) 255 | n := len(clientMap) 256 | mu.Unlock() 257 | broadcast(&Message{msg: &Stat{NumClients: n}}) 258 | } 259 | 260 | // clients returns all connected clients. 261 | func clients() (cs []Client) { 262 | mu.Lock() 263 | defer mu.Unlock() 264 | for c := range clientMap { 265 | cs = append(cs, c) 266 | } 267 | return 268 | } 269 | 270 | func broadcast(m *Message) { 271 | for _, c := range clients() { 272 | c.Deliver(m) 273 | } 274 | } 275 | 276 | func streamMail(ws *websocket.Conn) { 277 | log.Printf("websocket connection from %v", ws.RemoteAddr()) 278 | client := Client(make(chan *Message, 100)) 279 | register(client) 280 | defer unregister(client) 281 | 282 | deadc := make(chan bool, 1) 283 | 284 | // Wait for incoming messages. Don't really care about them, but 285 | // use this to find out if client goes away. 286 | go func() { 287 | var msg Message 288 | for { 289 | err := websocket.JSON.Receive(ws, &msg) 290 | switch err { 291 | case nil: 292 | log.Printf("Unexpected message from %v: %+v", ws.RemoteAddr(), msg) 293 | continue 294 | case io.EOF: 295 | default: 296 | log.Printf("Receive error from %v: %v", ws.RemoteAddr(), err) 297 | } 298 | deadc <- true 299 | } 300 | }() 301 | 302 | for { 303 | select { 304 | case <-deadc: 305 | return 306 | case m := <-client: 307 | var err error 308 | if m.msg != nil { 309 | err = websocket.JSON.Send(ws, m.msg) 310 | } else { 311 | err = websocket.JSON.Send(ws, m) 312 | } 313 | if err != nil { 314 | return 315 | } 316 | } 317 | } 318 | } 319 | 320 | var uiTemplate = template.Must(template.ParseFiles("ui.html")) 321 | 322 | type uiTemplateData struct { 323 | WSAddr string 324 | Domain string 325 | } 326 | 327 | func home(w http.ResponseWriter, r *http.Request) { 328 | var err error 329 | if *debug { 330 | uiTemplate, err = template.ParseFiles("ui.html") 331 | if err != nil { 332 | fmt.Fprint(w, err) 333 | return 334 | } 335 | } 336 | err = uiTemplate.Execute(w, uiTemplateData{ 337 | WSAddr: *wsAddr, 338 | Domain: *domain, 339 | }) 340 | if err != nil { 341 | log.Println(err) 342 | } 343 | } 344 | 345 | // countingListener tracks how many outstanding connections are open 346 | // from l, running fn on changel 347 | type countingListener struct { 348 | net.Listener 349 | fn func(int) 350 | mu sync.Mutex // guards n 351 | n int 352 | } 353 | 354 | func (cl *countingListener) inc(delta int) { 355 | cl.mu.Lock() 356 | cl.n += delta 357 | defer cl.fn(cl.n) 358 | cl.mu.Unlock() 359 | } 360 | 361 | func (cl *countingListener) Accept() (c net.Conn, err error) { 362 | c, err = cl.Listener.Accept() 363 | if err == nil { 364 | cl.inc(1) 365 | c = &watchCloseConn{c, cl} 366 | } 367 | return 368 | } 369 | 370 | type watchCloseConn struct { 371 | net.Conn 372 | cl *countingListener 373 | } 374 | 375 | func (w *watchCloseConn) Close() error { 376 | if cl := w.cl; cl != nil { 377 | cl.inc(-1) 378 | w.cl = nil 379 | } 380 | return w.Conn.Close() 381 | } 382 | 383 | func main() { 384 | flag.Parse() 385 | 386 | http.HandleFunc("/", home) 387 | if *debug { 388 | http.HandleFunc("/resend", resend) 389 | } 390 | http.Handle("/stream", websocket.Handler(streamMail)) 391 | 392 | sln, err := net.Listen("tcp", *smtpListen) 393 | if err != nil { 394 | log.Fatalf("error listening for SMTP: %v", err) 395 | } 396 | 397 | log.Printf("websomtep listening for HTTP on %q and SMTP on %q\n", *webListen, *smtpListen) 398 | go http.ListenAndServe(*webListen, nil) 399 | 400 | s := &smtpd.Server{ 401 | OnNewMail: func(c smtpd.Connection, from smtpd.MailAddress) (smtpd.Envelope, error) { 402 | log.Printf("New message from %q", from) 403 | e := &Message{ 404 | From: from.Email(), 405 | } 406 | return e, nil 407 | }, 408 | } 409 | 410 | smtpCountListener := &countingListener{ 411 | Listener: sln, 412 | fn: func(count int) { 413 | broadcast(&Message{msg: &SMTPStat{NumSenders: count}}) 414 | }, 415 | } 416 | 417 | err = s.Serve(smtpCountListener) 418 | if err != nil { 419 | log.Fatalf("ListenAndServe: %v", err) 420 | } 421 | } 422 | -------------------------------------------------------------------------------- /websomtep_test.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "crypto/sha1" 5 | "fmt" 6 | "os" 7 | "reflect" 8 | "strings" 9 | "testing" 10 | ) 11 | 12 | type parseTest struct { 13 | File string 14 | Subject, Body string 15 | Images map[string]string // sha1 -> content-type 16 | } 17 | 18 | var parseTests = []parseTest{ 19 | { 20 | File: "plain-text", 21 | Subject: "plain text subject", 22 | Body: "plain text body", 23 | }, 24 | { 25 | File: "html-msg", 26 | Subject: "html subject", 27 | Body: "html *body*", 28 | }, 29 | { 30 | File: "with-image", 31 | Subject: "subjecto", 32 | Body: "bodyo", 33 | Images: map[string]string{ 34 | "87cf0355fc364349590bc7b676d92298a2065146": "image/png", 35 | }, 36 | }, 37 | { 38 | File: "mail-app-with-image", 39 | Subject: "always handy design guidelines", 40 | Body: "-- \r\ndustin sallings", 41 | Images: map[string]string{ 42 | "72ba4970bc8fa484e8ed9c8c072644a92b2faa05": "image/jpg", 43 | }, 44 | }, 45 | } 46 | 47 | func TestMailParsing(t *testing.T) { 48 | for i, tt := range parseTests { 49 | f, err := os.Open("testdata/" + tt.File) 50 | if err != nil { 51 | t.Fatal(err) 52 | } 53 | defer f.Close() 54 | var m Message 55 | err = m.parse(f) 56 | if g, w := m.Subject, tt.Subject; g != w { 57 | t.Errorf("%d. subject = %q; want %q", i, g, w) 58 | } 59 | if g, w := strings.TrimSpace(m.Body), tt.Body; g != w { 60 | if len(g) > 1024 { 61 | g = g[:1024] 62 | } 63 | t.Errorf("%d. body = %q; want %q", i, g, w) 64 | } 65 | gotImages := map[string]string{} 66 | for _, im := range m.images { 67 | s1 := sha1.New() 68 | s1.Write(im.Data) 69 | gotImages[fmt.Sprintf("%x", s1.Sum(nil))] = im.Type 70 | } 71 | if m.images == nil && len(gotImages) == 0 { 72 | gotImages = nil 73 | } 74 | if !reflect.DeepEqual(tt.Images, gotImages) { 75 | t.Errorf("%d. got images = %+v; want %+v", i, gotImages, tt.Images) 76 | } 77 | } 78 | } 79 | --------------------------------------------------------------------------------