forked from External/grumble
Filter text according to server rules before storing.
This commit is contained in:
parent
4e89b124fb
commit
739cd1ca9b
3 changed files with 163 additions and 11 deletions
|
|
@ -194,6 +194,8 @@ func (channel *Channel) Freeze() (fc frozenChannel, err os.Error) {
|
||||||
}
|
}
|
||||||
fc.Links = links
|
fc.Links = links
|
||||||
|
|
||||||
|
fc.DescriptionBlob = channel.DescriptionBlob
|
||||||
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
48
message.go
48
message.go
|
|
@ -225,8 +225,11 @@ func (server *Server) handleChannelStateMessage(client *Client, msg *Message) {
|
||||||
|
|
||||||
// Extract the description and perform sanity checks.
|
// Extract the description and perform sanity checks.
|
||||||
if chanstate.Description != nil {
|
if chanstate.Description != nil {
|
||||||
description = *chanstate.Description
|
description, err = server.FilterText(*chanstate.Description)
|
||||||
// fixme(mkrautz): Check length
|
if err != nil {
|
||||||
|
client.sendPermissionDeniedType("TextTooLong")
|
||||||
|
return
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Extract the the name of channel and check whether it's valid.
|
// Extract the the name of channel and check whether it's valid.
|
||||||
|
|
@ -467,11 +470,15 @@ func (server *Server) handleChannelStateMessage(client *Client, msg *Message) {
|
||||||
|
|
||||||
// Description change
|
// Description change
|
||||||
if chanstate.Description != nil {
|
if chanstate.Description != nil {
|
||||||
key, err := blobstore.Put([]byte(*chanstate.Description))
|
if len(description) == 0 {
|
||||||
if err != nil {
|
channel.DescriptionBlob = ""
|
||||||
server.Panicf("Blobstore error: %v", err.String())
|
} else {
|
||||||
|
key, err := blobstore.Put([]byte(description))
|
||||||
|
if err != nil {
|
||||||
|
server.Panicf("Blobstore error: %v", err.String())
|
||||||
|
}
|
||||||
|
channel.DescriptionBlob = key
|
||||||
}
|
}
|
||||||
channel.DescriptionBlob = key
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Position change
|
// Position change
|
||||||
|
|
@ -654,17 +661,27 @@ func (server *Server) handleUserStateMessage(client *Client, msg *Message) {
|
||||||
|
|
||||||
// Only allow empty text.
|
// Only allow empty text.
|
||||||
if len(comment) > 0 {
|
if len(comment) > 0 {
|
||||||
client.Panic("Cannot clear another user's comment")
|
client.sendPermissionDeniedType("TextTooLong")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// todo(mkrautz): Check if the text is allowed.
|
filtered, err := server.FilterText(comment)
|
||||||
|
if err != nil {
|
||||||
|
client.sendPermissionDeniedType("TextTooLong")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
userstate.Comment = proto.String(filtered)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Texture change
|
// Texture change
|
||||||
if userstate.Texture != nil {
|
if userstate.Texture != nil {
|
||||||
// Check the length of the texture
|
maximg := server.cfg.IntValue("MaxImageMessageLength")
|
||||||
|
if maximg > 0 && len(userstate.Texture) > maximg {
|
||||||
|
client.sendPermissionDeniedType("TextTooLong")
|
||||||
|
return
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Registration
|
// Registration
|
||||||
|
|
@ -960,8 +977,17 @@ func (server *Server) handleTextMessage(client *Client, msg *Message) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// fixme(mkrautz): Check text message length.
|
filtered, err := server.FilterText(*txtmsg.Message)
|
||||||
// fixme(mkrautz): Sanitize text as well.
|
if err != nil {
|
||||||
|
client.sendPermissionDeniedType("TextTooLong")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(filtered) == 0 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
txtmsg.Message = proto.String(filtered)
|
||||||
|
|
||||||
clients := make(map[uint32]*Client)
|
clients := make(map[uint32]*Client)
|
||||||
|
|
||||||
|
|
|
||||||
124
server.go
124
server.go
|
|
@ -31,6 +31,7 @@ import (
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
"xml"
|
||||||
)
|
)
|
||||||
|
|
||||||
// The default port a Murmur server listens on
|
// The default port a Murmur server listens on
|
||||||
|
|
@ -1190,6 +1191,129 @@ func (server *Server) IsBanned(conn net.Conn) bool {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Filter incoming text according to the server's current rules.
|
||||||
|
func (server *Server) FilterText(text string) (filtered string, err os.Error) {
|
||||||
|
// This function filters incoming text from clients according to three server settings:
|
||||||
|
//
|
||||||
|
// AllowHTML:
|
||||||
|
// If false, all HTML shall be stripped.
|
||||||
|
// When stripping br tags, append a newline to the output stream.
|
||||||
|
// When stripping p tags, append a newline after the end tag.
|
||||||
|
//
|
||||||
|
// MaxTextMessageLength:
|
||||||
|
// Text length for "plain" messages (messages without images)
|
||||||
|
//
|
||||||
|
// MaxImageTextMessageLength:
|
||||||
|
// Text length for messages with images.
|
||||||
|
|
||||||
|
max := server.cfg.IntValue("MaxTextMessageLength")
|
||||||
|
maximg := server.cfg.IntValue("MaxImageMessageLength")
|
||||||
|
|
||||||
|
if !server.cfg.BoolValue("AllowHTML") {
|
||||||
|
if strings.Index(text, "<") == -1 {
|
||||||
|
filtered = strings.TrimSpace(text)
|
||||||
|
} else {
|
||||||
|
// Strip away all HTML
|
||||||
|
out := bytes.NewBuffer(nil)
|
||||||
|
buf := bytes.NewBufferString(text)
|
||||||
|
parser := xml.NewParser(buf)
|
||||||
|
parser.Strict = false
|
||||||
|
parser.AutoClose = xml.HTMLAutoClose
|
||||||
|
parser.Entity = xml.HTMLEntity
|
||||||
|
for {
|
||||||
|
tok, err := parser.Token()
|
||||||
|
if err == os.EOF {
|
||||||
|
break
|
||||||
|
} else if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
switch t := tok.(type) {
|
||||||
|
case xml.CharData:
|
||||||
|
out.Write(t)
|
||||||
|
case xml.EndElement:
|
||||||
|
if t.Name.Local == "p" || t.Name.Local == "br" {
|
||||||
|
out.WriteString("\n")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
filtered = strings.TrimSpace(out.String())
|
||||||
|
}
|
||||||
|
if max != 0 && len(filtered) > max {
|
||||||
|
return "", os.NewError("Message exceeds max length")
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// No limits
|
||||||
|
if max == 0 && maximg == 0 {
|
||||||
|
return text, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Too big for images?
|
||||||
|
if maximg != 0 && len(text) > maximg {
|
||||||
|
return "", os.NewError("Message exceeds max image message length")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Under max plain length?
|
||||||
|
if max == 0 || len(text) <= max {
|
||||||
|
return text, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Over max length, under image limit. If text doesn't include
|
||||||
|
// any HTML, this is a no-go. If there is XML, we can attempt to
|
||||||
|
// strip away data URIs to see if we can get the message to fit
|
||||||
|
// into the plain message limit.
|
||||||
|
if strings.Index(text, "<") == -1 {
|
||||||
|
return "", os.NewError("Over plain length")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Simplify the received HTML data by stripping away data URIs
|
||||||
|
out := bytes.NewBuffer(nil)
|
||||||
|
buf := bytes.NewBufferString(text)
|
||||||
|
parser := xml.NewParser(buf)
|
||||||
|
parser.Strict = false
|
||||||
|
parser.AutoClose = xml.HTMLAutoClose
|
||||||
|
parser.Entity = xml.HTMLEntity
|
||||||
|
for {
|
||||||
|
tok, err := parser.Token()
|
||||||
|
if err == os.EOF {
|
||||||
|
break
|
||||||
|
} else if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
switch t := tok.(type) {
|
||||||
|
case xml.CharData:
|
||||||
|
out.Write(t)
|
||||||
|
case xml.StartElement:
|
||||||
|
out.WriteString("<")
|
||||||
|
xml.Escape(out, []byte(t.Name.Local))
|
||||||
|
for _, attr := range t.Attr {
|
||||||
|
if t.Name.Local == "img" && attr.Name.Local == "src" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
out.WriteString(" ")
|
||||||
|
xml.Escape(out, []byte(attr.Name.Local))
|
||||||
|
out.WriteString(`="`)
|
||||||
|
out.WriteString(attr.Value)
|
||||||
|
out.WriteString(`"`)
|
||||||
|
}
|
||||||
|
out.WriteString(">")
|
||||||
|
case xml.EndElement:
|
||||||
|
out.WriteString("</")
|
||||||
|
xml.Escape(out, []byte(t.Name.Local))
|
||||||
|
out.WriteString(">")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
filtered = strings.TrimSpace(out.String())
|
||||||
|
if len(filtered) > max {
|
||||||
|
return "", os.NewError("Data URI stripped message longer than max length")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
// The accept loop of the server.
|
// The accept loop of the server.
|
||||||
func (s *Server) ListenAndMurmur() {
|
func (s *Server) ListenAndMurmur() {
|
||||||
// Launch the event handler goroutine
|
// Launch the event handler goroutine
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue