/* * The contents of this file are subject to the Netscape Public License * Version 1.0 (the "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * http://www.mozilla.org/NPL/. * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the License. * * The Original Code is the Netscape Messaging Access SDK Version 3.5 code, * released on or about June 15, 1998. * * The Initial Developer of the Original Code is Netscape Communications * Corporation. Portions created by Netscape are Copyright (C) 1998 Netscape * Communications Corporation. All Rights Reserved. * * Contributor(s): ______________________________________. */ /* * Copyright (c) 1997 and 1998 Netscape Communications Corporation * (http://home.netscape.com/misc/trademarks.html) */ /* * nsio.c * @author derekt@netscape.com * @version 1.0 */ #include "nsmail.h" #include "nsio.h" #include "nssocket.h" #include #include #include #include /* Internal functions not to be used directly. */ int IO_checkTimeout( IO_t * in_pIO ); void IO_resetFunctions( IO_t * in_pIO ); int UpdateBuffer( IO_t * in_pIO ); /* Initalize the IO structure. */ int IO_initialize( IO_t * in_pIO, unsigned int in_readBufferLimit, unsigned int in_writeBufferLimit ) { /* * Parameter validation. */ if ( in_pIO == NULL || in_pIO->readBuffer != NULL || in_pIO->writeBuffer != NULL ) { return NSMAIL_ERR_INVALIDPARAM; } if ( in_readBufferLimit == 0 ) { in_pIO->readBufferLimit = DEFAULT_BUFFER_LENGTH; } else { in_pIO->readBufferLimit = in_readBufferLimit; } if ( in_writeBufferLimit == 0 ) { in_pIO->writeBufferLimit = DEFAULT_BUFFER_LENGTH; } else { in_pIO->writeBufferLimit = in_writeBufferLimit; } /* * Set the socket descriptor to an invalid value to identify a * disconnected state. */ in_pIO->socket = -1; /* * Initialize the read buffer. */ in_pIO->readBuffer = (char*)malloc( in_pIO->readBufferLimit * sizeof( char ) ); if ( in_pIO->readBuffer == NULL ) { return NSMAIL_ERR_OUTOFMEMORY; } memset(in_pIO->readBuffer, 0, in_pIO->readBufferLimit * sizeof( char ) ); /* * Initialize the write buffer. */ in_pIO->writeBuffer = (char*)malloc( in_pIO->writeBufferLimit * sizeof( char ) ); if ( in_pIO->writeBuffer == NULL ) { return NSMAIL_ERR_OUTOFMEMORY; } memset(in_pIO->writeBuffer, 0, in_pIO->writeBufferLimit * sizeof( char ) ); /* Set the current size of the write buffer. */ in_pIO->writeBufferSize = 0; /* Set the current size of the read buffer. */ in_pIO->readBufferSize = 0; /* Set the IO function pointers. */ IO_resetFunctions( in_pIO ); /* Set the default timeout value. */ return IO_setTimeout( in_pIO, -1 ); } /* Free the IO structure. */ void IO_free( IO_t * in_pIO ) { if ( in_pIO == NULL ) { return; } /* * Reset the data members. */ if ( in_pIO->readBuffer != NULL ) { free( in_pIO->readBuffer ); in_pIO->readBuffer = NULL; } if ( in_pIO->writeBuffer != NULL ) { free( in_pIO->writeBuffer ); in_pIO->writeBuffer = NULL; } /* * Terminate the socket session. */ NSSock_Terminate(); } /* Connect to the specified server with the specified port. */ int IO_connect( IO_t * in_pIO, const char * in_szServer, unsigned short in_nPort ) { struct sockaddr_in l_hostInetAddr; LinkedList_t * l_addressList; LinkedList_t * l_tempList; int fHostIsIPAddr; int dotCount = 0; int i; /* * Parameter validation. */ if ( in_pIO == NULL || in_szServer == NULL ) { return NSMAIL_ERR_INVALIDPARAM; } if ( in_pIO->socket > 0 ) { return NSMAIL_ERR_ALREADYCONNECTED; } /* * Initialize the socket session. */ NSSock_Initialize(); /* * Create a handle to the new socket. */ in_pIO->socket = in_pIO->ioFunctions.liof_socket( AF_INET, SOCK_STREAM, 0 ); if ( in_pIO->socket < 0 ) { return NSMAIL_ERR_IO_SOCKET; } /* initialize the address structures */ memset (&l_hostInetAddr, 0, sizeof(l_hostInetAddr)); l_hostInetAddr.sin_family = AF_INET; l_hostInetAddr.sin_port = htons(in_nPort); /* * Determine if a host name or IP address was specified. Assume * it is, then see if we can find something that means it isn't. * (The test below is not very careful, it should verify there * are numbers between the dots, and the numbers are less than 256.) */ fHostIsIPAddr = 1; for (i=0; iioFunctions.liof_connect( in_pIO->socket, (const struct sockaddr *)&l_hostInetAddr, in_nPort ) != FALSE ) { return NSMAIL_OK; } } else { l_addressList = NSSock_GetHostByName( in_szServer ); while ( l_addressList != NULL ) { if ( ( l_hostInetAddr.sin_addr.s_addr = inet_addr(l_addressList->pData) ) != 0 ) { if ( in_pIO->ioFunctions.liof_connect( in_pIO->socket, (const struct sockaddr *)&l_hostInetAddr, in_nPort ) != FALSE ) { while ( l_addressList != NULL ) { free( l_addressList->pData ); l_tempList = l_addressList; l_addressList = l_tempList->next; free( l_tempList ); } return NSMAIL_OK; } } free( l_addressList->pData ); l_tempList = l_addressList; l_addressList = l_tempList->next; free( l_tempList ); } } in_pIO->socket = -1; return NSMAIL_ERR_IO_CONNECT; } /* Disconnect from the server. */ int IO_disconnect( IO_t * in_pIO ) { /* * Parameter validation. */ if ( in_pIO == NULL ) { return NSMAIL_ERR_INVALIDPARAM; } if ( in_pIO->socket < 0 ) { return NSMAIL_ERR_NOTCONNECTED; } /* * Close the socket. */ if ( in_pIO->ioFunctions.liof_close( in_pIO->socket ) == FALSE ) { in_pIO->socket = -1; return NSMAIL_ERR_IO_CLOSE; } in_pIO->socket = -1; return NSMAIL_OK; } /*Send data to the server appending the \r\n and optionally flushing.*/ int IO_send( IO_t * in_pIO, const char * in_data, boolean in_bufferData ) { int l_nReturn; l_nReturn = IO_write( in_pIO, in_data, strlen(in_data) ); if ( l_nReturn != NSMAIL_OK ) { return l_nReturn; } l_nReturn = IO_write( in_pIO, eoline, 2 ); if ( l_nReturn != NSMAIL_OK ) { return l_nReturn; } if ( in_bufferData == FALSE ) { l_nReturn = IO_flush( in_pIO ); if ( l_nReturn != NSMAIL_OK ) { return l_nReturn; } } return NSMAIL_OK; } /* Write operation to the buffered socket. */ int IO_write( IO_t * in_pIO, const char * in_data, unsigned int in_length ) { int l_nReturn; /* Parameter validation.*/ if ( in_pIO == NULL || in_data == NULL ) { return NSMAIL_ERR_INVALIDPARAM; } if ( in_pIO->socket < 0 ) { return NSMAIL_ERR_NOTCONNECTED; } /* Fill the buffer and send the data if it is full. */ if ( in_pIO->writeBufferSize + in_length < in_pIO->writeBufferLimit ) { memcpy( &(in_pIO->writeBuffer[in_pIO->writeBufferSize]), in_data, in_length ); in_pIO->writeBufferSize += in_length; } else { l_nReturn = IO_flush( in_pIO ); if ( l_nReturn != NSMAIL_OK ) { return l_nReturn; } if ( in_length < in_pIO->writeBufferLimit ) { memcpy( in_pIO->writeBuffer, in_data, in_length ); in_pIO->writeBufferSize = in_length; } else { if ( in_pIO->ioFunctions.liof_write( in_pIO->socket, (char*)in_data, in_length ) == FALSE ) { return NSMAIL_ERR_IO_WRITE; } } } return NSMAIL_OK; } int IO_writeByte( IO_t * in_pIO, const char in_byte ) { int l_nReturn; /* Parameter validation */ if ( in_pIO == NULL ) { return NSMAIL_ERR_INVALIDPARAM; } if ( in_pIO->socket < 0 ) { return NSMAIL_ERR_NOTCONNECTED; } /* * Fill the buffer and send the data if it is full. */ if ( in_pIO->writeBufferSize + 1 < in_pIO->writeBufferLimit ) { in_pIO->writeBuffer[(in_pIO->writeBufferSize)++] = in_byte; } else { l_nReturn = IO_flush( in_pIO ); if ( l_nReturn != NSMAIL_OK ) { return l_nReturn; } in_pIO->writeBuffer[(in_pIO->writeBufferSize)++] = in_byte; } return NSMAIL_OK; } int IO_flush( IO_t * in_pIO ) { /* Parameter validation */ if ( in_pIO == NULL || in_pIO->writeBuffer == NULL ) { return NSMAIL_ERR_INVALIDPARAM; } if ( in_pIO->socket < 0 ) { return NSMAIL_ERR_NOTCONNECTED; } /* Send the data and verify that it was sent ok. */ if ( in_pIO->writeBufferSize > 0 ) { if ( in_pIO->ioFunctions.liof_write( in_pIO->socket, (char*)in_pIO->writeBuffer, in_pIO->writeBufferSize ) == FALSE ) { return NSMAIL_ERR_IO_WRITE; } memset( in_pIO->writeBuffer, 0, in_pIO->writeBufferLimit ); in_pIO->writeBufferSize = 0; } return NSMAIL_OK; } /** * Read in in_size bytes into out_response * @param in_pIO The IO structure * @param out_ppResponse The memory to store the data in * @param in_size The amount of data requested & @param out_bytesRead The total number of bytes read */ int IO_read( IO_t * in_pIO, char * out_response, int in_size, int * out_bytesRead ) { int l_returnCode; int l_bytesRead = 0; int l_bytesToRead = 0; int l_offset = 0; /* Parameter validation. */ if ((in_pIO == NULL) || (out_response == NULL) || (in_size < 1) || (out_bytesRead == NULL)) { return NSMAIL_ERR_INVALIDPARAM; } /* Make sure the IO buffer is not empty */ if ( in_pIO->readBufferSize == 0 ) { l_returnCode = UpdateBuffer( in_pIO ); if ( l_returnCode != NSMAIL_OK ) { return l_returnCode; } } /* Read in_size bytes */ while ( l_bytesRead < in_size ) { /* Determine amount of data to read in from the IO buffer */ if( (in_pIO->readBufferSize + l_bytesRead) <= in_size) { l_bytesToRead = in_pIO->readBufferSize; } else { l_bytesToRead = in_size - l_bytesRead; } memcpy(out_response + l_bytesRead, in_pIO->readBuffer, l_bytesToRead); in_pIO->readBufferSize -= l_bytesToRead; l_bytesRead += l_bytesToRead; l_offset += l_bytesToRead; /* Make sure the IO buffer is not empty */ if ( in_pIO->readBufferSize == 0 ) { l_returnCode = UpdateBuffer( in_pIO ); if ( l_returnCode != NSMAIL_OK ) { return l_returnCode; } l_offset = 0; } } /* Move the rest of the buffer to the beginning of the buffer. */ memcpy( in_pIO->readBuffer, (&in_pIO->readBuffer[l_offset]), in_pIO->readBufferSize ); (*out_bytesRead) = l_bytesRead; return NSMAIL_OK;; } /* Reads a line from the socket. */ int IO_readLine( IO_t * in_pIO, char * out_response, int in_maxLength ) { int l_returnCode; unsigned int offset = 0; /* Parameter validation. */ if ( in_pIO == NULL || out_response == NULL || out_response == NULL ) { return NSMAIL_ERR_INVALIDPARAM; } if ( in_pIO->socket < 0 ) { return NSMAIL_ERR_NOTCONNECTED; } if ( in_pIO->readBufferSize == 0 ) { l_returnCode = UpdateBuffer( in_pIO ); if ( l_returnCode != NSMAIL_OK ) { return l_returnCode; } } /* Read byte by byte looking for the newline character. */ while ( in_pIO->readBuffer[offset] != '\n' ) { offset++; if ( offset == in_pIO->readBufferSize ) { if ( offset == in_pIO->readBufferLimit ) { return NSMAIL_ERR_UNEXPECTED; } l_returnCode = UpdateBuffer( in_pIO ); if ( l_returnCode != NSMAIL_OK ) { return l_returnCode; } } } if ( offset > in_maxLength ) { return NSMAIL_ERR_UNEXPECTED; } /* Copy the line into the buffer. */ memcpy( out_response, in_pIO->readBuffer, offset + 1 ); in_pIO->readBufferSize -= (offset + 1); /* Move the rest of the buffer to the beginning of the buffer. */ memcpy( in_pIO->readBuffer, (&in_pIO->readBuffer[offset+1]), in_pIO->readBufferSize ); /* * Look for the carriage return character. * If it is there, place the null character there, * else put the null character in the location of the linefeed character. */ if ( out_response[offset-1] == '\r' ) { out_response[offset-1] = '\0'; } else { out_response[offset] = '\0'; } return NSMAIL_OK;; } /* Reads a line from the socket and dynamically allocates the necessary space for the response */ int IO_readDLine( IO_t * in_pIO, char** out_ppResponse ) { int l_returnCode; unsigned int offset = 0; char* l_tempBuffer = NULL; int l_bufferlength = 0; /* Parameter validation. */ if ( in_pIO == NULL || (*out_ppResponse) != NULL ) { return NSMAIL_ERR_INVALIDPARAM; } if ( in_pIO->socket < 0 ) { return NSMAIL_ERR_NOTCONNECTED; } /* Make sure the IO buffer is not empty */ while( in_pIO->readBufferSize == 0 ) { l_returnCode = UpdateBuffer( in_pIO ); if ( l_returnCode != NSMAIL_OK ) { return l_returnCode; } } /* Read byte by byte looking for the newline character. */ while(in_pIO->readBuffer[offset] != '\n') { offset++; if(offset >= in_pIO->readBufferSize) { if(offset == in_pIO->readBufferLimit) { /*Grow the size of the read buffer*/ l_bufferlength = in_pIO->readBufferLimit + DEFAULT_BUFFER_LENGTH; l_tempBuffer = in_pIO->readBuffer; in_pIO->readBuffer = (char*)malloc(l_bufferlength*sizeof(char)); if(in_pIO->readBuffer == NULL) { return NSMAIL_ERR_OUTOFMEMORY; } memset(in_pIO->readBuffer, 0, l_bufferlength*sizeof(char)); in_pIO->readBufferLimit = l_bufferlength; memcpy( in_pIO->readBuffer, l_tempBuffer, in_pIO->readBufferSize ); free(l_tempBuffer); l_tempBuffer = NULL; } while(offset >= in_pIO->readBufferSize) { /* Put more data in the buffer. */ l_returnCode = UpdateBuffer( in_pIO ); if ( l_returnCode != NSMAIL_OK ) { return l_returnCode; } } } } (*out_ppResponse) = (char*)malloc((offset + 2)*sizeof(char)); if((*out_ppResponse) == NULL) { return NSMAIL_ERR_OUTOFMEMORY; } memset((*out_ppResponse), 0, (offset + 2)*sizeof(char)); memcpy((*out_ppResponse), in_pIO->readBuffer, offset + 1); in_pIO->readBufferSize -= (offset + 1); /* Move the rest of the buffer to the beginning of the buffer. */ memcpy( in_pIO->readBuffer, (&in_pIO->readBuffer[offset + 1]), in_pIO->readBufferSize ); memset((&in_pIO->readBuffer[in_pIO->readBufferSize]), 0, in_pIO->readBufferLimit - in_pIO->readBufferSize); return NSMAIL_OK; } int IO_freeDLine( char ** out_ppResponse ) { if ( out_ppResponse == NULL || (*out_ppResponse) == NULL ) { return NSMAIL_ERR_INVALIDPARAM; } free( (*out_ppResponse) ); (*out_ppResponse) = NULL; return NSMAIL_OK; } /* Updates the read buffer. */ int UpdateBuffer( IO_t * in_pIO ) { int l_nByteCount; int l_nSelectResponse; /* Determine if there is any data to be read in. */ l_nSelectResponse = IO_checkTimeout( in_pIO ); if ( l_nSelectResponse == 0 ) { return NSMAIL_ERR_TIMEOUT; } /* Determine if there was an error. */ if ( l_nSelectResponse < 0 ) { return NSMAIL_ERR_IO_SELECT; } /* Read in as much data as possible up to the max. size from the socket. */ l_nByteCount = in_pIO->ioFunctions.liof_read( in_pIO->socket, in_pIO->readBuffer + in_pIO->readBufferSize, in_pIO->readBufferLimit - in_pIO->readBufferSize ); if ( l_nByteCount <= 0 ) { return NSMAIL_ERR_IO_READ; } /* Set the new size of the read buffer. */ in_pIO->readBufferSize += l_nByteCount; return NSMAIL_OK; } /* Set the timeout value. */ int IO_setTimeout( IO_t * in_pIO, double in_timeout ) { if ( in_pIO == NULL ) { return NSMAIL_ERR_INVALIDPARAM; } /* represents infinite blocking, not timeout */ if ( in_timeout == -1 ) { in_pIO->timeout.tv_sec = (long)in_timeout; return NSMAIL_OK; } if (in_timeout < 0.0) in_timeout = 0.0; if (in_timeout > 1.0e8) in_timeout = 1.0e8; in_pIO->timeout.tv_sec = (long) in_timeout; in_pIO->timeout.tv_usec = (long) (1.0e6 * (in_timeout-(double)(long)in_timeout)); if (in_pIO->timeout.tv_usec >= 1000000) { in_pIO->timeout.tv_sec++; in_pIO->timeout.tv_usec = 0; } return NSMAIL_OK; } /* Check for a timeout using select() */ int IO_checkTimeout( IO_t * in_pIO ) { fd_set * l_fdSet; int l_nSelectResponse; int l_nSock = in_pIO->socket + 1; /* * Create the fd_set structure to be used in select(). */ l_fdSet = (fd_set *)malloc( sizeof(fd_set) ); FD_ZERO( l_fdSet ); FD_SET( in_pIO->socket, l_fdSet ); /* * Determine if the timeout value is infinite or not. */ if ( in_pIO->timeout.tv_sec == -1 ) { l_nSelectResponse = in_pIO->ioFunctions.liof_select( l_nSock, l_fdSet, NULL, NULL, NULL ); } else { l_nSelectResponse = in_pIO->ioFunctions.liof_select( l_nSock, l_fdSet, NULL, NULL, &(in_pIO->timeout) ); } /* * Free the fd_set structure. */ free( l_fdSet ); /* * Return the result. */ return l_nSelectResponse; } /* Reset the IO functions. */ void IO_resetFunctions( IO_t * in_pIO ) { in_pIO->ioFunctions.liof_read = NSSock_Read; in_pIO->ioFunctions.liof_write = NSSock_Write; in_pIO->ioFunctions.liof_socket = NSSock_Socket; in_pIO->ioFunctions.liof_select = NSSock_Select; in_pIO->ioFunctions.liof_connect = NSSock_Connect; in_pIO->ioFunctions.liof_close = NSSock_Close; } /* Get a structure to the IO functions. */ void IO_getFunctions( IO_t * in_pIO, nsmail_io_fns_t * in_pIOFunctions ) { in_pIOFunctions->liof_read = in_pIO->ioFunctions.liof_read; in_pIOFunctions->liof_write = in_pIO->ioFunctions.liof_write; in_pIOFunctions->liof_socket = in_pIO->ioFunctions.liof_socket; in_pIOFunctions->liof_select = in_pIO->ioFunctions.liof_select; in_pIOFunctions->liof_connect = in_pIO->ioFunctions.liof_connect; in_pIOFunctions->liof_close = in_pIO->ioFunctions.liof_close; } /* Set the IO functions. */ void IO_setFunctions( IO_t * in_pIO, nsmail_io_fns_t * in_pIOFunctions ) { in_pIO->ioFunctions.liof_read = in_pIOFunctions->liof_read; in_pIO->ioFunctions.liof_write = in_pIOFunctions->liof_write; in_pIO->ioFunctions.liof_socket = in_pIOFunctions->liof_socket; in_pIO->ioFunctions.liof_select = in_pIOFunctions->liof_select; in_pIO->ioFunctions.liof_connect = in_pIOFunctions->liof_connect; in_pIO->ioFunctions.liof_close = in_pIOFunctions->liof_close; }