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