From da77deba78c8a7447b4a38324d2422a5df293b26 Mon Sep 17 00:00:00 2001 From: clsr Date: Fri, 18 Aug 2017 13:46:10 +0200 Subject: Initial commit --- common.go | 141 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 141 insertions(+) create mode 100644 common.go (limited to 'common.go') diff --git a/common.go b/common.go new file mode 100644 index 0000000..e6173b0 --- /dev/null +++ b/common.go @@ -0,0 +1,141 @@ +package cnp + +import ( + "bufio" + "bytes" + "io" + "mime" + "strconv" + "strings" + "time" +) + +func readLimitedLine(br *bufio.Reader, length int) ([]byte, error) { + var buf bytes.Buffer + + for { + data, err := br.ReadSlice('\n') + if len(data) > 0 { + buf.Write(data) + } + if buf.Len() > length { + return nil, ErrorTooLarge{"header exceeds maximum permitted size"} + } + if err == nil || err == io.EOF { + return buf.Bytes(), nil + } + if err != bufio.ErrBufferFull { + return nil, ErrorSyntax{"invalid header: missing line feed"} + } + } +} + +func getInt(m *Message, param string) (int64, error) { + p := m.Param(param) + if p == "" { + return 0, nil + } + n, err := strconv.ParseUint(p, 10, 63) + if err != nil || (n != 0 && p[0] == '0') || (n == 0 && len(p) != 1) { + return int64(n), ErrorInvalid{"invalid parameter: " + param + " is not a valid integer"} + } + return int64(n), nil +} + +func setInt(m *Message, param string, n int64) { + if n < 0 { + n = 0 + } + m.SetParam(param, strconv.FormatInt(n, 10)) +} + +func getFilename(m *Message, param string) (string, error) { + name := m.Param(param) + if strings.ContainsAny(name, "/\x00") { + return name, ErrorInvalid{"invalid parameter: " + param + " contains invalid characters"} + } + return name, nil +} + +func setFilename(m *Message, param, name string) error { + if strings.ContainsAny(name, "/\x00") { + return ErrorInvalid{"invalid parameter: " + param + " contains invalid characters"} + } + m.SetParam(param, name) + return nil +} + +func getType(m *Message, param string) (typ string, err error) { + t := m.Param(param) + if t != "" { + var params map[string]string + typ, params, err = mime.ParseMediaType(t) + if err != nil || !validMimeType(t) || len(params) > 0 { // may not contain params + err = ErrorInvalid{"invalid parameter: " + param + " is not a valid mime type"} + } + } + if typ == "" { + typ = "application/octet-stream" + } + return +} + +func setType(m *Message, param, typ string) error { + if typ == "" { + m.SetParam(param, "") + return nil + } + + /*ss := strings.Split(typ, "/") + if len(ss) != 2 || len(ss[0]) == 0 || strings.ContainsAny(typ, "\x00 \n\t\r;,") { + return ErrorInvalid{…} + }*/ + + t := mime.FormatMediaType(typ, nil) + if t == "" || !validMimeType(typ) { + return ErrorInvalid{"invalid parameter: " + param + " is not a valid mime type"} + } + m.SetParam(param, t) + + return nil +} + +func validMimeType(typ string) bool { + ss := strings.Split(typ, "/") + if len(ss) != 2 || ss[0] == "" || ss[1] == "" { + return false + } + for _, r := range typ { + /*switch r { + case '(', ')', '<', '>', '@', ',', ';', ':', '\\', '"', '[', ']', '?', '=': + // tspecials except / + return false + }*/ // handled by mime.ParseMediaType + if r <= ' ' || r >= '\x7f' { + // control codes, whitespace, null + return false + } + } + return true +} + +func getTime(m *Message, param string) (time.Time, error) { + t := m.Param(param) + var z time.Time + if t == "" { + return z, nil + } + ts, err := time.Parse(time.RFC3339, t) + if err != nil || !strings.HasSuffix(t, "Z") { + return z, ErrorInvalid{"invalid parameter: " + param + " is not a valid RFC3339 timestamp"} + } + return ts, nil +} + +func setTime(m *Message, param string, t time.Time) { + if t.IsZero() { + m.SetParam(param, "") + } else { + m.SetParam(param, t.UTC().Format(time.RFC3339)) + } +} -- cgit