2 ctrlproxy: A modular IRC proxy
3 (c) 2003,2006 Jelmer Vernooij <jelmer@nl.linux.org>
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation; either version 2 of the License, or
8 (at your option) any later version.
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
15 You should have received a copy of the GNU General Public License
16 along with this program; if not, write to the Free Software
17 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
20 #include "ctrlproxy.h"
30 * Nickname/password combination for a particular network or globally.
32 struct nickserv_entry {
38 const char *nickserv_find_nick(struct network *n, const char *nick)
41 for (gl = n->global->nickserv_nicks; gl; gl = gl->next) {
42 struct nickserv_entry *e = gl->data;
44 if (g_strcasecmp(e->nick, nick))
47 if (!e->network) return e->pass;
48 if (!g_strcasecmp(e->network, n->name)) return e->pass;
54 const char *nickserv_nick(struct network *n)
59 void nickserv_identify_me(struct network *network, char *nick)
63 /* Don't try to identify if we're already identified */
64 /* FIXME: Apparently, +e indicates being registered on Freenode,
65 * +R is only used on OFTC */
66 if (network->state->me.modes['R'])
69 pass = nickserv_find_nick(network, nick);
72 const char *nickserv_n = nickserv_nick(network);
74 raw = g_strdup_printf("IDENTIFY %s", pass);
75 log_network(LOG_INFO, network, "Sending password for %s", nickserv_n);
76 network_send_args(network, "PRIVMSG", nickserv_n, raw, NULL);
79 log_network(LOG_INFO, network, "No password known for `%s'", nick);
83 static gboolean log_data(struct network *n, const struct line *l, enum data_direction dir, void *userdata)
85 static char *nickattempt = NULL;
87 /* User has changed his/her nick. Check whether this nick needs to be identified */
88 if(dir == FROM_SERVER && !g_strcasecmp(l->args[0], "NICK") &&
89 nickattempt && !g_strcasecmp(nickattempt, l->args[1])) {
90 nickserv_identify_me(n, l->args[1]);
93 /* Keep track of the last nick that the user tried to take */
94 if(dir == TO_SERVER && !g_strcasecmp(l->args[0], "NICK")) {
95 if(nickattempt) g_free(nickattempt);
96 nickattempt = g_strdup(l->args[1]);
99 if (dir == TO_SERVER &&
100 (!g_strcasecmp(l->args[0], "PRIVMSG") || !g_strcasecmp(l->args[0], "NOTICE")) &&
101 (!g_strcasecmp(l->args[1], nickserv_nick(n)) && !g_strncasecmp(l->args[2], "IDENTIFY ", strlen("IDENTIFY ")))) {
102 struct nickserv_entry *e = NULL;
104 char *newpass = g_strdup(l->args[2] + strlen("IDENTIFY "));
106 for (gl = n->global->nickserv_nicks; gl; gl = gl->next) {
109 if (e->network && !g_strcasecmp(e->network, n->name) &&
110 !g_strcasecmp(e->nick, n->state->me.nick)) {
114 if (!e->network && !g_strcasecmp(e->nick, n->state->me.nick) &&
115 !g_strcasecmp(e->pass, newpass)) {
121 e = g_new0(struct nickserv_entry, 1);
122 e->nick = g_strdup(n->state->me.nick);
123 e->network = g_strdup(n->name);
124 n->global->nickserv_nicks = g_list_prepend(n->global->nickserv_nicks, e);
127 if (e->pass == NULL ||
128 strcmp(e->pass, newpass) != 0) {
129 e->pass = g_strdup(newpass);
130 log_network(LOG_INFO, n, "Caching password for nick %s", e->nick);
136 /* If we receive a nick-already-in-use message, ghost the current user */
137 if(dir == FROM_SERVER && atol(l->args[0]) == ERR_NICKNAMEINUSE) {
138 const char *pass = nickserv_find_nick(n, nickattempt);
139 if(nickattempt && pass) {
140 const char *nickserv_n = nickserv_nick(n);
143 log_network(LOG_INFO, n, "Ghosting current user using '%s'", nickattempt);
145 raw = g_strdup_printf("GHOST %s %s", nickattempt, pass);
146 network_send_args(n, "PRIVMSG", nickserv_n, raw, NULL);
148 network_send_args(n, "NICK", nickattempt, NULL);
156 gboolean nickserv_save(struct global *global, const char *dir)
158 char *filename = g_build_filename(dir, "nickserv", NULL);
162 fd = open(filename, O_WRONLY | O_CREAT, 0600);
165 log_global(LOG_WARNING, "Unable to write nickserv file `%s': %s", filename, strerror(errno));
170 for (gl = global->nickserv_nicks; gl; gl = gl->next) {
171 struct nickserv_entry *n = gl->data;
174 line = g_strdup_printf("%s\t%s\t%s\n", n->nick, n->pass, n->network?n->network:"*");
175 if (write(fd, line, strlen(line)) < 0) {
176 log_global(LOG_WARNING, "error writing line `%s': %s", line, strerror(errno));
188 gboolean nickserv_load(struct global *global)
190 char *filename = g_build_filename(global->config->config_dir, "nickserv", NULL);
195 gio = g_io_channel_new_file(filename, "r", NULL);
202 while (G_IO_STATUS_NORMAL == g_io_channel_read_line(gio, &ret, &nr, &term, NULL))
205 struct nickserv_entry *e;
209 parts = g_strsplit(ret, "\t", 3);
212 if (!parts[0] || !parts[1]) {
217 e = g_new0(struct nickserv_entry, 1);
220 if (!parts[2] || !strcmp(parts[2], "*")) {
224 e->network = parts[2];
227 global->nickserv_nicks = g_list_append(global->nickserv_nicks, e);
233 g_io_channel_shutdown(gio, TRUE, NULL);
234 g_io_channel_unref(gio);
239 void init_nickserv(void)
241 add_server_filter("nickserv", log_data, NULL, 1);