xref: /haiku/src/add-ons/mail_daemon/inbound_protocols/imap/imap_lib/Response.cpp (revision 220d04022750f40f8bac8f01fa551211e28d04f2)
1 /*
2  * Copyright 2011, Axel Dörfler, axeld@pinc-software.de.
3  * Distributed under the terms of the MIT License.
4  */
5 
6 
7 #include "Response.h"
8 
9 
10 namespace IMAP {
11 
12 
13 ArgumentList::ArgumentList()
14 	:
15 	BObjectList<Argument>(5, true)
16 {
17 }
18 
19 
20 ArgumentList::~ArgumentList()
21 {
22 }
23 
24 
25 bool
26 ArgumentList::Contains(const char* string) const
27 {
28 	for (int32 i = 0; i < CountItems(); i++) {
29 		if (StringArgument* argument
30 				= dynamic_cast<StringArgument*>(ItemAt(i))) {
31 			if (argument->String().ICompare(string) == 0)
32 				return true;
33 		}
34 	}
35 	return false;
36 }
37 
38 
39 BString
40 ArgumentList::StringAt(int32 index) const
41 {
42 	if (index >= 0 && index < CountItems()) {
43 		if (StringArgument* argument
44 				= dynamic_cast<StringArgument*>(ItemAt(index)))
45 			return argument->String();
46 	}
47 	return "";
48 }
49 
50 
51 bool
52 ArgumentList::IsStringAt(int32 index) const
53 {
54 	if (index >= 0 && index < CountItems()) {
55 		if (dynamic_cast<StringArgument*>(ItemAt(index)) != NULL)
56 			return true;
57 	}
58 	return false;
59 }
60 
61 
62 bool
63 ArgumentList::EqualsAt(int32 index, const char* string) const
64 {
65 	return StringAt(index).ICompare(string) == 0;
66 }
67 
68 
69 ArgumentList&
70 ArgumentList::ListAt(int32 index) const
71 {
72 	if (index >= 0 && index < CountItems()) {
73 		if (ListArgument* argument = dynamic_cast<ListArgument*>(ItemAt(index)))
74 			return argument->List();
75 	}
76 
77 	static ArgumentList empty;
78 	return empty;
79 }
80 
81 
82 bool
83 ArgumentList::IsListAt(int32 index) const
84 {
85 	if (index >= 0 && index < CountItems()) {
86 		if (ListArgument* argument = dynamic_cast<ListArgument*>(ItemAt(index)))
87 			return true;
88 	}
89 	return false;
90 }
91 
92 
93 bool
94 ArgumentList::IsListAt(int32 index, char kind) const
95 {
96 	if (index >= 0 && index < CountItems()) {
97 		if (ListArgument* argument = dynamic_cast<ListArgument*>(ItemAt(index)))
98 			return argument->Kind() == kind;
99 	}
100 	return false;
101 }
102 
103 
104 int32
105 ArgumentList::IntegerAt(int32 index) const
106 {
107 	return atoi(StringAt(index).String());
108 }
109 
110 
111 bool
112 ArgumentList::IsIntegerAt(int32 index) const
113 {
114 	BString string = StringAt(index);
115 	for (int32 i = 0; i < string.Length(); i++) {
116 		if (!isdigit(string.ByteAt(i)))
117 			return false;
118 	}
119 	return string.Length() > 0;
120 }
121 
122 
123 BString
124 ArgumentList::ToString() const
125 {
126 	BString string;
127 
128 	for (int32 i = 0; i < CountItems(); i++) {
129 		if (i > 0)
130 			string += ", ";
131 		string += ItemAt(i)->ToString();
132 	}
133 	return string;
134 }
135 
136 
137 // #pragma mark -
138 
139 
140 Argument::Argument()
141 {
142 }
143 
144 
145 Argument::~Argument()
146 {
147 }
148 
149 
150 // #pragma mark -
151 
152 
153 ListArgument::ListArgument(char kind)
154 	:
155 	fKind(kind)
156 {
157 }
158 
159 
160 BString
161 ListArgument::ToString() const
162 {
163 	BString string(fKind == '[' ? "[" : "(");
164 	string += fList.ToString();
165 	string += fKind == '[' ? "]" : ")";
166 
167 	return string;
168 }
169 
170 
171 // #pragma mark -
172 
173 
174 StringArgument::StringArgument(const BString& string)
175 	:
176 	fString(string)
177 {
178 }
179 
180 
181 StringArgument::StringArgument(const StringArgument& other)
182 	:
183 	fString(other.fString)
184 {
185 }
186 
187 
188 BString
189 StringArgument::ToString() const
190 {
191 	return fString;
192 }
193 
194 
195 // #pragma mark -
196 
197 
198 ParseException::ParseException()
199 	:
200 	fMessage(NULL)
201 {
202 }
203 
204 
205 ParseException::ParseException(const char* message)
206 	:
207 	fMessage(message)
208 {
209 }
210 
211 
212 ParseException::~ParseException()
213 {
214 }
215 
216 
217 // #pragma mark -
218 
219 
220 ExpectedParseException::ExpectedParseException(char expected, char instead)
221 {
222 	snprintf(fBuffer, sizeof(fBuffer), "Expected \"%c\", but got \"%c\"!",
223 		expected, instead);
224 	fMessage = fBuffer;
225 }
226 
227 
228 // #pragma mark -
229 
230 
231 Response::Response()
232 	:
233 	fTag(0),
234 	fContinuation(false)
235 {
236 }
237 
238 
239 Response::~Response()
240 {
241 }
242 
243 
244 void
245 Response::SetTo(const char* line) throw(ParseException)
246 {
247 	MakeEmpty();
248 	fTag = 0;
249 	fContinuation = false;
250 
251 	if (line[0] == '*') {
252 		// Untagged response
253 		Consume(line, '*');
254 		Consume(line, ' ');
255 	} else if (line[0] == '+') {
256 		// Continuation
257 		Consume(line, '+');
258 		fContinuation = true;
259 	} else {
260 		// Tagged response
261 		Consume(line, 'A');
262 		fTag = strtoul(line, (char**)&line, 10);
263 		if (line == NULL)
264 			ParseException("Invalid tag!");
265 		Consume(line, ' ');
266 	}
267 
268 	char c = ParseLine(*this, line);
269 	if (c != '\0')
270 		throw ExpectedParseException('\0', c);
271 }
272 
273 
274 bool
275 Response::IsCommand(const char* command) const
276 {
277 	return IsUntagged() && EqualsAt(0, command);
278 }
279 
280 
281 char
282 Response::ParseLine(ArgumentList& arguments, const char*& line)
283 {
284 	while (line[0] != '\0') {
285 		char c = line[0];
286 		switch (c) {
287 			case '(':
288 				ParseList(arguments, line, '(', ')');
289 				break;
290 			case '[':
291 				ParseList(arguments, line, '[', ']');
292 				break;
293 			case ')':
294 			case ']':
295 				Consume(line, c);
296 				return c;
297 			case '"':
298 				ParseQuoted(arguments, line);
299 				break;
300 			case '{':
301 				ParseLiteral(arguments, line);
302 				break;
303 
304 			case ' ':
305 			case '\t':
306 				// whitespace
307 				Consume(line, c);
308 				break;
309 
310 			case '\r':
311 				Consume(line, '\r');
312 				Consume(line, '\n');
313 				return '\0';
314 			case '\n':
315 				Consume(line, '\n');
316 				return '\0';
317 
318 			default:
319 				ParseString(arguments, line);
320 				break;
321 		}
322 	}
323 
324 	return '\0';
325 }
326 
327 
328 void
329 Response::Consume(const char*& line, char c)
330 {
331 	if (line[0] != c)
332 		throw ExpectedParseException(c, line[0]);
333 
334 	line++;
335 }
336 
337 
338 void
339 Response::ParseList(ArgumentList& arguments, const char*& line, char start,
340 	char end)
341 {
342 	Consume(line, start);
343 
344 	ListArgument* argument = new ListArgument(start);
345 	arguments.AddItem(argument);
346 
347 	char c = ParseLine(argument->List(), line);
348 	if (c != end)
349 		throw ExpectedParseException(end, c);
350 }
351 
352 
353 void
354 Response::ParseQuoted(ArgumentList& arguments, const char*& line)
355 {
356 	Consume(line, '"');
357 
358 	BString string;
359 	char* output = string.LockBuffer(strlen(line));
360 	int32 index = 0;
361 
362 	while (line[0] != '\0') {
363 		char c = line[0];
364 		if (c == '\\') {
365 			line++;
366 			if (line[0] == '\0')
367 				break;
368 		} else if (c == '"') {
369 			line++;
370 			output[index] = '\0';
371 			string.UnlockBuffer(index);
372 			arguments.AddItem(new StringArgument(string));
373 			return;
374 		}
375 
376 		output[index++] = c;
377 		line++;
378 	}
379 
380 	throw ParseException("Unexpected end of qouted string!");
381 }
382 
383 
384 void
385 Response::ParseLiteral(ArgumentList& arguments, const char*& line)
386 {
387 	// TODO!
388 	throw ParseException("Literals are not yet supported!");
389 }
390 
391 
392 void
393 Response::ParseString(ArgumentList& arguments, const char*& line)
394 {
395 	arguments.AddItem(new StringArgument(ExtractString(line)));
396 }
397 
398 
399 BString
400 Response::ExtractString(const char*& line)
401 {
402 	const char* start = line;
403 
404 	while (line[0] != '\0') {
405 		char c = line[0];
406 		if (c <= ' ' || strchr("()[]{}\"", c) != NULL)
407 			return BString(start, line - start);
408 
409 		line++;
410 	}
411 
412 	throw ParseException("Unexpected end of string");
413 }
414 
415 
416 }	// namespace IMAP
417