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