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