diff options
Diffstat (limited to 'mimelib/protocol.cpp')
-rw-r--r-- | mimelib/protocol.cpp | 531 |
1 files changed, 531 insertions, 0 deletions
diff --git a/mimelib/protocol.cpp b/mimelib/protocol.cpp new file mode 100644 index 000000000..b0685aa72 --- /dev/null +++ b/mimelib/protocol.cpp @@ -0,0 +1,531 @@ +//============================================================================= +// File: proto_un.cpp +// Contents: Definitions for DwClientProtocol +// Maintainer: Doug Sauder <dwsauder@fwb.gulf.net> +// WWW: http://www.fwb.gulf.net/~dwsauder/mimepp.html +// +// Copyright (c) 1996, 1997 Douglas W. Sauder +// All rights reserved. +// +// IN NO EVENT SHALL DOUGLAS W. SAUDER BE LIABLE TO ANY PARTY FOR DIRECT, +// INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT OF +// THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF DOUGLAS W. SAUDER +// HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// DOUGLAS W. SAUDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, BUT +// NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A +// PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS ON AN "AS IS" +// BASIS, AND DOUGLAS W. SAUDER HAS NO OBLIGATION TO PROVIDE MAINTENANCE, +// SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. +// +//============================================================================= + +// Comments: +// +// 1. The program should handle the SIGPIPE signal. Ignoring it should be okay. +// +// 2. The recv() and send() system calls are *not* restarted if they are +// interrupted by a signal. This behavior is necessary if we want to +// be able to timeout a blocked call. + +#define DW_IMPLEMENTATION + +#include <mimelib/config.h> +#include <mimelib/protocol.h> +#include <assert.h> +#include <stdio.h> +#include <string.h> +#include <unistd.h> +#include <netdb.h> +#include <arpa/inet.h> +#include <errno.h> +#include <sys/time.h> + +#if defined(_AIX) +#include <sys/select.h> +#include <sys/types.h> +#include <strings.h> +#endif + +#ifndef INADDR_NONE +#define INADDR_NONE (-1) +#endif + +#if defined(DW_DEBUG_PROTO) +# define DBG_PROTO_STMT(x) x +#else +# define DBG_PROTO_STMT(x) +#endif + +// WABA: This should be defined by netdb.h +// deller: Needed for HP/UX +#if defined(__hpux) +extern int h_errno; +#endif +static int translate_h_errno(int herrno); +static const char* get_error_text(int aErrorCode); + + +DwProtocolClient::DwProtocolClient() +{ + mIsDllOpen = DwTrue; + mIsOpen = DwFalse; + mSocket = -1; + mPort = 0; + mServerName = 0; + mReceiveTimeout = 90; + mLastCommand = 0; + mFailureCode = kFailNoFailure; + mFailureStr = ""; + mErrorCode = kErrNoError; + mErrorStr = get_error_text(kErrNoError); +} + + +DwProtocolClient::~DwProtocolClient() +{ + if (mIsOpen) { + Close(); + } + if (mServerName) { + delete [] mServerName; + mServerName = 0; + } +} + + +int DwProtocolClient::Open(const char* aServer, DwUint16 aPort) +{ + mFailureCode = kFailNoFailure; + mFailureStr = ""; + mErrorCode = kErrNoError; + mErrorStr = get_error_text(mErrorCode); + + if (mIsOpen) { + // error! + mErrorCode = kErrBadUsage; + mErrorStr = get_error_text(mErrorCode); + return -1; + } + if (aServer == 0 || aServer[0] == 0) { + // error! + mErrorCode = kErrBadParameter; + mErrorStr = get_error_text(mErrorCode); + return -1; + } + if (mServerName) { + delete [] mServerName; + mServerName = 0; + } + mServerName = new char[strlen(aServer)+1]; + strcpy(mServerName, aServer); + mPort = aPort; + + // Open the socket + + mSocket = socket(PF_INET, SOCK_STREAM, 0); + if (mSocket == -1) { + // error! + int err = errno; + HandleError(err, ksocket); + return -1; + } + + // If the server is specified by an IP number in dotted decimal form, + // then try to connect to that IP number. + + int err = -1; + struct sockaddr_in serverAddr; + memset(&serverAddr, 0, sizeof(struct sockaddr_in)); + serverAddr.sin_family = AF_INET; + serverAddr.sin_port = htons(mPort); + serverAddr.sin_addr.s_addr = inet_addr(mServerName); + if (serverAddr.sin_addr.s_addr != INADDR_NONE) { + DBG_PROTO_STMT(cout << "Trying connection to " << mServerName << endl;) + err = connect(mSocket, (struct sockaddr*)&serverAddr, + sizeof(struct sockaddr_in)); + } + + // Otherwise, do a host name lookup. + + else { + struct hostent* hostentp = gethostbyname(mServerName); + if (hostentp == NULL) { + // error! + int err = h_errno; + close(mSocket); + mSocket = -1; + err = translate_h_errno(err); + HandleError(err, kgethostbyname); + return -1; + } + + // Connect to the server. Try each IP number until one succeeds. + + char** addr_list = hostentp->h_addr_list; + while (*addr_list) { + struct in_addr* in_addrp = (struct in_addr*)*addr_list; + memcpy(&serverAddr.sin_addr.s_addr, in_addrp, sizeof(struct in_addr)); + DBG_PROTO_STMT(cout << "Trying connection to " << mServerName;) + DBG_PROTO_STMT(cout << " (" << inet_ntoa(*in_addrp) << ')' << endl;) + err = connect(mSocket, (struct sockaddr*)&serverAddr, + sizeof(struct sockaddr_in)); + if (err != -1) { + break; + } + ++addr_list; + } + } + + if (err == -1) { + // error! + mErrorCode = errno; + close(mSocket); + mSocket = -1; + HandleError(err, kconnect); + return -1; + } + DBG_PROTO_STMT(cout << "Connection okay" << endl;) + mIsOpen = DwTrue; + return 0; +} + + +DwBool DwProtocolClient::IsOpen() const +{ + return mIsOpen; +} + + +int DwProtocolClient::Close() +{ + mFailureCode = kFailNoFailure; + mFailureStr = ""; + mErrorCode = kErrNoError; + mErrorStr = get_error_text(mErrorCode); + + if (! mIsOpen) { + // error! + mErrorCode = kErrBadUsage; + mErrorStr = get_error_text(mErrorCode); + return -1; + } + int err = close(mSocket); + if (err < 0) { + // error! + int err = errno; + HandleError(err, kclose); + return -1; + } + mIsOpen = DwFalse; + return 0; +} + + +int DwProtocolClient::SetReceiveTimeout(int aSecs) +{ + mReceiveTimeout = aSecs; + return 0; +} + + +int DwProtocolClient::LastCommand() const +{ + return mLastCommand; +} + + +int DwProtocolClient::LastFailure() const +{ + return mFailureCode; +} + + +const char* DwProtocolClient::LastFailureStr() const +{ + return mFailureStr; +} + + +int DwProtocolClient::LastError() const +{ + return mErrorCode; +} + + +const char* DwProtocolClient::LastErrorStr() const +{ + return mErrorStr; +} + + +int DwProtocolClient::PSend(const char* aBuf, int aBufLen) +{ + mFailureCode = kFailNoFailure; + mFailureStr = ""; + mErrorCode = kErrNoError; + mErrorStr = get_error_text(mErrorCode); + + if (! mIsOpen) { + // error! + mErrorCode = kErrBadUsage; + mErrorStr = get_error_text(mErrorCode); + return 0; + } + int ret; + int numToSend = aBufLen; + int numSent = 0; + while (numToSend > 0) { + ret = send(mSocket, &aBuf[numSent], numToSend, 0); + if (ret == -1) { + // error! + int err = errno; + HandleError(err, ksend); + break; + } + else { + numSent += ret; + numToSend -= ret; + } + } + return numSent; +} + + +int DwProtocolClient::PReceive(char* aBuf, int aBufSize) +{ + mFailureCode = kFailNoFailure; + mFailureStr = ""; + mErrorCode = kErrNoError; + mErrorStr = get_error_text(mErrorCode); + + if (! mIsOpen) { + // error! + mErrorCode = kErrBadUsage; + mErrorStr = get_error_text(mErrorCode); + return 0; + } + + // Suspend until there's input to read + + fd_set readfds; + FD_ZERO(&readfds); + FD_SET(mSocket, &readfds); + struct timeval timeout; + timeout.tv_sec = mReceiveTimeout; + timeout.tv_usec = 0; + int numFds = select(mSocket+1, &readfds, 0, 0, &timeout); + int numReceived = 0; + + // If an error occurred, deal with it + + if (numFds == -1) { + int err = errno; + HandleError(err, kselect); + numReceived = 0; + } + + // Read the input, if available + + else if (numFds == 1) { + int ret = recv(mSocket, aBuf, aBufSize, 0); + if (ret == -1) { + // error! + int err = errno; + HandleError(err, krecv); + numReceived = 0; + } + else /* if (ret != -1) */ { + numReceived = ret; + } + } + + // Otherwise, there was a timeout + + else if (numFds == 0) { + DBG_PROTO_STMT(cout << "Receive timed out" << endl;) + int err = ETIMEDOUT; + HandleError(err, kselect); + numReceived = 0; + } + + return numReceived; +} + + +void DwProtocolClient::HandleError(int aErrorCode, int aSystemCall) +{ + mErrorCode = aErrorCode; + mErrorStr = get_error_text(mErrorCode); + switch (aSystemCall) { + case ksocket: + switch (mErrorCode) { + case EMFILE: + case ENFILE: + case ENOBUFS: + mFailureCode = kFailNoResources; + mFailureStr = "Cannot get required system resources"; + break; + case EPROTONOSUPPORT: + case EACCES: + break; + } + break; + case kgethostbyname: + switch (mErrorCode) { + case kErrHostNotFound: + case kErrTryAgain: + case kErrNoRecovery: + case kErrNoData: + mFailureCode = kFailHostNotFound; + mFailureStr = "The server was not found"; + break; + default: + break; + } + break; + case ksetsockopt: + break; + case kconnect: + switch (aErrorCode) { + case ETIMEDOUT: + mFailureCode = kFailTimedOut; + mFailureStr = "The connection attempt to the server timed out"; + break; + case ECONNREFUSED: + mFailureCode = kFailConnRefused; + mFailureStr = "The connection was refused by the server"; + break; + case ENETUNREACH: + mFailureCode = kFailNetUnreachable; + mFailureStr = "The network is unreachable"; + break; + case EBADF: + case ENOTSOCK: + case EADDRNOTAVAIL: + case EAFNOSUPPORT: + case EISCONN: + case EADDRINUSE: + case EFAULT: + case EINPROGRESS: + case EALREADY: + break; + } + break; + case ksend: + switch(aErrorCode) { + case ENOBUFS: + mFailureCode = kFailNoResources; + mFailureStr = "Cannot get required system resources"; + break; + case EBADF: + case ENOTSOCK: + case EFAULT: + case EMSGSIZE: + case EWOULDBLOCK: + case ECONNREFUSED: + case EISCONN: + case EACCES: + break; + } + break; + case krecv: + switch(aErrorCode) { + case EBADF: + case ENOTSOCK: + case EWOULDBLOCK: + case EINTR: + case EFAULT: + break; + } + break; + case kclose: + switch (aErrorCode) { + case EBADF: + case EINTR: + case ETIMEDOUT: + break; + } + break; + case kselect: + switch (aErrorCode) { + case ETIMEDOUT: + mFailureCode = kFailTimedOut; + mFailureStr = "Timed out while waiting for the server"; + break; + case EBADF: + case EINTR: + case EINVAL: + break; + } + break; + default: + break; + } +} + + +static int translate_h_errno(int herrno) +{ + int err = 0; + switch (herrno) { + case HOST_NOT_FOUND: + err = DwProtocolClient::kErrHostNotFound; + break; + case TRY_AGAIN: + err = DwProtocolClient::kErrTryAgain; + break; + case NO_RECOVERY: + err = DwProtocolClient::kErrNoRecovery; + break; + case NO_DATA: + err = DwProtocolClient::kErrNoData; + break; + default: + err = DwProtocolClient::kErrUnknownError; + break; + } + return err; +} + + +static const char* get_error_text(int aErrorCode) +{ + const char* msg = ""; + switch (aErrorCode) { + case DwProtocolClient::kErrNoError: + msg = "No error"; + break; + case DwProtocolClient::kErrUnknownError: + msg = "Unknown error"; + break; + case DwProtocolClient::kErrBadParameter: + msg = "(MIME++) bad parameter passed to function"; + break; + case DwProtocolClient::kErrBadUsage: + msg = "(MIME++) bad library usage"; + break; + case DwProtocolClient::kErrNoWinsock: + msg = "(MIME++) incompatible Winsock version"; + break; + case DwProtocolClient::kErrHostNotFound: + msg = "Host not found"; + break; + case DwProtocolClient::kErrTryAgain: + msg = "Nonauthoritative host not found"; + break; + case DwProtocolClient::kErrNoRecovery: + msg = "Nonrecoverable errors: FORMERR, REFUSED, NOTIMP"; + break; + case DwProtocolClient::kErrNoData: + msg = "Valid name, no data record of requested type"; + break; + case DwProtocolClient::kErrNoAddress: + msg = "No address, look for MX record"; + break; + default: + msg = strerror(aErrorCode); + break; + } + return msg; +} |