diff --git a/include/mailio/codec.hpp b/include/mailio/codec.hpp index de00da0..cdbdd0e 100644 --- a/include/mailio/codec.hpp +++ b/include/mailio/codec.hpp @@ -64,11 +64,26 @@ class MAILIO_EXPORT codec **/ static const char PLUS_CHAR = '+'; + /** + Minus character. + **/ + static const char MINUS_CHAR = '-'; + + /** + Asterisk character. + **/ + static const char ASTERISK_CHAR = '*'; + + /** + Asterisk character as string. + **/ + static const std::string ASTERISK_STR; + /** Slash character. **/ static const char SLASH_CHAR = '/'; - + /** Backslash character. **/ @@ -79,6 +94,11 @@ class MAILIO_EXPORT codec **/ static const char EQUAL_CHAR = '='; + /** + Equal character as string. + **/ + static const std::string EQUAL_STR; + /** Space character. **/ @@ -108,25 +128,40 @@ class MAILIO_EXPORT codec Dot character. **/ static const char DOT_CHAR = '.'; - + + /** + Dot character string. + **/ + static const std::string DOT_STR; + /** Comma character. **/ static const char COMMA_CHAR = ','; - + + /** + Comma character as string. + **/ + static const std::string COMMA_STR; + /** Colon character. **/ static const char COLON_CHAR = ':'; - + + /** + Colon character as string. + **/ + static const std::string COLON_STR; + /** Semicolon character. - */ + **/ static const char SEMICOLON_CHAR = ';'; /** Semicolon character as string. - */ + **/ static const std::string SEMICOLON_STR; /** @@ -157,7 +192,7 @@ class MAILIO_EXPORT codec /** Quote character as string. **/ - static const std::string QUOTE_CHAR_STR; + static const std::string QUOTE_STR; /** Left parenthesis character. @@ -168,7 +203,7 @@ class MAILIO_EXPORT codec Right parenthesis character. **/ static const char RIGHT_PARENTHESIS_CHAR = ')'; - + /** Left bracket chartacter. **/ @@ -188,22 +223,32 @@ class MAILIO_EXPORT codec Right brace character. **/ static const char RIGHT_BRACE_CHAR = '}'; - + /** Monkey character. **/ static const char MONKEY_CHAR = '@'; - + /** Less than character. **/ static const char LESS_THAN_CHAR = '<'; - + + /** + Less than character as string. + **/ + static const std::string LESS_THAN_STR; + /** Greater than character. **/ static const char GREATER_THAN_CHAR = '>'; + /** + Greater than character as string. + **/ + static const std::string GREATER_THAN_STR; + /** Underscore character. **/ diff --git a/include/mailio/imap.hpp b/include/mailio/imap.hpp index 62f1042..d03c673 100644 --- a/include/mailio/imap.hpp +++ b/include/mailio/imap.hpp @@ -177,7 +177,7 @@ class MAILIO_EXPORT imap /** Sending the logout command and closing the connection. - **/ + **/ virtual ~imap(); imap(const imap&) = delete; @@ -192,7 +192,7 @@ class MAILIO_EXPORT imap Authenticating with the given credentials. The method should be called only once on an existing object - it is not possible to authenticate again within the same connection. - + @param username Username to authenticate. @param password Password to authenticate. @param method Authentication method to use. @@ -202,7 +202,7 @@ class MAILIO_EXPORT imap /** Selecting a mailbox. - + @param folder_name Folder to list. @return Mailbox statistics. @throw imap_error Selecting mailbox failure. @@ -215,10 +215,10 @@ class MAILIO_EXPORT imap /** Fetching a message from the mailbox. - + Some servers report success if a message with the given number does not exist, so the method returns with the empty `msg`. Other considers fetching non-existing message to be an error, and an exception is thrown. - + @param mailbox Mailbox to fetch from. @param message_no Number of the message to fetch. @param msg Message to store the result. @@ -278,7 +278,7 @@ class MAILIO_EXPORT imap Getting the mailbox statistics. The server might not support unseen, uidnext, or uidvalidity, which will cause an exception, so those parameters are optional. - + @param mailbox Mailbox name. @param info Statistics information to be retrieved. @return Mailbox statistics. @@ -292,7 +292,7 @@ class MAILIO_EXPORT imap /** Removing a message from the given mailbox. - + @param mailbox Mailbox to use. @param message_no Number of the message to remove. @throw imap_error Deleting message failure. @@ -304,7 +304,7 @@ class MAILIO_EXPORT imap /** Removing a message from an already selected mailbox. - + @param message_no Number of the message to remove. @param is_uid Using a message uid number instead of a message sequence number. @throw imap_error Deleting message failure. @@ -317,7 +317,7 @@ class MAILIO_EXPORT imap /** Searching a mailbox. - + @param conditions List of conditions taken in conjuction way. @param results Store resulting list of message sequence numbers or UIDs here. Does not clear the list first, so that results can be accumulated. @@ -389,10 +389,14 @@ class MAILIO_EXPORT imap **/ static std::string messages_range_list_to_string(std::list ranges); + /** + Untagged response character as defined by the protocol. + **/ + static const std::string UNTAGGED_RESPONSE; /** Initiating a session to the server. - + @throw imap_error Connection to server failure. @throw imap_error Parsing failure. @throw * `parse_tag_result(const string&)`, `dialog::receive()`. @@ -402,7 +406,7 @@ class MAILIO_EXPORT imap /** Performing an authentication by using the login method. - + @param username Username to authenticate. @param password Password to authenticate. @throw imap_error Authentication failure. @@ -427,7 +431,7 @@ class MAILIO_EXPORT imap /** Searching a mailbox. - + @param conditions String of search keys. @param results Store resulting list of indexes here. @param want_uids Return a list of message uids instead of message sequence numbers. @@ -502,16 +506,16 @@ class MAILIO_EXPORT imap /** Parsing a line into tag, result and response which is the rest of the line. - + @param line Response line to parse. @return Tuple with the tag, result and response. @throw imap_error Parsing failure. */ tag_result_response_t parse_tag_result(const std::string& line) const; - + /** Parsing a response (without tag and result) into optional and mandatory part. - + This is the main function that deals with the IMAP grammar. @param response Response to parse without tag and result. @@ -567,10 +571,10 @@ class MAILIO_EXPORT imap Tag used to identify requests and responses. **/ unsigned _tag; - + /** Token of the response defined by the grammar. - + Its type is determined by the content, and can be either atom, string literal or parenthesized list. Thus, it can be considered as union of those three types. **/ @@ -580,29 +584,29 @@ class MAILIO_EXPORT imap Token type which can be empty in the case that is not determined yet, atom, string literal or parenthesized list. **/ enum class token_type_t {EMPTY, ATOM, LITERAL, LIST} token_type; - + /** Token content in case it is atom. **/ std::string atom; - + /** Token content in case it is string literal. **/ std::string literal; - + /** String literal is first determined by its size, so it's stored here before reading the literal itself. **/ std::string literal_size; - + /** Token content in case it is parenthesized list. - + It can store either of the three types, so the definition is recursive. **/ std::list> parenthesized_list; - + /** Default constructor. **/ @@ -615,42 +619,42 @@ class MAILIO_EXPORT imap Optional part of the response, determined by the square brackets. **/ std::list> _optional_part; - + /** Mandatory part of the response, which is any text outside of the square brackets. **/ std::list> _mandatory_part; - + /** Parser state if an optional part is reached. **/ bool _optional_part_state; - + /** Parser state if an atom is reached. **/ enum class atom_state_t {NONE, PLAIN, QUOTED} _atom_state; - + /** Counting open parenthesis of a parenthized list, thus it also keeps parser state if a parenthesized list is reached. **/ unsigned int _parenthesis_list_counter; - + /** Parser state if a string literal is reached. **/ enum class string_literal_state_t {NONE, SIZE, WAITING, READING, DONE} _literal_state; - + /** Keeping the number of bytes read so far while parsing a string literal. **/ std::string::size_type _literal_bytes_read; - + /** Finding last token of the list at the given depth in terms of parenthesis count. - + When a new token is found, this method enables to find the last current token and append the new one. - + @param token_list Token sequence to traverse. @return Last token of the given sequence at the current depth of parenthesis count. **/ @@ -685,7 +689,7 @@ class MAILIO_EXPORT imaps : public imap /** Making a connection to the server. - + Calls parent constructor to do all the work. @param hostname Hostname of the server. @@ -697,7 +701,7 @@ class MAILIO_EXPORT imaps : public imap /** Sending the logout command and closing the connection. - + Calls parent destructor to do all the work. **/ virtual ~imaps() = default; @@ -724,7 +728,7 @@ class MAILIO_EXPORT imaps : public imap /** Switching to TLS layer. - + @throw imap_error Bad server response. @throw imap_error Start TLS refused by server. @throw * `parse_tag_result(const std::string&)`, `switch_to_ssl()`, `dialog::send(const std::string&)`, `dialog::receive()`. diff --git a/include/mailio/mime.hpp b/include/mailio/mime.hpp index cf27e2d..08364b3 100644 --- a/include/mailio/mime.hpp +++ b/include/mailio/mime.hpp @@ -454,15 +454,55 @@ class MAILIO_EXPORT mime **/ static const std::string CONTENT_DISPOSITION_INLINE; + /** + End of line as specified by the MIME format. + **/ + static const std::string END_OF_LINE; + + /** + Header name and value separator character. + **/ + static const char HEADER_SEPARATOR_CHAR = codec::COLON_CHAR; + /** Colon string used to separate header name from the header value. **/ - static const std::string COLON; + static const std::string HEADER_SEPARATOR_STR; + + /** + Attribute name and value separator. + **/ + static const char NAME_VALUE_SEPARATOR_CHAR = codec::EQUAL_CHAR; + + /** + Attribute name and value separator as string. + **/ + static const std::string NAME_VALUE_SEPARATOR_STR; + + /** + Separator of multiple attributes. + **/ + static const char ATTRIBUTES_SEPARATOR_CHAR = codec::SEMICOLON_CHAR; /** Semicolon string used to separate header attributes. **/ - static const std::string SEMICOLON; + static const std::string ATTRIBUTES_SEPARATOR_STR; + + /** + Attribute name part. + **/ + static const std::string ATTRIBUTE_NAME; + + /** + Attribute filename part. + **/ + static const std::string ATTRIBUTE_FILENAME; + + /** + Boundary special characters. + **/ + static const std::string BOUNDARY_DELIMITER; /** Alphanumerics plus some special character allowed in the quoted text. diff --git a/include/mailio/smtp.hpp b/include/mailio/smtp.hpp index 264e45a..7cf4779 100644 --- a/include/mailio/smtp.hpp +++ b/include/mailio/smtp.hpp @@ -192,6 +192,11 @@ class MAILIO_EXPORT smtp **/ static bool permanent_negative(int status); + /** + Server status when ready. + **/ + static const uint16_t SERVICE_READY_STATUS = 220; + /** Name of the host which client is connecting from. **/ diff --git a/src/codec.cpp b/src/codec.cpp index 3bffca6..b5cdea1 100644 --- a/src/codec.cpp +++ b/src/codec.cpp @@ -23,16 +23,30 @@ namespace mailio const string codec::HEX_DIGITS = "0123456789ABCDEF"; +const string codec::ASTERISK_STR(1, codec::ASTERISK_CHAR); + const string codec::CRLF = "\r\n"; +const string codec::EQUAL_STR(1, codec::EQUAL_CHAR); + const string codec::SPACE_STR(1, codec::SPACE_CHAR); const string codec::DOUBLE_SPACE_STR(2, codec::SPACE_CHAR); -const string codec::QUOTE_CHAR_STR(1, codec::QUOTE_CHAR); +const string codec::DOT_STR(1, codec::DOT_CHAR); + +const string codec::COMMA_STR(1, codec::COMMA_CHAR); + +const string codec::COLON_STR(1, codec::COLON_CHAR); const string codec::SEMICOLON_STR(1, codec::SEMICOLON_CHAR); +const string codec::QUOTE_STR(1, codec::QUOTE_CHAR); + +const string codec::LESS_THAN_STR(1, codec::LESS_THAN_CHAR); + +const string codec::GREATER_THAN_STR(1, codec::GREATER_THAN_CHAR); + int codec::hex_digit_to_int(char digit) { diff --git a/src/imap.cpp b/src/imap.cpp index 10bc208..e232f92 100644 --- a/src/imap.cpp +++ b/src/imap.cpp @@ -9,7 +9,7 @@ Distributed under the FreeBSD license, see the accompanying file LICENSE or copy at http://www.freebsd.org/copyright/freebsd-license.html. */ - + #include #include @@ -60,16 +60,17 @@ using boost::algorithm::is_any_of; namespace mailio { +const string imap::UNTAGGED_RESPONSE = codec::ASTERISK_STR; string imap::messages_range_to_string(imap::messages_range_t id_pair) { - return to_string(id_pair.first) + (id_pair.second.has_value() ? ":" + to_string(id_pair.second.value()) : ""); + return to_string(id_pair.first) + (id_pair.second.has_value() ? codec::COLON_STR + to_string(id_pair.second.value()) : ""); } string imap::messages_range_list_to_string(list ranges) { - return boost::join(ranges | boost::adaptors::transformed(static_cast(messages_range_to_string)), ","); + return boost::join(ranges | boost::adaptors::transformed(static_cast(messages_range_to_string)), codec::COMMA_STR); } @@ -91,15 +92,15 @@ imap::search_condition_t::search_condition_t(imap::search_condition_t::key_type } case SUBJECT: - imap_string = "SUBJECT \"" + std::get(value) + "\""; + imap_string = "SUBJECT " + codec::QUOTE_STR + std::get(value) + codec::QUOTE_STR; break; case FROM: - imap_string = "FROM \"" + std::get(value) + "\""; + imap_string = "FROM " + codec::QUOTE_STR + std::get(value) + codec::QUOTE_STR; break; case TO: - imap_string = "TO \"" + std::get(value) + "\""; + imap_string = "TO " + codec::QUOTE_STR + std::get(value) + codec::QUOTE_STR; break; case BEFORE_DATE: @@ -204,17 +205,17 @@ void imap::fetch(unsigned long message_no, message& msg, bool is_uid, bool heade string cmd; if (is_uid) cmd.append("UID "); - cmd.append("FETCH " + to_string(message_no) + " " + RFC822_TOKEN); + cmd.append("FETCH " + to_string(message_no) + codec::SPACE_STR + RFC822_TOKEN); _dlg->send(format(cmd)); - + bool has_more = true; while (has_more) { reset_response_parser(); string line = _dlg->receive(); tag_result_response_t parsed_line = parse_tag_result(line); - - if (parsed_line.tag == "*") + + if (parsed_line.tag == UNTAGGED_RESPONSE) { parse_response(parsed_line.response); @@ -243,7 +244,7 @@ void imap::fetch(unsigned long message_no, message& msg, bool is_uid, bool heade rfc_found = true; continue; } - + if ((*token)->token_type == response_token_t::token_type_t::LITERAL) { if (rfc_found) @@ -307,17 +308,17 @@ void imap::fetch(const list messages_range, mapsend(format(cmd)); - + bool has_more = true; while (has_more) { reset_response_parser(); string line = _dlg->receive(); tag_result_response_t parsed_line = parse_tag_result(line); - - if (parsed_line.tag == "*") + + if (parsed_line.tag == UNTAGGED_RESPONSE) { parse_response(parsed_line.response); unsigned long msg_no = 0; @@ -424,7 +425,7 @@ void imap::fetch(const list messages_range, map mailbox_stat_ // It doesn't like search terms it doesn't recognize. // Some older protocol versions or some servers may not support them. // So unseen uidnext and uidvalidity are optional. - string cmd = "STATUS \"" + mailbox + "\" (messages recent"; + string cmd = "STATUS " + codec::QUOTE_STR + mailbox + codec::QUOTE_STR + " (messages recent"; if (info & mailbox_stat_t::UNSEEN) cmd += " unseen"; if (info & mailbox_stat_t::UID_NEXT) @@ -442,10 +443,10 @@ auto imap::statistics(const string& mailbox, unsigned int info) -> mailbox_stat_ if (info & mailbox_stat_t::UID_VALIDITY) cmd += " uidvalidity"; cmd += ")"; - + _dlg->send(format(cmd)); mailbox_stat_t stat; - + bool has_more = true; while (has_more) { @@ -453,13 +454,13 @@ auto imap::statistics(const string& mailbox, unsigned int info) -> mailbox_stat_ string line = _dlg->receive(); tag_result_response_t parsed_line = parse_tag_result(line); - if (parsed_line.tag == "*") + if (parsed_line.tag == UNTAGGED_RESPONSE) { parse_response(parsed_line.response); if (!iequals(_mandatory_part.front()->atom, "STATUS")) throw imap_error("Getting statistics failure."); _mandatory_part.pop_front(); - + bool mess_found = false, recent_found = false; for (auto it = _mandatory_part.begin(); it != _mandatory_part.end(); it++) if ((*it)->token_type == response_token_t::token_type_t::LIST && (*it)->parenthesized_list.size() >= 2) @@ -544,8 +545,8 @@ void imap::remove(unsigned long message_no, bool is_uid) reset_response_parser(); string line = _dlg->receive(); tag_result_response_t parsed_line = parse_tag_result(line); - - if (parsed_line.tag == "*") + + if (parsed_line.tag == UNTAGGED_RESPONSE) { parse_response(parsed_line.response); auto token = _mandatory_part.front(); @@ -584,7 +585,7 @@ void imap::search(const list& conditions, list& folder_tree) { string delim = folder_delimiter(); string folder_str = folder_tree_to_string(folder_tree, delim); - _dlg->send(format("CREATE \"" + folder_str + "\"")); + _dlg->send(format("CREATE " + codec::QUOTE_STR + folder_str + codec::QUOTE_STR)); string line = _dlg->receive(); tag_result_response_t parsed_line = parse_tag_result(line); @@ -613,7 +614,8 @@ auto imap::list_folders(const list& folder_name) -> mailbox_folder { string delim = folder_delimiter(); string folder_name_s = folder_tree_to_string(folder_name, delim); - _dlg->send(format("LIST \"\" \"" + folder_name_s + "*\"")); + _dlg->send(format("LIST " + codec::QUOTE_STR + codec::QUOTE_STR + codec::SPACE_STR + codec::QUOTE_STR + folder_name_s + codec::ASTERISK_STR + + codec::QUOTE_STR)); mailbox_folder mailboxes; bool has_more = true; @@ -622,7 +624,7 @@ auto imap::list_folders(const list& folder_name) -> mailbox_folder string line = _dlg->receive(); tag_result_response_t parsed_line = parse_tag_result(line); parse_response(parsed_line.response); - if (parsed_line.tag == "*") + if (parsed_line.tag == UNTAGGED_RESPONSE) { auto token = _mandatory_part.front(); _mandatory_part.pop_front(); @@ -665,7 +667,7 @@ bool imap::delete_folder(const list& folder_name) { string delim = folder_delimiter(); string folder_name_s = folder_tree_to_string(folder_name, delim); - _dlg->send(format("DELETE \"" + folder_name_s + "\"")); + _dlg->send(format("DELETE " + codec::QUOTE_STR + folder_name_s + codec::QUOTE_STR)); string line = _dlg->receive(); tag_result_response_t parsed_line = parse_tag_result(line); @@ -684,7 +686,7 @@ bool imap::rename_folder(const list& old_name, const list& new_n string delim = folder_delimiter(); string old_name_s = folder_tree_to_string(old_name, delim); string new_name_s = folder_tree_to_string(new_name, delim); - _dlg->send(format("RENAME \"" + old_name_s + "\" \"" + new_name_s + "\"")); + _dlg->send(format("RENAME " + codec::QUOTE_STR + old_name_s + codec::QUOTE_STR + codec::SPACE_STR + codec::QUOTE_STR + new_name_s + codec::QUOTE_STR)); string line = _dlg->receive(); tag_result_response_t parsed_line = parse_tag_result(line); @@ -704,7 +706,7 @@ void imap::connect() string line = _dlg->receive(); tag_result_response_t parsed_line = parse_tag_result(line); - if (parsed_line.tag != "*") + if (parsed_line.tag != UNTAGGED_RESPONSE) throw imap_error("Parsing failure."); if (!parsed_line.result.has_value() || parsed_line.result.value() != tag_result_response_t::OK) throw imap_error("Connection to server failure."); @@ -713,22 +715,22 @@ void imap::connect() void imap::auth_login(const string& username, const string& password) { - auto cmd = format("LOGIN " + username + " " + password); + auto cmd = format("LOGIN " + username + codec::SPACE_STR + password); _dlg->send(cmd); - + bool has_more = true; while (has_more) { string line = _dlg->receive(); tag_result_response_t parsed_line = parse_tag_result(line); - if (parsed_line.tag == "*") + if (parsed_line.tag == UNTAGGED_RESPONSE) continue; if (parsed_line.tag != to_string(_tag)) throw imap_error("Parsing failure."); if (parsed_line.result.value() != tag_result_response_t::OK) throw imap_error("Authentication failure."); - + has_more = false; } } @@ -738,9 +740,9 @@ auto imap::select(const string& mailbox, bool read_only) -> mailbox_stat_t { string cmd; if (read_only) - cmd = format("EXAMINE \"" + mailbox + "\""); + cmd = format("EXAMINE " + codec::QUOTE_STR + mailbox + codec::QUOTE_STR); else - cmd = format("SELECT \"" + mailbox + "\""); + cmd = format("SELECT " + codec::QUOTE_STR + mailbox + codec::QUOTE_STR); _dlg->send(cmd); mailbox_stat_t stat; @@ -757,7 +759,7 @@ auto imap::select(const string& mailbox, bool read_only) -> mailbox_stat_t tag_result_response_t parsed_line = parse_tag_result(line); parse_response(parsed_line.response); - if (parsed_line.tag == "*") + if (parsed_line.tag == UNTAGGED_RESPONSE) { const auto result = parsed_line.result; if (result.has_value() && result.value() == tag_result_response_t::OK) @@ -856,7 +858,7 @@ void imap::search(const string& conditions, list& results, bool w reset_response_parser(); string line = _dlg->receive(); tag_result_response_t parsed_line = parse_tag_result(line); - if (parsed_line.tag == "*") + if (parsed_line.tag == UNTAGGED_RESPONSE) { parse_response(parsed_line.response); @@ -878,7 +880,7 @@ void imap::search(const string& conditions, list& results, bool w { if (parsed_line.result.value() != tag_result_response_t::OK) throw imap_error("Search mailbox failure."); - + has_more = false; } else @@ -902,13 +904,13 @@ void imap::search(const string& conditions, list& results, bool w string imap::folder_delimiter() { string delimiter; - _dlg->send(format("LIST \"\" \"\"")); + _dlg->send(format("LIST " + codec::QUOTE_STR + codec::QUOTE_STR + codec::SPACE_STR + codec::QUOTE_STR + codec::QUOTE_STR)); bool has_more = true; while (has_more) { string line = _dlg->receive(); tag_result_response_t parsed_line = parse_tag_result(line); - if (parsed_line.tag == "*" && delimiter.empty()) + if (parsed_line.tag == UNTAGGED_RESPONSE && delimiter.empty()) { parse_response(parsed_line.response); if (!iequals(_mandatory_part.front()->atom, "LIST")) @@ -937,13 +939,13 @@ string imap::folder_delimiter() auto imap::parse_tag_result(const string& line) const -> tag_result_response_t { - string::size_type tag_pos = line.find(' '); + string::size_type tag_pos = line.find(codec::SPACE_CHAR); if (tag_pos == string::npos) throw imap_error("Parsing failure."); string tag = line.substr(0, tag_pos); string::size_type result_pos = string::npos; - result_pos = line.find(' ', tag_pos + 1); + result_pos = line.find(codec::SPACE_CHAR, tag_pos + 1); string result_s = line.substr(tag_pos + 1, result_pos - tag_pos - 1); std::optional result = std::nullopt; if (iequals(result_s, "OK")) @@ -971,15 +973,15 @@ parenthesized list: 2. if a brace is read, then string literal size is found, so read a number and then literal itself 3. if a parenthesis is found, then a list is being read, so increase the parenthesis counter and proceed 4. for a regular char check the state and determine if an atom or string size/literal is read - + Token of the grammar is defined by `response_token_t` and stores one of the three types. Since parenthesized list is recursively defined, it keeps sequence of tokens. When a character is read, it belongs to the last token of the sequence of tokens at the given parenthesis depth. The last token -of the response expression is found by getting the last token of the token sequence at the given depth (in terms of parenthesis count). -*/ +of the response expression is found by getting the last token of the token sequence at the given depth (in terms of parenthesis count). +*/ void imap::parse_response(const string& response) { list>* token_list; - + if (_literal_state == string_literal_state_t::READING) { token_list = _optional_part_state ? find_last_token_list(_optional_part) : find_last_token_list(_mandatory_part); @@ -1004,7 +1006,7 @@ void imap::parse_response(const string& response) return; } } - + shared_ptr cur_token; for (auto ch : response) { @@ -1023,7 +1025,7 @@ void imap::parse_response(const string& response) } } break; - + case codec::RIGHT_BRACKET_CHAR: { if (_atom_state == atom_state_t::QUOTED) @@ -1038,7 +1040,7 @@ void imap::parse_response(const string& response) } } break; - + case codec::LEFT_PARENTHESIS_CHAR: { if (_atom_state == atom_state_t::QUOTED) @@ -1054,7 +1056,7 @@ void imap::parse_response(const string& response) } } break; - + case codec::RIGHT_PARENTHESIS_CHAR: { if (_atom_state == atom_state_t::QUOTED) @@ -1069,7 +1071,7 @@ void imap::parse_response(const string& response) } } break; - + case codec::LEFT_BRACE_CHAR: { if (_atom_state == atom_state_t::QUOTED) @@ -1088,7 +1090,7 @@ void imap::parse_response(const string& response) } } break; - + case codec::RIGHT_BRACE_CHAR: { if (_atom_state == atom_state_t::QUOTED) @@ -1102,7 +1104,7 @@ void imap::parse_response(const string& response) } } break; - + case codec::SPACE_CHAR: { if (_atom_state == atom_state_t::QUOTED) @@ -1132,14 +1134,14 @@ void imap::parse_response(const string& response) _atom_state = atom_state_t::NONE; } break; - + default: { if (_literal_state == string_literal_state_t::SIZE) { if (!isdigit(ch)) throw imap_error("Parser failure."); - + cur_token->literal_size += ch; } else if (_literal_state == string_literal_state_t::WAITING) @@ -1181,7 +1183,7 @@ void imap::reset_response_parser() string imap::format(const string& command) { - return to_string(++_tag) + " " + command; + return to_string(++_tag) + codec::SPACE_STR + command; } @@ -1262,7 +1264,7 @@ void imaps::start_tls() _dlg->send(format("STARTTLS")); string line = _dlg->receive(); tag_result_response_t parsed_line = parse_tag_result(line); - if (parsed_line.tag == "*") + if (parsed_line.tag == UNTAGGED_RESPONSE) throw imap_error("Bad server response."); if (parsed_line.result.value() != tag_result_response_t::OK) throw imap_error("Start TLS refused by server."); diff --git a/src/message.cpp b/src/message.cpp index d066a12..55d73ac 100644 --- a/src/message.cpp +++ b/src/message.cpp @@ -108,7 +108,7 @@ void message::format(string& message_str, bool dot_escape) const content_part.header_codec(_header_codec); string cps; content_part.format(cps, dot_escape); - message_str += "--" + _boundary + codec::CRLF + cps + codec::CRLF; + message_str += BOUNDARY_DELIMITER + _boundary + END_OF_LINE + cps + END_OF_LINE; } // recursively format mime parts @@ -116,9 +116,9 @@ void message::format(string& message_str, bool dot_escape) const { string p_str; p.format(p_str, dot_escape); - message_str += "--" + _boundary + codec::CRLF + p_str + codec::CRLF; + message_str += BOUNDARY_DELIMITER + _boundary + END_OF_LINE + p_str + END_OF_LINE; } - message_str += "--" + _boundary + "--" + codec::CRLF; + message_str += BOUNDARY_DELIMITER + _boundary + BOUNDARY_DELIMITER + END_OF_LINE; } else message_str += format_content(dot_escape); @@ -367,12 +367,12 @@ string message::format_header() const if (_from.addresses.size() > 1 && _sender.empty()) throw message_error("No sender for multiple authors."); - header += FROM_HEADER + COLON + from_to_string() + codec::CRLF; - header += _sender.address.empty() ? "" : SENDER_HEADER + COLON + sender_to_string() + codec::CRLF; - header += _reply_address.name.empty() ? "" : REPLY_TO_HEADER + COLON + reply_address_to_string() + codec::CRLF; - header += TO_HEADER + COLON + recipients_to_string() + codec::CRLF; - header += _cc_recipients.empty() ? "" : CC_HEADER + COLON + cc_recipients_to_string() + codec::CRLF; - header += _bcc_recipients.empty() ? "" : BCC_HEADER + COLON + bcc_recipients_to_string() + codec::CRLF; + header += FROM_HEADER + HEADER_SEPARATOR_STR + from_to_string() + END_OF_LINE; + header += _sender.address.empty() ? "" : SENDER_HEADER + HEADER_SEPARATOR_STR + sender_to_string() + END_OF_LINE; + header += _reply_address.name.empty() ? "" : REPLY_TO_HEADER + HEADER_SEPARATOR_STR + reply_address_to_string() + END_OF_LINE; + header += TO_HEADER + HEADER_SEPARATOR_STR + recipients_to_string() + END_OF_LINE; + header += _cc_recipients.empty() ? "" : CC_HEADER + HEADER_SEPARATOR_STR + cc_recipients_to_string() + END_OF_LINE; + header += _bcc_recipients.empty() ? "" : BCC_HEADER + HEADER_SEPARATOR_STR + bcc_recipients_to_string() + END_OF_LINE; // TODO: move formatting datetime to a separate method if (!_date_time->is_not_a_date_time()) @@ -382,14 +382,14 @@ string message::format_header() const local_time_facet* facet = new local_time_facet("%a, %d %b %Y %H:%M:%S %q"); ss.imbue(locale(ss.getloc(), facet)); ss << *_date_time; - header += DATE_HEADER + COLON + ss.str() + codec::CRLF; + header += DATE_HEADER + HEADER_SEPARATOR_STR + ss.str() + END_OF_LINE; } if (!_parts.empty()) - header += MIME_VERSION_HEADER + COLON + _version + codec::CRLF; + header += MIME_VERSION_HEADER + HEADER_SEPARATOR_STR + _version + END_OF_LINE; header += mime::format_header(); - header += SUBJECT_HEADER + COLON + format_subject() + codec::CRLF; + header += SUBJECT_HEADER + HEADER_SEPARATOR_STR + format_subject() + END_OF_LINE; return header; } @@ -465,16 +465,16 @@ string message::format_address_list(const mailboxes& mailbox_list) const for (auto ma = mailbox_list.addresses.begin(); ma != mailbox_list.addresses.end(); ma++) { if (mailbox_list.addresses.size() > 1 && ma != mailbox_list.addresses.begin()) - mailbox_str += " " + format_address(ma->name, ma->address); + mailbox_str += codec::DOUBLE_SPACE_STR + format_address(ma->name, ma->address); else mailbox_str += format_address(ma->name, ma->address); if (ma != mailbox_list.addresses.end() - 1) - mailbox_str += "," + codec::CRLF; + mailbox_str += codec::COMMA_STR + END_OF_LINE; } if (!mailbox_list.groups.empty() && !mailbox_list.addresses.empty()) - mailbox_str += "," + codec::CRLF + " "; + mailbox_str += codec::COMMA_STR + END_OF_LINE + codec::DOUBLE_SPACE_STR; for (auto mg = mailbox_list.groups.begin(); mg != mailbox_list.groups.end(); mg++) { @@ -485,14 +485,14 @@ string message::format_address_list(const mailboxes& mailbox_list) const for (auto ma = mg->members.begin(); ma != mg->members.end(); ma++) { if (mg->members.size() > 1 && ma != mg->members.begin()) - mailbox_str += " " + format_address(ma->name, ma->address); + mailbox_str += codec::DOUBLE_SPACE_STR + format_address(ma->name, ma->address); else mailbox_str += format_address(ma->name, ma->address); if (ma != mg->members.end() - 1) - mailbox_str += "," + codec::CRLF; + mailbox_str += codec::COMMA_STR + END_OF_LINE; } - mailbox_str += mg != mailbox_list.groups.end() - 1 ? ";" + codec::CRLF + " " : ";"; + mailbox_str += mg != mailbox_list.groups.end() - 1 ? codec::SEMICOLON_STR + END_OF_LINE + codec::DOUBLE_SPACE_STR : codec::SEMICOLON_STR; } return mailbox_str; @@ -1124,13 +1124,13 @@ string message::format_subject() const { q_codec qc(_line_policy, _decoder_line_policy, _header_codec); vector hdr = qc.encode(_subject); - subject += hdr.at(0) + codec::CRLF; + subject += hdr.at(0) + END_OF_LINE; if (hdr.size() > 1) for (auto h = hdr.begin() + 1; h != hdr.end(); h++) - subject += " " + *h + codec::CRLF; + subject += codec::SPACE_STR + *h + END_OF_LINE; } else - subject += _subject + codec::CRLF; + subject += _subject + END_OF_LINE; return subject; } @@ -1148,11 +1148,11 @@ string message::parse_address_name(const string& address_name) const q_codec qc(_line_policy, _decoder_line_policy, _header_codec); const string::size_type Q_CODEC_SEPARATORS_NO = 4; string::size_type addr_len = address_name.size(); - if (address_name.size() >= Q_CODEC_SEPARATORS_NO && address_name.at(0) == codec::EQUAL_CHAR && address_name.at(1) == codec::QUESTION_MARK_CHAR && - address_name.at(addr_len - 1) == codec::EQUAL_CHAR && address_name.at(addr_len - 2) == codec::QUESTION_MARK_CHAR) - { + bool is_q_encoded = address_name.size() >= Q_CODEC_SEPARATORS_NO && address_name.at(0) == codec::EQUAL_CHAR && + address_name.at(1) == codec::QUESTION_MARK_CHAR && address_name.at(addr_len - 1) == codec::EQUAL_CHAR && + address_name.at(addr_len - 2) == codec::QUESTION_MARK_CHAR; + if (is_q_encoded) return qc.decode(address_name.substr(1, addr_len - 3)); - } return address_name; } diff --git a/src/mime.cpp b/src/mime.cpp index d9058ce..769b6b8 100644 --- a/src/mime.cpp +++ b/src/mime.cpp @@ -84,8 +84,13 @@ const string mime::CONTENT_TRANSFER_ENCODING_BINARY{"Binary"}; const string mime::CONTENT_DISPOSITION_HEADER{"Content-Disposition"}; const string mime::CONTENT_DISPOSITION_ATTACHMENT{"attachment"}; const string mime::CONTENT_DISPOSITION_INLINE{"inline"}; -const string mime::COLON{": "}; -const string mime::SEMICOLON{"; "}; +const string mime::END_OF_LINE = codec::CRLF; +const string mime::HEADER_SEPARATOR_STR{": "}; +const string mime::NAME_VALUE_SEPARATOR_STR = codec::EQUAL_STR; +const string mime::ATTRIBUTES_SEPARATOR_STR{"; "}; +const string mime::ATTRIBUTE_NAME{"name"}; +const string mime::ATTRIBUTE_FILENAME{"filename"}; +const string mime::BOUNDARY_DELIMITER(2, codec::MINUS_CHAR); const string mime::QTEXT{"\t !#$%&'()*+,-.@/:;<=>?[]^_`{|}~"}; // exluded double quote and backslash characters from the header name const string mime::HEADER_NAME_ALPHABET{"!#$%&'()*+,-./;<=>?@[]^_`{|}~"}; @@ -106,22 +111,22 @@ void mime::format(string& mime_str, bool dot_escape) const if (!_boundary.empty() && _content_type.type != media_type_t::MULTIPART) throw mime_error("Formatting failure, non multipart message with boundary."); - mime_str += format_header() + codec::CRLF; + mime_str += format_header() + END_OF_LINE; string content = format_content(dot_escape); mime_str += content; if (!_parts.empty()) { if (!content.empty()) - mime_str += codec::CRLF; + mime_str += END_OF_LINE; // recursively format mime parts for (auto& p : _parts) { string p_str; p.format(p_str, dot_escape); - mime_str += "--" + _boundary + codec::CRLF + p_str + codec::CRLF; + mime_str += BOUNDARY_DELIMITER + _boundary + END_OF_LINE + p_str + END_OF_LINE; } - mime_str += "--" + _boundary + "--" + codec::CRLF; + mime_str += BOUNDARY_DELIMITER + _boundary + BOUNDARY_DELIMITER + END_OF_LINE; } } @@ -129,7 +134,7 @@ void mime::format(string& mime_str, bool dot_escape) const void mime::parse(const string& mime_string, bool dot_escape) { string::size_type line_begin = 0; - string::size_type line_end = mime_string.find("\r\n", line_begin); + string::size_type line_end = mime_string.find(END_OF_LINE, line_begin); string line; while (line_end != string::npos) { @@ -140,12 +145,12 @@ void mime::parse(const string& mime_string, bool dot_escape) line_end += 2; line_begin = line_end; } - line_end = mime_string.find("\r\n", line_begin); + line_end = mime_string.find(END_OF_LINE, line_begin); } line = mime_string.substr(line_begin); if (!line.empty()) parse_by_line(line, dot_escape); - parse_by_line("\r\n", dot_escape); + parse_by_line(END_OF_LINE, dot_escape); } @@ -153,7 +158,7 @@ void mime::parse(const string& mime_string, bool dot_escape) mime& mime::parse_by_line(const string& line, bool dot_escape) { // mark end of header and parse it - if (_parsing_header && line == "") + if (_parsing_header && line.empty()) { _parsing_header = false; parse_header(); @@ -165,7 +170,7 @@ mime& mime::parse_by_line(const string& line, bool dot_escape) else { // end of message reached, decode the body - if (line == "\r\n") + if (line == END_OF_LINE) { parse_content(); _mime_status = mime_parsing_status_t::END; @@ -174,22 +179,22 @@ mime& mime::parse_by_line(const string& line, bool dot_escape) else { // mime part sequence begins - if (line == "--" + _boundary && !_boundary.empty()) + if (line == BOUNDARY_DELIMITER + _boundary && !_boundary.empty()) { _mime_status = mime_parsing_status_t::BEGIN; // begin of another mime part means that the current part (if exists) is ended and parsed; another part is created if (!_parts.empty()) - _parts.back().parse_by_line("\r\n"); + _parts.back().parse_by_line(END_OF_LINE); mime m; m.line_policy(_line_policy, _decoder_line_policy); m.strict_codec_mode(_strict_codec_mode); _parts.push_back(m); } // mime part sequence ends, so parse the last mime part - else if (line == "--" + _boundary + "--" && !_boundary.empty()) + else if (line == BOUNDARY_DELIMITER + _boundary + BOUNDARY_DELIMITER && !_boundary.empty()) { _mime_status = mime_parsing_status_t::END; - _parts.back().parse_by_line("\r\n"); + _parts.back().parse_by_line(END_OF_LINE); } // mime content being parsed else @@ -430,9 +435,9 @@ string mime::format_content(bool dot_escape) const string content; for (const auto& s : content_lines) if (dot_escape && s[0] == codec::DOT_CHAR) - content += string(1, codec::DOT_CHAR) + s + codec::CRLF; + content += string(1, codec::DOT_CHAR) + s + END_OF_LINE; else - content += s + codec::CRLF; + content += s + END_OF_LINE; return content; } @@ -444,20 +449,22 @@ string mime::format_content_type() const if (_content_type.type != media_type_t::NONE) { - line += CONTENT_TYPE_HEADER + COLON + mime_type_as_str(_content_type.type) + codec::SLASH_CHAR + _content_type.subtype; + line += CONTENT_TYPE_HEADER + HEADER_SEPARATOR_STR + mime_type_as_str(_content_type.type) + codec::SLASH_CHAR + _content_type.subtype; if (!_content_type.charset.empty()) - line += SEMICOLON + content_type_t::ATTR_CHARSET + codec::EQUAL_CHAR + _content_type.charset; + line += ATTRIBUTES_SEPARATOR_STR + content_type_t::ATTR_CHARSET + NAME_VALUE_SEPARATOR_STR + _content_type.charset; if (!_name.empty()) { string mn = format_mime_name(_name); - if (line.size() + SEMICOLON.size() + sizeof("name=\"") + mn.size() + sizeof(codec::QUOTE_CHAR) < string::size_type(_line_policy) - 2) - line += SEMICOLON + "name=\"" + mn + "\""; - else - line += SEMICOLON + codec::CRLF + " name=\"" + mn + codec::QUOTE_CHAR; + const string::size_type line_new_size = line.size() + ATTRIBUTES_SEPARATOR_STR.size() + ATTRIBUTE_NAME.size() + NAME_VALUE_SEPARATOR_STR.size() + + sizeof(codec::QUOTE_CHAR) + mn.size() + sizeof(codec::QUOTE_CHAR); + line += ATTRIBUTES_SEPARATOR_STR; + if (line_new_size >= string::size_type(_line_policy) - 2) + line += END_OF_LINE + codec::DOUBLE_SPACE_STR; + line += ATTRIBUTE_NAME + NAME_VALUE_SEPARATOR_STR + codec::QUOTE_STR + mn + codec::QUOTE_STR; } if (!_boundary.empty()) - line += SEMICOLON + content_type_t::ATTR_BOUNDARY + codec::EQUAL_CHAR + codec::QUOTE_CHAR + _boundary + codec::QUOTE_CHAR; - line += codec::CRLF; + line += ATTRIBUTES_SEPARATOR_STR + content_type_t::ATTR_BOUNDARY + NAME_VALUE_SEPARATOR_STR + codec::QUOTE_CHAR + _boundary + codec::QUOTE_CHAR; + line += END_OF_LINE; } return line; @@ -471,19 +478,19 @@ string mime::format_transfer_encoding() const switch (_encoding) { case content_transfer_encoding_t::BASE_64: - line += CONTENT_TRANSFER_ENCODING_HEADER + COLON + CONTENT_TRANSFER_ENCODING_BASE64 + codec::CRLF; + line += CONTENT_TRANSFER_ENCODING_HEADER + HEADER_SEPARATOR_STR + CONTENT_TRANSFER_ENCODING_BASE64 + END_OF_LINE; break; case content_transfer_encoding_t::QUOTED_PRINTABLE: - line += CONTENT_TRANSFER_ENCODING_HEADER + COLON + CONTENT_TRANSFER_ENCODING_QUOTED_PRINTABLE + codec::CRLF; + line += CONTENT_TRANSFER_ENCODING_HEADER + HEADER_SEPARATOR_STR + CONTENT_TRANSFER_ENCODING_QUOTED_PRINTABLE + END_OF_LINE; break; case content_transfer_encoding_t::BIT_8: - line += CONTENT_TRANSFER_ENCODING_HEADER + COLON + CONTENT_TRANSFER_ENCODING_BIT8 + codec::CRLF; + line += CONTENT_TRANSFER_ENCODING_HEADER + HEADER_SEPARATOR_STR + CONTENT_TRANSFER_ENCODING_BIT8 + END_OF_LINE; break; case content_transfer_encoding_t::BIT_7: - line += CONTENT_TRANSFER_ENCODING_HEADER + COLON + CONTENT_TRANSFER_ENCODING_BIT7 + codec::CRLF; + line += CONTENT_TRANSFER_ENCODING_HEADER + HEADER_SEPARATOR_STR + CONTENT_TRANSFER_ENCODING_BIT7 + END_OF_LINE; break; // default is no transfer encoding specified @@ -498,7 +505,6 @@ string mime::format_transfer_encoding() const string mime::format_content_disposition() const { - const string FILENAME_ATTR = "filename"; string line; switch (_disposition) @@ -506,26 +512,27 @@ string mime::format_content_disposition() const case content_disposition_t::ATTACHMENT: { string name = format_mime_name(_name); - line += CONTENT_DISPOSITION_HEADER + COLON + CONTENT_DISPOSITION_ATTACHMENT + codec::SEMICOLON_STR + codec::SPACE_CHAR; - int line_new_size = CONTENT_DISPOSITION_HEADER.size() + COLON.size() + CONTENT_DISPOSITION_ATTACHMENT.size() + FILENAME_ATTR.size() + - sizeof(codec::EQUAL_CHAR) + sizeof(codec::QUOTE_CHAR) + name.size() + sizeof(codec::QUOTE_CHAR) + codec::CRLF.size(); - if (line_new_size < string::size_type(_line_policy) - 2) - line += FILENAME_ATTR + codec::EQUAL_CHAR + codec::QUOTE_CHAR + name + codec::QUOTE_CHAR_STR + codec::CRLF; - else - line += codec::CRLF + codec::DOUBLE_SPACE_STR + FILENAME_ATTR + codec::EQUAL_CHAR + codec::QUOTE_CHAR + name + codec::QUOTE_CHAR + codec::CRLF; + line += CONTENT_DISPOSITION_HEADER + HEADER_SEPARATOR_STR + CONTENT_DISPOSITION_ATTACHMENT + ATTRIBUTES_SEPARATOR_STR; + const string::size_type line_new_size = CONTENT_DISPOSITION_HEADER.size() + HEADER_SEPARATOR_STR.size() + CONTENT_DISPOSITION_ATTACHMENT.size() + + ATTRIBUTE_FILENAME.size() + sizeof(codec::EQUAL_CHAR) + sizeof(codec::QUOTE_CHAR) + name.size() + sizeof(codec::QUOTE_CHAR) + + END_OF_LINE.size(); + if (line_new_size >= string::size_type(_line_policy) - 2) + line += END_OF_LINE + codec::DOUBLE_SPACE_STR; + line += ATTRIBUTE_FILENAME + NAME_VALUE_SEPARATOR_STR + codec::QUOTE_CHAR + name + codec::QUOTE_STR + END_OF_LINE; + break; } case content_disposition_t::INLINE: { string name = format_mime_name(_name); - line += CONTENT_DISPOSITION_HEADER + COLON + CONTENT_DISPOSITION_INLINE + codec::SEMICOLON_STR + codec::SPACE_CHAR; - const string::size_type line_new_size = CONTENT_DISPOSITION_HEADER.size() + COLON.size() + CONTENT_DISPOSITION_INLINE.size() + - sizeof(FILENAME_ATTR + codec::EQUAL_CHAR + codec::QUOTE_CHAR) + name.size() + sizeof(codec::QUOTE_CHAR + codec::CRLF); - if (line_new_size < string::size_type(_line_policy) - 2) - line += FILENAME_ATTR + codec::EQUAL_CHAR + codec::QUOTE_CHAR + name + codec::QUOTE_CHAR + codec::CRLF; - else - line += codec::CRLF + codec::DOUBLE_SPACE_STR + FILENAME_ATTR + codec::EQUAL_CHAR + codec::QUOTE_CHAR + name + codec::QUOTE_CHAR + codec::CRLF; + line += CONTENT_DISPOSITION_HEADER + HEADER_SEPARATOR_STR + CONTENT_DISPOSITION_INLINE + ATTRIBUTES_SEPARATOR_STR; + const string::size_type line_new_size = CONTENT_DISPOSITION_HEADER.size() + HEADER_SEPARATOR_STR.size() + CONTENT_DISPOSITION_INLINE.size() + + ATTRIBUTE_FILENAME.size() + NAME_VALUE_SEPARATOR_STR.size() + sizeof(codec::QUOTE_CHAR) + name.size() + sizeof(codec::QUOTE_CHAR) + END_OF_LINE.size(); + if (line_new_size >= string::size_type(_line_policy) - 2) + line += END_OF_LINE + codec::DOUBLE_SPACE_STR; + line += ATTRIBUTE_FILENAME + NAME_VALUE_SEPARATOR_STR + codec::QUOTE_CHAR + name + codec::QUOTE_CHAR + END_OF_LINE; + break; } @@ -649,13 +656,13 @@ void mime::parse_header_line(const string& header_line) _content_type.type = media_type; _content_type.subtype = to_lower_copy(media_subtype); - attributes_t::iterator bound_it = attributes.find("boundary"); + attributes_t::iterator bound_it = attributes.find(content_type_t::ATTR_BOUNDARY); if (bound_it != attributes.end()) _boundary = bound_it->second; - attributes_t::iterator charset_it = attributes.find("charset"); + attributes_t::iterator charset_it = attributes.find(content_type_t::ATTR_CHARSET); if (charset_it != attributes.end()) _content_type.charset = to_lower_copy(charset_it->second); - attributes_t::iterator name_it = attributes.find("name"); + attributes_t::iterator name_it = attributes.find(ATTRIBUTE_NAME); // name is set if not already set by content disposition if (name_it != attributes.end() && _name.empty()) { @@ -673,7 +680,7 @@ void mime::parse_header_line(const string& header_line) attributes_t attributes; parse_content_disposition(header_value, _disposition, attributes); // filename is stored no matter if name is already set by content type - attributes_t::iterator filename_it = attributes.find("filename"); + attributes_t::iterator filename_it = attributes.find(ATTRIBUTE_FILENAME); if (filename_it != attributes.end()) { q_codec qc(_line_policy, _decoder_line_policy, _header_codec); @@ -690,7 +697,7 @@ void mime::parse_header_name_value(const string& header_line, string& header_nam bool is_header_name = true; while (colon_pos++ < header_line.length()) { - if (header_line[colon_pos] == codec::COLON_CHAR) + if (header_line[colon_pos] == HEADER_SEPARATOR_CHAR) { header_name = header_line.substr(0, colon_pos); trim(header_name); @@ -839,7 +846,7 @@ void mime::parse_header_value_attributes(const string& header, string& header_va case state_t::VALUE: if (isalpha(*ch) || isdigit(*ch) || CONTENT_HEADER_VALUE_ALPHABET.find(*ch) != string::npos) header_value += *ch; - else if (*ch == codec::SEMICOLON_CHAR) + else if (*ch == ATTRIBUTES_SEPARATOR_CHAR) state = state_t::ATTR_BEGIN; else throw mime_error("Parsing header value failure at `" + string(1, *ch) + "`."); @@ -853,7 +860,7 @@ void mime::parse_header_value_attributes(const string& header, string& header_va state = state_t::ATTR_NAME; attribute_name += *ch; } - else if (*ch == codec::EQUAL_CHAR) + else if (*ch == NAME_VALUE_SEPARATOR_CHAR) state = state_t::ATTR_VALUE; else throw mime_error("Parsing attribute name failure at `" + string(1, *ch) + "`."); @@ -862,7 +869,7 @@ void mime::parse_header_value_attributes(const string& header, string& header_va case state_t::ATTR_NAME: if (isalpha(*ch) || isdigit(*ch) || CONTENT_ATTR_ALPHABET.find(*ch) != string::npos) attribute_name += *ch; - else if (*ch == codec::EQUAL_CHAR) + else if (*ch == NAME_VALUE_SEPARATOR_CHAR) state = state_t::ATTR_VALUE; break; @@ -894,7 +901,7 @@ void mime::parse_header_value_attributes(const string& header, string& header_va attribute_value += *ch; else if (isspace(*ch)) state = state_t::ATTR_VALUE_END; - else if (*ch == codec::SEMICOLON_CHAR) + else if (*ch == ATTRIBUTES_SEPARATOR_CHAR) { state = state_t::ATTR_BEGIN; attributes[attribute_name] = attribute_value; @@ -914,7 +921,7 @@ void mime::parse_header_value_attributes(const string& header, string& header_va if (isspace(*ch)) ; - else if (*ch == codec::SEMICOLON_CHAR) + else if (*ch == ATTRIBUTES_SEPARATOR_CHAR) state = state_t::ATTR_BEGIN; else throw mime_error("Parsing attribute value failure at `" + string(1, *ch) + "`."); @@ -931,7 +938,7 @@ string mime::make_boundary() const std::uniform_int_distribution<> index_dist(0, codec::HEX_DIGITS.size() - 1); for (int i = 0; i < 10; i++) bound += codec::HEX_DIGITS[index_dist(rng)]; - return "-------" + bound; + return BOUNDARY_DELIMITER + BOUNDARY_DELIMITER + BOUNDARY_DELIMITER + BOUNDARY_DELIMITER + bound; } diff --git a/src/pop3.cpp b/src/pop3.cpp index a2e5900..efc81b3 100644 --- a/src/pop3.cpp +++ b/src/pop3.cpp @@ -89,7 +89,7 @@ auto pop3::list(unsigned message_no) -> message_list_t throw pop3_error("Listing message failure."); // parse data - string::size_type pos = std::get<1>(stat_msg).find(' '); + string::size_type pos = std::get<1>(stat_msg).find(codec::SPACE_CHAR); if (pos == string::npos) throw pop3_error("Parser failure."); unsigned msg_id = stoi(std::get<1>(stat_msg).substr(0, pos)); @@ -109,11 +109,11 @@ auto pop3::list(unsigned message_no) -> message_list_t while (!end_of_msg) { line = _dlg->receive(); - if (line == ".") + if (line == codec::DOT_STR) end_of_msg = true; else { - string::size_type pos = line.find(' '); + string::size_type pos = line.find(codec::SPACE_CHAR); if (pos == string::npos) throw pop3_error("Parser failure."); unsigned msg_id = stoi(line.substr(0, pos)); @@ -145,7 +145,7 @@ auto pop3::statistics() -> mailbox_stat_t throw pop3_error("Reading statistics failure."); // parse data - string::size_type pos = std::get<1>(stat_msg).find(' '); + string::size_type pos = std::get<1>(stat_msg).find(codec::SPACE_CHAR); if (pos == string::npos) throw pop3_error("Parser failure."); mailbox_stat_t mailbox_stat; @@ -194,12 +194,12 @@ void pop3::fetch(unsigned long message_no, message& msg, bool header_only) { line = _dlg->receive(); // reading line by line ensures that crlf are the last characters read; so, reaching single dot in the line means that it's end of message - if (line == ".") + if (line == codec::DOT_STR) { // if header only, then mark the header end with the empty line if (header_only) msg.parse_by_line(""); - msg.parse_by_line("\r\n"); + msg.parse_by_line(codec::CRLF); break; } else if (line.empty()) @@ -263,7 +263,7 @@ void pop3::auth_login(const string& username, const string& password) tuple pop3::parse_status(const string& line) { - string::size_type pos = line.find(' '); + string::size_type pos = line.find(codec::SPACE_CHAR); string status = line.substr(0, pos); if (!iequals(status, "+OK") && !iequals(status, "-ERR")) throw pop3_error("Response status unknown."); diff --git a/src/smtp.cpp b/src/smtp.cpp index 025457a..c41a6c4 100644 --- a/src/smtp.cpp +++ b/src/smtp.cpp @@ -79,9 +79,9 @@ void smtp::authenticate(const string& username, const string& password, auth_met void smtp::submit(const message& msg) { if (!msg.sender().address.empty()) - _dlg->send("MAIL FROM: <" + msg.sender().address + ">"); + _dlg->send("MAIL FROM: " + codec::LESS_THAN_STR + msg.sender().address + codec::GREATER_THAN_STR); else - _dlg->send("MAIL FROM: <" + msg.from().addresses.at(0).address + ">"); + _dlg->send("MAIL FROM: " + codec::LESS_THAN_STR + msg.from().addresses.at(0).address + codec::GREATER_THAN_STR); string line = _dlg->receive(); tuple tokens = parse_line(line); if (std::get<1>(tokens) && !positive_completion(std::get<0>(tokens))) @@ -89,7 +89,7 @@ void smtp::submit(const message& msg) for (const auto& rcpt : msg.recipients().addresses) { - _dlg->send("RCPT TO: <" + rcpt.address + ">"); + _dlg->send("RCPT TO: " + codec::LESS_THAN_STR + rcpt.address + codec::GREATER_THAN_STR); line = _dlg->receive(); tokens = parse_line(line); if (!positive_completion(std::get<0>(tokens))) @@ -98,7 +98,7 @@ void smtp::submit(const message& msg) for (const auto& rcpt : msg.recipients().groups) { - _dlg->send("RCPT TO: <" + rcpt.name + ">"); + _dlg->send("RCPT TO: " + codec::LESS_THAN_STR + rcpt.name + codec::GREATER_THAN_STR); line = _dlg->receive(); tokens = parse_line(line); if (!positive_completion(std::get<0>(tokens))) @@ -107,7 +107,7 @@ void smtp::submit(const message& msg) for (const auto& rcpt : msg.cc_recipients().addresses) { - _dlg->send("RCPT TO: <" + rcpt.address + ">"); + _dlg->send("RCPT TO: " + codec::LESS_THAN_STR + rcpt.address + codec::GREATER_THAN_STR); line = _dlg->receive(); tokens = parse_line(line); if (!positive_completion(std::get<0>(tokens))) @@ -116,7 +116,7 @@ void smtp::submit(const message& msg) for (const auto& rcpt : msg.cc_recipients().groups) { - _dlg->send("RCPT TO: <" + rcpt.name + ">"); + _dlg->send("RCPT TO: " + codec::LESS_THAN_STR + rcpt.name + codec::GREATER_THAN_STR); line = _dlg->receive(); tokens = parse_line(line); if (!positive_completion(std::get<0>(tokens))) @@ -125,7 +125,7 @@ void smtp::submit(const message& msg) for (const auto& rcpt : msg.bcc_recipients().addresses) { - _dlg->send("RCPT TO: <" + rcpt.address + ">"); + _dlg->send("RCPT TO: " + codec::LESS_THAN_STR + rcpt.address + codec::GREATER_THAN_STR); line = _dlg->receive(); tokens = parse_line(line); if (!positive_completion(std::get<0>(tokens))) @@ -134,7 +134,7 @@ void smtp::submit(const message& msg) for (const auto& rcpt : msg.bcc_recipients().groups) { - _dlg->send("RCPT TO: <" + rcpt.name + ">"); + _dlg->send("RCPT TO: " + codec::LESS_THAN_STR + rcpt.name + codec::GREATER_THAN_STR); line = _dlg->receive(); tokens = parse_line(line); if (!positive_completion(std::get<0>(tokens))) @@ -149,7 +149,7 @@ void smtp::submit(const message& msg) string msg_str; msg.format(msg_str, true); - _dlg->send(msg_str + "\r\n."); + _dlg->send(msg_str + codec::CRLF + codec::DOT_CHAR); line = _dlg->receive(); tokens = parse_line(line); if (!positive_completion(std::get<0>(tokens))) @@ -173,7 +173,7 @@ void smtp::connect() { string line = _dlg->receive(); tuple tokens = parse_line(line); - if (std::get<0>(tokens) != 220) + if (std::get<0>(tokens) != SERVICE_READY_STATUS) throw smtp_error("Connection rejection."); } @@ -206,7 +206,7 @@ void smtp::ehlo() { _dlg->send("EHLO " + _src_host); string line = _dlg->receive(); - tuple tokens = parse_line(line); + tuple tokens = parse_line(line); while (!std::get<1>(tokens)) { line = _dlg->receive(); @@ -216,7 +216,7 @@ void smtp::ehlo() if (!positive_completion(std::get<0>(tokens))) { _dlg->send("HELO " + _src_host); - + line = _dlg->receive(); tokens = parse_line(line); while (!std::get<1>(tokens)) @@ -319,7 +319,7 @@ void smtps::start_tls() _dlg->send("STARTTLS"); string line = _dlg->receive(); tuple tokens = parse_line(line); - if (std::get<1>(tokens) && std::get<0>(tokens) != 220) + if (std::get<1>(tokens) && std::get<0>(tokens) != SERVICE_READY_STATUS) throw smtp_error("Start tls refused by server."); switch_to_ssl();