/* * $Id: bip.c,v 1.39 2005/04/21 06:58:50 nohar Exp $ * * This file is part of the bip project * Copyright (C) 2004 2005 Arnaud Cornet and Loïc Gomez * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * See the file "COPYING" for the exact licensing terms. */ #include "config.h" #include #include #include #include #include #include "irc.h" #include "conf.h" #include "tuple.h" #include "log.h" #include "irc.h" #include "bip.h" #include "line.h" int sighup = 0; char *conf_log_root; char *conf_log_format; int conf_log_level; char *conf_ip; unsigned short conf_port; int conf_css; #ifdef HAVE_LIBSSL char *conf_ssl_certfile; #endif int conf_daemonize; char *conf_pid_file; char *conf_biphome; /* log options, for sure the trickiest :) */ /* no backlog at all */ int conf_backlog = 0; extern int conf_memlog; int conf_log = 0; int conf_log_system = 0; int conf_log_sync_interval = 0; list_t *parse_conf(FILE *file); static void conf_die(char *fmt, ...); #ifdef HAVE_LIBSSL int adm_trust(struct link_client *ic, struct line *line); #endif static char *get_tuple_value(list_t *tuple_l, int lex); static void hash_binary(char *hex, unsigned char **password, unsigned int *seed) { unsigned char *md5; unsigned int buf; int i; if (strlen(hex) != 40) fatal("Incorrect password format %s\n", hex); md5 = malloc(20); for (i = 0; i < 20; i++) { sscanf(hex + 2 * i, "%02x", &buf); md5[i] = buf; } *seed = 0; sscanf(hex, "%02x", &buf); *seed |= buf << 24; sscanf(hex + 2, "%02x", &buf); *seed |= buf << 16; sscanf(hex + 2 * 2, "%02x", &buf); *seed |= buf << 8; sscanf(hex + 2 * 3, "%02x", &buf); *seed |= buf; *password = md5; } static int add_server(struct server *s, list_t *data) { struct tuple *t; s->port = 6667; /* default port */ while ((t = list_remove_first(data))) { switch (t->type) { case LEX_HOST: s->host = t->pdata; break; case LEX_PORT: s->port = t->ndata; break; default: fatal("Config error in server block (%d)", t->type); } } if (!s->host) { free(s); conf_die("Server conf: host not set"); return 0; } return 1; } #define ERRBUFSZ 128 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; va_end(ap); } FILE *conf_global_log_file; static pid_t daemonize(void) { switch (fork()) { case -1: fatal("Fork failed"); break; case 0: break; default: _exit(0); } if (setsid() < 0) fatal("setsid() failed"); switch (fork()) { case -1: fatal("Fork failed"); break; case 0: break; default: _exit(0); } close(0); close(1); close(2); /* This better be the very last action since fatal makes use of * conf_global_log_file */ return getpid(); } /* RACE CONDITION! */ int do_pid_stuff(void) { char hname[1024]; char longpath[1024]; FILE *f; try_again: f = fopen(conf_pid_file, "r"); if (f) goto pid_is_there; if (gethostname(hname, 1023) == -1) fatal("%s %s", "gethostname", strerror(errno)); snprintf(longpath, 1023, "%s.%s.%ld", conf_pid_file, hname, (long unsigned int)getpid()); int fd; if ((fd = open(longpath, O_CREAT|O_WRONLY, S_IWUSR|S_IRUSR)) == -1) fatal("%s %s", "open", strerror(errno)); if (link(longpath, conf_pid_file) == -1) { struct stat buf; if (stat(longpath, &buf) == -1) { if (buf.st_nlink != 2) { f = fopen(conf_pid_file, "r"); goto pid_is_there; } } } unlink(longpath); return fd; pid_is_there: { pid_t pid; long unsigned int p; if (f) { int c = fscanf(f, "%ld", &p); pid = p; if (c != 1 || p == 0) { mylog(LOG_INFO, "pid file found but invalid " "data inside. Continuing...\n"); if (unlink(conf_pid_file)) { fatal("Cannot delete pid file '%s', " "check permissions.\n", conf_pid_file); } goto try_again; } } else pid = 0; int kr = kill(pid, 0); if (kr == -1 && (errno == ESRCH || errno == EPERM)) { /* that's not bip! */ fclose(f); if (unlink(conf_pid_file)) { fatal("Cannot delete pid file '%s', check " "permissions.\n", conf_pid_file); } goto try_again; } if (pid) mylog(LOG_INFO, "pid file found (pid %ld).", pid); mylog(LOG_STD, "Another instance of bip is certainly runing."); mylog(LOG_STD, "If you are sure this is not the case remove" " %s.", conf_pid_file); exit(2); } return 0; } #define S_CONF "/.bip/bip.conf" static void usage(char *name) { printf( "Usage: %s [-f config_file] [-h] [-n]\n" " -f config_file: Use config_file as the configuration file\n" " If no config file is given %s will try to open ~" S_CONF "\n" " -n: Don't daemonize, log in stderr\n" " -h: This help\n", name, name); exit(1); } void reload_config(int i) { (void)i; sighup = 1; } bip_t *_bip; void bad_quit(int i) { list_iterator_t it; for (list_it_init(&_bip->link_list, &it); list_it_item(&it); list_it_next(&it)) { struct link *l = list_it_item(&it); struct link_server *ls = l->l_server; if (ls && l->s_state == IRCS_CONNECTED) { write_line_fast(CONN(ls), "QUIT :Coyote finally " "caught me\r\n"); } } unlink(conf_pid_file); exit(i); } static int add_network(bip_t *bip, list_t *data) { struct tuple *t; struct network *n; int i; char *name = get_tuple_value(data, LEX_NAME); if (name == NULL) { conf_die("Network with no name"); return 0; } n = hash_get(&bip->networks, name); if (n) { for (i = 0; i < n->serverc; i++) free(n->serverv[i].host); free(n->serverv); n->serverv = NULL; n->serverc = 0; } else { n = calloc(sizeof(struct network), 1); hash_insert(&bip->networks, name, n); } while ((t = list_remove_first(data))) { switch (t->type) { case LEX_NAME: MOVE_STRING(n->name, t->pdata); break; #ifdef HAVE_LIBSSL case LEX_SSL: n->ssl = t->ndata; break; #endif case LEX_SERVER: n->serverv = realloc(n->serverv, (n->serverc + 1) * sizeof(struct server)); n->serverc++; add_server(&n->serverv[n->serverc - 1], t->pdata); free(t->pdata); t->pdata = NULL; break; default: conf_die("unknown keyword in network statement"); if (t->type == TUPLE_STR) free(t->pdata); break; } if (t->type == TUPLE_STR && t->pdata) free(t->pdata); free(t); } return 1; } static int add_connection(bip_t *bip, struct user *user, list_t *data) { struct tuple *t; struct link *l; struct chan_info *ci; char *name = get_tuple_value(data, LEX_NAME); if (name == NULL) { conf_die("Connection with no name"); return 0; } l = hash_get(&user->connections, name); if (!l) { l = irc_link_new(); hash_insert(&user->connections, name, l); list_add_last(&bip->link_list, l); l->user = user; l->log = log_new(user, name); } else { #warning "CODEME (user switch..)" l->network = NULL; log_reinit_all(l->log); } while ((t = list_remove_first(data))) { switch (t->type) { case LEX_NAME: MOVE_STRING(l->name, t->pdata); break; case LEX_NETWORK: l->network = hash_get(&bip->networks, t->pdata); if (!l->network) { conf_die("Undefined network %s.\n", t->pdata); return 0; } break; case LEX_NICK: if (!is_valid_nick(t->pdata)) conf_die("Invalid nickname %s.", t->pdata); MOVE_STRING(l->connect_nick, t->pdata); break; case LEX_USER: MOVE_STRING(l->username, t->pdata); break; case LEX_REALNAME: MOVE_STRING(l->realname, t->pdata); break; case LEX_PASSWORD: MOVE_STRING(l->s_password, t->pdata); break; case LEX_VHOST: MOVE_STRING(l->vhost, t->pdata); break; case LEX_CHANNEL: ci = calloc(sizeof(struct chan_info), 1); ci->name = get_tuple_value(t->pdata, LEX_NAME); ci->key = get_tuple_value(t->pdata, LEX_KEY); 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; break; case LEX_IGN_FIRST_NICK: l->ignore_first_nick = t->ndata; break; case LEX_AWAY_NICK: MOVE_STRING(l->away_nick, t->pdata); break; case LEX_NO_CLIENT_AWAY_MSG: MOVE_STRING(l->no_client_away_msg, t->pdata); break; case LEX_ON_CONNECT_SEND: list_add_last(&l->on_connect_send, t->pdata); break; default: conf_die("unknown keyword in connection statement"); if (t->type == TUPLE_STR) free(t->pdata); } if (t->type == TUPLE_STR && t->pdata) free(t->pdata); free(t); } /* checks that can only be here, or must */ if (!l->network) conf_die("Missing network in connection block"); return 1; } static char *get_tuple_value(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->pdata; } return NULL; } static int add_user(bip_t *bip, list_t *data) { int r; struct tuple *t; struct user *u; char *name = get_tuple_value(data, LEX_NAME); if (name == NULL) { conf_die("User with no name"); return 0; } u = hash_get(&bip->users, name); if (!u) { u = calloc(sizeof(struct user), 1); hash_insert(&bip->users, name, u); hash_init(&u->connections, HASH_NOCASE); } else { FREE(u->name); FREE(u->password); FREE(u->default_nick); FREE(u->default_username); FREE(u->default_realname); #ifdef HAVE_LIBSSL FREE(u->ssl_check_store); #endif } while ((t = list_remove_first(data))) { switch (t->type) { case LEX_NAME: MOVE_STRING(u->name, t->pdata); break; case LEX_PASSWORD: hash_binary(t->pdata, &u->password, &u->seed); free(t->pdata); break; case LEX_DEFAULT_NICK: MOVE_STRING(u->default_nick, t->pdata); break; case LEX_DEFAULT_USER: MOVE_STRING(u->default_username, t->pdata); break; case LEX_DEFAULT_REALNAME: MOVE_STRING(u->default_realname, t->pdata); break; case LEX_ALWAYS_BACKLOG: u->always_backlog = t->ndata; break; case LEX_BACKLOG: u->backlog = t->ndata; break; case LEX_BL_MSG_ONLY: u->bl_msg_only = t->ndata; break; case LEX_BACKLOG_LINES: u->backlog_lines = t->ndata; break; case LEX_BACKLOG_NO_TIMESTAMP: u->backlog_no_timestamp = t->ndata; break; case LEX_BLRESET_ON_TALK: u->blreset_on_talk = t->ndata; break; case LEX_CONNECTION: r = add_connection(bip, u, t->pdata); free(t->pdata); if (!r) return 0; break; #ifdef HAVE_LIBSSL case LEX_SSL_CHECK_MODE: if (!strncmp(t->pdata, "basic", 5)) u->ssl_check_mode = SSL_CHECK_BASIC; if (!strncmp(t->pdata, "ca", 2)) u->ssl_check_mode = SSL_CHECK_CA; free(t->pdata); break; case LEX_SSL_CHECK_STORE: u->ssl_check_store = t->pdata; break; #endif default: conf_die("Uknown keyword in user statement"); break; } if (t->type == TUPLE_STR && t->pdata) free(t->pdata); free(t); } if (!u->password) { conf_die("Missing password in user block"); return 0; } return 1; } static int validate_config(bip_t *bip) { /* nick username realname or default_{nick,username,realname} in user */ hash_iterator_t it, sit; struct user *user; struct link *link; int r = 1; for (hash_it_init(&bip->users, &it); (user = hash_it_item(&it)); hash_it_next(&it)) { if (!user->default_nick || !user->default_username || !user->default_realname) { for (hash_it_init(&user->connections, &sit); (link = hash_it_item(&sit)); hash_it_next(&sit)) { if ((!link->username && !user->default_username) || (!link->connect_nick && !user->default_nick) || (!link->realname && !user->default_realname)) link_kill(bip, link); //conf_die("user: ... net: ... can i has nick/user/rael"); r = 0; } } } #warning CODE ME #if 0 if (conf_backlog && !conf_log) { if (conf_backlog_lines == 0) { conf_die("You must set conf_backlog_lines if " "conf_log = false and " "conf_backlog = true"); } } #endif return r; } int fireup(bip_t *bip, FILE *conf) { struct tuple *t; list_t *l; conf_start(); l = parse_conf(conf); if (conf_error) return 0; while ((t = list_remove_first(l))) { switch (t->type) { case LEX_LOG_SYNC_INTERVAL: conf_log_sync_interval = t->ndata; break; /* 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; */ case LEX_LOG: conf_log = t->ndata; break; case LEX_LOG_SYSTEM: conf_log_system = t->ndata; break; case LEX_LOG_ROOT: MOVE_STRING(conf_log_root, t->pdata); break; case LEX_LOG_FORMAT: MOVE_STRING(conf_log_format, t->pdata); break; case LEX_LOG_LEVEL: conf_log_level = t->ndata; break; case LEX_IP: MOVE_STRING(conf_ip, t->pdata); break; case LEX_PORT: conf_port = t->ndata; break; case LEX_CSS: conf_css = t->ndata; break; case LEX_PID_FILE: MOVE_STRING(conf_pid_file, t->pdata); break; case LEX_NETWORK: add_network(bip, t->pdata); list_free(t->pdata); break; case LEX_USER: add_user(bip, t->pdata); list_free(t->pdata); break; default: conf_die("Config error in base config (%d)", t->type); } if (t->type == TUPLE_STR && t->pdata) free(t->pdata); free(t); } free(root_list); root_list = NULL; validate_config(bip); return 1; } static void log_file_setup(void) { char buf[4096]; if (conf_log_system) { if (conf_global_log_file && conf_global_log_file != stderr) fclose(conf_global_log_file); snprintf(buf, 4095, "%s/bip.log", conf_log_root); FILE *f = fopen(buf, "a"); if (!f) fatal("Can't open %s: %s", buf, strerror(errno)); conf_global_log_file = f; } else { conf_global_log_file = stderr; } } int main(int argc, char **argv) { FILE *conf = NULL; char *confpath = NULL; int ch; int r, fd; char buf[30]; bip_t bip; bip_init(&bip); _bip = &bip; conf_ip = strdup("0.0.0.0"); conf_port = 7778; conf_css = 0; signal(SIGPIPE, SIG_IGN); signal(SIGHUP, reload_config); signal(SIGINT, bad_quit); signal(SIGQUIT, bad_quit); signal(SIGTERM, bad_quit); conf_log_root = NULL; conf_log_format = NULL; conf_log_level = LOG_INFO; conf_backlog = 1; conf_log = 1; conf_log_system = 1; conf_log_sync_interval = 5; conf_daemonize = 1; conf_global_log_file = stderr; conf_pid_file = NULL; while ((ch = getopt(argc, argv, "hnf:s:")) != -1) { switch (ch) { case 'f': confpath = strdup(optarg); break; case 'n': conf_daemonize = 0; break; case 's': conf_biphome = strdup(optarg); break; default: usage(argv[0]); } } umask(0027); if (confpath) { conf = fopen(confpath, "r"); if (!conf) fatal("config file not found"); } if (!conf) { char *home; home = getenv("HOME"); if (!home) fatal("no home"); confpath = malloc(strlen(home) + 1 + strlen(S_CONF) + 1); *confpath = 0; strcat(confpath, home); strcat(confpath, "/"); strcat(confpath, S_CONF); conf = fopen(confpath, "r"); if (!conf) fatal("%s config file not found", confpath); } r = fireup(&bip, conf); fclose(conf); if (!r) { fatal("%s", conf_errstr); exit(28); } if (!conf_biphome) { char *home = getenv("HOME"); if (!home) { conf_die("no $HOME !, do you live in a trailer ?"); return 0; } conf_biphome = malloc(strlen(home) + strlen("/.bip") + 1); strcpy(conf_biphome, home); strcat(conf_biphome, "/.bip"); } if (!conf_log_root) { char *ap = "/logs"; conf_log_root = malloc(strlen(conf_biphome) + strlen(ap) + 1); strcpy(conf_log_root, conf_biphome); strcat(conf_log_root, ap); mylog(LOG_INFO, "Default log root: %s", conf_log_root); } if (!conf_pid_file) { char *pid = "/bip.pid"; conf_pid_file = malloc(strlen(conf_biphome) + strlen(pid) + 1); strcpy(conf_pid_file, conf_biphome); strcat(conf_pid_file, pid); mylog(LOG_INFO, "Default pid file: %s", conf_pid_file); } #ifdef HAVE_LIBSSL conf_ssl_certfile = NULL; /* Make into a config option */ if (!conf_ssl_certfile) { char *ap = "/bip.pem"; if (conf_ssl_certfile) { free(conf_ssl_certfile); conf_ssl_certfile = NULL; } conf_ssl_certfile = malloc(strlen(conf_biphome) + strlen(ap) + 1); strcpy(conf_ssl_certfile, conf_biphome); strcat(conf_ssl_certfile, ap); mylog(LOG_INFO, "Default SSL certificate file: %s", conf_ssl_certfile); } #endif if (!conf_log_format) conf_log_format = "%u/%n/%Y-%m/%c.%d.log"; check_dir(conf_log_root, 1); fd = do_pid_stuff(); pid_t pid = 0; log_file_setup(); if (conf_daemonize) pid = daemonize(); else pid = getpid(); snprintf(buf, 29, "%ld\n", (long unsigned int)pid); write(fd, buf, strlen(buf)); close(fd); bip.listener = listen_new(conf_ip, conf_port, conf_css); if (!bip.listener) fatal("Could not create listening socket"); for (;;) { if (conf_error) mylog(LOG_ERROR, "conf error: %s", conf_errstr); irc_main(&bip); sighup = 0; conf = fopen(confpath, "r"); if (!conf) fatal("%s config file not found", confpath); fireup(&bip, conf); fclose(conf); /* re-open to allow logfile rotate */ log_file_setup(); } return 1; } void write_user_list(connection_t *c, char *dest) { hash_iterator_t it; hash_iterator_t lit; char buf[4096]; WRITE_LINE2(c, P_IRCMASK, "PRIVMSG", dest, "bip user list:"); for (hash_it_init(&_bip->users, &it); hash_it_item(&it); hash_it_next(&it)) { struct user *u = hash_it_item(&it); snprintf(buf, 4095, "* %s:", u->name); buf[4095] = 0; WRITE_LINE2(c, P_IRCMASK, "PRIVMSG", dest, buf); for (hash_it_init(&u->connections, &lit); hash_it_item(&lit); hash_it_next(&lit)) { struct link *con = hash_it_item(&lit); snprintf(buf, 4095, " - %s", con->name); buf[4095] = 0; WRITE_LINE2(c, P_IRCMASK, "PRIVMSG", dest, buf); } } WRITE_LINE2(c, P_IRCMASK, "PRIVMSG", dest, "End of bip user list"); } #ifdef HAVE_LIBSSL int link_add_untrusted(struct link_server *ls, X509 *cert) { int i; /* Check whether the cert is already in the stack */ for (i = 0; i < sk_X509_num(LINK(ls)->untrusted_certs); i++) { if (!X509_cmp(cert, sk_X509_value(LINK(ls)->untrusted_certs, i))) return 1; } return sk_X509_push(LINK(ls)->untrusted_certs, cert); } int ssl_check_trust(struct link_client *ic) { X509 *trustcert = NULL; char subject[270]; char issuer[270]; unsigned char fp[EVP_MAX_MD_SIZE]; char fpstr[EVP_MAX_MD_SIZE * 3 + 20]; unsigned int fplen; int i; if(!LINK(ic)->untrusted_certs || sk_X509_num(LINK(ic)->untrusted_certs) <= 0) return 0; trustcert = sk_X509_value(LINK(ic)->untrusted_certs, 0); strcpy(subject, "Subject: "); strcpy(issuer, "Issuer: "); strcpy(fpstr, "MD5 fingerprint: "); X509_NAME_oneline(X509_get_subject_name(trustcert), subject + 9, 256); X509_NAME_oneline(X509_get_issuer_name(trustcert), issuer + 9, 256); X509_digest(trustcert, EVP_md5(), fp, &fplen); for(i = 0; i < (int)fplen; i++) sprintf(fpstr + 17 + (i * 3), "%02X%c", fp[i], (i == (int)fplen - 1) ? '\0' : ':'); WRITE_LINE2(CONN(ic), P_SERV, "NOTICE", "TrustEm", "This server SSL certificate was not " "accepted because it is not in your store " "of trusted certificates:"); WRITE_LINE2(CONN(ic), P_SERV, "NOTICE", "TrustEm", subject); WRITE_LINE2(CONN(ic), P_SERV, "NOTICE", "TrustEm", issuer); WRITE_LINE2(CONN(ic), P_SERV, "NOTICE", "TrustEm", fpstr); WRITE_LINE2(CONN(ic), P_SERV, "NOTICE", "TrustEm", "WARNING: if you've already trusted a " "certificate for this server before, that " "probably means it has changed."); WRITE_LINE2(CONN(ic), P_SERV, "NOTICE", "TrustEm", "If so, YOU MAY BE SUBJECT OF A " "MAN-IN-THE-MIDDLE ATTACK! PLEASE DON'T TRUST " "THIS CERTIFICATE IF YOU'RE NOT SURE THIS IS " "NOT THE CASE."); WRITE_LINE2(CONN(ic), P_SERV, "NOTICE", "TrustEm", "Type /QUOTE BIP TRUST OK to trust this " "certificate, /QUOTE BIP TRUST NO to discard it."); return 1; } #if 0 static int ssl_trust_next_cert(struct link_client *ic) { (void)ic; } static int ssl_discard_next_cert(struct link_client *ic) { (void)ic; } #endif /* 0 */ #endif #ifdef HAVE_LIBSSL int adm_trust(struct link_client *ic, struct line *line) { if (ic->allow_trust != 1) { mylog(LOG_ERROR, "User attempted TRUST command without " "being allowed to!"); unbind_from_link(ic); return OK_CLOSE; } if(!LINK(ic)->untrusted_certs || sk_X509_num(LINK(ic)->untrusted_certs) <= 0) { /* shouldn't have been asked to /QUOTE BIP TRUST but well... */ WRITE_LINE2(CONN(ic), P_SERV, "NOTICE", "TrustEm", "No untrusted certificates."); return ERR_PROTOCOL; } if (line->elemc != 3) return ERR_PROTOCOL; if (!strcasecmp(line->elemv[2], "OK")) { /* OK, attempt to trust the cert! */ BIO *bio = BIO_new_file(LINK(ic)->ssl_check_store, "a+"); X509 *trustcert = sk_X509_shift(LINK(ic)->untrusted_certs); if(!bio || !trustcert || PEM_write_bio_X509(bio, trustcert) <= 0) write_line_fast(CONN(ic), ":irc.bip.net NOTICE pouet " ":==== Error while trusting test!\r\n"); else write_line_fast(CONN(ic), ":irc.bip.net NOTICE pouet " ":==== Certificate now trusted.\r\n"); BIO_free_all(bio); X509_free(trustcert); } else if (!strcasecmp(line->elemv[2], "NO")) { /* NO, discard the cert! */ write_line_fast(CONN(ic), ":irc.bip.net NOTICE pouet " ":==== Certificate discarded.\r\n"); X509_free(sk_X509_shift(LINK(ic)->untrusted_certs)); } else return ERR_PROTOCOL; if (!ssl_check_trust(ic)) { write_line_fast(CONN(ic), ":irc.bip.net NOTICE pouet " ":No more certificates waiting awaiting " "user trust, thanks!\r\n"); write_line_fast(CONN(ic), ":irc.bip.net NOTICE pouet " ":If the certificate is trusted, bip should " "be able to connect to the server on the " "next retry. Please wait a while and try " "connecting your client again.\r\n"); LINK(ic)->recon_timer = 1; /* Speed up reconnection... */ unbind_from_link(ic); return OK_CLOSE; } return OK_FORGET; } #endif void adm_reply(struct link_client *ic, char *str) { char *nick; if (LINK(ic)->l_server) nick = LINK(ic)->l_server->nick; else nick = LINK(ic)->prev_nick; WRITE_LINE2(CONN(ic), P_IRCMASK, "PRIVMSG", nick, str); } extern struct link_client *reloading_client; void adm_blreset(struct link_client *ic) { log_reinit_all(LINK(ic)->log); adm_reply(ic, "Resetted."); } void adm_follow_nick(struct link_client *ic, char *val) { struct link *link = LINK(ic); if (strncasecmp(val, "TRUE", 4) == 0) { link->follow_nick = 1; adm_reply(ic, "follow_nick is now true."); } else { link->follow_nick = 0; adm_reply(ic, "follow_nick is now false."); } } void adm_ignore_first_nick(struct link_client *ic, char *val) { struct link *link = LINK(ic); if (strncasecmp(val, "TRUE", 4) == 0) { link->ignore_first_nick = 1; adm_reply(ic, "ignore_first_nick is now true."); } else { link->ignore_first_nick = 0; adm_reply(ic, "ignore_first_nick is now false."); } } void adm_on_connect_send(struct link_client *ic, char *val) { struct link *link = LINK(ic); char *s; if (val != NULL) { list_add_last(&link->on_connect_send, strdup(val)); adm_reply(ic, "added to on_connect_send."); } else { s = list_remove_last(&link->on_connect_send); if (s) free(s); adm_reply(ic, "on_connect_send emtpy."); } } void adm_away_nick(struct link_client *ic, char *val) { struct link *link = LINK(ic); if (link->away_nick) { free(link->away_nick); link->away_nick = NULL; } if (val != NULL) { link->away_nick = strdup(val); adm_reply(ic, "away_nick emtpy."); } else { adm_reply(ic, "away_nick set."); } } int adm_bip(struct link_client *ic, struct line *line, unsigned int privmsg) { char *nick; if (LINK(ic)->l_server) nick = LINK(ic)->l_server->nick; else nick = LINK(ic)->prev_nick; if (line->elemc < privmsg + 2) return OK_FORGET; if (strcasecmp(line->elemv[privmsg + 1], "RELOAD") == 0) { reloading_client = ic; sighup = 1; } else if (strcasecmp(line->elemv[privmsg + 1], "LIST") == 0) { write_user_list(CONN(ic), nick); } else if (strcasecmp(line->elemv[privmsg + 1], "JUMP") == 0) { if (LINK(ic)->l_server) { WRITE_LINE1(CONN(LINK(ic)->l_server), NULL, "QUIT", "jumpin' jumpin'"); connection_close(CONN(LINK(ic)->l_server)); } } else if (strcasecmp(line->elemv[privmsg + 1], "BLRESET") == 0) { adm_blreset(ic); } else if (strcasecmp(line->elemv[privmsg + 1], "HELP") == 0) { WRITE_LINE2(CONN(ic), P_IRCMASK, "PRIVMSG", nick, "/BIP (RELOAD|LIST|JUMP|BLRESET|HELP|TRUST)"); WRITE_LINE2(CONN(ic), P_IRCMASK, "PRIVMSG", nick, "/BIP FOLLOW_NICK|IGNORE_FIRST_NICK TRUE|FALSE"); WRITE_LINE2(CONN(ic), P_IRCMASK, "PRIVMSG", nick, "/BIP ON_CONNECT_SEND # Adds a string to " "send on connect"); WRITE_LINE2(CONN(ic), P_IRCMASK, "PRIVMSG", nick, "/BIP ON_CONNECT_SEND # Clears on_connect_send"); WRITE_LINE2(CONN(ic), P_IRCMASK, "PRIVMSG", nick, "/BIP AWAY_NICK # Set away nick"); WRITE_LINE2(CONN(ic), P_IRCMASK, "PRIVMSG", nick, "/BIP AWAY_NICK # clear away nick"); } else if (strcasecmp(line->elemv[privmsg + 1], "FOLLOW_NICK") == 0) { if (line->elemc != privmsg + 3) return OK_FORGET; adm_follow_nick(ic, line->elemv[privmsg + 2]); } else if (strcasecmp(line->elemv[privmsg + 1], "IGNORE_FIRST_NICK") == 0) { if (line->elemc != privmsg + 3) return OK_FORGET; adm_ignore_first_nick(ic, line->elemv[privmsg + 2]); } else if (strcasecmp(line->elemv[privmsg + 1], "ON_CONNECT_SEND") == 0) { if (line->elemc == privmsg + 2) adm_on_connect_send(ic, NULL); else if (line->elemc == privmsg + 3) adm_on_connect_send(ic, line->elemv[privmsg + 2]); else return OK_FORGET; } else if (strcasecmp(line->elemv[privmsg + 1], "AWAY_NICK") == 0) { if (line->elemc == privmsg + 2) adm_away_nick(ic, NULL); else if (line->elemc == privmsg + 3) adm_away_nick(ic, line->elemv[privmsg + 2]); else return OK_FORGET; #ifdef HAVE_LIBSSL } else if (strcasecmp(line->elemv[privmsg + 1], "TRUST") == 0) { return adm_trust(ic, line); #endif } return OK_FORGET; } 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 */ case TUPLE_INT: free(t); break; case TUPLE_LIST: free_conf(t->pdata); break; default: fatal("internal error free_conf"); break; } } }