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