summaryrefslogtreecommitdiffstats
path: root/mimelib/protocol.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'mimelib/protocol.cpp')
-rw-r--r--mimelib/protocol.cpp531
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;
+}