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

# Content
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 */