1 /* $Id: automode.c 448 2010-08-22 09:20:48Z tsaviran $
2  * -------------------------------------------------------
3  * Copyright (C) 2002-2006 Tommi Saviranta <wnd@iki.fi>
4  * -------------------------------------------------------
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.
9  *
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.
14  */
15 
16 
17 
18 #ifdef HAVE_CONFIG_H
19 #include <config.h>
20 #endif /* ifdef HAVE_CONFIG_H */
21 
22 #ifdef AUTOMODE
23 
24 #include "automode.h"
25 #include "common.h"
26 #include "llist.h"
27 #include "perm.h"
28 #include "irc.h"
29 #include "miau.h"
30 /* vsnprintf */
31 #include "tools.h"
32 
33 #include <string.h>
34 
35 #if HAVE_STRINGS_H
36 #include <strings.h>
37 #endif
38 
39 
40 
41 permlist_type	automodelist;
42 
43 
44 
45 static inline void
automode_drop(automode_type * line,llist_node * node,llist_list * list)46 automode_drop(automode_type *line, llist_node *node, llist_list *list)
47 {
48 	xfree(line->nick);
49 	xfree(node->data);
50 	llist_delete(node, list);
51 } /* static inline void automode_drop(automode_type *line, llist_node *node,
52 		llist_list *list) */
53 
54 
55 
56 /*
57  * Process mode queues.
58  */
59 void
automode_do(void)60 automode_do(void)
61 {
62 	channel_type *channel;
63 	char modes[4];
64 	char *nicks;
65 	size_t size;
66 
67 	size = 1;
68 	nicks = (char *) xmalloc(size);
69 
70 	LLIST_WALK_H(active_channels.head, channel_type *);
71 		channel = data;
72 
73 		if (channel->oper == 1) {
74 			int count;
75 			size_t nlen, tlen;
76 
77 			nicks[0] = '\0'; /* clear nicks */
78 			tlen = 1;
79 			memset(modes, 0, sizeof(modes)); /* clear modes */
80 			count = 0;
81 
82 			/* Commit three modes at a time. */
83 			LLIST_WALK_H(channel->mode_queue.head, automode_type *);
84 				modes[count] = data->mode;
85 				/* paranoid */
86 				nlen = strlen(data->nick);
87 				tlen += nlen + 1;
88 				if (tlen > size) {
89 					size = tlen;
90 					nicks = (char *) xrealloc(nicks, size);
91 				}
92 				strcat(nicks, " ");
93 				strncat(nicks, data->nick, nlen);
94 				count++;
95 				if (count == 3) {
96 					irc_write_head(&c_server,
97 							"MODE %s +%s%s",
98 							channel->name,
99 							modes,
100 							nicks);
101 					/* clear nicks and modes */
102 					nicks[0] = '\0';
103 					memset(modes, 0, 4);
104 					count = 0;
105 					tlen = 1;
106 				}
107 			LLIST_WALK_F; /* walk queue */
108 
109 			/* Commit remaining modes. */
110 			if (count > 0) {
111 				irc_write_head(&c_server,
112 						"MODE %s +%s%s",
113 						channel->name,
114 						modes,
115 						nicks);
116 			}
117 
118 			/* Clear mode-queue as there are all now processed. */
119 			automode_clear(&data->mode_queue);
120 		} /* if (channel->oper == 1) */
121 	LLIST_WALK_F;	/* walk channels */
122 
123 	xfree(nicks);
124 } /* void automode_do(void) */
125 
126 
127 
128 /*
129  * Add nick to be auto-opped.
130  */
131 void
automode_queue(const char * nick,const char * hostname,channel_type * channel)132 automode_queue(const char *nick, const char *hostname, channel_type *channel)
133 {
134 	automode_type	*modeact;
135 	char		*mask;
136 	size_t		msize;
137 
138 	char		modes[] = "ov";
139 	int		mode_c;
140 
141 	/* termination and validity guaranteed */
142 	msize = strlen(nick) + strlen(hostname) + strlen(channel->name) + 5;
143 	mask = (char *) xmalloc(msize);
144 
145 	for (mode_c = 0; mode_c < 2; mode_c++) { /* strlen(ov) - 1 */
146 		/* generate mask and see if any automode should take place */
147 		snprintf(mask, msize, "%c:%s!%s/%s",
148 				modes[mode_c], nick, hostname,
149 				channel->name);
150 		mask[msize - 1] = '\0';
151 		if (is_perm(&automodelist, mask) && automode_lookup(nick,
152 					channel, modes[mode_c]) == NULL) {
153 			modeact = (automode_type *)
154 				xmalloc(sizeof(automode_type));
155 			modeact->nick = xstrdup(nick);
156 			modeact->mode = modes[mode_c];
157 			llist_add_tail(llist_create(modeact),
158 					&channel->mode_queue);
159 			status.automodes++;
160 		}
161 	}
162 
163 	xfree(mask);
164 } /* void automode_queue(const char *, const char *, channel_type *) */
165 
166 
167 
168 void
automode_clear(llist_list * queue)169 automode_clear(llist_list *queue)
170 {
171 	LLIST_WALK_H(queue->head, automode_type *);
172 		automode_drop(data, node, queue);
173 		status.automodes--;
174 	LLIST_WALK_F;
175 } /* void automode_clear(llist_list *queue) */
176 
177 
178 
179 /*
180  * Drop automode action from automode -queue.
181  *
182  * If mode == NULL, don't care about mode.
183  * If channel == NULL, don't care about channel.
184  */
185 void
automode_drop_nick(const char * nick,const char mode)186 automode_drop_nick(const char *nick, const char mode)
187 {
188 	LLIST_WALK_H(active_channels.head, channel_type *);
189 		automode_drop_channel(data, nick, mode);
190 	LLIST_WALK_F;
191 } /* void automode_drop_nick(const char *, const char) */
192 
193 
194 
195 void
automode_drop_channel(channel_type * channel,const char * nick,const char mode)196 automode_drop_channel(channel_type *channel, const char *nick, const char mode)
197 {
198 	int nick_ok, mode_ok;
199 	LLIST_WALK_H(channel->mode_queue.head, automode_type *);
200 		nick_ok = (nick == NULL) || (nick != NULL
201 				&& xstrcasecmp(nick, data->nick) == 0);
202 		mode_ok = (mode == '\0') || (mode == data->mode);
203 		if (nick_ok == 1 && mode_ok == 1) {
204 			automode_drop(data, node, &channel->mode_queue);
205 		}
206 	LLIST_WALK_F;
207 } /* void automode_drop_channel(channel_type *, const char *, const char) */
208 
209 
210 
211 /*
212  * Look up for an auto-op action in queue.
213  *
214  * Returns pointer to automode_type if found, otherwise NULL:
215  */
216 llist_node *
automode_lookup(const char * nick,channel_type * channel,const char mode)217 automode_lookup(const char *nick, channel_type *channel, const char mode)
218 {
219 	llist_node	*ptr;
220 	automode_type	*modeact;
221 
222 	for (ptr = channel->mode_queue.head; ptr != NULL; ptr = ptr->next) {
223 		modeact = (automode_type *) ptr->data;
224 		if (xstrcasecmp(nick, modeact->nick) == 0
225 				&& modeact->mode == mode) {
226 			return ptr;
227 		}
228 	}
229 
230 	return NULL;
231 } /* llist_nost *automode_lookup(channel_type *, const char *, const char) */
232 
233 
234 
235 #endif /* ifdef AUTOMODE */
236