]> jfr.im git - irc/rqf/shadowircd.git/blob - modules/m_resv.c
Update TODO
[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[0] = sender prefix
74 * parv[1] = channel/nick to forbid
75 * parv[2] = reason
76 */
77 static int
78 mo_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
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
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 }
134 else if(rb_dlink_list_length(&cluster_conf_list) > 0)
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 */
148 static int
149 ms_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
167 static int
168 me_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 */
187 static void
188 parse_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,
195 source_p->servptr->name,
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;
226 aconf->name = rb_strdup(name);
227 aconf->passwd = rb_strdup(reason);
228 add_to_resv_hash(aconf->name, aconf);
229
230 if(temp_time > 0)
231 {
232 aconf->hold = rb_current_time() + temp_time;
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
245 write_confitem(RESV_TYPE, source_p, NULL, aconf->name,
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
273 if(find_nick_resv_mask(name))
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;
284 aconf->name = rb_strdup(name);
285 aconf->passwd = rb_strdup(reason);
286 rb_dlinkAddAlloc(aconf, &resv_conf_list);
287
288 if(temp_time > 0)
289 {
290 aconf->hold = rb_current_time() + temp_time;
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
303 write_confitem(RESV_TYPE, source_p, NULL, aconf->name,
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
312 static void
313 propagate_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
334 static void
335 cluster_resv(struct Client *source_p, int temp_time, const char *name,
336 const char *reason)
337 {
338 struct remote_conf *shared_p;
339 rb_dlink_node *ptr;
340
341 RB_DLINK_FOREACH(ptr, cluster_conf_list.head)
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 */
376 static int
377 mo_unresv(struct Client *client_p, struct Client *source_p, int parc, const char *parv[])
378 {
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
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 }
401 else if(rb_dlink_list_length(&cluster_conf_list) > 0)
402 cluster_generic(source_p, "UNRESV", SHARED_UNRESV, CAP_CLUSTER,
403 "%s", parv[1]);
404
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 */
414 static int
415 ms_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
433 static int
434 me_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
444 static void
445 handle_remote_unresv(struct Client *source_p, const char *name)
446 {
447 if(!find_shared_conf(source_p->username, source_p->host,
448 source_p->servptr->name, SHARED_UNRESV))
449 return;
450
451 remove_resv(source_p, name);
452
453 return;
454 }
455
456 static void
457 remove_resv(struct Client *source_p, const char *name)
458 {
459 struct ConfItem *aconf = NULL;
460
461 if(IsChannelName(name))
462 {
463 if((aconf = hash_find_resv(name)) == NULL)
464 {
465 sendto_one_notice(source_p, ":No RESV for %s", name);
466 return;
467 }
468
469 if(!aconf->hold)
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 }
482 del_from_resv_hash(name, aconf);
483 }
484 else
485 {
486 rb_dlink_node *ptr;
487
488 RB_DLINK_FOREACH(ptr, resv_conf_list.head)
489 {
490 aconf = ptr->data;
491
492 if(irccmp(aconf->name, name))
493 aconf = NULL;
494 else
495 break;
496 }
497
498 if(aconf == NULL)
499 {
500 sendto_one_notice(source_p, ":No RESV for %s", name);
501 return;
502 }
503
504 if(!aconf->hold)
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 }
517 /* already have ptr from the loop above.. */
518 rb_dlinkDestroy(ptr, &resv_conf_list);
519 }
520 free_conf(aconf);
521
522 return;
523 }
524
525 /* remove_resv_from_file()
526 *
527 * inputs - client removing the resv
528 * - resv to remove
529 * outputs -
530 * side effects - resv if found, is removed from conf
531 * - does not touch resv hash or resv_conf_list
532 */
533 static int
534 remove_resv_from_file(struct Client *source_p, const char *name)
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
546 rb_sprintf(temppath, "%s.tmp", ConfigFileEntry.resvfile);
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);
552 return 0;
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);
562 return 0;
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
579 rb_strlcpy(buff, buf, sizeof(buff));
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");
613 return 0;
614 }
615 else if(!found_resv)
616 {
617 sendto_one_notice(source_p, ":Cannot find RESV for %s in file", name);
618
619 if(temppath != NULL)
620 (void) unlink(temppath);
621
622 return 0;
623 }
624
625 if (rename(temppath, filename))
626 {
627 sendto_one_notice(source_p, ":Couldn't rename temp file, aborted");
628 return 0;
629 }
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);
635
636 return 1;
637 }