summaryrefslogtreecommitdiffstats
path: root/common.go
diff options
context:
space:
mode:
Diffstat (limited to 'common.go')
-rw-r--r--common.go141
1 files changed, 141 insertions, 0 deletions
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))
+ }
+}