]> jfr.im git - irc/rqf/shadowircd.git/blame - modules/m_resv.c
Automated merge with ssh://hg.atheme.org//hg/charybdis
[irc/rqf/shadowircd.git] / modules / m_resv.c
CommitLineData
212380e3 1/*
2 * ircd-ratbox: A slightly useful ircd.
3 * m_resv.c: Reserves(jupes) a nickname or channel.
4 *
5 * Copyright (C) 2001-2002 Hybrid Development Team
6 * Copyright (C) 2002-2005 ircd-ratbox development team
7 *
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, write to the Free Software
20 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
21 * USA
22 *
23 * $Id: m_resv.c 3045 2006-12-26 23:16:57Z jilles $
24 */
25
26#include "stdinc.h"
27#include "client.h"
28#include "channel.h"
29#include "ircd.h"
30#include "numeric.h"
31#include "s_serv.h"
32#include "send.h"
33#include "msg.h"
34#include "parse.h"
35#include "modules.h"
36#include "s_conf.h"
37#include "s_newconf.h"
38#include "hash.h"
d3455e2c 39#include "logger.h"
212380e3 40
41static int mo_resv(struct Client *, struct Client *, int, const char **);
42static int ms_resv(struct Client *, struct Client *, int, const char **);
43static int me_resv(struct Client *, struct Client *, int, const char **);
44static int mo_unresv(struct Client *, struct Client *, int, const char **);
45static int ms_unresv(struct Client *, struct Client *, int, const char **);
46static int me_unresv(struct Client *, struct Client *, int, const char **);
47
48struct Message resv_msgtab = {
49 "RESV", 0, 0, 0, MFLG_SLOW | MFLG_UNREG,
50 {mg_ignore, mg_not_oper, {ms_resv, 4}, {ms_resv, 4}, {me_resv, 5}, {mo_resv, 3}}
51};
52struct Message unresv_msgtab = {
53 "UNRESV", 0, 0, 0, MFLG_SLOW | MFLG_UNREG,
54 {mg_ignore, mg_not_oper, {ms_unresv, 3}, {ms_unresv, 3}, {me_unresv, 2}, {mo_unresv, 2}}
55};
56
57mapi_clist_av1 resv_clist[] = { &resv_msgtab, &unresv_msgtab, NULL };
58DECLARE_MODULE_AV1(resv, NULL, NULL, resv_clist, NULL, NULL, "$Revision: 3045 $");
59
60static void parse_resv(struct Client *source_p, const char *name,
61 const char *reason, int temp_time);
62static void propagate_resv(struct Client *source_p, const char *target,
63 int temp_time, const char *name, const char *reason);
64static void cluster_resv(struct Client *source_p, int temp_time,
65 const char *name, const char *reason);
66
67static void handle_remote_unresv(struct Client *source_p, const char *name);
68static void remove_resv(struct Client *source_p, const char *name);
1328da86 69static int remove_resv_from_file(struct Client *source_p, const char *name);
212380e3 70
71/*
72 * mo_resv()
212380e3 73 * parv[1] = channel/nick to forbid
74 * parv[2] = reason
75 */
76static int
77mo_resv(struct Client *client_p, struct Client *source_p, int parc, const char *parv[])
78{
79 const char *name;
80 const char *reason;
81 const char *target_server = NULL;
82 int temp_time;
83 int loc = 1;
84
1ebe6ffc
JT
85 if(!IsOperResv(source_p))
86 {
87 sendto_one(source_p, form_str(ERR_NOPRIVS),
88 me.name, source_p->name, "resv");
89 return 0;
90 }
91
212380e3 92 /* RESV [time] <name> [ON <server>] :<reason> */
93
94 if((temp_time = valid_temp_time(parv[loc])) >= 0)
95 loc++;
96 /* we just set temp_time to -1! */
97 else
98 temp_time = 0;
99
100 name = parv[loc];
101 loc++;
102
103 if((parc >= loc+2) && (irccmp(parv[loc], "ON") == 0))
104 {
105 if(!IsOperRemoteBan(source_p))
106 {
107 sendto_one(source_p, form_str(ERR_NOPRIVS),
108 me.name, source_p->name, "remoteban");
109 return 0;
110 }
111
112 target_server = parv[loc+1];
113 loc += 2;
114 }
115
116 if(parc <= loc || EmptyString(parv[loc]))
117 {
118 sendto_one(source_p, form_str(ERR_NEEDMOREPARAMS),
119 me.name, source_p->name, "RESV");
120 return 0;
121 }
122
123 reason = parv[loc];
124
125 /* remote resv.. */
126 if(target_server)
127 {
128 propagate_resv(source_p, target_server, temp_time, name, reason);
129
130 if(match(target_server, me.name) == 0)
131 return 0;
132 }
08d11e34 133 else if(rb_dlink_list_length(&cluster_conf_list) > 0)
212380e3 134 cluster_resv(source_p, temp_time, name, reason);
135
136 parse_resv(source_p, name, reason, temp_time);
137
138 return 0;
139}
140
141/* ms_resv()
212380e3 142 * parv[1] = target server
143 * parv[2] = channel/nick to forbid
144 * parv[3] = reason
145 */
146static int
147ms_resv(struct Client *client_p, struct Client *source_p,
148 int parc, const char *parv[])
149{
77f3c1f4
JT
150 /* source_p parv[1] parv[2] parv[3]
151 * oper target server resv reason
212380e3 152 */
153 propagate_resv(source_p, parv[1], 0, parv[2], parv[3]);
154
155 if(!match(parv[1], me.name))
156 return 0;
157
158 if(!IsPerson(source_p))
159 return 0;
160
161 parse_resv(source_p, parv[2], parv[3], 0);
162 return 0;
163}
164
165static int
166me_resv(struct Client *client_p, struct Client *source_p,
167 int parc, const char *parv[])
168{
169 /* time name 0 :reason */
170 if(!IsPerson(source_p))
171 return 0;
172
173 parse_resv(source_p, parv[2], parv[4], atoi(parv[1]));
174 return 0;
175}
176
177/* parse_resv()
178 *
179 * inputs - source_p if error messages wanted
180 * - thing to resv
181 * - reason for resv
182 * outputs -
183 * side effects - will parse the resv and create it if valid
184 */
185static void
186parse_resv(struct Client *source_p, const char *name,
187 const char *reason, int temp_time)
188{
189 struct ConfItem *aconf;
190
191 if(!MyClient(source_p) &&
192 !find_shared_conf(source_p->username, source_p->host,
c88cdb00 193 source_p->servptr->name,
212380e3 194 (temp_time > 0) ? SHARED_TRESV : SHARED_PRESV))
195 return;
196
197 if(IsChannelName(name))
198 {
199 if(hash_find_resv(name))
200 {
201 sendto_one_notice(source_p,
202 ":A RESV has already been placed on channel: %s",
203 name);
204 return;
205 }
206
207 if(strlen(name) > CHANNELLEN)
208 {
209 sendto_one_notice(source_p, ":Invalid RESV length: %s",
210 name);
211 return;
212 }
6e5b8a5d
JT
213
214 if(strchr(name, ','))
215 {
216 sendto_one_notice(source_p,
217 ":Invalid character ',' in channel RESV");
218 return;
219 }
212380e3 220
221 if(strchr(reason, '"'))
222 {
223 sendto_one_notice(source_p,
224 ":Invalid character '\"' in comment");
225 return;
226 }
227
228 aconf = make_conf();
229 aconf->status = CONF_RESV_CHANNEL;
230 aconf->port = 0;
bf019bb0 231 aconf->name = rb_strdup(name);
62d28946 232 aconf->passwd = rb_strdup(reason);
bf019bb0 233 add_to_resv_hash(aconf->name, aconf);
212380e3 234
235 if(temp_time > 0)
236 {
9f6bbe3c 237 aconf->hold = rb_current_time() + temp_time;
212380e3 238
239 sendto_realops_snomask(SNO_GENERAL, L_ALL,
240 "%s added temporary %d min. RESV for [%s] [%s]",
241 get_oper_name(source_p), temp_time / 60,
242 name, reason);
243 ilog(L_KLINE, "R %s %d %s %s",
244 get_oper_name(source_p), temp_time / 60,
245 name, reason);
246 sendto_one_notice(source_p, ":Added temporary %d min. RESV [%s]",
247 temp_time / 60, name);
248 }
249 else
bf019bb0 250 write_confitem(RESV_TYPE, source_p, NULL, aconf->name,
212380e3 251 aconf->passwd, NULL, NULL, 0);
252 }
253 else if(clean_resv_nick(name))
254 {
255 if(strlen(name) > NICKLEN*2)
256 {
257 sendto_one_notice(source_p, ":Invalid RESV length: %s",
258 name);
259 return;
260 }
261
262 if(strchr(reason, '"'))
263 {
264 sendto_one_notice(source_p,
265 ":Invalid character '\"' in comment");
266 return;
267 }
268
269 if(!valid_wild_card_simple(name))
270 {
271 sendto_one_notice(source_p,
272 ":Please include at least %d non-wildcard "
273 "characters with the resv",
274 ConfigFileEntry.min_nonwildcard_simple);
275 return;
276 }
277
0fdb2570 278 if(find_nick_resv_mask(name))
212380e3 279 {
280 sendto_one_notice(source_p,
281 ":A RESV has already been placed on nick: %s",
282 name);
283 return;
284 }
285
286 aconf = make_conf();
287 aconf->status = CONF_RESV_NICK;
288 aconf->port = 0;
bf019bb0 289 aconf->name = rb_strdup(name);
62d28946 290 aconf->passwd = rb_strdup(reason);
7f4fa195 291 rb_dlinkAddAlloc(aconf, &resv_conf_list);
212380e3 292
293 if(temp_time > 0)
294 {
9f6bbe3c 295 aconf->hold = rb_current_time() + temp_time;
212380e3 296
297 sendto_realops_snomask(SNO_GENERAL, L_ALL,
298 "%s added temporary %d min. RESV for [%s] [%s]",
299 get_oper_name(source_p), temp_time / 60,
300 name, reason);
301 ilog(L_KLINE, "R %s %d %s %s",
302 get_oper_name(source_p), temp_time / 60,
303 name, reason);
304 sendto_one_notice(source_p, ":Added temporary %d min. RESV [%s]",
305 temp_time / 60, name);
306 }
307 else
bf019bb0 308 write_confitem(RESV_TYPE, source_p, NULL, aconf->name,
212380e3 309 aconf->passwd, NULL, NULL, 0);
310 }
311 else
312 sendto_one_notice(source_p,
313 ":You have specified an invalid resv: [%s]",
314 name);
315}
316
317static void
318propagate_resv(struct Client *source_p, const char *target,
319 int temp_time, const char *name, const char *reason)
320{
321 if(!temp_time)
322 {
323 sendto_match_servs(source_p, target,
324 CAP_CLUSTER, NOCAPS,
325 "RESV %s %s :%s",
326 target, name, reason);
327 sendto_match_servs(source_p, target,
328 CAP_ENCAP, CAP_CLUSTER,
329 "ENCAP %s RESV %d %s 0 :%s",
330 target, temp_time, name, reason);
331 }
332 else
333 sendto_match_servs(source_p, target,
334 CAP_ENCAP, NOCAPS,
335 "ENCAP %s RESV %d %s 0 :%s",
336 target, temp_time, name, reason);
337}
338
339static void
340cluster_resv(struct Client *source_p, int temp_time, const char *name,
341 const char *reason)
342{
343 struct remote_conf *shared_p;
08d11e34 344 rb_dlink_node *ptr;
212380e3 345
08d11e34 346 RB_DLINK_FOREACH(ptr, cluster_conf_list.head)
212380e3 347 {
348 shared_p = ptr->data;
349
350 /* old protocol cant handle temps, and we dont really want
351 * to convert them to perm.. --fl
352 */
353 if(!temp_time)
354 {
355 if(!(shared_p->flags & SHARED_PRESV))
356 continue;
357
358 sendto_match_servs(source_p, shared_p->server,
359 CAP_CLUSTER, NOCAPS,
360 "RESV %s %s :%s",
361 shared_p->server, name, reason);
362 sendto_match_servs(source_p, shared_p->server,
363 CAP_ENCAP, CAP_CLUSTER,
364 "ENCAP %s RESV 0 %s 0 :%s",
365 shared_p->server, name, reason);
366 }
367 else if(shared_p->flags & SHARED_TRESV)
368 sendto_match_servs(source_p, shared_p->server,
369 CAP_ENCAP, NOCAPS,
370 "ENCAP %s RESV %d %s 0 :%s",
371 shared_p->server, temp_time, name, reason);
372 }
373}
374
375
376/*
377 * mo_unresv()
212380e3 378 * parv[1] = channel/nick to unforbid
379 */
380static int
381mo_unresv(struct Client *client_p, struct Client *source_p, int parc, const char *parv[])
382{
1ebe6ffc
JT
383 if(!IsOperResv(source_p))
384 {
385 sendto_one(source_p, form_str(ERR_NOPRIVS),
386 me.name, source_p->name, "resv");
387 return 0;
388 }
389
212380e3 390 if((parc == 4) && (irccmp(parv[2], "ON") == 0))
391 {
392 if(!IsOperRemoteBan(source_p))
393 {
394 sendto_one(source_p, form_str(ERR_NOPRIVS),
395 me.name, source_p->name, "remoteban");
396 return 0;
397 }
398
399 propagate_generic(source_p, "UNRESV", parv[3], CAP_CLUSTER,
400 "%s", parv[1]);
401
402 if(match(parv[3], me.name) == 0)
403 return 0;
404 }
08d11e34 405 else if(rb_dlink_list_length(&cluster_conf_list) > 0)
212380e3 406 cluster_generic(source_p, "UNRESV", SHARED_UNRESV, CAP_CLUSTER,
407 "%s", parv[1]);
408
212380e3 409 remove_resv(source_p, parv[1]);
410 return 0;
411}
412
413/* ms_unresv()
212380e3 414 * parv[1] = target server
415 * parv[2] = resv to remove
416 */
417static int
418ms_unresv(struct Client *client_p, struct Client *source_p, int parc, const char *parv[])
419{
77f3c1f4
JT
420 /* source_p parv[1] parv[2]
421 * oper target server resv to remove
212380e3 422 */
423 propagate_generic(source_p, "UNRESV", parv[1], CAP_CLUSTER,
424 "%s", parv[2]);
425
426 if(!match(parv[1], me.name))
427 return 0;
428
429 if(!IsPerson(source_p))
430 return 0;
431
432 handle_remote_unresv(source_p, parv[2]);
433 return 0;
434}
435
436static int
437me_unresv(struct Client *client_p, struct Client *source_p, int parc, const char *parv[])
438{
439 /* name */
440 if(!IsPerson(source_p))
441 return 0;
442
443 handle_remote_unresv(source_p, parv[1]);
444 return 0;
445}
446
447static void
448handle_remote_unresv(struct Client *source_p, const char *name)
449{
450 if(!find_shared_conf(source_p->username, source_p->host,
c88cdb00 451 source_p->servptr->name, SHARED_UNRESV))
212380e3 452 return;
453
212380e3 454 remove_resv(source_p, name);
455
456 return;
457}
458
1328da86
JT
459static void
460remove_resv(struct Client *source_p, const char *name)
212380e3 461{
462 struct ConfItem *aconf = NULL;
463
464 if(IsChannelName(name))
465 {
466 if((aconf = hash_find_resv(name)) == NULL)
1328da86
JT
467 {
468 sendto_one_notice(source_p, ":No RESV for %s", name);
469 return;
470 }
212380e3 471
212380e3 472 if(!aconf->hold)
1328da86
JT
473 {
474 if (!remove_resv_from_file(source_p, name))
475 return;
476 }
477 else
478 {
479 sendto_one_notice(source_p, ":RESV for [%s] is removed", name);
480 sendto_realops_snomask(SNO_GENERAL, L_ALL,
481 "%s has removed the RESV for: [%s]",
482 get_oper_name(source_p), name);
483 ilog(L_KLINE, "UR %s %s", get_oper_name(source_p), name);
484 }
212380e3 485 del_from_resv_hash(name, aconf);
212380e3 486 }
487 else
488 {
08d11e34 489 rb_dlink_node *ptr;
212380e3 490
08d11e34 491 RB_DLINK_FOREACH(ptr, resv_conf_list.head)
212380e3 492 {
493 aconf = ptr->data;
494
bf019bb0 495 if(irccmp(aconf->name, name))
212380e3 496 aconf = NULL;
497 else
498 break;
499 }
500
501 if(aconf == NULL)
1328da86
JT
502 {
503 sendto_one_notice(source_p, ":No RESV for %s", name);
504 return;
505 }
212380e3 506
212380e3 507 if(!aconf->hold)
1328da86
JT
508 {
509 if (!remove_resv_from_file(source_p, name))
510 return;
511 }
512 else
513 {
514 sendto_one_notice(source_p, ":RESV for [%s] is removed", name);
515 sendto_realops_snomask(SNO_GENERAL, L_ALL,
516 "%s has removed the RESV for: [%s]",
517 get_oper_name(source_p), name);
518 ilog(L_KLINE, "UR %s %s", get_oper_name(source_p), name);
519 }
212380e3 520 /* already have ptr from the loop above.. */
9f6c3353 521 rb_dlinkDestroy(ptr, &resv_conf_list);
212380e3 522 }
1328da86 523 free_conf(aconf);
212380e3 524
1328da86 525 return;
212380e3 526}
527
1328da86 528/* remove_resv_from_file()
212380e3 529 *
530 * inputs - client removing the resv
531 * - resv to remove
532 * outputs -
1328da86
JT
533 * side effects - resv if found, is removed from conf
534 * - does not touch resv hash or resv_conf_list
212380e3 535 */
1328da86
JT
536static int
537remove_resv_from_file(struct Client *source_p, const char *name)
212380e3 538{
539 FILE *in, *out;
540 char buf[BUFSIZE];
541 char buff[BUFSIZE];
542 char temppath[BUFSIZE];
543 const char *filename;
544 mode_t oldumask;
545 char *p;
546 int error_on_write = 0;
547 int found_resv = 0;
548
581fa5c4 549 rb_sprintf(temppath, "%s.tmp", ConfigFileEntry.resvfile);
212380e3 550 filename = get_conf_name(RESV_TYPE);
551
552 if((in = fopen(filename, "r")) == NULL)
553 {
554 sendto_one_notice(source_p, ":Cannot open %s", filename);
1328da86 555 return 0;
212380e3 556 }
557
558 oldumask = umask(0);
559
560 if((out = fopen(temppath, "w")) == NULL)
561 {
562 sendto_one_notice(source_p, ":Cannot open %s", temppath);
563 fclose(in);
564 umask(oldumask);
1328da86 565 return 0;
212380e3 566 }
567
568 umask(oldumask);
569
570 while (fgets(buf, sizeof(buf), in))
571 {
572 const char *resv_name;
573
574 if(error_on_write)
575 {
576 if(temppath != NULL)
577 (void) unlink(temppath);
578
579 break;
580 }
581
907468c4 582 rb_strlcpy(buff, buf, sizeof(buff));
212380e3 583
584 if((p = strchr(buff, '\n')) != NULL)
585 *p = '\0';
586
587 if((*buff == '\0') || (*buff == '#'))
588 {
589 error_on_write = (fputs(buf, out) < 0) ? YES : NO;
590 continue;
591 }
592
593 if((resv_name = getfield(buff)) == NULL)
594 {
595 error_on_write = (fputs(buf, out) < 0) ? YES : NO;
596 continue;
597 }
598
599 if(irccmp(resv_name, name) == 0)
600 {
601 found_resv++;
602 }
603 else
604 {
605 error_on_write = (fputs(buf, out) < 0) ? YES : NO;
606 }
607 }
608
609 fclose(in);
610 if (fclose(out))
611 error_on_write = YES;
612
613 if(error_on_write)
614 {
615 sendto_one_notice(source_p, ":Couldn't write temp resv file, aborted");
1328da86 616 return 0;
212380e3 617 }
618 else if(!found_resv)
619 {
1328da86 620 sendto_one_notice(source_p, ":Cannot find RESV for %s in file", name);
212380e3 621
622 if(temppath != NULL)
623 (void) unlink(temppath);
624
1328da86 625 return 0;
212380e3 626 }
627
628 if (rename(temppath, filename))
629 {
630 sendto_one_notice(source_p, ":Couldn't rename temp file, aborted");
1328da86 631 return 0;
212380e3 632 }
212380e3 633
634 sendto_one_notice(source_p, ":RESV for [%s] is removed", name);
635 sendto_realops_snomask(SNO_GENERAL, L_ALL,
636 "%s has removed the RESV for: [%s]", get_oper_name(source_p), name);
637 ilog(L_KLINE, "UR %s %s", get_oper_name(source_p), name);
1328da86
JT
638
639 return 1;
212380e3 640}