/* IRCfs - IRC FileServ for *nix. * Copyright (C) 2002 Nick 'Zaf' Clifford * For licensing details, refer to the LICENSE file in the source * code directory. */ #include "module.h" #include "runtime.h" #include "server.h" #include "servint.h" #include "list.h" #include "socket.h" #include "str_hash.h" #include "num_hash.h" #include "event.h" #include "archdep.h" #include "appconf/appconf.h" #include "user.h" #include "magic.h" #include #include #include struct network_server { const char *network, *server; }; static int server_socket_error(struct socket *socket, enum socket_event_type type, struct server *s); static int server_socket_connect(struct socket *socket, enum socket_event_type type, struct server *s); static int server_socket_read(struct socket *socket, enum socket_event_type type, struct server *s); static int server_call_command_handlers(struct server *s, char *command, int argc, char *argv[]); static int server_call_command_handler(struct list *l, struct server_command_handler *h, struct server_command_msg *msg); static int server_call_code_handlers(struct server *s, int code, int argc, char *argv[]); static int server_call_code_handler(struct list *l, struct server_code_handler *h, struct server_code_msg *msg); static int split_irc_line(const char *line, int *argc, char *argv[],int maxp); static int server_default_code_handler(struct server *s, int code, int argc, char *argv[], void *appdata); static int server_default_command_handler(struct server *s, const char *command, int argc, char *argv[], void *appdata); static int server_callfunc(struct list *list, struct server *server, server_foreach_func func); static int server_event_filter(struct eventmsg *msg, void *filter); extern int server_reg_internal(struct server *s); static int server_find_network_server_comp(struct network_server *ns, struct server *s); static struct conf_value *server_find_value( const char *network, const char *server, const char *setting); void server_destroy(struct server *s); void server_dump(struct server *s); static int server_connect_next_server(struct server *s); static int server_connect_network_server(struct server *s, const char *network, const char *server); static int server_load_cfg(); MODULE_NAME("server"); MODULE_INIT(server_init); MODULE_DESTROY(server_unload); MODULE_DEPENDS("socket","appconf"); MODULE_REGISTER(server); /* This string is just a match. The contents aren't used, and don't matter * and should be ignored */ const char *SERVER_TRIGGER_MYNICK={"\x00\n\xFFMYNICK\xFF\n"}; static struct list *server_list; DEBUG server_debug; POOL server_pool; struct eventobj *server_eventobj; enum server_use_ip { server_use_ip_hostname, server_use_ip_default_route, server_use_ip_from_server, server_use_ip_manual }; struct conf_enum_list server_valid_use_ip[] = { { .num = server_use_ip_hostname, .str = "hostname" }, { .num = server_use_ip_default_route, .str = "default route" }, { .num = server_use_ip_from_server, .str = "from server" }, { .num = server_use_ip_manual, .str = "manual" } }; /* Default settings */ char **default_nicks; char *default_info; char *default_username; int server_init() { server_pool = pool_new(NULL); server_debug = debug_register("server"); server_list = list_create(server_pool); server_eventobj = event_create_obj(server_pool,NULL); event_set_filter_func(server_eventobj,server_event_filter); /* Load default settings */ return server_load_cfg(); } static int server_load_cfg() { struct conf_value *cv; const char *ccp; cv = conf_get_value_str(server_pool, "/server/nicks"); if (cv == NULL) { conf_missing(server_debug,"/server/nicks"); return -1; } if (conf_value_to_strarray(server_pool, cv, &default_nicks) == -1) { conf_invalid(server_debug,"/server/nicks",conf_type_strarray); return -1; } debug(server_debug,"Default nicks: "); conf_debug_strarray(server_debug,default_nicks); cv = conf_get_value_str(server_pool, "/server/info"); if (cv == NULL) { conf_missing(server_debug,"/server/info"); return -1; } ccp = conf_value_to_str_const(cv); if (ccp == NULL) { conf_invalid(server_debug,"/server/info",conf_type_str); return -1; } default_info = pstrdup(server_pool,ccp); debug(server_debug,"Default info: %s", default_info); cv = conf_get_value_str(server_pool, "/server/username"); if (cv == NULL) { conf_missing(server_debug,"/server/username"); return -1; } ccp = conf_value_to_str_const(cv); if (ccp == NULL) { conf_invalid(server_debug,"/server/username",conf_type_str); return -1; } default_username = pstrdup(server_pool,ccp); debug(server_debug,"Default username: %s", default_username); return 0; } int server_unload() { return -1; } struct eventobj *server_get_event_obj() { return server_eventobj; } static int server_event_filter(struct eventmsg *msg, void *filter) { struct server *s1,*s2; s1 = (struct server *) filter; s2 = (struct server *) msg->msg; if (s1 == s2) return 0; return -1; } int server_find_network_server_comp(struct network_server *ns, struct server *s) { // ASSERT(is_magic(s,SERVER_MAGIC)); if (!s) return -1; if (ns->server != NULL && strcasecmp(ns->server,s->servername) != 0) return -1; if (strcasecmp(ns->network,s->network) == 0) return 0; return -1; } struct server *server_find_network_server(const char *network, const char *server) { struct network_server ns; ns.network = network; ns.server = server; return list_find(server_list, &ns, (list_compare_t)server_find_network_server_comp); } struct conf_value *server_find_value( const char *network, const char *server, const char *setting) { struct conf_value *cv; cv = conf_get_value(server_pool, "/server/%s/%s/%s", network,server ? server : "" ,setting); //debug(server_debug,"Fetching /server/%s/%s/%s => %s", // network,server,setting, // cv ? conf_value_to_str_const(cv) : ""); if (cv) return cv; cv = conf_get_value(server_pool, "/server/%s/%s", network,setting); //debug(server_debug,"Fetching /server/%s/%s => %s", // network,setting, // cv ? conf_value_to_str_const(cv) : ""); if (cv) return cv; cv = conf_get_value(server_pool, "/server/%s", setting); //debug(server_debug,"Reading conf /server/%s => %s", // setting, // cv ? conf_value_to_str_const(cv) : ""); return cv; } struct server *server_open(const char *network, const char *server) { struct server *s; char **nicks_array,*nick; const char *ccp; struct conf_value *cv; s = server_create(); if (s == NULL) return NULL; /* See if we can find the configuration stuff for this network & * server */ /* Get the nicks array */ cv = server_find_value(network,server,"nicks"); if (cv != NULL) { if (conf_value_to_strarray(server_pool, cv, &nicks_array) == -1) { error(server_debug,"The specified nicks list " "doesn't seem to be valid!"); server_destroy(s); return NULL; } nick = nicks_array[0]; } else { nick = default_nicks[0]; } s->my_nick = pstrdup(s->pool,nick); cv = server_find_value(network,server,"username"); if (cv != NULL && (ccp = conf_value_to_str_const(cv)) != NULL) { /* Do nothing */ } else { ccp = default_username; } s->my_username = pstrdup(s->pool,ccp); cv = server_find_value(network,server,"info"); if (cv != NULL && (ccp = conf_value_to_str_const(cv)) != NULL) { /* Do nothing */ } else { ccp = default_info; } s->my_info = pstrdup(s->pool,ccp); s->network = pstrdup(s->pool,network); /* Ok, so we've setup the server with the configured stuff, * except for the IP and ports, etc */ if (server == NULL || *server == 0) { /* Ok, they want to connect to any server on a given network */ s->network_hunt_id = 1; if (server_connect_next_server(s) == -1) { return NULL; } return s; } s->network = pstrdup(s->pool,network); s->network_hunt_id = -1; if (server_connect_network_server(s,network,server) == -1) { server_raise_event(s,server_event_error); server_destroy(s); return NULL; } return s; } int server_connect_next_server(struct server *s) { struct conf_group *cgnetwork,*cg; struct conf_value *cv; if (s->network_hunt_id == -1) { /* Oops, we don't have another server to try */ return -1; } /* Check the socket is in a valid state */ if (socket_is_valid(s->socket) == -1) { if (s->socket) socket_destroy(s->socket); s->state = server_state_disconnected; s->socket = socket_create(socket_type_tcp); socket_set_buffer_size(s->socket,1024); } /* Find the next server entry in the network config after * network_hunt_id */ cv = conf_get_value(s->pool,"/server/%s",s->network); if (cv == NULL || (cgnetwork = conf_value_to_group(cv)) == NULL) { error(server_debug,"Error, expecing configuration entry " "/server/%s to be a group, not a value!", s->network); server_raise_event(s,server_event_error); server_destroy(s); return -1; } while(1) { cv = conf_group_value_n(s->pool,cgnetwork,s->network_hunt_id); s->network_hunt_id++; if (cv == NULL) { break; } cg = conf_value_to_group(cv); if (cg == NULL) { continue; } info(server_debug,"network hunt trying server %s", conf_group_name(cg)); if (server_connect_network_server(s,s->network, conf_group_name(cg)) == -1) { continue; } return 0; } error(server_debug,"Can't connect to network %s", s->network); return -1; } int server_connect_network_server(struct server *s, const char *network, const char *server) { struct conf_value *cv; const char *ccp; char **ports_array; cv = conf_get_value(s->pool,"/server/%s/%s/host", network,server); if (cv == NULL || (ccp = conf_value_to_str_const(cv)) == NULL) { error(server_debug,"The required configuration entry " "/server/%s/%s/host is missing.", network,server); return -1; } s->host = pstrdup(s->pool,ccp); cv = conf_get_value(s->pool,"/server/%s/%s/ports", network,server); if (cv == NULL || (ccp = conf_value_to_str_const(cv)) == NULL) { error(server_debug,"The required configuration entry " "/server/%s/%s/ports is missing.", network,server); return -1; } if (conf_value_to_strarray(s->pool, cv, &ports_array) == -1) { error(server_debug,"The configuration entry " "/server/%s/%s/ports is invalid. Expecting " "a comma delimited list of integers", network,server); return -1; } ccp = strarray_random(ports_array); if (ccp == NULL || !isdigit(*ccp) || strtol(ccp,NULL,0) < 0) { error(server_debug,"The configuration entry " "/server/%s/%s/ports is invalid. Expecting " "a comma delimited list of integers", network,server); return -1; } s->port = strtol(ccp,NULL,0); s->servername = pstrdup(s->pool, server); s->state = server_state_connecting; socket_set_callback(s->socket, socket_event_connect, (socket_callback_t) server_socket_connect, s); socket_set_callback(s->socket, socket_event_error, (socket_callback_t) server_socket_error, s); server_dump(s); if (socket_connect_to(s->socket, s->host, s->port) == -1) { return -1; } return 0; } void server_dump(struct server *s) { debug(server_debug,"server: host: %s:%d my_nick: %s my_info: %s my_username: %s", s->host,s->port, s->my_nick,s->my_info,s->my_username); debug(server_debug," my_ip: %s state: %d", s->my_ip, s->state); } struct server *server_create() { POOL p; struct server *s; p = pool_new(server_pool); s = XMALLOC(p,struct server); s->pool = p; s->state = server_state_disconnected; s->socket = socket_create(socket_type_tcp); socket_set_buffer_size(s->socket,1024); s->module_list = str_hash_create_n(s->pool,10); s->command_handler_list = str_hash_create_n(s->pool,30); s->code_handler_list = num_hash_create_n(s->pool,100); server_set_default_code_handler(s, 0, server_default_code_handler,NULL); server_set_default_command_handler(s, NULL, server_default_command_handler,NULL); server_reg_internal(s); list_add_end(server_list,s); server_raise_event(s,server_event_new); return s; }; void server_destroy(struct server *s) { warn(server_debug,"Destroying server!"); server_dump(s); if (list_find_data(server_list,s)) { list_del_current(server_list); } else { error(server_debug,"Destroy server not in list?!?!"); return; } event_del_listener_by_data(user_get_event_obj(),user_event_destroy, NULL,server_me_destroyed, s); server_raise_event(s,server_event_disconnected); socket_destroy(s->socket); s->socket = NULL; /* TODO Find callbacks, get rid of them */ pool_destroy(s->pool); } POOL server_get_pool(struct server *s) { return s->pool; } int server_callfunc(struct list *list, struct server *server, server_foreach_func func) { return func(server); } int server_foreach(server_foreach_func func) { return list_foreach(server_list, (list_callback_t) server_callfunc, func); } int server_setnick(struct server *s,const char *nick) { if (s->state == server_state_disconnected) { s->my_nick = pstrdup(s->pool,nick); return 0; } server_send(s,"NICK %s",nick); return 0; } int server_me_changed_nick(struct eventmsg *em) { struct user_event_msg *uem; struct server *s; uem = (struct user_event_msg *)em->msg; ASSERT(is_magic(uem, MAGIC_USER_EVENT_MSG)); s = uem->server; s->my_nick = pstrdup(s->pool,user_get_nick(uem->user)); return 0; } int server_me_destroyed(struct eventmsg *em) { struct user_event_msg *uem; struct server *s; uem = (struct user_event_msg *)em->msg; ASSERT(is_magic(uem, MAGIC_USER_EVENT_MSG)); s = uem->server; error(server_debug,"Server %s: ARG! My user record has been " "destroyed!",s->servername); server_destroy(s); return 0; } const char *server_getnick(struct server *s) { return s->my_nick; } int server_setinfo(struct server *s,const char *info) { if (s->state != server_state_disconnected) { error(server_debug,"server->setinfo() when not disconnected"); return -1; } s->my_info = pstrdup(s->pool,info); return 0; } int server_socket_error(struct socket *socket, enum socket_event_type type, struct server *s) { error(server_debug,"Server %s: Socket error occured", s->servername); switch(s->state) { case server_state_connecting: error(server_debug,"Error connecting to host"); server_connect_next_server(s); break; default: /* TODO If reconnect on disconnect, then reconnect */ server_destroy(s); } return 0; } int server_socket_connect(struct socket *socket, enum socket_event_type type, struct server *s) { const char *nick,*username,*userinfo; socket_set_callback(s->socket, socket_event_read, (socket_callback_t) server_socket_read, s); notice(server_debug,"Connected to host"); if (s->my_nick) nick = s->my_nick; else { nick = conf_value_to_str_const(conf_get_value( server_pool,"server/nicks")); if (nick == NULL) nick = default_nicks[0]; s->my_nick = pstrdup(s->pool,nick); } if (s->my_info) userinfo = s->my_info; else { userinfo = default_info; if (userinfo == NULL) userinfo = "IRCfs"; s->my_info = pstrdup(s->pool,userinfo); } username = conf_value_to_str_const( conf_get_value(server_pool,"server/username")); if (username == NULL) username = nick; if (server_send(s,"NICK %s",nick) == -1) return 0; server_send(s,"USER %s 0 0 :%s",username,userinfo); return 0; } int server_connect(struct server *s,const char *host, unsigned short port) { if (s->state != server_state_disconnected) { error(server_debug,"server->connect() when not disconnected"); return -1; } s->host = pstrdup(s->pool,host); s->port = port; s->state = server_state_connecting; socket_set_callback(s->socket, socket_event_connect, (socket_callback_t) server_socket_connect, s); socket_connect_to(s->socket, host, port); return 0; } const char *server_get_host(struct server *s) { return s->host; } unsigned short server_get_port(struct server *s) { return s->port; } int server_connected(struct server *s) { return s->state == server_state_connected ? 1 : 0; } void *server_set_module_data(struct server *s, const char *module_name, void *data) { void *old=NULL; if (str_hash_set(s->module_list,module_name, data,&old) != -1) return old; return NULL; } void *server_get_module_data(struct server *s, const char *module_name) { return str_hash_get(s->module_list,module_name); } int server_set_default_code_handler(struct server *s, int code, server_code_handler_func func, void *appdata) { struct server_code_handler *ch; struct server_code_handler_list *chl=NULL; if (code != 0) { chl = num_hash_get(s->code_handler_list,code); if (chl == NULL) { chl = XMALLOC(s->pool, struct server_code_handler_list); chl->code = code; chl->handlers = list_create(s->pool); chl->defhandler = NULL; num_hash_add(s->code_handler_list,code,chl); } } ch = XMALLOC(s->pool, struct server_code_handler); ch->code = code; ch->func = func; ch->appdata = appdata; if (code) chl->defhandler = ch; else s->def_code_handler = ch; return 0; } int server_set_code_handler(struct server *s, int code, int parm_filter_num, const char *parm_filter, server_code_handler_func func, void *appdata) { struct server_code_handler *ch; struct server_code_handler_list *chl; chl = num_hash_get(s->code_handler_list,code); if (chl == NULL) { chl = XMALLOC(s->pool, struct server_code_handler_list); chl->code = code; chl->handlers = list_create(s->pool); chl->defhandler = NULL; num_hash_add(s->code_handler_list,code,chl); } ch = XMALLOC(s->pool, struct server_code_handler); ch->code = code; if (parm_filter == SERVER_TRIGGER_MYNICK) ch->parm_filter = SERVER_TRIGGER_MYNICK; else if (parm_filter) ch->parm_filter = pstrdup(s->pool,parm_filter); else ch->parm_filter = NULL; ch->parm_filter_num = parm_filter_num; ch->func = func; ch->appdata = appdata; list_add_end(chl->handlers,ch); return 0; } int server_del_code_handler(struct server *s, int code, const char *first_parm_filter, server_code_handler_func func); int server_set_command_handler(struct server *s, const char *command, int parm_filter_num, const char *parm_filter, server_command_handler_func func, void *appdata) { struct server_command_handler *ch; struct server_command_handler_list *chl; ASSERT(command); chl = str_hash_get(s->command_handler_list,command); if (chl == NULL) { chl = XMALLOC(s->pool, struct server_command_handler_list); chl->command = pstrdup(s->pool,command); chl->handlers = list_create(s->pool); chl->defhandler = NULL; str_hash_add(s->command_handler_list,command,chl); } ch = XMALLOC(s->pool, struct server_command_handler); ch->command = pstrdup(s->pool,command); if (parm_filter == SERVER_TRIGGER_MYNICK) ch->parm_filter = SERVER_TRIGGER_MYNICK; else if (parm_filter) ch->parm_filter = pstrdup(s->pool,parm_filter); else ch->parm_filter = NULL; ch->parm_filter_num = parm_filter_num; ch->func = func; ch->appdata = appdata; list_add_end(chl->handlers,ch); return 0; } int server_set_default_command_handler(struct server *s, const char *command, server_command_handler_func func, void *appdata) { struct server_command_handler *ch; struct server_command_handler_list *chl=NULL; if (command) { chl = str_hash_get(s->command_handler_list,command); if (chl == NULL) { chl = XMALLOC(s->pool, struct server_command_handler_list); chl->command = pstrdup(s->pool,command); chl->handlers = list_create(s->pool); chl->defhandler = NULL; str_hash_add(s->command_handler_list,command,chl); } } ch = XMALLOC(s->pool, struct server_command_handler); ch->command = command ? pstrdup(s->pool,command) : NULL; ch->func = func; ch->appdata = appdata; if (command) chl->defhandler = ch; else s->def_command_handler = ch; return 0; } int server_socket_read(struct socket *socket, enum socket_event_type type, struct server *s) { static char line[1024]; char *argv[10],*command,*cp; int argc,code; int r; // debug(server_debug,"Got socket read"); r = 1; while(r != 0) { r = socket_readline(s->socket, line,1024); if (r == -1) { error(server_debug,"Socket error!"); server_raise_event(s,server_event_disconnected); return 0; } else if (r == 0 && socket_get_error(s->socket) == socket_err_closed) { /* Socket closed! */ server_destroy(s); break; } else if (r == 0) { //debug(server_debug,"Surpurflous read event"); break; } cp = strchr(line,'\r'); if (cp) *cp = 0; cp = strchr(line,'\n'); if (cp) *cp = 0; //debug(server_debug,"Got line: %s",line); if (split_irc_line(line,&argc, argv,9) == -1 || argc < 1) { error(server_debug,"Failed to parse irc line %s", line); } else { command=argv[1]; if (isdigit(*command)) { /* Numeric */ code = strtol(command,NULL,10); server_call_code_handlers(s,code, argc, argv); } else { server_call_command_handlers(s,command, argc,argv); } } } return 0; } int server_call_code_handler(struct list *l, struct server_code_handler *h, struct server_code_msg *msg) { const char *p, *matchstr; if (!h->func) return 0; if (h->parm_filter && h->parm_filter_num <= msg->argc) { p = msg->argv[h->parm_filter_num]; if (h->parm_filter == SERVER_TRIGGER_MYNICK) matchstr = msg->s->my_nick; else matchstr = h->parm_filter; if (strcasecmp(p,matchstr) != 0) return 0; } else if (h->parm_filter && h->parm_filter_num > msg->argc) { /* Match Num too high */ return 0; } msg->count++; h->func(msg->s,msg->code, msg->argc, msg->argv, h->appdata); return 0; } int server_call_code_handlers(struct server *s, int code, int argc, char *argv[]) { struct server_code_handler_list *hl; struct server_code_msg msg; msg.s = s; msg.code = code; msg.argc = argc; msg.argv = argv; msg.count = 0; hl = num_hash_get(s->code_handler_list,code); if (hl == NULL) { if (s->def_code_handler && s->def_code_handler->func) { s->def_code_handler->func(s,msg.code,msg.argc, msg.argv, NULL); return 0; } return -1; } list_foreach(hl->handlers, (list_callback_t) server_call_code_handler, &msg); if (msg.count == 0 && hl->defhandler && hl->defhandler->func) { /* Default */ hl->defhandler->func(s,msg.code, msg.argc, msg.argv, NULL); return 0; } else if (msg.count == 0) { if (s->def_code_handler && s->def_code_handler->func) { s->def_code_handler->func(s,msg.code,msg.argc, msg.argv,NULL); return 0; } return -1; } return 0; } int server_call_command_handler(struct list *l, struct server_command_handler *h, struct server_command_msg *msg) { const char *p,*matchstr; if (!h->func) { return 0; } if (h->parm_filter && h->parm_filter_num <= msg->argc) { p = msg->argv[h->parm_filter_num]; if (h->parm_filter == SERVER_TRIGGER_MYNICK) matchstr = msg->s->my_nick; else matchstr = h->parm_filter; if (strcasecmp(p,matchstr) != 0) return 0; } else if (h->parm_filter && h->parm_filter_num > msg->argc) { /* Match Num too high */ return 0; } msg->count++; h->func(msg->s,msg->command, msg->argc, msg->argv, h->appdata); return 0; } int server_call_command_handlers(struct server *s, char *command, int argc, char *argv[]) { struct server_command_handler_list *hl; struct server_command_msg msg; msg.s = s; msg.command = command; msg.argc = argc; msg.argv = argv; msg.count = 0; hl = str_hash_get(s->command_handler_list,command); if (hl == NULL) { if (s->def_command_handler && s->def_command_handler->func) { s->def_command_handler->func(s,msg.command,msg.argc, msg.argv,NULL); return 0; } return -1; } list_foreach(hl->handlers, (list_callback_t) server_call_command_handler, &msg); if (msg.count == 0 && hl->defhandler && hl->defhandler->func) { /* Default */ hl->defhandler->func(s,msg.command, msg.argc, msg.argv, NULL); return 0; } else if (msg.count == 0) { if (s->def_command_handler && s->def_command_handler->func) { s->def_command_handler->func(s,msg.command,msg.argc, msg.argv,NULL); return 0; } return -1; } return 0; } int server_raise_event(struct server *s, enum server_events event) { return event_raise(server_eventobj, event, s); } int split_irc_line(const char *line, int *argc, char *argv[], int maxp) { static char temp[1024]; char *cp,*cpword; int i=1; strncpy(temp,line,1024); //debug(server_debug,"split_irc_line(%s)",line); cp = temp; argv[0] = NULL; if (*cp == ':') { argv[0] = cp+1; cpword = skip_to_whitespace(cp); if (*cpword) { *cpword = 0; cpword++; cpword = skip_whitespace(cpword); } cp = cpword; } while(*cp) { if (*cp == ':') { argv[i] = ++cp; i++; break; } if (i == maxp-1) { argv[i] = cp; i++; break; } argv[i] = cp; i++; cpword = skip_to_whitespace(cp); if (*cpword && i == 2) { /* There is always an exception to the rule, and * the throttling code in the undernet ircd * is it. It generates errors as: * ERROR: * Instead of: * ERROR : * stupid, but now we have to support it. * So in this buggy case, we generate * argv[0] = NULL * argv[1] = "ERROR:" * argv[2] = "" */ if (cpword != cp && *(cpword-1) == ':') { /* Ok, its a match */ cp = skip_whitespace(cpword); *cpword = 0; argv[i] = cp; i++; break; } } if (*cpword) { *cpword = 0; cpword++; cpword = skip_whitespace(cpword); } cp = cpword; } argv[i] = NULL; *argc = i-1; return *argc; } int server_send(struct server *s, const char *format, ...) { static char outbuffer[1024],*cp; va_list va; size_t len; ASSERT(s); len = sizeof(outbuffer)-1; cp = outbuffer; va_start(va,format); vsnprintf(cp,len,format,va); va_end(va); len = strlen(outbuffer); if (len > sizeof(outbuffer)-4) { len = sizeof(outbuffer)-4; } info(server_debug,"Send: %s",outbuffer); outbuffer[len++]='\r'; outbuffer[len++]='\n'; socket_write(s->socket,outbuffer,len); return 0; } static int server_default_code_handler(struct server *s, int code, int argc, char *argv[], void *appdata) { static char outbuffer[80],*cp; size_t len; int i; cp = outbuffer; *cp = 0; if (argv[0]) { snprintf(outbuffer,sizeof(outbuffer)-5,":%s ", argv[0]); outbuffer[sizeof(outbuffer)-1] = 0; } len = strlen(outbuffer); snprintf(outbuffer+len,sizeof(outbuffer)-5-len,"%03d", code); outbuffer[sizeof(outbuffer)-1] = 0; len = strlen(outbuffer); for(i = 2; i <= argc; i++) { snprintf(outbuffer + len, sizeof(outbuffer)-len-5, " %s%s", i == argc ? ":" : "",argv[i]); outbuffer[sizeof(outbuffer)-1] = 0; len = strlen(outbuffer); } info(server_debug,outbuffer); return 0; } static int server_default_command_handler(struct server *s, const char *command, int argc, char *argv[],void *appdata) { static char outbuffer[513],*cp; size_t len; int i; cp = outbuffer; debug(server_debug,"Msg: 0]%s 1]%s 2]%s", argv[0], argv[1], argv[2]); snprintf(outbuffer,sizeof(outbuffer)-1,"%s%s%s%s", argv[0] ? ":" : "", argv[0] ? argv[0] : "", argv[0] ? " " : "", command); len = strlen(outbuffer); for(i = 2; i <= argc; i++) { snprintf(outbuffer + len, (sizeof(outbuffer)-1)-len, " %s%s", i == argc ? ":" : "",argv[i]); len = strlen(outbuffer); } info(server_debug,outbuffer); return 0; } int is_nickchar(char ch) { /* '-' | '[' | ']' | '\' | '`' | '^' | '{' | '}' */ switch(ch) { case '+': case '@': case '!': case '&': case '*': case '%': return 0; } if (((unsigned char)ch) > 32) return 1; return 0; } unsigned long server_get_my_addr_long(struct server *s) { struct conf_value *cv; int setting=0; struct in_addr ip; cv = conf_get_value(server_pool,"server/my_ip"); if (!cv || conf_value_to_enum(cv,&setting,server_valid_use_ip) == -1) { error(server_debug,"Failed to understand server/my_ip " "config setting"); return 0; } switch(setting) { case server_use_ip_hostname: return arch_get_hostname_addr(); case server_use_ip_default_route: return arch_get_default_route_addr(); case server_use_ip_manual: cv = conf_get_value(server_pool,"server/manual_ip"); if (cv == NULL) { error(server_debug,"Missing manual IP address"); return 0; } if (inet_aton(conf_value_to_str_const(cv),&ip) == -1) { error(server_debug,"Failed to parse manual IP address"); return 0; } return ip.s_addr; case server_use_ip_from_server: /* TODO Use IRC Servers idea of our address */ } return 0; } const char *server_get_name(struct server *s) { ASSERT(s); return s->servername; } int server_is_connected(struct server *s) { ASSERT(s); if (s->state == server_state_connected) return 0; return -1; } struct user *server_get_me(struct server *s) { ASSERT(s); return s->me; }