]> jfr.im git - irc/rqf/shadowircd.git/blob - modules/m_resv.c
Disallow commas in channel resvs.
[irc/rqf/shadowircd.git] / modules / m_resv.c
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"
39 #include "logger.h"
40
41 static int mo_resv(struct Client *, struct Client *, int, const char **);
42 static int ms_resv(struct Client *, struct Client *, int, const char **);
43 static int me_resv(struct Client *, struct Client *, int, const char **);
44 static int mo_unresv(struct Client *, struct Client *, int, const char **);
45 static int ms_unresv(struct Client *, struct Client *, int, const char **);
46 static int me_unresv(struct Client *, struct Client *, int, const char **);
47
48 struct 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 };
52 struct 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
57 mapi_clist_av1 resv_clist[] = { &resv_msgtab, &unresv_msgtab, NULL };
58 DECLARE_MODULE_AV1(resv, NULL, NULL, resv_clist, NULL, NULL, "$Revision: 3045 $");
59
60 static void parse_resv(struct Client *source_p, const char *name,
61 const char *reason, int temp_time);
62 static void propagate_resv(struct Client *source_p, const char *target,
63 int temp_time, const char *name, const char *reason);
64 static void cluster_resv(struct Client *source_p, int temp_time,
65 const char *name, const char *reason);
66
67 static void handle_remote_unresv(struct Client *source_p, const char *name);
68 static void remove_resv(struct Client *source_p, const char *name);
69 static int remove_resv_from_file(struct Client *source_p, const char *name);
70
71 /*
72 * mo_resv()
73 * parv[1] = channel/nick to forbid
74 * parv[2] = reason
75 */
76 static int
77 mo_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
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
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 }
133 else if(rb_dlink_list_length(&cluster_conf_list) > 0)
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()
142 * parv[1] = target server
143 * parv[2] = channel/nick to forbid
144 * parv[3] = reason
145 */
146 static int
147 ms_resv(struct Client *client_p, struct Client *source_p,
148 int parc, const char *parv[])
149 {
150 /* source_p parv[1] parv[2] parv[3]
151 * oper target server resv reason
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
165 static int
166 me_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 */
185 static void
186 parse_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,
193 source_p->servptr->name,
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 }
213
214 if(strchr(name, ','))
215 {
216 sendto_one_notice(source_p,
217 ":Invalid character ',' in channel RESV");
218 return;
219 }
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;
231 aconf->name = rb_strdup(name);
232 aconf->passwd = rb_strdup(reason);
233 add_to_resv_hash(aconf->name, aconf);
234
235 if(temp_time > 0)
236 {
237 aconf->hold = rb_current_time() + temp_time;
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
250 write_confitem(RESV_TYPE, source_p, NULL, aconf->name,
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
278 if(find_nick_resv_mask(name))
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;
289 aconf->name = rb_strdup(name);
290 aconf->passwd = rb_strdup(reason);
291 rb_dlinkAddAlloc(aconf, &resv_conf_list);
292
293 if(temp_time > 0)
294 {
295 aconf->hold = rb_current_time() + temp_time;
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
308 write_confitem(RESV_TYPE, source_p, NULL, aconf->name,
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
317 static void
318 propagate_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
339 static void
340 cluster_resv(struct Client *source_p, int temp_time, const char *name,
341 const char *reason)
342 {
343 struct remote_conf *shared_p;
344 rb_dlink_node *ptr;
345
346 RB_DLINK_FOREACH(ptr, cluster_conf_list.head)
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()
378 * parv[1] = channel/nick to unforbid
379 */
380 static int
381 mo_unresv(struct Client *client_p, struct Client *source_p, int parc, const char *parv[])
382 {
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
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 }
405 else if(rb_dlink_list_length(&cluster_conf_list) > 0)
406 cluster_generic(source_p, "UNRESV", SHARED_UNRESV, CAP_CLUSTER,
407 "%s", parv[1]);
408
409 remove_resv(source_p, parv[1]);
410 return 0;
411 }
412
413 /* ms_unresv()
414 * parv[1] = target server
415 * parv[2] = resv to remove
416 */
417 static int
418 ms_unresv(struct Client *client_p, struct Client *source_p, int parc, const char *parv[])
419 {
420 /* source_p parv[1] parv[2]
421 * oper target server resv to remove
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
436 static int
437 me_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
447 static void
448 handle_remote_unresv(struct Client *source_p, const char *name)
449 {
450 if(!find_shared_conf(source_p->username, source_p->host,
451 source_p->servptr->name, SHARED_UNRESV))
452 return;
453
454 remove_resv(source_p, name);
455
456 return;
457 }
458
459 static void
460 remove_resv(struct Client *source_p, const char *name)
461 {
462 struct ConfItem *aconf = NULL;
463
464 if(IsChannelName(name))
465 {
466 if((aconf = hash_find_resv(name)) == NULL)
467 {
468 sendto_one_notice(source_p, ":No RESV for %s", name);
469 return;
470 }
471
472 if(!aconf->hold)
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 }
485 del_from_resv_hash(name, aconf);
486 }
487 else
488 {
489 rb_dlink_node *ptr;
490
491 RB_DLINK_FOREACH(ptr, resv_conf_list.head)
492 {
493 aconf = ptr->data;
494
495 if(irccmp(aconf->name, name))
496 aconf = NULL;
497 else
498 break;
499 }
500
501 if(aconf == NULL)
502 {
503 sendto_one_notice(source_p, ":No RESV for %s", name);
504 return;
505 }
506
507 if(!aconf->hold)
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 }
520 /* already have ptr from the loop above.. */
521 rb_dlinkDestroy(ptr, &resv_conf_list);
522 }
523 free_conf(aconf);
524
525 return;
526 }
527
528 /* remove_resv_from_file()
529 *
530 * inputs - client removing the resv
531 * - resv to remove
532 * outputs -
533 * side effects - resv if found, is removed from conf
534 * - does not touch resv hash or resv_conf_list
535 */
536 static int
537 remove_resv_from_file(struct Client *source_p, const char *name)
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
549 rb_sprintf(temppath, "%s.tmp", ConfigFileEntry.resvfile);
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);
555 return 0;
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);
565 return 0;
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
582 rb_strlcpy(buff, buf, sizeof(buff));
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");
616 return 0;
617 }
618 else if(!found_resv)
619 {
620 sendto_one_notice(source_p, ":Cannot find RESV for %s in file", name);
621
622 if(temppath != NULL)
623 (void) unlink(temppath);
624
625 return 0;
626 }
627
628 if (rename(temppath, filename))
629 {
630 sendto_one_notice(source_p, ":Couldn't rename temp file, aborted");
631 return 0;
632 }
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);
638
639 return 1;
640 }