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, 8 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

# User Rev Content
1 pippijn 1.1 /*
2     * services.C: Routines commonly used by various services.
3 pippijn 1.10 *
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 pippijn 1.2 * Rights to this code are documented in doc/pod/license.pod.
10 pippijn 1.5 * Copyright © 2005-2007 Atheme Project (http://www.atheme.org)
11 pippijn 1.1 */
12    
13 pippijn 1.11 static char const rcsid[] = "$Id: services.C,v 1.10 2007-09-16 18:54:45 pippijn Exp $";
14 pippijn 1.5
15     #include <boost/foreach.hpp>
16 pippijn 1.1
17     #include "atheme.h"
18 pippijn 1.11 #include <libermyth.h>
19 pippijn 1.9 #include "servers.h"
20 pippijn 1.1 #include <account/myuser.h>
21     #include <account/mynick.h>
22     #include <account/mychan.h>
23     #include "pmodule.h"
24    
25 pippijn 1.5 unsigned authservice_loaded = 0;
26 pippijn 1.1 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 pippijn 1.5 phandler->mode_sts (sender->nick, c, modemask);
56 pippijn 1.1 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 pippijn 1.5 for (n = phandler->next_matching_ban (chan, target, type, chan->bans.head); n != NULL; n = phandler->next_matching_ban (chan, target, type, tn))
74 pippijn 1.1 {
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 pippijn 1.5 phandler->mode_sts (source->nick, chan, change);
80 pippijn 1.1 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 pippijn 1.5 join (char const * const chan, char const * const nick)
96 pippijn 1.1 {
97     channel_t *c;
98     user_t *u;
99     chanuser_t *cu;
100     bool isnew = false;
101     mychan_t *mc;
102 pippijn 1.8 metadata *md;
103 pippijn 1.1 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 pippijn 1.7 mc = mychan_t::find (chan);
112 pippijn 1.1 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 pippijn 1.5 phandler->join_sts (c, u, isnew, channel_modes (c, true));
138 pippijn 1.1 cu = chanuser_add (c, CLIENT_NAME (u));
139     cu->modes |= CMODE_OP;
140     if (isnew)
141     {
142 pippijn 1.5 c->callback.add (c);
143 pippijn 1.1 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 pippijn 1.5 phandler->part_sts (c, u);
161 pippijn 1.1 chanuser_delete (c, u);
162     }
163    
164     void
165     services_init (void)
166     {
167     service_t *svs;
168    
169 pippijn 1.5 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 pippijn 1.1 }
179    
180     void
181     joinall (char *name)
182     {
183     if (name == NULL)
184     return;
185    
186 pippijn 1.5 foreach (service_pair &sp, services)
187     join (name, sp.second->name);
188 pippijn 1.1 }
189    
190     void
191     partall (char *name)
192     {
193     service_t *svs;
194     mychan_t *mc;
195    
196     if (name == NULL)
197     return;
198 pippijn 1.7 mc = mychan_t::find (name);
199 pippijn 1.5 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 pippijn 1.1 }
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 pippijn 1.5 phandler->introduce_nick (u);
227 pippijn 1.1 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 pippijn 1.5 phandler->join_sts (c, u, 0, channel_modes (c, true));
232 pippijn 1.1 else
233     {
234     /* channel will have been destroyed... */
235     /* XXX resend the bans instead of destroying them? */
236     chanban_clear (c);
237 pippijn 1.5 phandler->join_sts (c, u, 1, channel_modes (c, true));
238 pippijn 1.1 if (c->topic != NULL)
239 pippijn 1.5 phandler->topic_sts (c, c->topic_setter, c->topicts, 0, c->topic);
240 pippijn 1.1 }
241     }
242     }
243    
244     void
245 pippijn 1.5 verbose (mychan_t *mychan, char const * const fmt, ...)
246 pippijn 1.1 {
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 pippijn 1.5 phandler->wallchops (chansvs.me->me, mychan->chan, buf);
261 pippijn 1.1 }
262    
263     void
264 pippijn 1.5 snoop (char const * const fmt, ...)
265 pippijn 1.1 {
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 pippijn 1.5 phandler->privmsg (opersvs.nick, config_options.chan, "%s", buf);
283 pippijn 1.1 }
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 pippijn 1.9 if (!(mn = mynick_t::find (u->nick)))
310 pippijn 1.1 {
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 pippijn 1.6 if (mn->owner->access_verify (u))
332 pippijn 1.1 {
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 pippijn 1.5 mn->callback.enforce (mn, u);
339 pippijn 1.1 }
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 pippijn 1.6 mu = myuser_t::find (login);
362 pippijn 1.1 else
363     {
364 pippijn 1.9 mn = mynick_t::find (u->nick);
365 pippijn 1.1 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 pippijn 1.5 phandler->ircd_on_logout (u->nick, login, NULL);
377 pippijn 1.1 }
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 pippijn 1.5 phandler->ircd_on_logout (u->nick, login, NULL);
390 pippijn 1.1 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 pippijn 1.5 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 pippijn 1.1 {
429     va_list args;
430     char buf[BUFSIZE];
431     user_t *u;
432     channel_t *c;
433    
434 pippijn 1.5 va_start (args, message);
435     vsnprintf (buf, BUFSIZE, message, args);
436 pippijn 1.1 va_end (args);
437    
438     if (config_options.use_privmsg)
439 pippijn 1.5 phandler->privmsg (from, to, "%s", buf);
440 pippijn 1.1 else
441     {
442     if (*to == '#')
443     {
444     c = channel_find (to);
445     if (c != NULL)
446 pippijn 1.5 phandler->notice_channel_sts (user_find_named (from), c, buf);
447 pippijn 1.1 }
448     else
449     {
450     u = user_find_named (to);
451     if (u != NULL)
452 pippijn 1.5 phandler->notice_user_sts (user_find_named (from), u, buf);
453 pippijn 1.1 }
454     }
455     }
456    
457     void
458 pippijn 1.7 command_fail (sourceinfo_t *si, fault::code code, char const * const fmt, ...)
459 pippijn 1.1 {
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 pippijn 1.5 phandler->privmsg (si->service->name, si->su->nick, "%s", buf);
476 pippijn 1.1 else
477 pippijn 1.5 phandler->notice_user_sts (si->service->me, si->su, buf);
478 pippijn 1.1 }
479    
480     void
481 pippijn 1.5 command_success_nodata (sourceinfo_t *si, char const * const fmt, ...)
482 pippijn 1.1 {
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 pippijn 1.5 phandler->privmsg (si->service->name, si->su->nick, "%s", buf);
499 pippijn 1.1 else
500 pippijn 1.5 phandler->notice_user_sts (si->service->me, si->su, buf);
501 pippijn 1.1 }
502    
503     void
504 pippijn 1.5 command_success_string (sourceinfo_t *si, char const * const result, char const * const fmt, ...)
505 pippijn 1.1 {
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 pippijn 1.5 phandler->privmsg (si->service->name, si->su->nick, "%s", buf);
522 pippijn 1.1 else
523 pippijn 1.5 phandler->notice_user_sts (si->service->me, si->su, buf);
524 pippijn 1.1 }
525    
526     static void
527 pippijn 1.5 command_table_cb (char const * const line, void *data)
528 pippijn 1.1 {
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 pippijn 1.11 char const *
539 pippijn 1.1 get_source_name (sourceinfo_t *si)
540     {
541     static char result[NICKLEN + NICKLEN + 10];
542    
543     if (si->su != NULL)
544 pippijn 1.11 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 pippijn 1.1 else if (si->s != NULL)
549     snprintf (result, sizeof result, "%s", si->s->name);
550     else
551 pippijn 1.11 snprintf (result, sizeof result, "<%s>%s", si->v->description, si->smu ? si->smu->name : "");
552 pippijn 1.1 return result;
553     }
554    
555 pippijn 1.11 char const *
556 pippijn 1.1 get_source_mask (sourceinfo_t *si)
557     {
558     static char result[NICKLEN + USERLEN + HOSTLEN + 10];
559    
560     if (si->su != NULL)
561 pippijn 1.11 snprintf (result, sizeof result, "%s!%s@%s", si->su->nick, si->su->user, si->su->vhost);
562 pippijn 1.1 else if (si->s != NULL)
563     snprintf (result, sizeof result, "%s", si->s->name);
564     else
565 pippijn 1.11 snprintf (result, sizeof result, "<%s>%s", si->v->description, si->smu ? si->smu->name : "");
566 pippijn 1.1 return result;
567     }
568    
569 pippijn 1.11 char const *
570 pippijn 1.1 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 pippijn 1.11 snprintf (result, sizeof result, "<%s>%s", si->v->description, si->smu ? si->smu->name : "");
587 pippijn 1.1 return result;
588     }
589    
590     void
591 pippijn 1.5 wallops (char const * const fmt, ...)
592 pippijn 1.1 {
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 pippijn 1.5 phandler->wallops_sts (buf);
605 pippijn 1.1 else
606     slog (LG_ERROR, "wallops(): unable to send: %s", buf);
607     }
608    
609     void
610 pippijn 1.5 verbose_wallops (char const * const fmt, ...)
611 pippijn 1.1 {
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 pippijn 1.5 phandler->wallops_sts (buf);
624 pippijn 1.1 else
625     slog (LG_ERROR, "verbose_wallops(): unable to send: %s", buf);
626     }