ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/ermyth/src/services.C
Revision: 1.6
Committed: Tue Aug 28 22:18:31 2007 UTC (16 years, 9 months ago) by pippijn
Content type: text/plain
Branch: MAIN
Changes since 1.5: +3 -3 lines
Log Message:
added type traits

File Contents

# User Rev Content
1 pippijn 1.1 /*
2     * services.C: Routines commonly used by various services.
3 pippijn 1.2 * Rights to this code are documented in doc/pod/license.pod.
4 pippijn 1.1 *
5 pippijn 1.5 * Copyright © 2005-2007 Atheme Project (http://www.atheme.org)
6 pippijn 1.1 */
7    
8 pippijn 1.6 static char const rcsid[] = "$Id: services.C,v 1.5 2007-08-28 17:08:12 pippijn Exp $";
9 pippijn 1.5
10     #include <boost/foreach.hpp>
11 pippijn 1.1
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 pippijn 1.5 unsigned authservice_loaded = 0;
19 pippijn 1.1 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 pippijn 1.5 phandler->mode_sts (sender->nick, c, modemask);
49 pippijn 1.1 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 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))
67 pippijn 1.1 {
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 pippijn 1.5 phandler->mode_sts (source->nick, chan, change);
73 pippijn 1.1 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 pippijn 1.5 join (char const * const chan, char const * const nick)
89 pippijn 1.1 {
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_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 pippijn 1.5 phandler->join_sts (c, u, isnew, channel_modes (c, true));
131 pippijn 1.1 cu = chanuser_add (c, CLIENT_NAME (u));
132     cu->modes |= CMODE_OP;
133     if (isnew)
134     {
135 pippijn 1.5 c->callback.add (c);
136 pippijn 1.1 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 pippijn 1.5 phandler->part_sts (c, u);
154 pippijn 1.1 chanuser_delete (c, u);
155     }
156    
157     void
158     services_init (void)
159     {
160     service_t *svs;
161    
162 pippijn 1.5 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 pippijn 1.1 }
172    
173     void
174     joinall (char *name)
175     {
176     if (name == NULL)
177     return;
178    
179 pippijn 1.5 foreach (service_pair &sp, services)
180     join (name, sp.second->name);
181 pippijn 1.1 }
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_find (name);
192 pippijn 1.5 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 pippijn 1.1 }
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 pippijn 1.5 phandler->introduce_nick (u);
220 pippijn 1.1 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 pippijn 1.5 phandler->join_sts (c, u, 0, channel_modes (c, true));
225 pippijn 1.1 else
226     {
227     /* channel will have been destroyed... */
228     /* XXX resend the bans instead of destroying them? */
229     chanban_clear (c);
230 pippijn 1.5 phandler->join_sts (c, u, 1, channel_modes (c, true));
231 pippijn 1.1 if (c->topic != NULL)
232 pippijn 1.5 phandler->topic_sts (c, c->topic_setter, c->topicts, 0, c->topic);
233 pippijn 1.1 }
234     }
235     }
236    
237     void
238 pippijn 1.5 verbose (mychan_t *mychan, char const * const fmt, ...)
239 pippijn 1.1 {
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 pippijn 1.5 phandler->wallchops (chansvs.me->me, mychan->chan, buf);
254 pippijn 1.1 }
255    
256     void
257 pippijn 1.5 snoop (char const * const fmt, ...)
258 pippijn 1.1 {
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 pippijn 1.5 phandler->privmsg (opersvs.nick, config_options.chan, "%s", buf);
276 pippijn 1.1 }
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 pippijn 1.6 if (mn->owner->access_verify (u))
325 pippijn 1.1 {
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 pippijn 1.5 mn->callback.enforce (mn, u);
332 pippijn 1.1 }
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 pippijn 1.6 mu = myuser_t::find (login);
355 pippijn 1.1 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 pippijn 1.5 phandler->ircd_on_logout (u->nick, login, NULL);
370 pippijn 1.1 }
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 pippijn 1.5 phandler->ircd_on_logout (u->nick, login, NULL);
383 pippijn 1.1 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 pippijn 1.5 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 pippijn 1.1 {
422     va_list args;
423     char buf[BUFSIZE];
424     user_t *u;
425     channel_t *c;
426    
427 pippijn 1.5 va_start (args, message);
428     vsnprintf (buf, BUFSIZE, message, args);
429 pippijn 1.1 va_end (args);
430    
431     if (config_options.use_privmsg)
432 pippijn 1.5 phandler->privmsg (from, to, "%s", buf);
433 pippijn 1.1 else
434     {
435     if (*to == '#')
436     {
437     c = channel_find (to);
438     if (c != NULL)
439 pippijn 1.5 phandler->notice_channel_sts (user_find_named (from), c, buf);
440 pippijn 1.1 }
441     else
442     {
443     u = user_find_named (to);
444     if (u != NULL)
445 pippijn 1.5 phandler->notice_user_sts (user_find_named (from), u, buf);
446 pippijn 1.1 }
447     }
448     }
449    
450     void
451 pippijn 1.5 command_fail (sourceinfo_t *si, faultcode_t code, char const * const fmt, ...)
452 pippijn 1.1 {
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 pippijn 1.5 phandler->privmsg (si->service->name, si->su->nick, "%s", buf);
469 pippijn 1.1 else
470 pippijn 1.5 phandler->notice_user_sts (si->service->me, si->su, buf);
471 pippijn 1.1 }
472    
473     void
474 pippijn 1.5 command_success_nodata (sourceinfo_t *si, char const * const fmt, ...)
475 pippijn 1.1 {
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 pippijn 1.5 phandler->privmsg (si->service->name, si->su->nick, "%s", buf);
492 pippijn 1.1 else
493 pippijn 1.5 phandler->notice_user_sts (si->service->me, si->su, buf);
494 pippijn 1.1 }
495    
496     void
497 pippijn 1.5 command_success_string (sourceinfo_t *si, char const * const result, char const * const fmt, ...)
498 pippijn 1.1 {
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 pippijn 1.5 phandler->privmsg (si->service->name, si->su->nick, "%s", buf);
515 pippijn 1.1 else
516 pippijn 1.5 phandler->notice_user_sts (si->service->me, si->su, buf);
517 pippijn 1.1 }
518    
519     static void
520 pippijn 1.5 command_table_cb (char const * const line, void *data)
521 pippijn 1.1 {
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 pippijn 1.5 char const * const
532 pippijn 1.1 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 pippijn 1.5 char const * const
553 pippijn 1.1 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 pippijn 1.5 char const * const
571 pippijn 1.1 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 pippijn 1.5 wallops (char const * const fmt, ...)
595 pippijn 1.1 {
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 pippijn 1.5 phandler->wallops_sts (buf);
608 pippijn 1.1 else
609     slog (LG_ERROR, "wallops(): unable to send: %s", buf);
610     }
611    
612     void
613 pippijn 1.5 verbose_wallops (char const * const fmt, ...)
614 pippijn 1.1 {
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 pippijn 1.5 phandler->wallops_sts (buf);
627 pippijn 1.1 else
628     slog (LG_ERROR, "verbose_wallops(): unable to send: %s", buf);
629     }