ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/ermyth/src/services.C
Revision: 1.1
Committed: Thu Jul 19 08:24:59 2007 UTC (16 years, 10 months ago) by pippijn
Content type: text/plain
Branch: MAIN
Log Message:
initial import. the most important changes since Atheme are:
- fixed many memory leaks
- fixed many bugs
- converted to C++ and use more STL containers
- added a (not very enhanced yet) perl module
- greatly improved XML-RPC speed
- added a JSON-RPC module with code from json-cpp
- added a valgrind memcheck module to operserv
- added a more object oriented base64 implementation
- added a specialised unit test framework
- improved stability
- use gettimeofday() if available
- reworked adding/removing commands
- MemoServ IGNORE DEL can now remove indices

File Contents

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