1
0
forked from bip/bip

Merge branch 'master' of http://bip.t1r.net/bip

Conflicts:

	src/bip.c
This commit is contained in:
Loc Gomez 2007-11-25 16:12:36 +01:00
commit b68b0dc71d
16 changed files with 564 additions and 237 deletions

5
NEWS
View File

@ -1,3 +1,8 @@
29-10-2007: Certificate validation change. In "basic" mode, expired
certificates are now accepted as long as they are in store (therefore trusted).
This makes the basic mode be more SSH like. Some extreme security zealots might
want to be warned.
02-09-2007: as of now log parameters go in the user {} statment. This brakes
every config and there is no backwrads compatibility as of now.
Lots of internal changes, expect crashes.

1
TODO
View File

@ -1,3 +1,4 @@
- check conn_list usage: esp wrt list_remove and closes
- uid, gid
- keep invites when detached ?
- allow global (or per net ?) IP filtering

View File

@ -226,7 +226,12 @@ The password. It \fBMUST\fP be generated with \fBbimkpw\fP or it'll not work.
Tells whether BIP should check the server SSL certificate and against what.
Can be \fBnone\fP for no check at all, \fBca\fP to check if the cert is signed
by a Certificate Authority in repository, or \fBbasic\fP to check if cert
exists in repository. The repository is defined by \fBssl_check_store\fP.
exists in repository. The repository is defined by \fBssl_check_store\fP. This
allows a "ssh-like" private key generation scheme. Note that in basic mode:
.br
- expired certificates that are in the store are considered valid.
.br
- CA-signed certificates are considered valid even if not in store.
.TP
\fBssl_check_store\fP (default: \fBnot set\fP)

View File

@ -37,7 +37,7 @@ log_level = 3;
# %m -> 2 digit month
# %d -> 2 digit day
# %c -> destination (#chan, privates, ...)
#log_format = "%n/%Y-%m/%c.%d.log";
#log_format = "%u/%n/%Y-%m/%c.%d.log";
# Sets the frequency (in seconds) of log syncing (real write to kernel)
#log_sync_interval = 5;

13
scripts/bip-take-snapshot Executable file
View File

@ -0,0 +1,13 @@
#!/bin/sh
set -e
prefix=bip-$(date +%y%m%d)
if [ ! -d src ] ; then
echo "Please run me in bip sources root." >&2
exit 1
fi
git-archive --format=tar --prefix=$prefix/ HEAD | gzip -c > ../$prefix.tar.gz

399
src/bip.c
View File

@ -17,6 +17,7 @@
#include <unistd.h>
#include <signal.h>
#include <stdarg.h>
#include <string.h>
#include <sys/time.h>
#include <sys/resource.h>
#include "irc.h"
@ -48,7 +49,7 @@ int conf_log = DEFAULT_LOG;
int conf_log_system = DEFAULT_LOG_SYSTEM;
int conf_log_sync_interval = DEFAULT_LOG_SYNC_INTERVAL;
list_t *parse_conf(FILE *file);
list_t *parse_conf(FILE *file, int *err);
static void conf_die(char *fmt, ...);
#ifdef HAVE_LIBSSL
int adm_trust(struct link_client *ic, struct line *line);
@ -56,6 +57,7 @@ int adm_trust(struct link_client *ic, struct line *line);
static char *get_tuple_value(list_t *tuple_l, int lex);
void bip_notify(struct link_client *ic, char *fmt, ...);
void adm_list_connections(struct link_client *ic, struct user *bu);
void free_conf(list_t *l);
static void hash_binary(char *hex, unsigned char **password, unsigned int *seed)
{
@ -94,7 +96,7 @@ static int add_server(struct server *s, list_t *data)
while ((t = list_remove_first(data))) {
switch (t->type) {
case LEX_HOST:
s->host = t->pdata;
MOVE_STRING(s->host, t->pdata);
break;
case LEX_PORT:
s->port = t->ndata;
@ -102,6 +104,9 @@ static int add_server(struct server *s, list_t *data)
default:
fatal("Config error in server block (%d)", t->type);
}
if (t->tuple_type == TUPLE_STR && t->pdata)
free(t->pdata);
free(t);
}
if (!s->host) {
free(s);
@ -115,23 +120,12 @@ static int add_server(struct server *s, list_t *data)
extern list_t *root_list;
int yyparse();
int conf_error;
char conf_errstr[ERRBUFSZ];
static void conf_start(void)
{
conf_error = 0;
}
static void conf_die(char *fmt, ...)
{
va_list ap;
va_start(ap, fmt);
vsnprintf(conf_errstr, ERRBUFSZ, fmt, ap);
conf_errstr[ERRBUFSZ - 1] = 0;
conf_error = 1;
_mylog(LOG_ERROR, fmt, ap);
va_end(ap);
}
@ -307,8 +301,9 @@ static int add_network(bip_t *bip, list_t *data)
struct tuple *t;
struct network *n;
int i;
int r;
char *name = get_tuple_value(data, LEX_NAME);
char *name = get_tuple_pvalue(data, LEX_NAME);
if (name == NULL) {
conf_die("Network with no name");
@ -340,29 +335,103 @@ static int add_network(bip_t *bip, list_t *data)
n->serverv = realloc(n->serverv, (n->serverc + 1)
* sizeof(struct server));
n->serverc++;
add_server(&n->serverv[n->serverc - 1], t->pdata);
memset(&n->serverv[n->serverc - 1], 0,
sizeof(struct server));
r = add_server(&n->serverv[n->serverc - 1], t->pdata);
if (!r) {
n->serverc--;
return 0;
}
free(t->pdata);
t->pdata = NULL;
break;
default:
conf_die("unknown keyword in network statement");
if (t->type == TUPLE_STR)
free(t->pdata);
return 0;
break;
}
if (t->type == TUPLE_STR && t->pdata)
if (t->tuple_type == TUPLE_STR && t->pdata)
free(t->pdata);
free(t);
}
return 1;
}
void adm_bip_delconn(bip_t *bip, struct link_client *ic, char *conn_name)
{
struct user *user = LINK(ic)->user;
struct link *l;
if (!hash_get(&user->connections, conn_name)) {
adm_reply(ic, "cannot find this connection");
return;
}
l = hash_get(&user->connections, conn_name);
link_kill(bip, l);
adm_reply(ic, "deleted");
}
void adm_bip_addconn(bip_t *bip, struct link_client *ic, char *conn_name,
char *network_name)
{
struct user *user = LINK(ic)->user;
struct network *network;
/* check name uniqueness */
if (hash_get(&user->connections, conn_name)) {
adm_reply(ic, "connection name already exists for this user.");
return;
}
/* check we know about this network */
network = hash_get(&bip->networks, network_name);
if (!network) {
adm_reply(ic, "no such network name");
return;
}
struct link *l;
l = irc_link_new();
l->name = strdup(conn_name);
hash_insert(&user->connections, conn_name, l);
list_add_last(&bip->link_list, l);
l->user = user;
l->network = network;
l->log = log_new(user, conn_name);
#ifdef HAVE_LIBSSL
l->ssl_check_mode = user->ssl_check_mode;
l->untrusted_certs = sk_X509_new_null();
#endif
#define SCOPY(member) l->member = (LINK(ic)->member ? strdup(LINK(ic)->member) : NULL)
#define ICOPY(member) l->member = LINK(ic)->member
SCOPY(connect_nick);
SCOPY(username);
SCOPY(realname);
/* we don't copy server password */
SCOPY(vhost);
ICOPY(follow_nick);
ICOPY(ignore_first_nick);
SCOPY(away_nick);
SCOPY(no_client_away_msg);
/* we don't copy on_connect_send */
#ifdef HAVE_LIBSSL
ICOPY(ssl_check_mode);
#endif
#undef SCOPY
#undef ICOPY
adm_reply(ic, "connection added, you should soon be able to connect");
}
static int add_connection(bip_t *bip, struct user *user, list_t *data)
{
struct tuple *t, *t2;
struct link *l;
struct chan_info *ci;
char *name = get_tuple_value(data, LEX_NAME);
char *name = get_tuple_pvalue(data, LEX_NAME);
if (name == NULL) {
conf_die("Connection with no name");
@ -380,7 +449,6 @@ static int add_connection(bip_t *bip, struct user *user, list_t *data)
l->untrusted_certs = sk_X509_new_null();
#endif
} else {
#warning "CODEME (user switch..)"
l->network = NULL;
log_reinit_all(l->log);
}
@ -416,8 +484,20 @@ static int add_connection(bip_t *bip, struct user *user, list_t *data)
MOVE_STRING(l->vhost, t->pdata);
break;
case LEX_CHANNEL:
ci = calloc(sizeof(struct chan_info), 1);
ci->backlog = 1;
name = get_tuple_pvalue(t->pdata, LEX_NAME);
if (name == NULL) {
conf_die("Channel with no name");
return 0;
}
ci = hash_get(&l->chan_infos, name);
if (!ci) {
ci = chan_info_new();
hash_insert(&l->chan_infos, name, ci);
/* FIXME: this order is not reloaded */
list_add_last(&l->chan_infos_order, ci);
ci->backlog = 1;
}
while ((t2 = list_remove_first(t->pdata))) {
switch (t2->type) {
@ -431,11 +511,11 @@ static int add_connection(bip_t *bip, struct user *user, list_t *data)
ci->backlog = t2->ndata;
break;
}
if (t2->tuple_type == TUPLE_STR && t2->pdata)
free(t2->pdata);
free(t2);
}
list_free(t->pdata);
hash_insert(&l->chan_infos, ci->name, ci);
list_add_last(&l->chan_infos_order, ci);
break;
case LEX_FOLLOW_NICK:
l->follow_nick = t->ndata;
@ -451,6 +531,7 @@ static int add_connection(bip_t *bip, struct user *user, list_t *data)
break;
case LEX_ON_CONNECT_SEND:
list_add_last(&l->on_connect_send, t->pdata);
t->pdata = NULL;
break;
#ifdef HAVE_LIBSSL
case LEX_SSL_CHECK_MODE:
@ -458,15 +539,13 @@ static int add_connection(bip_t *bip, struct user *user, list_t *data)
l->ssl_check_mode = SSL_CHECK_BASIC;
if (strcmp(t->pdata, "ca") == 0)
l->ssl_check_mode = SSL_CHECK_CA;
free(t->pdata);
break;
#endif
default:
conf_die("unknown keyword in connection statement");
if (t->type == TUPLE_STR)
free(t->pdata);
return 0;
}
if (t->type == TUPLE_STR && t->pdata)
if (t->tuple_type == TUPLE_STR && t->pdata)
free(t->pdata);
free(t);
}
@ -488,10 +567,12 @@ static int add_connection(bip_t *bip, struct user *user, list_t *data)
conf_die("No realname set and no default realname.");
l->realname = strdup(user->default_realname);
}
l->in_use = 1;
return 1;
}
static char *get_tuple_value(list_t *tuple_l, int lex)
static char *get_tuple_pvalue(list_t *tuple_l, int lex)
{
struct tuple *t;
list_iterator_t it;
@ -504,13 +585,35 @@ static char *get_tuple_value(list_t *tuple_l, int lex)
return NULL;
}
static int add_user(bip_t *bip, list_t *data)
static int get_tuple_nvalue(list_t *tuple_l, int lex)
{
struct tuple *t;
list_iterator_t it;
for (list_it_init(tuple_l, &it); (t = list_it_item(&it));
list_it_next(&it)) {
if (t->type == lex)
return t->ndata;
}
return -1;
}
struct historical_directives {
int always_backlog;
int backlog;
int bl_msg_only;
int backlog_lines;
int backlog_no_timestamp;
int blreset_on_talk;
};
static int add_user(bip_t *bip, list_t *data, struct historical_directives *hds)
{
int r;
struct tuple *t;
struct user *u;
char *name = get_tuple_value(data, LEX_NAME);
char *name = get_tuple_pvalue(data, LEX_NAME);
if (name == NULL) {
conf_die("User with no name");
@ -540,17 +643,25 @@ static int add_user(bip_t *bip, list_t *data)
#endif
}
u->backlog = hds->backlog;
u->always_backlog = hds->always_backlog;
u->bl_msg_only = hds->bl_msg_only;
u->backlog_lines = hds->backlog_lines;
u->backlog_no_timestamp = hds->backlog_no_timestamp;
u->blreset_on_talk = hds->blreset_on_talk;
while ((t = list_remove_first(data))) {
switch (t->type) {
case LEX_NAME:
MOVE_STRING(u->name, t->pdata);
break;
case LEX_ADMIN:
u->admin = t->ndata;
break;
case LEX_ADMIN:
u->admin = t->ndata;
break;
case LEX_PASSWORD:
hash_binary(t->pdata, &u->password, &u->seed);
free(t->pdata);
t->pdata = NULL;
break;
case LEX_DEFAULT_NICK:
MOVE_STRING(u->default_nick, t->pdata);
@ -585,6 +696,7 @@ static int add_user(bip_t *bip, list_t *data)
case LEX_CONNECTION:
r = add_connection(bip, u, t->pdata);
free(t->pdata);
t->pdata = NULL;
if (!r)
return 0;
break;
@ -595,16 +707,17 @@ static int add_user(bip_t *bip, list_t *data)
if (!strncmp(t->pdata, "ca", 2))
u->ssl_check_mode = SSL_CHECK_CA;
free(t->pdata);
t->pdata = NULL;
break;
case LEX_SSL_CHECK_STORE:
u->ssl_check_store = t->pdata;
MOVE_STRING(u->ssl_check_store, t->pdata);
break;
#endif
default:
conf_die("Uknown keyword in user statement");
break;
return 0;
}
if (t->type == TUPLE_STR && t->pdata)
if (t->tuple_type == TUPLE_STR && t->pdata)
free(t->pdata);
free(t);
}
@ -613,6 +726,7 @@ static int add_user(bip_t *bip, list_t *data)
return 0;
}
u->in_use = 1;
return 1;
}
@ -649,7 +763,6 @@ static int validate_config(bip_t *bip)
link->name);
#endif
//conf_die("user: ... net: ... can i has nick/user/rael");
r = 0;
for (hash_it_init(&link->chan_infos, &cit);
@ -672,21 +785,99 @@ static int validate_config(bip_t *bip)
}
}
if (strstr(conf_log_format, "\%u") == NULL)
mylog(LOG_WARN, "log_format doesn't contain \%u, all users'"
" logs will be mixed !");
return r;
}
void clear_marks(bip_t *bip)
{
list_iterator_t lit;
hash_iterator_t hit;
for (list_it_init(&bip->link_list, &lit); list_it_item(&lit);
list_it_next(&lit))
((struct link *)list_it_item(&lit))->in_use = 0;
for (hash_it_init(&bip->users, &hit); hash_it_item(&hit);
hash_it_next(&hit))
((struct user *)hash_it_item(&hit))->in_use = 0;
}
void user_kill(bip_t *bip, struct user *user)
{
(void)bip;
if (!hash_is_empty(&user->connections))
fatal("user_kill, user still has connections");
free(user->name);
free(user->password);
MAYFREE(user->default_nick);
MAYFREE(user->default_username);
MAYFREE(user->default_realname);
#ifdef HAVE_LIBSSL
MAYFREE(user->ssl_check_store);
#endif
free(user);
}
void sweep(bip_t *bip)
{
list_iterator_t lit;
hash_iterator_t hit;
for (list_it_init(&bip->link_list, &lit); list_it_item(&lit);
list_it_next(&lit)) {
struct link *l = ((struct link *)list_it_item(&lit));
if (!l->in_use) {
mylog(LOG_INFO, "Administratively killing %s/%s",
l->user->name, l->name);
link_kill(bip, l);
list_remove_if_exists(&bip->conn_list, l);
list_it_remove(&lit);
}
}
for (hash_it_init(&bip->users, &hit); hash_it_item(&hit);
hash_it_next(&hit)) {
struct user *u = (struct user *)hash_it_item(&hit);
if (!u->in_use) {
hash_it_remove(&hit);
user_kill(bip, u);
}
}
}
int fireup(bip_t *bip, FILE *conf)
{
int r;
struct tuple *t;
list_t *l;
int err = 0;
struct historical_directives hds;
conf_start();
l = parse_conf(conf);
if (conf_error)
clear_marks(bip);
parse_conf(conf, &err);
if (err) {
free_conf(root_list);
root_list = NULL;
return 0;
}
while ((t = list_remove_first(l))) {
#define SET_HV(d, n) do {\
int __gtv = get_tuple_nvalue(root_list, LEX_##n);\
if (__gtv != -1) \
d = __gtv;\
else\
d = DEFAULT_##n;\
} while(0);
SET_HV(hds.always_backlog, ALWAYS_BACKLOG);
SET_HV(hds.backlog, BACKLOG);
SET_HV(hds.bl_msg_only, BL_MSG_ONLY);
SET_HV(hds.backlog_lines, BACKLOG_LINES);
SET_HV(hds.backlog_no_timestamp, BACKLOG_NO_TIMESTAMP);
SET_HV(hds.blreset_on_talk, BLRESET_ON_TALK);
#undef SET_HV
while ((t = list_remove_first(root_list))) {
switch (t->type) {
case LEX_LOG_SYNC_INTERVAL:
conf_log_sync_interval = t->ndata;
@ -718,44 +909,41 @@ int fireup(bip_t *bip, FILE *conf)
case LEX_PID_FILE:
MOVE_STRING(conf_pid_file, t->pdata);
break;
case LEX_ALWAYS_BACKLOG:
hds.always_backlog = t->ndata;
break;
case LEX_BACKLOG:
hds.backlog = t->ndata;
break;
case LEX_BL_MSG_ONLY:
hds.bl_msg_only = t->ndata;
break;
case LEX_BACKLOG_LINES:
hds.backlog_lines = t->ndata;
break;
case LEX_BACKLOG_NO_TIMESTAMP:
hds.backlog_no_timestamp = t->ndata;
break;
case LEX_BLRESET_ON_TALK:
hds.blreset_on_talk = t->ndata;
break;
case LEX_NETWORK:
add_network(bip, t->pdata);
r = add_network(bip, t->pdata);
list_free(t->pdata);
if (!r)
goto out_conf_error;
break;
case LEX_USER:
add_user(bip, t->pdata);
r = add_user(bip, t->pdata, &hds);
list_free(t->pdata);
if (!r)
goto out_conf_error;
break;
#warning deprecated but we still need to support these
#if 0
case LEX_ALWAYS_BACKLOG:
conf_always_backlog = t->ndata;
break;
case LEX_BACKLOG:
conf_backlog = t->ndata;
break;
case LEX_BL_MSG_ONLY:
conf_bl_msg_only = t->ndata;
break;
case LEX_BACKLOG_LINES:
conf_backlog_lines = t->ndata;
break;
case LEX_BACKLOG_NO_TIMESTAMP:
conf_backlog_no_timestamp = t->ndata;
break;
case LEX_BLRESET_ON_TALK:
conf_blreset_on_talk = t->ndata;
break;
/* end of deprectated */
#endif
default:
conf_die("Config error in base config (%d)", t->type);
goto out_conf_error;
}
if (t->type == TUPLE_STR && t->pdata)
if (t->tuple_type == TUPLE_STR && t->pdata)
free(t->pdata);
free(t);
}
@ -763,14 +951,20 @@ int fireup(bip_t *bip, FILE *conf)
root_list = NULL;
validate_config(bip);
sweep(bip);
return 1;
out_conf_error:
free_conf(root_list);
root_list = NULL;
return 0;
}
static void log_file_setup(void)
{
char buf[4096];
if (conf_log_system) {
if (conf_log_system && conf_daemonize) {
if (conf_global_log_file && conf_global_log_file != stderr)
fclose(conf_global_log_file);
snprintf(buf, 4095, "%s/bip.log", conf_log_root);
@ -881,7 +1075,7 @@ int main(int argc, char **argv)
signal(SIGXCPU, rlimit_cpu_reached);
conf_log_root = NULL;
conf_log_format = DEFAULT_LOG_FORMAT;
conf_log_format = strdup(DEFAULT_LOG_FORMAT);
conf_log_level = DEFAULT_LOG_LEVEL;
conf_daemonize = 1;
conf_global_log_file = stderr;
@ -932,13 +1126,10 @@ int main(int argc, char **argv)
fatal("%s config file not found", confpath);
}
r = fireup(&bip, conf);
fclose(conf);
if (!r) {
fatal("%s", conf_errstr);
exit(28);
}
if (!r)
fatal("Not starting: error in config file.");
if (!conf_biphome) {
char *home = getenv("HOME");
@ -1000,9 +1191,6 @@ int main(int argc, char **argv)
fatal("Could not create listening socket");
for (;;) {
if (conf_error)
mylog(LOG_ERROR, "conf error: %s", conf_errstr);
irc_main(&bip);
sighup = 0;
@ -1641,11 +1829,13 @@ void adm_bip_help(struct link_client *ic, int admin)
"configuration");
bip_notify(ic, "/BIP LIST networks|users|connections|all_links"
"|all_connections");
bip_notify(ic, "/BIP ADD_CONN <connection name> <network>");
bip_notify(ic, "/BIP DEL_CONN <connection name>");
} else {
bip_notify(ic, "/BIP LIST networks|connections");
}
bip_notify(ic, "/BIP JUMP # jump to next server (in same network)");
bip_notify(ic, "/BIP BLRESET # reset backlog (this connection only)");
bip_notify(ic, "/BIP BLRESET # reset backlog (this connection only). Add -q flag and the operation is quiet.");
#ifdef HAVE_LIBSSL
bip_notify(ic, "/BIP TRUST # trust this server certificate");
#endif
@ -1659,14 +1849,15 @@ void adm_bip_help(struct link_client *ic, int admin)
bip_notify(ic, "/BIP AWAY_NICK # clear away nick");
}
int adm_bip(struct link_client *ic, struct line *line, unsigned int privmsg)
int adm_bip(bip_t *bip, struct link_client *ic, struct line *line,
unsigned int privmsg)
{
int admin = LINK(ic)->user->admin;
if (line->elemc < privmsg + 2)
return OK_FORGET;
mylog(LOG_STD, "/BIP %s from %s", line->elemv[privmsg + 1],
mylog(LOG_INFO, "/BIP %s from %s", line->elemv[privmsg + 1],
LINK(ic)->user->name);
if (strcasecmp(line->elemv[privmsg + 1], "RELOAD") == 0) {
if (!admin) {
@ -1736,7 +1927,12 @@ int adm_bip(struct link_client *ic, struct line *line, unsigned int privmsg)
}
bip_notify(ic, "-- Jumping to next server");
} else if (strcasecmp(line->elemv[privmsg + 1], "BLRESET") == 0) {
adm_blreset(ic);
if (line->elemc == privmsg + 3 &&
strcmp(line->elemv[privmsg + 2], "-q") == 0) {
log_reinit_all(LINK(ic)->log);
} else {
adm_blreset(ic);
}
} else if (strcasecmp(line->elemv[privmsg + 1], "HELP") == 0) {
adm_bip_help(ic, admin);
} else if (strcasecmp(line->elemv[privmsg + 1], "FOLLOW_NICK") == 0) {
@ -1771,6 +1967,22 @@ int adm_bip(struct link_client *ic, struct line *line, unsigned int privmsg)
bip_notify(ic, "-- AWAY_NICK command needs zero or one"
" argument");
}
} else if (admin &&
strcasecmp(line->elemv[privmsg + 1], "ADD_CONN") == 0) {
if (line->elemc != privmsg + 4) {
adm_reply(ic, "/BIP ADD_CONN <connection name> "
"<network name>");
} else {
adm_bip_addconn(bip, ic, line->elemv[privmsg + 2],
line->elemv[privmsg + 3]);
}
} else if (admin &&
strcasecmp(line->elemv[privmsg + 1], "DEL_CONN") == 0) {
if (line->elemc != privmsg + 3) {
adm_reply(ic, "/BIP DEL_CONN <connection name>");
} else {
adm_bip_delconn(bip, ic, line->elemv[privmsg + 2]);
}
#ifdef HAVE_LIBSSL
} else if (strcasecmp(line->elemv[privmsg + 1], "TRUST") == 0) {
return adm_trust(ic, line);
@ -1785,12 +1997,14 @@ void free_conf(list_t *l)
{
struct tuple *t;
list_iterator_t li;
for (list_it_init(l, &li); (t = list_it_item(&li)); list_it_next(&li)) {
switch (t->tuple_type) {
case TUPLE_STR:
free(t->pdata); /* no break, for the style */
printf("freeconf: %s\n", (char *)t->pdata);
free(t->pdata);
break;
case TUPLE_INT:
free(t);
break;
case TUPLE_LIST:
free_conf(t->pdata);
@ -1799,5 +2013,8 @@ void free_conf(list_t *l)
fatal("internal error free_conf");
break;
}
free(t);
}
free(l);
}

View File

@ -17,7 +17,8 @@
#ifdef HAVE_LIBSSL
int adm_trust(struct link_client *ic, struct line *line);
#endif
int adm_bip(struct link_client *ic, struct line *line, unsigned int privmsg);
int adm_bip(bip_t *bip, struct link_client *ic, struct line *line,
unsigned int privmsg);
int ssl_check_trust(struct link_client *ic);
void adm_blreset(struct link_client *ic);
void bip_notify(struct link_client *ic, char *fmt, ...);

View File

@ -19,9 +19,7 @@
extern int yylex (void);
extern char *yytext;
extern int linec;
extern int conf_error;
#define ERRBUFSZ 80
extern char conf_errstr[ERRBUFSZ];
int conf_error;
int yywrap()
{
@ -30,9 +28,7 @@ int yywrap()
int yyerror()
{
snprintf(conf_errstr, ERRBUFSZ, "Parse error near %s, line %d\n",
yytext, linec + 1);
conf_errstr[ERRBUFSZ - 1] = 0;
mylog(LOG_ERROR, "Parse error near %s, line %d\n", yytext, linec + 1);
conf_error = 1;
return 1;
}
@ -114,6 +110,23 @@ command:
| LEX_LOG_SYNC_INTERVAL LEX_EQ LEX_INT { $$ = tuple_i_new(
LEX_LOG_SYNC_INTERVAL, $3); }
| LEX_PID_FILE LEX_EQ LEX_STRING { $$ = tuple_s_new(LEX_PID_FILE, $3); }
/* deprecated */
| LEX_BACKLOG_LINES LEX_EQ LEX_INT {
$$ = tuple_i_new(LEX_BACKLOG_LINES, $3);
}
| LEX_BACKLOG_NO_TIMESTAMP LEX_EQ LEX_BOOL {
$$ = tuple_i_new(LEX_BACKLOG_NO_TIMESTAMP, $3);
}
| LEX_BACKLOG LEX_EQ LEX_BOOL { $$ = tuple_i_new(LEX_BACKLOG, $3); }
| LEX_BLRESET_ON_TALK LEX_EQ LEX_BOOL {
$$ = tuple_i_new(LEX_BLRESET_ON_TALK, $3);
}
| LEX_BL_MSG_ONLY LEX_EQ LEX_BOOL {
$$ = tuple_i_new(LEX_BL_MSG_ONLY, $3);
}
| LEX_ALWAYS_BACKLOG LEX_EQ LEX_BOOL { $$ = tuple_i_new(
LEX_ALWAYS_BACKLOG, $3); }
/* /deprecated */
| LEX_NETWORK LEX_LBRA network LEX_RBRA { $$ = tuple_l_new(LEX_NETWORK,
$3); }
| LEX_USER LEX_LBRA user LEX_RBRA { $$ = tuple_l_new(LEX_USER, $3); }

View File

@ -52,7 +52,7 @@ void connection_close(connection_t *cn)
{
mylog(LOG_DEBUG, "Connection close asked. FD:%d ",
(long)cn->handle);
if (cn->connected != CONN_DISCONN) {
if (cn->connected != CONN_DISCONN && cn->connected != CONN_ERROR) {
cn->connected = CONN_DISCONN;
if (close(cn->handle) == -1)
mylog(LOG_WARN, "Error on socket close: %s",
@ -873,7 +873,8 @@ static void create_socket(char *dsthostname, char *dstport, char *srchostname,
err = getaddrinfo(dsthostname, dstport, &hint, &cdata->dst);
if (err) {
mylog(LOG_ERROR, "getaddrinfo(dst): %s", gai_strerror(err));
mylog(LOG_ERROR, "getaddrinfo(%s): %s", dsthostname,
gai_strerror(err));
connecting_data_free(cdata);
cdata = NULL;
return;
@ -1010,8 +1011,10 @@ connection_t *accept_new(connection_t *cn)
mylog(LOG_DEBUG, "Trying to accept new client on %d", cn->handle);
err = accept(cn->handle, &sa, &sa_len);
if (err < 0)
if (err < 0) {
mylog(LOG_ERROR, "accept failed: %s", strerror(errno));
return NULL;
}
socket_set_nonblock(err);
conn = connection_init(cn->anti_flood, cn->ssl, cn->timeout, 0);
@ -1175,14 +1178,20 @@ static int bip_ssl_verify_callback(int preverify_ok, X509_STORE_CTX *ctx)
(err == X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY ||
err == X509_V_ERR_CERT_UNTRUSTED ||
err == X509_V_ERR_UNABLE_TO_VERIFY_LEAF_SIGNATURE ||
err == X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT)) {
err == X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT ||
err == X509_V_ERR_CERT_HAS_EXPIRED)) {
if (X509_STORE_get_by_subject(ctx, X509_LU_X509,
X509_get_subject_name(err_cert), &xobj) > 0 &&
!X509_cmp(xobj.data.x509, err_cert)) {
mylog(LOG_INFO, "Basic mode; peer certificate found "
"in store, accepting it!");
if (err == X509_V_ERR_CERT_HAS_EXPIRED)
mylog(LOG_INFO, "Basic mode; Accepting "
"*expired* peer certificate "
"found in store.");
else
mylog(LOG_INFO, "Basic mode; Accepting peer "
"certificate found in store.");
result = 1;
err = X509_V_OK;

132
src/irc.c
View File

@ -49,14 +49,14 @@ void oidentd_dump(list_t *connl);
void irc_client_free(struct link_client *cli);
extern int conf_log_sync_interval;
extern int conf_error;
extern char conf_errstr[];
void write_user_list(connection_t *c, char *dest);
static void irc_copy_cli(struct link_client *src, struct link_client *dest,
struct line *line);
static void irc_cli_make_join(struct link_client *ic);
static void server_setup_reconnect_timer(struct link *link);
int irc_cli_bip(bip_t *bip, struct link_client *ic, struct line *line);
#define LAGOUT_TIME 480
#define LAGCHECK_TIME (90)
@ -377,11 +377,16 @@ int irc_dispatch_server(bip_t *bip, struct link_server *server,
size_t nicklen = strlen(server->nick);
char *newnick = malloc(nicklen + 2);
strcpy(newnick, server->nick);
if (strlen(server->nick) < 9)
if (strlen(server->nick) < 9) {
strcat(newnick, "`");
else if (newnick[8] != '`') {
newnick[8] = '`';
newnick[9] = 0;
} else if (newnick[7] != '`') {
if (newnick[8] != '`') {
newnick[8] = '`';
newnick[9] = 0;
} else {
newnick[7] = '`';
newnick[9] = 0;
}
} else {
newnick[8] = rand() * ('z' - 'a') / RAND_MAX +
'a';
@ -601,9 +606,9 @@ void unbind_from_link(struct link_client *ic)
fatal("realloc");
}
int irc_cli_bip(struct link_client *ic, struct line *line)
int irc_cli_bip(bip_t *bip, struct link_client *ic, struct line *line)
{
return adm_bip(ic, line, 0);
return adm_bip(bip, ic, line, 0);
}
#define PASS_SEP ':'
@ -853,13 +858,14 @@ static int irc_cli_quit(struct link_client *ic, struct line *line)
return OK_CLOSE;
}
static int irc_cli_privmsg(struct link_client *ic, struct line *line)
static int irc_cli_privmsg(bip_t *bip, struct link_client *ic,
struct line *line)
{
if (line->elemc >= 3)
log_cli_privmsg(LINK(ic)->log, LINK(ic)->l_server->nick,
line->elemv[1], line->elemv[2]);
if (strcmp(line->elemv[1], "-bip") == 0)
return adm_bip(ic, line, 1);
return adm_bip(bip, ic, line, 1);
if (LINK(ic)->user->blreset_on_talk)
log_reinit_all(LINK(ic)->log);
@ -1047,7 +1053,6 @@ static int irc_dispatch_trust_client(struct link_client *ic, struct line *line)
}
#endif
int irc_cli_bip(struct link_client *ic, struct line *line);
static int irc_dispatch_client(bip_t *bip, struct link_client *ic,
struct line *line)
{
@ -1066,7 +1071,7 @@ static int irc_dispatch_client(bip_t *bip, struct link_client *ic,
"before sending commands\r\n");
r = OK_FORGET;
} else if (strcasecmp(line->elemv[0], "BIP") == 0) {
r = irc_cli_bip(ic, line);
r = irc_cli_bip(bip, ic, line);
} else if (strcmp(line->elemv[0], "JOIN") == 0) {
r = irc_cli_join(ic, line);
} else if (strcmp(line->elemv[0], "PART") == 0) {
@ -1076,7 +1081,7 @@ static int irc_dispatch_client(bip_t *bip, struct link_client *ic,
} else if (strcmp(line->elemv[0], "QUIT") == 0) {
r = irc_cli_quit(ic, line);
} else if (strcmp(line->elemv[0], "PRIVMSG") == 0) {
r = irc_cli_privmsg(ic, line);
r = irc_cli_privmsg(bip, ic, line);
} else if (strcmp(line->elemv[0], "NOTICE") == 0) {
r = irc_cli_notice(ic, line);
} else if (strcmp(line->elemv[0], "WHO") == 0) {
@ -1425,11 +1430,6 @@ static void channel_free(struct channel *c)
if (c->create_ts)
free(c->create_ts);
/*
char *l;
while ((l = (char *)list_remove_first(&c->bans)))
free(l);
*/
hash_iterator_t hi;
for (hash_it_init(&c->nicks, &hi); hash_it_item(&hi); hash_it_next(&hi))
nick_free(hash_it_item(&hi));
@ -1475,7 +1475,7 @@ static int irc_part(struct link_server *server, struct line *line)
free(s_nick);
log_part(LINK(server)->log, line->origin, s_chan,
line->elemc == 3 ? line->elemv[2]:NULL);
line->elemc == 3 ? line->elemv[2] : NULL);
nick_free(nick);
return OK_COPY;
@ -1556,7 +1556,7 @@ static int irc_mode(struct link_server *server, struct line *line)
log_mode(LINK(server)->log, line->origin, line->elemv[1],
line->elemv[2], line->elemv + 3, line->elemc - 3);
/*
/*
* MODE -a+b.. #channel args
* ^ ^
* mode cur_arg
@ -1894,7 +1894,6 @@ void server_cleanup(struct link_server *server)
CONN(server) = NULL;
}
irc_lag_init(server);
}
void irc_client_close(struct link_client *ic)
@ -1918,6 +1917,22 @@ void irc_client_close(struct link_client *ic)
}
}
static void server_setup_reconnect_timer(struct link *link)
{
int timer = 0;
if (link->last_connection_attempt &&
time(NULL) - link->last_connection_attempt
< CONN_INTERVAL) {
timer = RECONN_TIMER * (link->s_conn_attempt);
if (timer > RECONN_TIMER_MAX)
timer = RECONN_TIMER_MAX;
}
mylog(LOG_ERROR, "%s dead, reconnecting in %d seconds", link->name,
timer);
link->recon_timer = timer;
}
static void irc_close(struct link_any *l)
{
if (CONN(l)) {
@ -1926,28 +1941,16 @@ static void irc_close(struct link_any *l)
}
if (TYPE(l) == IRC_TYPE_SERVER) {
/* TODO: free link_server as a whole */
int timer = 0;
struct link_server *is = (struct link_server *)l;
if (LINK(is)->s_state == IRCS_CONNECTED)
irc_notify_disconnection(is);
else
LINK(is)->s_conn_attempt++;
irc_server_shutdown(is);
log_disconnected(LINK(is)->log);
server_next(LINK(is));
server_cleanup(is);
if (LINK(is)->last_connection_attempt &&
time(NULL) - LINK(is)->last_connection_attempt
< CONN_INTERVAL) {
timer = RECONN_TIMER * (LINK(is)->s_conn_attempt);
if (timer > RECONN_TIMER_MAX)
timer = RECONN_TIMER_MAX;
}
mylog(LOG_ERROR, "%s dead, reconnecting in %d seconds",
LINK(l)->name, timer);
LINK(is)->recon_timer = timer;
server_setup_reconnect_timer(LINK(is));
LINK(is)->l_server = NULL;
irc_server_free((struct link_server *)is);
@ -1987,10 +1990,21 @@ struct link_server *irc_server_new(struct link *link, connection_t *conn)
void irc_server_free(struct link_server *s)
{
if (CONN(s))
connection_free(CONN(s));
if (s->nick)
free(s->nick);
if (s->user_mode)
free(s->user_mode);
hash_iterator_t hi;
for (hash_it_init(&s->channels, &hi); hash_it_item(&hi);
hash_it_next(&hi)) {
struct channel *chan = hash_it_item(&hi);
channel_free(chan);
}
free(s);
}
@ -1999,6 +2013,8 @@ connection_t *irc_server_connect(struct link *link)
struct link_server *ls;
connection_t *conn;
link->s_conn_attempt++;
mylog(LOG_INFO, "Connecting user '%s' to network '%s' using server "
"%s:%d", link->user->name, link->name,
link->network->serverv[link->cur_server].host,
@ -2015,6 +2031,11 @@ connection_t *irc_server_connect(struct link *link)
CONNECT_TIMEOUT);
if (!conn)
fatal("connection_new");
if (conn->handle == -1) {
mylog(LOG_INFO, "Cannot connect.");
connection_free(conn);
return NULL;
}
ls = irc_server_new(link, conn);
conn->user_data = ls;
@ -2105,10 +2126,7 @@ void oidentd_dump(list_t *connl)
content = (char *)malloc(stats.st_size + 1);
if (content == NULL){
mylog(LOG_WARN, "oidentd_dump : malloc failed, "
"returning");
fclose(f);
free(filename);
fatal("out of memory");
return;
}
@ -2154,6 +2172,7 @@ void oidentd_dump(list_t *connl)
content[stats.st_size - 1] != '\n')
fprintf(f, "\n");
}
free(content);
}
for (list_it_init(connl, &it); list_it_item(&it); list_it_next(&it)) {
@ -2261,8 +2280,10 @@ void bip_tick(bip_t *bip)
} else {
if (link->recon_timer == 0) {
connection_t *conn;
conn = irc_server_connect(link);
link->last_connection_attempt = time(NULL);
conn = irc_server_connect(link);
if (!conn)
server_setup_reconnect_timer(link);
} else {
link->recon_timer--;
}
@ -2374,9 +2395,12 @@ void irc_main(bip_t *bip)
{
int timeleft = 1000;
/* XXX: This one MUST be first */
/* TODO: maybe not anymore, check */
list_add_first(&bip->conn_list, bip->listener);
/*
* If the list is empty, we are starting. Otherwise we are reloading,
* and conn_list is kept accross reloads.
*/
if (list_is_empty(&bip->conn_list))
list_add_first(&bip->conn_list, bip->listener);
while (!sighup) {
connection_t *conn;
@ -2401,10 +2425,6 @@ void irc_main(bip_t *bip)
bip_on_event(bip, conn);
list_free(ready);
}
while (list_remove_first(&bip->conn_list))
;
while (list_remove_first(&bip->link_list))
;
while (list_remove_first(&bip->connecting_client_list))
;
return;
@ -2421,12 +2441,6 @@ void irc_client_free(struct link_client *cli)
free(cli);
}
/*
void irc_server_free(struct link_server *is)
{
free(is);
}
*/
struct link *irc_link_new()
{
struct link *link;
@ -2443,11 +2457,18 @@ struct link *irc_link_new()
void link_kill(bip_t *bip, struct link *link)
{
list_remove(&bip->conn_list, CONN(link->l_server));
server_cleanup(link->l_server);
irc_server_free(link->l_server);
while (link->l_clientc) {
struct link_client *lc = link->l_clientv[0];
list_remove(&bip->conn_list, CONN(lc));
unbind_from_link(lc);
irc_client_free(lc);
}
hash_remove(&link->user->connections, link->name);
free(link->name);
irc_close((struct link_any *)link->l_server);
while (link->l_clientc)
irc_close((struct link_any *)link->l_clientv[0]);
log_free(link->log);
MAYFREE(link->prev_nick);
MAYFREE(link->cli_nick);
@ -2473,5 +2494,6 @@ void link_kill(bip_t *bip, struct link *link)
#ifdef HAVE_LIBSSL
sk_X509_free(link->untrusted_certs);
#endif
free(link);
}

View File

@ -91,6 +91,7 @@ struct user {
#endif
hash_t connections;
int in_use; /* for mark and sweep on reload */
};
struct network
@ -158,6 +159,7 @@ struct link {
int ssl_check_mode;
STACK_OF(X509) *untrusted_certs;
#endif
int in_use; /* for mark and sweep on reload */
};
struct link_connection {

View File

@ -16,10 +16,10 @@
int linec = 0;
#define YY_NO_UNPUT
#include "util.h"
extern int conf_error;
extern list_t *root_list;
void yyparse(void);
void free_conf(list_t*);
extern int conf_error;
#if 0
/* SPANK ME WITH A SHOVEL */
@ -30,15 +30,14 @@ void dummy_lex_FFS(void)
}
#endif
list_t *parse_conf(FILE *file)
list_t *parse_conf(FILE *file, int *err)
{
YY_BUFFER_STATE in = yy_create_buffer(file, YY_BUF_SIZE);
yy_switch_to_buffer(in);
conf_error = 0;
yyparse();
if (conf_error) {
free_conf(root_list);
return NULL;
}
yy_delete_buffer(in);
*err = conf_error;
return root_list;
}
%}
@ -113,6 +112,6 @@ list_t *parse_conf(FILE *file)
"{" { return LEX_LBRA; }
"}" { return LEX_RBRA; }
";" { return LEX_SEMICOLON; }
. { printf("Parse error line %d, unknown character '%s'\n", linec + 1, yytext);
. { mylog(LOG_ERROR, "Parse error in config file line %d, unknown character '%s'\n", linec + 1, yytext);
return LEX_BUNCH; }
%%

View File

@ -288,6 +288,7 @@ logfilegroup_t *log_find_file(log_t *logdata, char *destination)
char *filename = NULL;
time_t t;
struct tm *ltime;
struct link *l;
if (!ischannel(*destination))
destination = "privates";
@ -312,6 +313,24 @@ logfilegroup_t *log_find_file(log_t *logdata, char *destination)
lfg = hash_get(&logdata->logfgs, destination);
if (!lfg)
fatal("internal log_find_file");
/* ok we are allocating a new lfg now, let's set it up for
* backlogging if applicable */
if (!logdata->user)
fatal("log_find_file: no user associated to logdata");
if (!logdata->network)
fatal("log_find_file: no network id associated to "
"logdata");
l = hash_get(&logdata->user->connections, logdata->network);
if (!l)
fatal("log_beautify: no connection associated to "
"logdata");
struct chan_info *ci = hash_get(&l->chan_infos, destination);
if (ci && !ci->backlog) {
lfg->track_backlog = 0;
} else {
lfg->track_backlog = 1;
}
if (filename)
free(filename);
return lfg;
@ -362,6 +381,8 @@ logfilegroup_t *log_find_file(log_t *logdata, char *destination)
/*
* Da log routines
* There are a lot of snprintf's here without enforcing the last \0 in the
* buffer, but _log_write takes care of this for us.
*/
void log_join(log_t *logdata, char *ircmask, char *channel)
{
@ -374,9 +395,14 @@ void log_join(log_t *logdata, char *ircmask, char *channel)
void log_part(log_t *logdata, char *ircmask, char *channel,
char *message)
{
snprintf(logdata->buffer, LOGLINE_MAXLEN,
if (message)
snprintf(logdata->buffer, LOGLINE_MAXLEN,
"%s -!- %s has left %s [%s]", timestamp(), ircmask,
channel, message);
else
snprintf(logdata->buffer, LOGLINE_MAXLEN,
"%s -!- %s has left %s", timestamp(), ircmask,
channel);
log_write(logdata, channel, logdata->buffer);
}
@ -424,7 +450,7 @@ static void _log_privmsg(log_t *logdata, char *ircmask, int src,
char *real_message = message;
if (*message == '+' || *message == '-')
real_message++;
real_message++;
if (strncmp(real_message, "\001ACTION ", 8) != 0)
return;
@ -634,7 +660,10 @@ void log_client_connected(log_t *logdata)
void log_advance_backlogs(log_t* ld, logfilegroup_t *lfg)
{
int c;
(void)ld;
if (!lfg->track_backlog)
return;
if (!ld->user->backlog || ld->user->backlog_lines == 0)
return;
@ -683,6 +712,9 @@ int log_has_backlog(log_t *logdata, char *destination)
if (lfg->memlog)
return !list_is_empty(lfg->memlog);
if (!lfg->track_backlog)
return 0;
logfile_t *lf;
lf = list_get_first(&lfg->file_group);
if (lf != list_get_last(&lfg->file_group))
@ -735,20 +767,6 @@ char *log_beautify(log_t *logdata, char *buf, char *dest)
lots = p - sots;
p++;
if (!logdata->user)
fatal("log_beautify: no user associated to logdata");
if (!logdata->network)
fatal("log_beautify: no network id associated to logdata");
l = hash_get(&logdata->user->connections, logdata->network);
if (!l)
fatal("log_beautify: no connection associated to logdata");
ci = hash_get(&l->chan_infos, dest);
if (ci && !ci->backlog) {
mylog(LOG_DEBUG, "Skipping unwanted channel %s for backlog",
dest);
return NULL;
}
if (strncmp(p, "-!-", 3) == 0) {
if (logdata->user->bl_msg_only)
return NULL;
@ -900,6 +918,9 @@ char *log_backread(log_t *logdata, char *destination, int *skip)
if (!lfg)
return NULL;
if (!lfg->track_backlog)
return NULL;
if (!logdata->backlogging) {
logdata->backlogging = 1;
mylog(LOG_DEBUG, "backlogging!");
@ -1171,7 +1192,7 @@ log_t *log_new(struct user *user, char *network)
fatal("out of memory");
logdata->connected = 0;
if (!log_all_logs)
log_all_logs = list_new(NULL);
log_all_logs = list_new(list_ptr_cmp);
list_add_last(log_all_logs, logdata);
return logdata;
}
@ -1182,12 +1203,16 @@ void log_free(log_t *log)
logfilegroup_t *lfg;
logfile_t *lf;
list_remove(log_all_logs, log);
free(log->network);
free(log->buffer);
for (hash_it_init(&log->logfgs, &it); (lfg = hash_it_item(&it));
hash_it_next(&it)) {
log_reset(lfg);
if ((lf = list_remove_first(&lfg->file_group)))
logfile_free(lf);
free(lf);
}
hash_clean(&log->logfgs);
free(log);

View File

@ -46,6 +46,7 @@ typedef struct logfilegroup
list_t *memlog;
int memc;
list_iterator_t backlog_it;
int track_backlog;
} logfilegroup_t;
typedef struct log {

View File

@ -42,7 +42,7 @@ int is_valid_nick(char *str)
while (*tmp != '\0' && (isalnum(*tmp) || *tmp == '-' || *tmp == '[' ||
*tmp == ']' || *tmp == '\\' || *tmp == '`' ||
*tmp == '^' || *tmp == '{' || *tmp == '}' ||
*tmp == '|'))
*tmp == '|' || *tmp == '_' ))
tmp++;
return (*tmp == '\0');
}
@ -118,6 +118,9 @@ void _mylog(int level, char *fmt, va_list ap)
{
char *prefix;
if (!conf_log_system)
return;
if (level > conf_log_level)
return;
@ -126,7 +129,7 @@ void _mylog(int level, char *fmt, va_list ap)
prefix = "FATAL: ";
break;
case LOG_DEBUGVERB:
prefix = "DEBUG: ";
prefix = "DEBUGVERB: ";
break;
case LOG_DEBUG:
prefix = "DEBUG: ";
@ -157,9 +160,6 @@ void mylog(int level, char *fmt, ...)
{
va_list ap;
if (!conf_log_system)
return;
va_start(ap, fmt);
_mylog(level, fmt, ap);
va_end(ap);
@ -308,28 +308,6 @@ void *list_remove_last(list_t *list)
return ptr;
}
/*
static void *list_remove_item(list_t *l, struct list_item *li)
{
void *ret = li->ptr;
if (!li->prev) {
if (l->first != li)
fatal("list_remove_item");
l->first = li->next;
} else
li->prev->next = li->next;
if (!li->next) {
if (l->last != li)
fatal("list_remove_item");
l->last = li->prev;
} else
li->next->prev = li->prev;
free(li);
return ret;
}
*/
void *list_remove_if_exists(list_t *list, void *ptr)
{
list_iterator_t li;
@ -383,12 +361,19 @@ void list_it_init(list_t *list, list_iterator_t *ti)
{
ti->list = list;
ti->cur = list->first;
ti->next = NULL;
}
void list_it_next(list_iterator_t *ti)
{
if (ti->cur)
if (ti->cur) {
if (ti->next)
fatal("list_it_next: inconsistent interator state");
ti->cur = ti->cur->next;
} else if (ti->next) {
ti->cur = ti->next;
ti->next = NULL;
}
}
void *list_it_item(list_iterator_t *ti)
@ -415,7 +400,8 @@ void *list_it_remove(list_iterator_t *li)
void *ptr = li->cur->ptr;
struct list_item *item = li->cur;
li->cur = li->cur->next;
li->next = li->cur->next;
li->cur = NULL;
free(item);
return ptr;
}
@ -555,50 +541,73 @@ void *hash_remove(hash_t *hash, char *key)
return ptr;
}
int hash_is_empty(hash_t *h)
{
int i;
for (i = 0; i < 256; i++) {
if (!list_is_empty(&h->lists[i]))
return 0;
}
return 1;
}
void hash_it_init(hash_t *h, hash_iterator_t *hi)
{
memset(hi, 0, sizeof(hash_iterator_t));
hi->hash = h;
while (list_is_empty(&h->lists[hi->list]) && hi->list < 256)
while (hi->list < 256 && list_is_empty(&h->lists[hi->list]))
hi->list++;
if (hi->list < 256)
hi->cur = h->lists[hi->list].first;
else
hi->cur = NULL;
list_it_init(&h->lists[hi->list], &hi->lit);
}
void hash_it_next(hash_iterator_t *hi)
{
hash_t *hash = hi->hash;
hi->cur = hi->cur->next;
while (!hi->cur) {
hi->list++;
if (hi->list == 256) {
hi->cur = NULL;
return;
}
hi->cur = hash->lists[hi->list].first;
list_it_next(&hi->lit);
if (!list_it_item(&hi->lit)) {
do {
hi->list++;
if (hi->list == 256)
return;
} while (list_is_empty(&hi->hash->lists[hi->list]));
list_it_init(&hi->hash->lists[hi->list], &hi->lit);
}
}
void *hash_it_item(hash_iterator_t *h)
{
if (!h->cur)
struct hash_item *hi;
hi = list_it_item(&h->lit);
if (!hi)
return NULL;
struct hash_item *hi = h->cur->ptr;
return hi->item;
}
char *hash_it_key(hash_iterator_t *h)
{
if (!h->cur)
struct hash_item *hi;
hi = list_it_item(&h->lit);
if (!hi)
return NULL;
struct hash_item *hi = h->cur->ptr;
return hi->key;
}
void *hash_it_remove(hash_iterator_t *hi)
{
struct hash_item *hitem;
void *ptr;
hitem = list_it_remove(&hi->lit);
ptr = hitem->item;
free(hitem->key);
free(hitem);
return ptr;
}
void hash_dump(hash_t *h)
{
hash_iterator_t it;
@ -625,4 +634,3 @@ int ischannel(char p)
{
return (p == '#' || p == '&' || p == '+' || p == '!');
}

View File

@ -15,6 +15,7 @@
#include <time.h>
#include <sys/types.h>
#include <unistd.h>
#include <stdarg.h>
/* Warning: must be in order, 0 = less output */
#define LOG_STD -1
@ -30,9 +31,11 @@
#define HASH_DEFAULT 0
void mylog(int level, char *fmt, ...);
void _mylog(int level, char *fmt, va_list ap);
void fatal(char *fmt, ...);
char *timestamp(void);
struct list_item;
struct hash_item;
typedef struct list {
struct list_item *first;
@ -43,16 +46,17 @@ typedef struct list {
typedef struct list_iterator {
list_t *list;
struct list_item *cur;
struct list_item *next;
} list_iterator_t;
/* our hash is also a list */
typedef struct hash {
list_t lists[256];
} hash_t;
typedef struct hash_iterator {
int list;
struct list_item *cur;
list_iterator_t lit;
struct hash_item *cur;
hash_t *hash;
} hash_iterator_t;
@ -102,10 +106,12 @@ void hash_insert(hash_t *hash, char *key, void *ptr);
void *hash_get(hash_t *, char *key);
void *hash_remove(hash_t *hash, char *key);
void *hash_remove_if_exists(hash_t *hash, char *key);
int hash_is_empty(hash_t *h);
void hash_it_init(hash_t *hash, hash_iterator_t *i);
void hash_it_next(hash_iterator_t *hi);
void *hash_it_item(hash_iterator_t *h);
char *hash_it_key(hash_iterator_t *h);
void *hash_it_remove(hash_iterator_t *li);
int is_valid_nick(char *str);
int is_valid_username(char *str);