ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/ermyth/src/services.C
Revision: 1.8
Committed: Wed Sep 5 11:23:15 2007 UTC (16 years, 8 months ago) by pippijn
Content type: text/plain
Branch: MAIN
Changes since 1.7: +2 -2 lines
Log Message:
removed GPLed code and put license back to BSD

File Contents

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