ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/ermyth/src/services.C
Revision: 1.11
Committed: Sat Sep 22 14:27:30 2007 UTC (16 years, 7 months ago) by pippijn
Content type: text/plain
Branch: MAIN
CVS Tags: HEAD
Changes since 1.10: +13 -22 lines
Log Message:
split up ermyth into ermyth-modules, libermyth (currently just ermyth-util) and ermyth-core

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