gecko-dev/extensions/irc/libbs/bsconnection.c

531 lines
13 KiB
C

/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
*
* The contents of this file are subject to the Mozilla Public
* License Version 1.1 (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/MPL/
*
* 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 Basic Socket Library
*
* The Initial Developer of the Original Code is New Dimensions Consulting,
* Inc. Portions created by New Dimensions Consulting, Inc. are
* Copyright (C) 1999 New Dimenstions Consulting, Inc. All
* Rights Reserved.
*
* Contributor(s):
*
*
* Contributor(s):
* Robert Ginda, rginda@ndcico.com, original author
* Simon Fraser, smfr@netscape.com, Mac warning fixes
*/
#include "string.h"
#include "prtypes.h"
#include "prerror.h"
#include "prmem.h"
#include "prio.h"
#include "prlog.h"
#include "bspubtd.h"
#include "bsutil.h"
#include "bsconfig.h"
#include "bserror.h"
#include "bsevent.h"
#include "bsqueue.h"
#include "bsapi.h"
#include "bsserver.h"
#include "bsconnection.h"
PR_BEGIN_EXTERN_C
BSConnectionClass *
bs_connection_new (BSServerClass *parent, PRFileDesc *fd, bsint port)
{
BSConnectionClass *new_connection;
new_connection = PR_NEW (BSConnectionClass);
if (!new_connection)
{
BS_ReportError (BSERR_OUT_OF_MEMORY);
return PR_FALSE;
}
memset (new_connection->callback_table, 0, BSCONNECTION_EVENT_COUNT);
new_connection->parent_server = parent;
new_connection->linebuffer = NULL;
new_connection->port = port;
new_connection->fd = fd;
new_connection->linebuffer_flag = BS_LINEBUFFER_FLAG_DEFAULT;
new_connection->is_connected = PR_TRUE;
return new_connection;
}
void
bs_connection_free (BSConnectionClass *connection)
{
PR_ASSERT (connection);
/* partially received line will be lost */
PR_FREEIF (connection->linebuffer);
bs_connection_disconnect (connection);
PR_Free (connection);
}
PRBool
bs_connection_disconnect (BSConnectionClass *connection)
{
PRStatus rv;
PR_ASSERT (connection);
if (connection->is_connected)
{
PR_FREEIF (connection->linebuffer);
rv = PR_Shutdown (connection->fd, PR_SHUTDOWN_BOTH);
if (PR_FAILURE == rv)
BS_ReportPRError (PR_GetError());
rv = PR_Close (connection->fd);
if (PR_FAILURE == rv)
BS_ReportPRError (PR_GetError());
}
connection->is_connected = PR_FALSE;
return PR_FALSE;
}
PRBool /* public method */
bs_connection_send_data (BSConnectionClass *connection, bschar *data,
bsuint length)
{
PRIntervalTime to;
PRInt32 count;
PR_ASSERT (connection);
to = PR_MillisecondsToInterval (BS_SEND_TIMEOUT_MS);
count = PR_Send (connection->fd, data, length, 0, to);
if (count == -1)
{
BS_ReportPRError (PR_GetError());
return PR_FALSE;
}
return PR_TRUE;
}
PRBool /* public method */
bs_connection_send_string (BSConnectionClass *connection, const bschar *data)
{
bsuint length;
PR_ASSERT (connection);
length = strlen (data);
return bs_connection_send_data (connection, (bschar *)data, length);
}
PRInt32 /* friend function */
bs_connection_recv_data (BSConnectionClass *connection, PRInt32 timeout,
bschar **buf)
{
PRIntervalTime to;
PRInt32 count;
PRErrorCode err;
static bschar recv_buf[BSMAX_SERVER_RECV];
PR_ASSERT (connection);
*buf = recv_buf;
to = PR_MillisecondsToInterval (timeout);
count = PR_Recv(connection->fd, recv_buf, BSMAX_SERVER_RECV, 0, to);
switch (count)
{
case -1: /* recv error */
err = PR_GetError();
switch (err)
{
case PR_IO_TIMEOUT_ERROR:
buf[0] = '\00';
count = 0;
break;
default:
BS_ReportPRError (err);
goto err_out;
break;
}
break;
case 0: /* connection closed */
count = -1;
goto err_out;
default: /* byte count */
((char *)(*buf))[count] = 0;
}
return count;
err_out:
*buf = NULL;
return count;
}
PRBool /* friend function */
bs_connection_recv (BSConnectionClass *connection, BSQueueClass *event_queue)
{
bschar *buf, *lastline = NULL;
bschar **lineary;
int i, lines;
BSEventClass *event;
PRBool completeline_flag;
PRInt32 count;
PR_ASSERT (connection);
count = bs_connection_recv_data (connection, BS_RECV_TIMEOUT_MS, &buf);
if (count == 0)
{
event = bs_event_new (BSOBJ_CONNECTION, connection,
BSEVENT_CONNECTION_DISCONNECT, NULL,
NULL, NULL);
if (!event)
return PR_FALSE;
if (0 == bs_queue_add (event_queue, event, 0))
{
BS_ReportError (BSERR_OUT_OF_MEMORY);
return PR_FALSE;
}
return PR_FALSE;
}
if (count < 0)
{
BS_ReportError (BSERR_UNDEFINED_ERROR);
return PR_FALSE;
}
if (connection->linebuffer_flag)
{
completeline_flag = (buf[count - 1] == '\n') ? PR_TRUE :
PR_FALSE;
lineary = bs_util_delimbuffer_to_array (buf, &lines, '\n');
for (i = 0; i < lines - 1; i++)
{
event = bs_event_new_copyZ (BSOBJ_CONNECTION, connection,
BSEVENT_CONNECTION_RAWDATA,
lineary[i], NULL);
if (!event)
return PR_FALSE;
if (0 == bs_queue_add (event_queue, event, 0))
{
BS_ReportError (BSERR_OUT_OF_MEMORY);
return PR_FALSE;
}
}
buf = lastline = bs_util_linebuffer (lineary[lines - 1],
&connection->linebuffer,
completeline_flag);
PR_Free (lineary);
}
if (buf)
{
connection->linebuffer = NULL;
event = bs_event_new_copyZ (BSOBJ_CONNECTION, connection,
BSEVENT_CONNECTION_RAWDATA,
buf, NULL);
if (!event)
return PR_FALSE;
if (0 == bs_queue_add (event_queue, event, 0))
{
BS_ReportError (BSERR_OUT_OF_MEMORY);
return PR_FALSE;
}
}
PR_FREEIF (lastline);
return PR_TRUE;
}
PRFileDesc * /* friend function */
bs_connection_get_fd (BSConnectionClass *connection)
{
PR_ASSERT (connection);
if (!connection->is_connected)
return NULL;
return connection->fd;
}
BSEventClass *
bs_connection_route (BSConnectionClass *connection, BSEventClass *event)
{
PR_ASSERT (connection);
if ((event->id < BSCONNECTION_EVENT_COUNT) &&
(connection->callback_table[event->id]))
return connection->callback_table[event->id](connection, event);
else
return NULL;
}
PRBool
bs_connection_set_handler (BSConnectionClass *connection,
BSConnectionEvent event_type,
BSConnectionEventCallback *handler)
{
PR_ASSERT (connection);
if (event_type > BSCONNECTION_EVENT_COUNT)
{
BS_ReportError (BSERR_NO_SUCH_EVENT);
return PR_FALSE;
}
connection->callback_table[event_type] = handler;
return PR_TRUE;
}
PRBool
bs_connection_set_property (BSConnectionClass *connection,
BSConnectionProperty prop,
bsint type, void *v)
{
PR_ASSERT (connection);
switch (prop)
{
case BSPROP_PARENT:
if (!BS_CHECK_TYPE (type, BSTYPE_OBJECT))
{
BS_ReportError (BSERR_PROPERTY_MISMATCH);
return PR_FALSE;
}
connection->parent_server = (BSServerClass *)v;
break;
case BSPROP_LINE_BUFFER:
if (!BS_CHECK_TYPE (type, BSTYPE_BOOLEAN))
{
BS_ReportError (BSERR_PROPERTY_MISMATCH);
return PR_FALSE;
}
connection->linebuffer_flag = *(PRBool *)v;
break;
case BSPROP_STATUS:
if (!BS_CHECK_TYPE (type, BSTYPE_BOOLEAN))
{
BS_ReportError (BSERR_PROPERTY_MISMATCH);
return PR_FALSE;
}
BS_ReportError (BSERR_READ_ONLY);
return PR_FALSE;
break;
case BSPROP_PORT:
if (!BS_CHECK_TYPE (type, BSTYPE_UINT))
{
BS_ReportError (BSERR_PROPERTY_MISMATCH);
return PR_FALSE;
}
if (connection->is_connected)
{
BS_ReportError (BSERR_READ_ONLY);
return PR_FALSE;
}
connection->port = *(bsuint *)v;
break;
default:
BS_ReportError (BSERR_NO_SUCH_PARAM);
return PR_FALSE;
break;
}
return PR_FALSE;
}
PRBool
bs_connection_set_uint_property (BSConnectionClass *connection,
BSConnectionProperty prop,
bsuint v)
{
return bs_connection_set_property (connection, prop, BSTYPE_UINT, &v);
}
PRBool
bs_connection_set_string_property (BSConnectionClass *connection,
BSConnectionProperty prop,
char *v)
{
return bs_connection_set_property (connection, prop, BSTYPE_STRING, v);
}
PRBool
bs_connection_set_bool_property (BSConnectionClass *connection,
BSConnectionProperty prop,
PRBool v)
{
return bs_connection_set_property (connection, prop, BSTYPE_BOOLEAN, &v);
}
PRBool
bs_connection_set_object_property (BSConnectionClass *connection,
BSConnectionProperty prop,
void *v)
{
return bs_connection_set_property (connection, prop, BSTYPE_OBJECT, v);
}
PRBool
bs_connection_get_property (BSConnectionClass *connection,
BSConnectionProperty prop,
bsint type, void *v)
{
PR_ASSERT (connection);
switch (prop)
{
case BSPROP_PARENT:
if (!BS_CHECK_TYPE (type, BSTYPE_OBJECT))
{
BS_ReportError (BSERR_PROPERTY_MISMATCH);
return PR_FALSE;
}
v = connection->parent_server;
break;
case BSPROP_LINE_BUFFER:
if (!BS_CHECK_TYPE (type, BSTYPE_BOOLEAN))
{
BS_ReportError (BSERR_PROPERTY_MISMATCH);
return PR_FALSE;
}
*(PRBool *)v = connection->linebuffer_flag;
break;
case BSPROP_PORT:
if (!BS_CHECK_TYPE (type, BSTYPE_UINT))
{
BS_ReportError (BSERR_PROPERTY_MISMATCH);
return PR_FALSE;
}
*(int *)v = connection->port;
break;
case BSPROP_STATUS:
if (!BS_CHECK_TYPE (type, BSTYPE_BOOLEAN))
{
BS_ReportError (BSERR_PROPERTY_MISMATCH);
return PR_FALSE;
}
*(PRBool *)v = connection->is_connected;
break;
default:
BS_ReportError (BSERR_NO_SUCH_PARAM);
return PR_FALSE;
break;
}
return PR_TRUE;
}
PRBool
bs_connection_get_uint_property (BSConnectionClass *connection,
BSConnectionProperty prop,
bsuint *v)
{
return bs_connection_get_property (connection, prop, BSTYPE_UINT, v);
}
PRBool
bs_connection_get_string_property (BSConnectionClass *connection,
BSConnectionProperty prop,
bschar **v)
{
return bs_connection_get_property (connection, prop, BSTYPE_STRING, v);
}
PRBool
bs_connection_get_bool_property (BSConnectionClass *connection,
BSConnectionProperty prop,
PRBool *v)
{
return bs_connection_get_property (connection, prop, BSTYPE_BOOLEAN, v);
}
PRBool
bs_connection_get_object_property (BSConnectionClass *connection,
BSConnectionProperty prop,
void **v)
{
return bs_connection_get_property (connection, prop, BSTYPE_OBJECT, v);
}
PR_END_EXTERN_C