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

514 строки
12 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
*/
#include <string.h>
#include "prtypes.h"
#include "prmem.h"
#include "prio.h"
#include "prclist.h"
#include "prlog.h"
#include "prnetdb.h"
#include "bspubtd.h"
#include "bsconfig.h"
#include "bserror.h"
#include "bsevent.h"
#include "bsapi.h"
#include "bsconnection.h"
#include "bsserver.h"
#include "bsutil.h"
PR_BEGIN_EXTERN_C
typedef struct BSConnectionEnumData BSConnectionEnumData;
typedef struct BSConnectionLink BSConnectionLink;
struct BSConnectionEnumData {
PRPollDesc *polldesc;
BSConnectionClass **connection;
};
struct BSConnectionLink {
PRCList list;
BSConnectionClass *connection;
};
BSServerClass *
bs_server_new (BSNetworkClass *parent, const char *hostname)
{
BSServerClass *new_server;
new_server = PR_NEW (BSServerClass);
if (!new_server)
{
BS_ReportError (BSERR_OUT_OF_MEMORY);
return PR_FALSE;
}
if (!hostname)
{
BS_ReportError (BSERR_INVALID_STATE);
PR_Free (new_server);
return NULL;
}
new_server->hostname = PR_Malloc ((strlen (hostname) + 1) *
sizeof (bschar));
if (!new_server->hostname)
{
BS_ReportError (BSERR_OUT_OF_MEMORY);
PR_Free (new_server);
return PR_FALSE;
}
strcpy (new_server->hostname, hostname);
memset (new_server->callback_table, 0,
BSSERVER_EVENT_COUNT);
new_server->parent_network = parent;
new_server->connection_count = 0;
new_server->input_timeout = BS_INPUT_TIMEOUT_DEFAULT;
PR_INIT_CLIST (&new_server->connection_list);
return new_server;
}
void
bs_server_free (BSServerClass *server)
{
PRCList *head;
BSConnectionClass *connection;
PR_ASSERT (server);
while (!PR_CLIST_IS_EMPTY(&server->connection_list))
{
head = PR_LIST_HEAD(&server->connection_list);
if (head == &server->connection_list)
break;
connection = ((BSConnectionLink *)head)->connection;
bs_connection_free (connection);
PR_REMOVE_AND_INIT_LINK(head);
server->connection_count--;
}
PR_ASSERT (server->connection_count == 0);
if (server->hostname)
PR_Free (server->hostname);
PR_Free (server);
}
BSConnectionClass *
bs_server_connect (BSServerClass *server, bsint port, const bschar *bind_addr,
PRBool tcp)
{
PRFileDesc *fd;
PRNetAddr naconnect, nabind;
PRIntervalTime to;
PRStatus rv;
BSConnectionClass *connection;
BSConnectionLink *connection_link;
PR_ASSERT (server);
if (tcp)
fd = PR_NewTCPSocket();
else
fd = PR_NewUDPSocket();
if (!fd)
{
BS_ReportPRError (PR_GetError());
return NULL;
}
if (bs_util_is_ip (server->hostname))
rv = PR_StringToNetAddr (server->hostname, &naconnect);
else
rv = bs_util_resolve_host (server->hostname, &naconnect);
if (PR_FAILURE == rv)
{
BS_ReportPRError (PR_GetError());
goto error_out;
}
if (bind_addr)
{
rv = PR_StringToNetAddr (bind_addr, &nabind);
if (PR_FAILURE == rv)
{
BS_ReportPRError (PR_GetError());
goto error_out;
}
PR_Bind (fd, &nabind);
}
rv = PR_InitializeNetAddr (PR_IpAddrNull, port, &naconnect);
if (PR_FAILURE == rv)
{
BS_ReportPRError (PR_GetError());
goto error_out;
}
to = PR_MillisecondsToInterval (BS_CONNECTION_TIMEOUT_MS);
rv = PR_Connect (fd, &naconnect, to);
if (PR_FAILURE == rv)
{
BS_ReportPRError (PR_GetError());
goto error_out;
}
connection = bs_connection_new (server, fd, port);
if (connection)
{
connection_link = PR_NEW (BSConnectionLink);
if (!connection_link)
{
BS_ReportError (BSERR_OUT_OF_MEMORY);
PR_Shutdown (fd, PR_SHUTDOWN_BOTH);
goto error_out;
}
connection_link->connection = connection;
PR_APPEND_LINK ((PRCList *)connection_link, &server->connection_list);
}
server->connection_count++;
return connection;
error_out:
PR_Close (fd);
return NULL;
}
PRBool
bs_server_enumerate_connections (BSServerClass *server,
BSEnumerateCallback *enum_func, void *data)
{
PRCList *link;
BSConnectionClass *connection;
bsuint i = 0;
PR_ASSERT (server);
if (PR_CLIST_IS_EMPTY(&server->connection_list))
return PR_TRUE;
link = PR_LIST_HEAD(&server->connection_list);
while (link != (PRCList *)server)
{
connection = ((BSConnectionLink *)link)->connection;
if (!enum_func (connection, data, i++))
break;
link = PR_NEXT_LINK (link);
}
return PR_TRUE;
}
PRBool
bs_server_connection_enumerator (void *connection,
void *enumdata, bsuint i)
{
PRFileDesc *fd;
BSConnectionEnumData *ed;
fd = bs_connection_get_fd ((BSConnectionClass *)connection);
ed = (BSConnectionEnumData *)enumdata;
ed->polldesc[i].fd = fd;
ed->polldesc[i].in_flags = PR_POLL_READ;
ed->connection[i] = (BSConnectionClass *)connection;
return PR_TRUE;
}
PRBool
bs_server_poll_connections (BSServerClass *server, BSQueueClass *event_queue)
{
PRInt32 rv, i;
BSConnectionEnumData enumdata;
enumdata.polldesc = PR_Calloc (server->connection_count,
sizeof (PRPollDesc *));
enumdata.connection = PR_Calloc (server->connection_count,
sizeof (BSConnectionClass *));
if (!enumdata.polldesc || !enumdata.connection)
{
BS_ReportError (BSERR_OUT_OF_MEMORY);
return PR_FALSE;
}
bs_server_enumerate_connections (server, bs_server_connection_enumerator,
&enumdata);
rv = PR_Poll (enumdata.polldesc, server->connection_count,
PR_INTERVAL_NO_WAIT);
for (i = 0; i < server->connection_count; i++)
if (enumdata.polldesc[i].out_flags & PR_POLL_READ)
bs_connection_recv (enumdata.connection[i], event_queue);
return PR_TRUE;
}
BSEventClass *
bs_server_route (BSServerClass *server, BSEventClass *event)
{
if ((event->id < BSSERVER_EVENT_COUNT) &&
(server->callback_table[event->id]))
return server->callback_table[event->id](server, event);
else
return NULL;
}
PRBool
bs_server_set_handler (BSServerClass *server, BSServerEvent event_type,
BSServerEventCallback *handler)
{
PR_ASSERT (server);
if (event_type > BSSERVER_EVENT_COUNT)
{
BS_ReportError (BSERR_NO_SUCH_EVENT);
return PR_FALSE;
}
server->callback_table[event_type] = handler;
return PR_TRUE;
}
PRBool
bs_server_set_property (BSServerClass *server, BSServerProperty prop,
bsint type, void *v)
{
PR_ASSERT (server);
switch (prop)
{
case BSPROP_PARENT:
if (!BS_CHECK_TYPE(type, BSTYPE_OBJECT))
{
BS_ReportError (BSERR_PROPERTY_MISMATCH);
return PR_FALSE;
}
server->parent_network = (BSNetworkClass *)v;
break;
case BSPROP_INPUT_TIMEOUT:
if (!BS_CHECK_TYPE(type, BSTYPE_UINT))
{
BS_ReportError (BSERR_PROPERTY_MISMATCH);
return PR_FALSE;
}
server->input_timeout = *(int *)v;
break;
case BSPROP_LINE_BUFFER:
if (!BS_CHECK_TYPE(type, BSTYPE_BOOLEAN))
{
BS_ReportError (BSERR_PROPERTY_MISMATCH);
return PR_FALSE;
}
server->linebuffer_flag = *(PRBool *)v;
break;
case BSPROP_HOSTNAME:
if (!BS_CHECK_TYPE(type, BSTYPE_STRING))
{
BS_ReportError (BSERR_PROPERTY_MISMATCH);
return PR_FALSE;
}
PR_FREEIF (server->hostname);
server->hostname = PR_Malloc ((strlen ((bschar *)v) + 1) *
sizeof (bschar));
if (!server->hostname)
{
BS_ReportError (BSERR_OUT_OF_MEMORY);
return PR_FALSE;
}
strcpy (server->hostname, (bschar *)v);
break;
default:
break;
}
return PR_FALSE;
}
PRBool
bs_server_set_uint_property (BSServerClass *server, BSServerProperty prop,
bsuint *v)
{
return bs_server_set_property (server, prop, BSTYPE_UINT, &v);
}
PRBool
bs_server_set_string_property (BSServerClass *server, BSServerProperty prop,
bschar *v)
{
return bs_server_set_property (server, prop, BSTYPE_STRING, v);
}
PRBool
bs_server_set_bool_property (BSServerClass *server, BSServerProperty prop,
PRBool *v)
{
return bs_server_set_property (server, prop, BSTYPE_BOOLEAN, &v);
}
PRBool
bs_server_set_object_property (BSServerClass *server, BSServerProperty prop,
void *v)
{
return bs_server_set_property (server, prop, BSTYPE_OBJECT, v);
}
PRBool
bs_server_get_property (BSServerClass *server, BSServerProperty prop,
bsint type, void *v)
{
PR_ASSERT (server);
switch (prop)
{
case BSPROP_PARENT:
if (!BS_CHECK_TYPE (type, BSTYPE_OBJECT))
{
BS_ReportError (BSERR_PROPERTY_MISMATCH);
return PR_FALSE;
}
v = server->parent_network;
break;
case BSPROP_INPUT_TIMEOUT:
if (!BS_CHECK_TYPE (type, BSTYPE_UINT))
{
BS_ReportError (BSERR_PROPERTY_MISMATCH);
return PR_FALSE;
}
*(bsuint *)v = server->input_timeout;
break;
case BSPROP_LINE_BUFFER:
if (!BS_CHECK_TYPE (type, BSTYPE_BOOLEAN))
{
BS_ReportError (BSERR_PROPERTY_MISMATCH);
return PR_FALSE;
}
*(PRBool *)v = server->linebuffer_flag;
break;
case BSPROP_HOSTNAME:
if (!BS_CHECK_TYPE (type, BSTYPE_STRING))
{
BS_ReportError (BSERR_PROPERTY_MISMATCH);
return PR_FALSE;
}
v = server->hostname;
break;
default:
break;
}
return PR_FALSE;
}
PRBool
bs_server_get_uint_property (BSServerClass *server, BSServerProperty prop,
bsuint *v)
{
return bs_server_get_property (server, prop, BSTYPE_UINT, v);
}
PRBool
bs_server_get_string_property (BSServerClass *server, BSServerProperty prop,
bschar **v)
{
return bs_server_get_property (server, prop, BSTYPE_STRING, v);
}
PRBool
bs_server_get_bool_property (BSServerClass *server, BSServerProperty prop,
PRBool *v)
{
return bs_server_get_property (server, prop, BSTYPE_BOOLEAN, v);
}
PRBool
bs_server_get_object_property (BSServerClass *server, BSServerProperty prop,
BSServerClass **v)
{
return bs_server_get_property (server, prop, BSTYPE_OBJECT, v);
}
PR_END_EXTERN_C