Commit ab548c502b5ecba40003067a6bd6050479efe5ee
1 parent
2cff4b70cd
Exists in
master
added Valid() to filters
Showing
2 changed files
with
14 additions
and
6 deletions
Show diff stats
email.go
1 | package webutility | 1 | package webutility |
2 | 2 | ||
3 | // TODO(markO): test test test test test (and open source?) | 3 | // TODO(markO): test test test test test (and open source?) |
4 | 4 | ||
5 | import ( | 5 | import ( |
6 | "crypto/tls" | 6 | "crypto/tls" |
7 | "errors" | 7 | "errors" |
8 | "fmt" | 8 | "fmt" |
9 | "net/smtp" | 9 | "net/smtp" |
10 | "strings" | 10 | "strings" |
11 | ) | 11 | ) |
12 | 12 | ||
13 | type Email struct { | 13 | type Email struct { |
14 | To []string | 14 | To []string |
15 | From string | 15 | From string |
16 | Subject string | 16 | Subject string |
17 | Body string | 17 | Body string |
18 | } | 18 | } |
19 | 19 | ||
20 | func NewEmail(to []string, from, subject, body string) *Email { | 20 | func NewEmail(to []string, from, subject, body string) *Email { |
21 | return &Email{ | 21 | return &Email{ |
22 | To: to, | 22 | To: to, |
23 | From: from, | 23 | From: from, |
24 | Subject: subject, | 24 | Subject: subject, |
25 | Body: body, | 25 | Body: body, |
26 | } | 26 | } |
27 | } | 27 | } |
28 | 28 | ||
29 | func (e *Email) String() string { | 29 | func (e *Email) String() string { |
30 | var str strings.Builder | 30 | var str strings.Builder |
31 | 31 | ||
32 | str.WriteString("From:" + e.From + "\r\n") | 32 | str.WriteString("From:" + e.From + "\r\n") |
33 | 33 | ||
34 | str.WriteString("To:") | 34 | str.WriteString("To:") |
35 | for i, _ := range e.To { | 35 | for i, _ := range e.To { |
36 | if i > 0 { | 36 | if i > 0 { |
37 | str.WriteString(",") | 37 | str.WriteString(",") |
38 | } | 38 | } |
39 | str.WriteString(e.To[i]) | 39 | str.WriteString(e.To[i]) |
40 | } | 40 | } |
41 | str.WriteString("\r\n") | 41 | str.WriteString("\r\n") |
42 | 42 | ||
43 | str.WriteString("Subject:" + e.Subject + "\r\n") | 43 | str.WriteString("Subject:" + e.Subject + "\r\n") |
44 | 44 | ||
45 | // body | 45 | // body |
46 | str.WriteString("\r\n" + e.Body + "\r\n") | 46 | str.WriteString("\r\n" + e.Body + "\r\n") |
47 | 47 | ||
48 | return str.String() | 48 | return str.String() |
49 | } | 49 | } |
50 | 50 | ||
51 | func (e *Email) Bytes() []byte { | 51 | func (e *Email) Bytes() []byte { |
52 | return []byte(e.String()) | 52 | return []byte(e.String()) |
53 | } | 53 | } |
54 | 54 | ||
55 | func SendEmail(email *Email, conf *EmailConfig) error { | 55 | func SendEmail(email *Email, conf *EmailConfig) error { |
56 | if email == nil { | 56 | if email == nil { |
57 | return errors.New("no email to send") | 57 | return errors.New("no email to send") |
58 | } | 58 | } |
59 | 59 | ||
60 | if conf == nil { | 60 | if conf == nil { |
61 | return errors.New("email configuration not provided") | 61 | return errors.New("email configuration not provided") |
62 | } | 62 | } |
63 | 63 | ||
64 | c, err := smtp.Dial(conf.ServerName) | 64 | c, err := smtp.Dial(conf.ServerName) |
65 | if err != nil { | 65 | if err != nil { |
66 | return err | 66 | return err |
67 | } | 67 | } |
68 | defer c.Close() | 68 | defer c.Close() |
69 | 69 | ||
70 | // not sure if this is needed | 70 | // not sure if this is needed |
71 | //if err = c.Hello(conf.ServerName); err != nil { | 71 | //if err = c.Hello(conf.ServerName); err != nil { |
72 | // return err | 72 | // return err |
73 | //} | 73 | //} |
74 | 74 | ||
75 | if ok, _ := c.Extension("STARTTLS"); ok { | 75 | if ok, _ := c.Extension("STARTTLS"); ok { |
76 | // disable stupid tls check for self-signed certificates | 76 | // disable stupid tls check for self-signed certificates |
77 | config := &tls.Config{ | 77 | config := &tls.Config{ |
78 | ServerName: conf.ServerName, | 78 | ServerName: conf.ServerName, |
79 | InsecureSkipVerify: true, | 79 | InsecureSkipVerify: true, |
80 | } | 80 | } |
81 | // for golang testing | 81 | // for golang testing |
82 | //if testHookStartTLS != nil { | 82 | //if testHookStartTLS != nil { |
83 | // testHookStartTLS(config) | 83 | // testHookStartTLS(config) |
84 | //} | 84 | //} |
85 | if err = c.StartTLS(config); err != nil { | 85 | if err = c.StartTLS(config); err != nil { |
86 | return err | 86 | return err |
87 | } | 87 | } |
88 | } | 88 | } |
89 | 89 | ||
90 | /* | 90 | /* |
91 | // don't know what to do with this | 91 | // don't know what to do with this |
92 | if a != nil && c.ext != nil { | 92 | if a != nil && c.ext != nil { |
93 | if _, ok := c.ext["AUTH"]; !ok { | 93 | if _, ok := c.ext["AUTH"]; !ok { |
94 | return errors.New("smtp: server doesn't support AUTH") | 94 | return errors.New("smtp: server doesn't support AUTH") |
95 | } | 95 | } |
96 | if err = c.Auth(a); err != nil { | 96 | if err = c.Auth(a); err != nil { |
97 | return err | 97 | return err |
98 | } | 98 | } |
99 | } | 99 | } |
100 | */ | 100 | */ |
101 | 101 | ||
102 | // Set up authentication information. | 102 | // Set up authentication information. |
103 | auth := smtp.PlainAuth(conf.Identity, conf.Username, conf.Password, conf.Host) | 103 | auth := smtp.PlainAuth(conf.Identity, conf.Username, conf.Password, conf.Host) |
104 | if err = c.Auth(auth); err != nil { | 104 | if err = c.Auth(auth); err != nil { |
105 | return err | 105 | return err |
106 | } | 106 | } |
107 | 107 | ||
108 | if err = c.Mail(email.From); err != nil { | 108 | if err = c.Mail(email.From); err != nil { |
109 | return err | 109 | return err |
110 | } | 110 | } |
111 | 111 | ||
112 | for _, addr := range email.To { | 112 | for _, addr := range email.To { |
113 | if err = c.Rcpt(addr); err != nil { | 113 | if err = c.Rcpt(addr); err != nil { |
114 | return err | 114 | return err |
115 | } | 115 | } |
116 | } | 116 | } |
117 | 117 | ||
118 | w, err := c.Data() | 118 | w, err := c.Data() |
119 | if err != nil { | 119 | if err != nil { |
120 | return err | 120 | return err |
121 | } | 121 | } |
122 | 122 | ||
123 | _, err = w.Write(email.Bytes()) | 123 | _, err = w.Write(email.Bytes()) |
124 | if err != nil { | 124 | if err != nil { |
125 | return err | 125 | return err |
126 | } | 126 | } |
127 | 127 | ||
128 | err = w.Close() | 128 | err = w.Close() |
129 | if err != nil { | 129 | if err != nil { |
130 | return err | 130 | return err |
131 | } | 131 | } |
132 | 132 | ||
133 | return c.Quit() | 133 | return c.Quit() |
134 | } | 134 | } |
135 | 135 | ||
136 | type EmailConfig struct { | 136 | type EmailConfig struct { |
137 | ServerName string | 137 | ServerName string `json:"-"` |
138 | Identity string | 138 | Identity string `json:"-"` |
139 | Username string | 139 | Username string `json:"username"` |
140 | Password string | 140 | Password string `json:"password"` |
141 | Host string | 141 | Host string `json:"host"` |
142 | Port int | 142 | Port int `json:"port"` |
143 | } | 143 | } |
144 | 144 | ||
145 | func NewEmailConfig(ident, uname, pword, host string, port int) *EmailConfig { | 145 | func NewEmailConfig(ident, uname, pword, host string, port int) *EmailConfig { |
146 | return &EmailConfig{ | 146 | return &EmailConfig{ |
147 | ServerName: host + fmt.Sprintf(":%d", port), | 147 | ServerName: host + fmt.Sprintf(":%d", port), |
148 | Identity: ident, | 148 | Identity: ident, |
149 | Username: uname, | 149 | Username: uname, |
150 | Password: pword, | 150 | Password: pword, |
151 | Host: host, | 151 | Host: host, |
152 | Port: port, | 152 | Port: port, |
153 | } | 153 | } |
154 | } | 154 | } |
155 | 155 |
filtering.go
1 | package webutility | 1 | package webutility |
2 | 2 | ||
3 | import ( | 3 | import ( |
4 | "fmt" | 4 | "fmt" |
5 | "net/http" | 5 | "net/http" |
6 | "strings" | 6 | "strings" |
7 | ) | 7 | ) |
8 | 8 | ||
9 | type Filter map[string]string | 9 | type Filter map[string]string |
10 | 10 | ||
11 | func (f *Filter) Size() int { | ||
12 | return len(*f) | ||
13 | } | ||
14 | |||
15 | func (f *Filter) Valid() bool { | ||
16 | return len(*f) > 0 | ||
17 | } | ||
18 | |||
11 | func (fs Filter) validate(validFilters []string) (Filter, bool) { | 19 | func (fs Filter) validate(validFilters []string) (Filter, bool) { |
12 | goodFilters := make(map[string]string) | 20 | goodFilters := make(map[string]string) |
13 | cnt := 0 | 21 | cnt := 0 |
14 | len := 0 | 22 | len := 0 |
15 | for f, _ := range fs { | 23 | for f, _ := range fs { |
16 | len++ | 24 | len++ |
17 | for _, v := range validFilters { | 25 | for _, v := range validFilters { |
18 | if f == v { | 26 | if f == v { |
19 | cnt++ | 27 | cnt++ |
20 | goodFilters[f] = fs[f] | 28 | goodFilters[f] = fs[f] |
21 | } | 29 | } |
22 | } | 30 | } |
23 | } | 31 | } |
24 | 32 | ||
25 | result := true | 33 | result := true |
26 | if len > 0 && cnt == 0 { | 34 | if len > 0 && cnt == 0 { |
27 | // if no valid filters are found declare filtering request as invalid | 35 | // if no valid filters are found declare filtering request as invalid |
28 | result = false | 36 | result = false |
29 | } | 37 | } |
30 | 38 | ||
31 | return goodFilters, result | 39 | return goodFilters, result |
32 | } | 40 | } |
33 | 41 | ||
34 | // requires input in format: "param1::value1|param2::value2..." | 42 | // requires input in format: "param1::value1|param2::value2..." |
35 | func ParseFilters(req *http.Request, header string) (filters Filter) { | 43 | func ParseFilters(req *http.Request, header string) (filters Filter) { |
36 | q := req.FormValue(header) | 44 | q := req.FormValue(header) |
37 | q = strings.Trim(q, "\"") | 45 | q = strings.Trim(q, "\"") |
38 | kvp := strings.Split(q, "|") | 46 | kvp := strings.Split(q, "|") |
39 | filters = make(map[string]string, len(kvp)) | 47 | filters = make(map[string]string, len(kvp)) |
40 | 48 | ||
41 | for i, _ := range kvp { | 49 | for i, _ := range kvp { |
42 | kv := strings.Split(kvp[i], "::") | 50 | kv := strings.Split(kvp[i], "::") |
43 | if len(kv) == 2 { | 51 | if len(kv) == 2 { |
44 | key := kv[0] | 52 | key := kv[0] |
45 | val := kv[1] | 53 | val := kv[1] |
46 | filters[key] = val | 54 | filters[key] = val |
47 | } | 55 | } |
48 | } | 56 | } |
49 | 57 | ||
50 | return filters | 58 | return filters |
51 | } | 59 | } |
52 | 60 | ||
53 | // TODO(marko): very dodgy, needs more robustness | 61 | // TODO(marko): very dodgy, needs more robustness |
54 | func MakeFilterString(prefix string, filters Filter, validFilters []string) (res string, ok bool) { | 62 | func MakeFilterString(prefix string, filters Filter, validFilters []string) (res string, ok bool) { |
55 | if prefix != "" { | 63 | if prefix != "" { |
56 | prefix += "." | 64 | prefix += "." |
57 | } | 65 | } |
58 | if len(filters) == 0 { | 66 | if len(filters) == 0 { |
59 | return "", true | 67 | return "", true |
60 | } | 68 | } |
61 | 69 | ||
62 | filters, ok = filters.validate(validFilters) | 70 | filters, ok = filters.validate(validFilters) |
63 | if !ok { | 71 | if !ok { |
64 | return "", ok | 72 | return "", ok |
65 | } | 73 | } |
66 | 74 | ||
67 | symbol := "=" | 75 | symbol := "=" |
68 | for k, v := range filters { | 76 | for k, v := range filters { |
69 | if res != "" { | 77 | if res != "" { |
70 | res += " and " | 78 | res += " and " |
71 | } else { | 79 | } else { |
72 | res += " where " | 80 | res += " where " |
73 | } | 81 | } |
74 | c := string(v[0]) | 82 | c := string(v[0]) |
75 | if c == "<" || c == ">" { | 83 | if c == "<" || c == ">" { |
76 | symbol = "" // examples: >3, >=3 | 84 | symbol = "" // examples: >3, >=3 |
77 | } else { | 85 | } else { |
78 | symbol = "=" | 86 | symbol = "=" |
79 | } | 87 | } |
80 | 88 | ||
81 | res += fmt.Sprintf("%s%s %s '%s'", prefix, k, symbol, v) | 89 | res += fmt.Sprintf("%s%s %s '%s'", prefix, k, symbol, v) |
82 | } | 90 | } |
83 | 91 | ||
84 | return res, ok | 92 | return res, ok |
85 | } | 93 | } |
86 | 94 |