ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/ermyth/src/services.C
Revision: 1.10
Committed: Sun Sep 16 18:54:45 2007 UTC (16 years, 9 months ago) by pippijn
Content type: text/plain
Branch: MAIN
Changes since 1.9: +7 -2 lines
Log Message:
#defines to enum

File Contents

# Content
1 /*
2 * services.C: Routines commonly used by various services.
3 *
4 * Copyright © 2007 Pippijn van Steenhoven / The Ermyth Team
5 * Rights to this code are as documented in COPYING.
6 *
7 *
8 * Portions of this file were derived from sources bearing the following license:
9 * Rights to this code are documented in doc/pod/license.pod.
10 * Copyright © 2005-2007 Atheme Project (http://www.atheme.org)
11 */
12
13 static char const rcsid[] = "$Id: services.C,v 1.9 2007-09-09 20:05:52 pippijn Exp $";
14
15 #include <boost/foreach.hpp>
16
17 #include "atheme.h"
18 #include "servers.h"
19 #include <account/myuser.h>
20 #include <account/mynick.h>
21 #include <account/mychan.h>
22 #include "pmodule.h"
23
24 unsigned authservice_loaded = 0;
25 int use_myuser_access = 0;
26 int use_svsignore = 0;
27
28 #define MAX_BUF 256
29
30 /* ban wrapper for cmode, returns number of bans added (0 or 1) */
31 int
32 ban (user_t *sender, channel_t *c, user_t *user)
33 {
34 char mask[MAX_BUF];
35 char modemask[MAX_BUF];
36 chanban_t *cb;
37
38 if (!c)
39 return 0;
40
41 snprintf (mask, MAX_BUF, "*!*@%s", user->vhost);
42 mask[MAX_BUF - 1] = '\0';
43
44 snprintf (modemask, MAX_BUF, "+b %s", mask);
45 modemask[MAX_BUF - 1] = '\0';
46
47 cb = chanban_find (c, mask, 'b');
48
49 if (cb != NULL)
50 return 0;
51
52 chanban_add (c, mask, 'b');
53
54 phandler->mode_sts (sender->nick, c, modemask);
55 return 1;
56 }
57
58 /* returns number of modes removed -- jilles */
59 int
60 remove_banlike (user_t *source, channel_t *chan, int type, user_t *target)
61 {
62 char change[MAX_BUF];
63 int count = 0;
64 node_t *n, *tn;
65 chanban_t *cb;
66
67 if (type == 0)
68 return 0;
69 if (source == NULL || chan == NULL || target == NULL)
70 return 0;
71
72 for (n = phandler->next_matching_ban (chan, target, type, chan->bans.head); n != NULL; n = phandler->next_matching_ban (chan, target, type, tn))
73 {
74 tn = n->next;
75 cb = static_cast<chanban_t *> (n->data);
76
77 snprintf (change, sizeof change, "-%c %s", cb->type, cb->mask);
78 phandler->mode_sts (source->nick, chan, change);
79 chanban_delete (cb);
80 count++;
81 }
82 return count;
83 }
84
85 /* returns number of exceptions removed -- jilles */
86 int
87 remove_ban_exceptions (user_t *source, channel_t *chan, user_t *target)
88 {
89 return remove_banlike (source, chan, ircd->except_mchar, target);
90 }
91
92 /* join a channel, creating it if necessary */
93 void
94 join (char const * const chan, char const * const nick)
95 {
96 channel_t *c;
97 user_t *u;
98 chanuser_t *cu;
99 bool isnew = false;
100 mychan_t *mc;
101 metadata *md;
102 time_t ts;
103
104 u = user_find_named (nick);
105 if (!u)
106 return;
107 c = channel_find (chan);
108 if (c == NULL)
109 {
110 mc = mychan_t::find (chan);
111 if (chansvs.changets && mc != NULL)
112 {
113 /* Use the previous TS if known, registration
114 * time otherwise, but never ever create a channel
115 * with TS 0 -- jilles */
116 ts = mc->registered;
117 md = mc->find_metadata ("private:channelts");
118 if (md != NULL)
119 ts = atol (md->value);
120 if (ts == 0)
121 ts = NOW;
122 }
123 else
124 ts = NOW;
125 c = channel_add (chan, ts, me.me);
126 c->modes |= CMODE_NOEXT | CMODE_TOPIC;
127 if (mc != NULL)
128 check_modes (mc, false);
129 isnew = true;
130 }
131 else if ((cu = chanuser_find (c, u)))
132 {
133 slog (LG_DEBUG, "join(): i'm already in `%s'", c->name);
134 return;
135 }
136 phandler->join_sts (c, u, isnew, channel_modes (c, true));
137 cu = chanuser_add (c, CLIENT_NAME (u));
138 cu->modes |= CMODE_OP;
139 if (isnew)
140 {
141 c->callback.add (c);
142 if (config_options.chan != NULL && !irccasecmp (config_options.chan, c->name))
143 joinall (config_options.chan);
144 }
145 }
146
147 /* part a channel */
148 void
149 part (char *chan, char *nick)
150 {
151 channel_t *c = channel_find (chan);
152 user_t *u = user_find_named (nick);
153
154 if (!u || !c)
155 return;
156 if (!chanuser_find (c, u))
157 return;
158 if (me.connected)
159 phandler->part_sts (c, u);
160 chanuser_delete (c, u);
161 }
162
163 void
164 services_init (void)
165 {
166 service_t *svs;
167
168 foreach (service_pair &sp, services)
169 {
170 svs = sp.second;
171 if (ircd->uses_uid && svs->me->uid[0] == '\0')
172 user_changeuid (svs->me, svs->uid);
173 else if (!ircd->uses_uid && svs->me->uid[0] != '\0')
174 user_changeuid (svs->me, NULL);
175 phandler->introduce_nick (svs->me);
176 }
177 }
178
179 void
180 joinall (char *name)
181 {
182 if (name == NULL)
183 return;
184
185 foreach (service_pair &sp, services)
186 join (name, sp.second->name);
187 }
188
189 void
190 partall (char *name)
191 {
192 service_t *svs;
193 mychan_t *mc;
194
195 if (name == NULL)
196 return;
197 mc = mychan_t::find (name);
198 foreach (service_pair &sp, services)
199 {
200 svs = sp.second;
201 if (svs == chansvs.me && mc != NULL && config_options.join_chans)
202 continue;
203 /* Do not cache this channel_find(), the
204 * channel may disappear under our feet
205 * -- jilles */
206 if (chanuser_find (channel_find (name), svs->me))
207 part (name, svs->name);
208 }
209 }
210
211 /* reintroduce a service e.g. after it's been killed -- jilles */
212 void
213 reintroduce_user (user_t *u)
214 {
215 node_t *n;
216 channel_t *c;
217 service_t *svs;
218
219 svs = find_service (u->nick);
220 if (svs == NULL)
221 {
222 slog (LG_DEBUG, "tried to reintroduce_user non-service %s", u->nick);
223 return;
224 }
225 phandler->introduce_nick (u);
226 LIST_FOREACH (n, u->channels.head)
227 {
228 c = ((chanuser_t *) n->data)->chan;
229 if (LIST_LENGTH (&c->members) > 1 || c->modes & ircd->perm_mode)
230 phandler->join_sts (c, u, 0, channel_modes (c, true));
231 else
232 {
233 /* channel will have been destroyed... */
234 /* XXX resend the bans instead of destroying them? */
235 chanban_clear (c);
236 phandler->join_sts (c, u, 1, channel_modes (c, true));
237 if (c->topic != NULL)
238 phandler->topic_sts (c, c->topic_setter, c->topicts, 0, c->topic);
239 }
240 }
241 }
242
243 void
244 verbose (mychan_t *mychan, char const * const fmt, ...)
245 {
246 va_list ap;
247 char buf[BUFSIZE];
248
249 if (mychan->chan == NULL)
250 return;
251
252 va_start (ap, fmt);
253 vsnprintf (buf, BUFSIZE, fmt, ap);
254 va_end (ap);
255
256 if ((MC_VERBOSE | MC_FORCEVERBOSE) & mychan->flags)
257 notice (chansvs.nick, mychan->name, "%s", buf);
258 else if (MC_VERBOSE_OPS & mychan->flags)
259 phandler->wallchops (chansvs.me->me, mychan->chan, buf);
260 }
261
262 void
263 snoop (char const * const fmt, ...)
264 {
265 va_list ap;
266 char buf[BUFSIZE];
267
268 if (!config_options.chan)
269 return;
270
271 if (me.bursting)
272 return;
273
274 if (!channel_find (config_options.chan))
275 return;
276
277 va_start (ap, fmt);
278 vsnprintf (buf, BUFSIZE, fmt, ap);
279 va_end (ap);
280
281 phandler->privmsg (opersvs.nick, config_options.chan, "%s", buf);
282 }
283
284 /* protocol wrapper for nickchange/nick burst */
285 void
286 handle_nickchange (user_t *u)
287 {
288 mynick_t *mn;
289
290 if (u == NULL)
291 return;
292
293 if (runflags & RF_LIVE && log_debug_enabled ())
294 notice (globsvs.nick, u->nick, "Services are presently running in debug mode, attached to a console. You should take extra caution when utilizing your services passwords.");
295
296 /* Only do the following checks if nicks are considered owned -- jilles */
297 if (nicksvs.me == NULL || nicksvs.no_nick_ownership)
298 return;
299
300 /* They're logged in, don't send them spam -- jilles */
301 if (u->myuser)
302 u->flags |= UF_SEENINFO;
303
304 /* Also don't send it if they came back from a split -- jilles */
305 if (!(u->server->flags & SF_EOB))
306 u->flags |= UF_SEENINFO;
307
308 if (!(mn = mynick_t::find (u->nick)))
309 {
310 if (!nicksvs.spam)
311 return;
312
313 if (!(u->flags & UF_SEENINFO))
314 {
315 notice (nicksvs.nick, u->nick, "Welcome to %s, %s! Here on %s, we provide services to enable the " "registration of nicknames and channels! For details, type \2/%s%s help\2 and \2/%s%s help\2.", me.netname, u->nick, me.netname, (ircd->uses_rcommand == false) ? "msg " : "", nicksvs.disp, (ircd->uses_rcommand == false) ? "msg " : "", chansvs.disp);
316
317 u->flags |= UF_SEENINFO;
318 }
319
320 return;
321 }
322
323 if (u->myuser == mn->owner)
324 {
325 mn->lastseen = NOW;
326 return;
327 }
328
329 /* OpenServices: is user on access list? -nenolod */
330 if (mn->owner->access_verify (u))
331 {
332 mn->lastseen = NOW;
333 return;
334 }
335
336 notice (nicksvs.nick, u->nick, _("This nickname is registered. Please choose a different nickname, or identify via \2/%s%s identify <password>\2."), (ircd->uses_rcommand == false) ? "msg " : "", nicksvs.disp);
337 mn->callback.enforce (mn, u);
338 }
339
340 /* User u is bursted as being logged in to login (if not NULL) or as
341 * being identified to their current nick (if login is NULL)
342 * Update the administration or log them out on ircd
343 * How to use this in protocol modules:
344 * 1. if login info is bursted in a command that always occurs, call
345 * this if the user is logged in, before handle_nickchange()
346 * 2. if it is bursted in a command that doesn't always occur, use
347 * netwide EOB as in the ratbox module; call this if the user is logged
348 * in; for all users, postpone handle_nickchange() until the user's
349 * server confirms EOB
350 * -- jilles
351 */
352 void
353 handle_burstlogin (user_t *u, char *login)
354 {
355 mynick_t *mn;
356 myuser_t *mu;
357
358 if (login != NULL)
359 /* don't allow alias nicks here -- jilles */
360 mu = myuser_t::find (login);
361 else
362 {
363 mn = mynick_t::find (u->nick);
364 mu = mn != NULL ? mn->owner : NULL;
365 login = mu != NULL ? mu->name : u->nick;
366 }
367 if (mu == NULL)
368 {
369 /* account dropped during split...
370 * if we have an authentication service, log them out */
371 slog (LG_DEBUG, "handle_burstlogin(): got nonexistent login %s for user %s", login, u->nick);
372 if (authservice_loaded)
373 {
374 notice (nicksvs.nick ? nicksvs.nick : me.name, u->nick, _("Account %s dropped, forcing logout"), login);
375 phandler->ircd_on_logout (u->nick, login, NULL);
376 }
377 return;
378 }
379 if (u->myuser != NULL) /* already logged in, hmm */
380 return;
381 if (mu->flags & MU_NOBURSTLOGIN && authservice_loaded)
382 {
383 /* no splits for this account, this bursted login cannot
384 * be legit...
385 * if we have an authentication service, log them out */
386 slog (LG_INFO, "handle_burstlogin(): got illegit login %s for user %s", login, u->nick);
387 notice (nicksvs.nick ? nicksvs.nick : me.name, u->nick, _("Login to account %s seems invalid, forcing logout"), login);
388 phandler->ircd_on_logout (u->nick, login, NULL);
389 return;
390 }
391 u->myuser = mu;
392 mu->logins.insert (u);
393 slog (LG_DEBUG, "handle_burstlogin(): automatically identified %s as %s", u->nick, login);
394 }
395
396 /* this could be done with more finesse, but hey! */
397 void
398 notice (char const * const from, char const * const to, char const * const message, va_list ap)
399 {
400 char buf[BUFSIZE];
401 user_t *u;
402 channel_t *c;
403
404 vsnprintf (buf, BUFSIZE, message, ap);
405
406 if (config_options.use_privmsg)
407 phandler->privmsg (from, to, "%s", buf);
408 else
409 {
410 if (*to == '#')
411 {
412 c = channel_find (to);
413 if (c != NULL)
414 phandler->notice_channel_sts (user_find_named (from), c, buf);
415 }
416 else
417 {
418 u = user_find_named (to);
419 if (u != NULL)
420 phandler->notice_user_sts (user_find_named (from), u, buf);
421 }
422 }
423 }
424
425 void
426 notice (char const * const from, char const * const to, char const * const message, ...)
427 {
428 va_list args;
429 char buf[BUFSIZE];
430 user_t *u;
431 channel_t *c;
432
433 va_start (args, message);
434 vsnprintf (buf, BUFSIZE, message, args);
435 va_end (args);
436
437 if (config_options.use_privmsg)
438 phandler->privmsg (from, to, "%s", buf);
439 else
440 {
441 if (*to == '#')
442 {
443 c = channel_find (to);
444 if (c != NULL)
445 phandler->notice_channel_sts (user_find_named (from), c, buf);
446 }
447 else
448 {
449 u = user_find_named (to);
450 if (u != NULL)
451 phandler->notice_user_sts (user_find_named (from), u, buf);
452 }
453 }
454 }
455
456 void
457 command_fail (sourceinfo_t *si, fault::code code, char const * const fmt, ...)
458 {
459 va_list args;
460 char buf[BUFSIZE];
461
462 va_start (args, fmt);
463 vsnprintf (buf, sizeof buf, fmt, args);
464 va_end (args);
465
466 if (si->su == NULL)
467 {
468 if (si->v != NULL && si->v->cmd_fail)
469 si->v->cmd_fail (si, code, buf);
470 return;
471 }
472
473 if (config_options.use_privmsg)
474 phandler->privmsg (si->service->name, si->su->nick, "%s", buf);
475 else
476 phandler->notice_user_sts (si->service->me, si->su, buf);
477 }
478
479 void
480 command_success_nodata (sourceinfo_t *si, char const * const fmt, ...)
481 {
482 va_list args;
483 char buf[BUFSIZE];
484
485 va_start (args, fmt);
486 vsnprintf (buf, BUFSIZE, fmt, args);
487 va_end (args);
488
489 if (si->su == NULL)
490 {
491 if (si->v != NULL && si->v->cmd_fail)
492 si->v->cmd_success_nodata (si, buf);
493 return;
494 }
495
496 if (config_options.use_privmsg)
497 phandler->privmsg (si->service->name, si->su->nick, "%s", buf);
498 else
499 phandler->notice_user_sts (si->service->me, si->su, buf);
500 }
501
502 void
503 command_success_string (sourceinfo_t *si, char const * const result, char const * const fmt, ...)
504 {
505 va_list args;
506 char buf[BUFSIZE];
507
508 va_start (args, fmt);
509 vsnprintf (buf, BUFSIZE, fmt, args);
510 va_end (args);
511
512 if (si->su == NULL)
513 {
514 if (si->v != NULL && si->v->cmd_fail)
515 si->v->cmd_success_string (si, result, buf);
516 return;
517 }
518
519 if (config_options.use_privmsg)
520 phandler->privmsg (si->service->name, si->su->nick, "%s", buf);
521 else
522 phandler->notice_user_sts (si->service->me, si->su, buf);
523 }
524
525 static void
526 command_table_cb (char const * const line, void *data)
527 {
528 command_success_nodata (static_cast<sourceinfo_t *> (data), "%s", line);
529 }
530
531 void
532 command_success_table (sourceinfo_t *si, table_t * table)
533 {
534 table_render (table, command_table_cb, si);
535 }
536
537 char const * const
538 get_source_name (sourceinfo_t *si)
539 {
540 static char result[NICKLEN + NICKLEN + 10];
541
542 if (si->su != NULL)
543 {
544 if (si->smu && !irccasecmp (si->su->nick, si->smu->name))
545 snprintf (result, sizeof result, "%s", si->su->nick);
546 else
547 snprintf (result, sizeof result, "%s(%s)", si->su->nick, si->smu ? si->smu->name : "");
548 }
549 else if (si->s != NULL)
550 snprintf (result, sizeof result, "%s", si->s->name);
551 else
552 {
553 snprintf (result, sizeof result, "<%s>%s", si->v->description, si->smu ? si->smu->name : "");
554 }
555 return result;
556 }
557
558 char const * const
559 get_source_mask (sourceinfo_t *si)
560 {
561 static char result[NICKLEN + USERLEN + HOSTLEN + 10];
562
563 if (si->su != NULL)
564 {
565 snprintf (result, sizeof result, "%s!%s@%s", si->su->nick, si->su->user, si->su->vhost);
566 }
567 else if (si->s != NULL)
568 snprintf (result, sizeof result, "%s", si->s->name);
569 else
570 {
571 snprintf (result, sizeof result, "<%s>%s", si->v->description, si->smu ? si->smu->name : "");
572 }
573 return result;
574 }
575
576 char const * const
577 get_oper_name (sourceinfo_t *si)
578 {
579 static char result[NICKLEN + USERLEN + HOSTLEN + NICKLEN + 10];
580
581 if (si->su != NULL)
582 {
583 if (si->smu == NULL)
584 snprintf (result, sizeof result, "%s!%s@%s{%s}", si->su->nick, si->su->user, si->su->vhost, si->su->server->name);
585 else if (!irccasecmp (si->su->nick, si->smu->name))
586 snprintf (result, sizeof result, "%s", si->su->nick);
587 else
588 snprintf (result, sizeof result, "%s(%s)", si->su->nick, si->smu ? si->smu->name : "");
589 }
590 else if (si->s != NULL)
591 snprintf (result, sizeof result, "%s", si->s->name);
592 else
593 {
594 snprintf (result, sizeof result, "<%s>%s", si->v->description, si->smu ? si->smu->name : "");
595 }
596 return result;
597 }
598
599 void
600 wallops (char const * const fmt, ...)
601 {
602 va_list args;
603 char buf[BUFSIZE];
604
605 if (config_options.silent)
606 return;
607
608 va_start (args, fmt);
609 vsnprintf (buf, BUFSIZE, fmt, args);
610 va_end (args);
611
612 if (me.me != NULL && me.connected)
613 phandler->wallops_sts (buf);
614 else
615 slog (LG_ERROR, "wallops(): unable to send: %s", buf);
616 }
617
618 void
619 verbose_wallops (char const * const fmt, ...)
620 {
621 va_list args;
622 char buf[BUFSIZE];
623
624 if (config_options.silent || !config_options.verbose_wallops)
625 return;
626
627 va_start (args, fmt);
628 vsnprintf (buf, BUFSIZE, fmt, args);
629 va_end (args);
630
631 if (me.me != NULL && me.connected)
632 phandler->wallops_sts (buf);
633 else
634 slog (LG_ERROR, "verbose_wallops(): unable to send: %s", buf);
635 }