Mercurial > projects > managesieve
comparison parser.go @ 0:6369453d47a3
Initial revision
author | Guido Berhoerster <guido+managesieve@berhoerster.name> |
---|---|
date | Thu, 15 Oct 2020 09:11:05 +0200 |
parents | |
children |
comparison
equal
deleted
inserted
replaced
-1:000000000000 | 0:6369453d47a3 |
---|---|
1 // Copyright (C) 2020 Guido Berhoerster <guido+managesieve@berhoerster.name> | |
2 // | |
3 // Permission is hereby granted, free of charge, to any person obtaining | |
4 // a copy of this software and associated documentation files (the | |
5 // "Software"), to deal in the Software without restriction, including | |
6 // without limitation the rights to use, copy, modify, merge, publish, | |
7 // distribute, sublicense, and/or sell copies of the Software, and to | |
8 // permit persons to whom the Software is furnished to do so, subject to | |
9 // the following conditions: | |
10 // | |
11 // The above copyright notice and this permission notice shall be included | |
12 // in all copies or substantial portions of the Software. | |
13 // | |
14 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, | |
15 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF | |
16 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. | |
17 // IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY | |
18 // CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, | |
19 // TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE | |
20 // SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. | |
21 | |
22 package managesieve | |
23 | |
24 import ( | |
25 "strings" | |
26 ) | |
27 | |
28 type response int | |
29 | |
30 const ( | |
31 responseInvalid response = iota | |
32 responseOk | |
33 responseNo | |
34 responseBye | |
35 ) | |
36 | |
37 func lookupResponse(s string) response { | |
38 switch s { | |
39 case "OK": | |
40 return responseOk | |
41 case "NO": | |
42 return responseNo | |
43 case "BYE": | |
44 return responseBye | |
45 } | |
46 return responseInvalid | |
47 } | |
48 | |
49 type reply struct { | |
50 lines [][]*token | |
51 resp response | |
52 code string | |
53 codeArgs []string | |
54 msg string | |
55 } | |
56 | |
57 func parseCapabilities(r *reply) (map[string]string, error) { | |
58 capa := make(map[string]string) | |
59 for _, tokens := range r.lines { | |
60 var k, v string | |
61 if tokens[0].typ != tokenQuotedString && | |
62 tokens[0].typ != tokenLiteralString { | |
63 return nil, ParserError("failed to parse capability name: expected string") | |
64 } | |
65 k = strings.ToUpper(tokens[0].literal) | |
66 | |
67 if len(tokens) > 1 { | |
68 if tokens[1].typ != tokenQuotedString && | |
69 tokens[1].typ != tokenLiteralString { | |
70 return nil, ParserError("failed to parse capability value: expected string") | |
71 } | |
72 v = tokens[1].literal | |
73 } | |
74 capa[k] = v | |
75 } | |
76 return capa, nil | |
77 } | |
78 | |
79 type parser struct { | |
80 s *scanner | |
81 } | |
82 | |
83 func (p *parser) isResponseLine(tokens []*token) bool { | |
84 return tokens[0].typ == tokenAtom && | |
85 lookupResponse(tokens[0].literal) != responseInvalid | |
86 } | |
87 | |
88 func (p *parser) parseResponseLine(tokens []*token) (*reply, error) { | |
89 var i int | |
90 next := func() (*token, bool) { | |
91 if i >= len(tokens) { | |
92 return nil, false | |
93 } | |
94 tok := tokens[i] | |
95 i++ | |
96 return tok, true | |
97 } | |
98 | |
99 // response | |
100 tok, cont := next() | |
101 r := &reply{resp: lookupResponse(tok.literal)} | |
102 | |
103 // code starts with left parenthesis | |
104 tok, cont = next() | |
105 if !cont { | |
106 // only response without code and/or message | |
107 return r, nil | |
108 } | |
109 if tok.typ == tokenLeftParenthesis { | |
110 // code atom | |
111 tok, cont = next() | |
112 if !cont || tok.typ != tokenAtom { | |
113 return nil, ParserError("failed to parse response code: expected atom") | |
114 } | |
115 r.code = tok.literal | |
116 | |
117 // followed by zero or more string arguments | |
118 for { | |
119 tok, cont = next() | |
120 if !cont { | |
121 return nil, ParserError("failed to parse response code: unexpected end of line") | |
122 } | |
123 if tok.typ != tokenQuotedString && | |
124 tok.typ != tokenLiteralString { | |
125 break | |
126 } | |
127 r.codeArgs = append(r.codeArgs, tok.literal) | |
128 } | |
129 | |
130 // terminated by a right parenthesis | |
131 if tok.typ != tokenRightParenthesis { | |
132 return nil, ParserError("failed to parse response code: expected right parenthesis") | |
133 } | |
134 | |
135 tok, cont = next() | |
136 if !cont { | |
137 // response with code but no message | |
138 return r, nil | |
139 } | |
140 } | |
141 | |
142 // message string | |
143 if tok.typ != tokenQuotedString && | |
144 tok.typ != tokenLiteralString { | |
145 return nil, ParserError("failed to parse response message: expected string") | |
146 } | |
147 r.msg = strings.TrimSpace(tok.literal) | |
148 | |
149 // end of line | |
150 if _, cont = next(); cont { | |
151 return nil, ParserError("failed to parse response line: unexpected trailing data") | |
152 } | |
153 | |
154 return r, nil | |
155 } | |
156 | |
157 func (p *parser) readLine() ([]*token, error) { | |
158 tokens := make([]*token, 0) | |
159 for { | |
160 tok, err := p.s.scan() | |
161 if err != nil { | |
162 return nil, err | |
163 } | |
164 if tok.typ == tokenCRLF { | |
165 break | |
166 } | |
167 tokens = append(tokens, tok) | |
168 } | |
169 | |
170 return tokens, nil | |
171 } | |
172 | |
173 func (p *parser) readReply() (*reply, error) { | |
174 var r *reply | |
175 var lines [][]*token = make([][]*token, 0, 1) | |
176 for { | |
177 tokens, err := p.readLine() | |
178 if err != nil { | |
179 return nil, err | |
180 } | |
181 if len(tokens) == 0 { | |
182 return nil, ParserError("unexpected empty line") | |
183 } | |
184 // check for response tokens | |
185 if p.isResponseLine(tokens) { | |
186 r, err = p.parseResponseLine(tokens) | |
187 if err != nil { | |
188 return nil, err | |
189 } | |
190 r.lines = lines | |
191 break | |
192 } | |
193 lines = append(lines, tokens) | |
194 } | |
195 | |
196 return r, nil | |
197 } |