/* ============================================================================ * Douglas Thrift's Web Contact License * * Copyright (C) 2002, Douglas Thrift. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * 2. Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * * 3. The end-user documentation included with the redistribution, if any, must * include the following acknowledgment: * * "This product includes software developed by Douglas Thrift * (http://computers.douglasthrift.net/webcontact.html)." * * Alternately, this acknowledgment may appear in the software itself, if * and wherever such third-party acknowledgments normally appear. * * 4. The names "Douglas Thrift" and "Douglas Thrift's Web Contact" must not be * used to endorse or promote products derived from this software without * specific prior written permission. For written permission, please visit * http://www.douglasthrift.net/contact.html for contact information. * * 5. Products derived from this software may not be called "Douglas Thrift's * Web Contact", nor may "Douglas Thrift's Web Contact" appear in their * name, without prior written permission. * * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * ============================================================================ */ // Windows XP FAQ Poll // // Douglas Thrift // // IMAPHandler.cpp #include "IMAPHandler.h" IMAPHandler::IMAPHandler(const string& server, bool tls) { letter = 'a'; number = 0; this->tls = tls; buffer = new char[BUFSIZ + 1]; #ifdef _WIN32 if (WSAStartup(MAKEWORD(2, 0), &data) != 0) { error(program + ": WSAStartup"); exit(1); } #endif // _WIN32 if ((sock = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) { error(program + ": Socket"); exit(1); } sockaddr_in address; hostent* host; address.sin_family = AF_INET; if ((host = gethostbyname(server.c_str())) == NULL) { error(program + ": Host: " + server, true); exit(1); } address.sin_addr = *((in_addr*)*host->h_addr_list); address.sin_port = htons(tls ? 993 : 143); if (connect(sock, (sockaddr*)&address, sizeof(sockaddr_in)) == SOCKET_ERROR) { error(program + ": Connect"); exit(1); } if (tls) { SSL_load_error_strings(); SSL_library_init(); char* seed = new char[BUFSIZ + 1]; time_t moment = time(NULL); sprintf(seed, "froofy%sfoofoo", ctime(&moment)); RAND_add(seed, strlen(seed), 0.007456); delete [] seed; ctx = SSL_CTX_new(TLSv1_client_method()); if (ctx == NULL) { cerr << program << ": SSL CTX New: " << ERR_reason_error_string(ERR_get_error()) << "\n"; exit(1); } ssl = SSL_new(ctx); if (SSL_set_fd(ssl, sock) == 0) { cerr << program << ": SSL Set FD: " << ERR_reason_error_string(ERR_get_error()) << "\n"; exit(1); } if (int code = SSL_connect(ssl) <= 0) { error(program + ": SSL Connect", code); exit(1); } } getline(); } IMAPHandler::~IMAPHandler() { if (tls) { SSL_shutdown(ssl); SSL_free(ssl); SSL_CTX_free(ctx); } closesocket(sock); delete [] buffer; #ifdef _WIN32 WSACleanup(); #endif // _WIN32 } string IMAPHandler::starttls() { string answer = imap("STARTTLS"); if (success) { tls = true; SSL_load_error_strings(); SSL_library_init(); char* seed = new char[BUFSIZ + 1]; time_t moment = time(NULL); sprintf(seed, "froofy%sfoofoo", ctime(&moment)); RAND_add(seed, strlen(seed), 0.007456); delete [] seed; ctx = SSL_CTX_new(TLSv1_client_method()); if (ctx == NULL) { cerr << program << ": SSL CTX New: " << ERR_reason_error_string(ERR_get_error()) << "\n"; exit(1); } ssl = SSL_new(ctx); if (SSL_set_fd(ssl, sock) == 0) { cerr << program << ": SSL Set FD: " << ERR_reason_error_string(ERR_get_error()) << "\n"; exit(1); } if (int code = SSL_connect(ssl) <= 0) { error(program + ": SSL Connect", code); exit(1); } } return answer; } string IMAPHandler::command() { char buffer[4]; sprintf(buffer, "%03u", number++); string sequence = letter + string(buffer); if (number > 999) { letter++; number = 0; } if (letter > 'z') { letter = 'a'; } return sequence; } string IMAPHandler::imap(const string& imap) { string result; string sequence = command(); putline(sequence + " " + imap); while (true) { string input = getline(); if (input.find(sequence + " OK") == 0) { success = true; break; } else if (input.find(sequence + " NO") == 0 || input.find(sequence + " BAD") == 0) { success = false; break; } else { result += input + "\r\n"; } } return result; } string IMAPHandler::imap(const string& imap, const string& args) { string result; string sequence = command(); putline(sequence + " " + imap + " " + args); while (true) { string input = getline(); if (input.find(sequence + " OK") == 0) { success = true; break; } else if (input.find(sequence + " NO") == 0 || input.find(sequence + " BAD") == 0) { success = false; break; } else { result += input + "\r\n"; } } return result; } string IMAPHandler::imap(const string& imap, const string& args, const string& message) { string result; string sequence = command(); putline(sequence + " " + imap + " " + args); putline(message); while (true) { string input = getline(); if (input.find(sequence + " OK") == 0) { success = true; break; } else if (input.find(sequence + " NO") == 0 || input.find(sequence + " BAD") == 0) { success = false; break; } else { result += input + "\r\n"; } } return result; } void IMAPHandler::putline(const string line) { if (debug) cerr << line << "\n"; sprintf(buffer, "%s\r\n", line.c_str()); if (tls) { if (int code = SSL_write(ssl, buffer, strlen(buffer)) <= 0) { error(program + ": SSL Write", code); exit(1); } } else { if (send(sock, buffer, strlen(buffer), 0) == SOCKET_ERROR) { error(program + ": Send"); exit(1); } } } string IMAPHandler::getline() { string line; char byte; do { if (tls) { if (int code = SSL_read(ssl, &byte, 1) <= 0) { error(program + ": SSL Read", code); exit(1); } } else { if (recv(sock, &byte, 1, 0) == SOCKET_ERROR) { error(program + ": Recv"); exit(1); } } if (byte != '\r' && byte != '\n') { line += byte; } } while (byte != '\n'); if (debug) cerr << line << "\n"; return line; } void IMAPHandler::error(const string& prefix, bool host) { #ifdef _WIN32 string error; switch (WSAGetLastError()) { case WSAEACCES: error = "Permission denied."; break; case WSAEADDRINUSE: error = "Address already in use."; break; case WSAEADDRNOTAVAIL: error = "Cannot assign requested address."; break; case WSAEAFNOSUPPORT: error = "Address family not supported by protocol family."; break; case WSAEALREADY: error = "Operation already in progress."; break; case WSAECONNABORTED: error = "Software caused connection abort."; break; case WSAECONNREFUSED: error = "Connection refused."; break; case WSAECONNRESET: error = "Connection reset by peer."; break; case WSAEDESTADDRREQ: error = "Destination address required."; break; case WSAEFAULT: error = "Bad address."; break; case WSAEHOSTDOWN: error = "Host is down."; break; case WSAEHOSTUNREACH: error = "No route to host."; break; case WSAEINPROGRESS: error = "Operation now in progress."; break; case WSAEINTR: error = "Interrupted function call."; break; case WSAEINVAL: error = "Invalid argument."; break; case WSAEISCONN: error = "Socket is already connected."; break; case WSAEMFILE: error = "Too many open files."; break; case WSAEMSGSIZE: error = "Message too long."; break; case WSAENETDOWN: error = "Network is down."; break; case WSAENETRESET: error = "Network dropped connection on reset."; break; case WSAENETUNREACH: error = "Network is unreachable."; break; case WSAENOBUFS: error = "No buffer space available."; break; case WSAENOPROTOOPT: error = "Bad protocol option."; break; case WSAENOTCONN: error = "Socket is not connected."; break; case WSAENOTSOCK: error = "Socket operation on non-socket."; break; case WSAEOPNOTSUPP: error = "Operation not supported."; break; case WSAEPFNOSUPPORT: error = "Protocol family not supported."; break; case WSAEPROCLIM: error = "Too many processes."; break; case WSAEPROTONOSUPPORT: error = "Protocol not supported."; break; case WSAEPROTOTYPE: error = "Protocol wrong type for socket."; break; case WSAESHUTDOWN: error = "Cannot send after socket shutdown."; break; case WSAESOCKTNOSUPPORT: error = "Socket type not supported."; break; case WSAETIMEDOUT: error = "Connection timed out."; break; case WSATYPE_NOT_FOUND: error = "Class type not found."; break; case WSAEWOULDBLOCK: error = "Resource temporarily unavailable."; break; case WSAHOST_NOT_FOUND: error = "Host not found."; break; case WSA_INVALID_HANDLE: error = "Specified event object handle is invalid."; break; case WSA_INVALID_PARAMETER: error = "One or more parameters are invalid."; break; // case WSAINVALIDPROCTABLE: // error = "Invalid procedure table from service provider."; // break; // case WSAINVALIDPROVIDER: // error = "Invalid service provider version number."; // break; case WSA_IO_INCOMPLETE: error = "Overlapped I/O event object not in signaled state."; break; case WSA_IO_PENDING: error = "Overlapped operations will complete later."; break; case WSA_NOT_ENOUGH_MEMORY: error = "Insufficient memory available."; break; case WSANOTINITIALISED: error = "Successful WSAStartup not yet performed."; break; case WSANO_DATA: error = "Valid name, no data record of requested type."; break; case WSANO_RECOVERY: error = "This is a non-recoverable error."; break; // case WSAPROVIDERFAILEDINIT: // error = "Unable to initialize a service provider."; // break; case WSASYSCALLFAILURE: error = "System call failure."; break; case WSASYSNOTREADY: error = "Network subsystem is unavailable."; break; case WSATRY_AGAIN: error = "Non-authoritative host not found."; break; case WSAVERNOTSUPPORTED: error = "WINSOCK.DLL version out of range."; break; case WSAEDISCON: error = "Graceful shutdown in progress."; break; case WSA_OPERATION_ABORTED: error = "Overlapped operation aborted."; break; default: error = "Unknown error."; break; } cerr << prefix << ": " << error << "\n"; #else if (host) { herror(prefix.c_str()); } else { perror(prefix.c_str()); } #endif // _WIN32 } void IMAPHandler::error(const string& prefix, int code) { string error; switch (SSL_get_error(ssl, code)) { case SSL_ERROR_NONE: error = "The TLS/SSL I/O operation completed."; break; case SSL_ERROR_ZERO_RETURN: error = "The TLS/SSL connection has been closed."; break; case SSL_ERROR_WANT_READ: case SSL_ERROR_WANT_WRITE: case SSL_ERROR_WANT_CONNECT: // case SSL_ERROR_WANT_ACCEPT: case SSL_ERROR_WANT_X509_LOOKUP: error = "The operation did not complete."; break; case SSL_ERROR_SYSCALL: if (int err = ERR_get_error() != 0) { error = ERR_reason_error_string(err); } else { switch (code) { case 0: error = "An EOF was observed that violates the protocol."; break; case -1: this->error(prefix); return; default: error = "Unknown error."; break; } } break; case SSL_ERROR_SSL: error = ERR_reason_error_string(ERR_get_error()); break; default: error = "Unknown error."; break; } cerr << prefix << ": " << error << "\n"; }