1 /*
2 * Copyright (c) 2005-2006 Atheme Development Group
3 * servtree.c: Services binary tree manipulation. (add_service,
4 * del_service, et al.)
5 *
6 * Permission to use, copy, modify, and/or distribute this software for any
7 * purpose with or without fee is hereby granted, provided that the above
8 * copyright notice and this permission notice appear in all copies.
9 *
10 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
11 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
12 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
13 * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
14 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
15 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
16 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
17 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
18 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
19 * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
20 * POSSIBILITY OF SUCH DAMAGE.
21 */
22
23 #include "atheme.h"
24 #include "pmodule.h"
25
26 mowgli_patricia_t *services_name;
27 mowgli_patricia_t *services_nick;
28 mowgli_heap_t *service_heap;
29
30 void servtree_update(void *dummy);
31
dummy_handler(sourceinfo_t * si,int parc,char ** parv)32 static void dummy_handler(sourceinfo_t *si, int parc, char **parv)
33 {
34 }
35
service_default_handler(sourceinfo_t * si,int parc,char * parv[])36 static void service_default_handler(sourceinfo_t *si, int parc, char *parv[])
37 {
38 char *cmd;
39 char *text;
40 char orig[BUFSIZE];
41
42 /* this should never happen */
43 if (parv[0][0] == '&')
44 {
45 slog(LG_ERROR, "services(): got parv with local channel: %s", parv[0]);
46 return;
47 }
48
49 /* make a copy of the original for debugging */
50 mowgli_strlcpy(orig, parv[parc - 1], BUFSIZE);
51
52 /* lets go through this to get the command */
53 cmd = strtok(parv[parc - 1], " ");
54 text = strtok(NULL, "");
55
56 if (!cmd)
57 return;
58 if (*orig == '\001')
59 {
60 handle_ctcp_common(si, cmd, text);
61 return;
62 }
63
64 /* take the command through the hash table */
65 command_exec_split(si->service, si, cmd, text, si->service->commands);
66 }
67
servtree_init(void)68 void servtree_init(void)
69 {
70 service_heap = sharedheap_get(sizeof(service_t));
71 services_name = mowgli_patricia_create(strcasecanon);
72 services_nick = mowgli_patricia_create(strcasecanon);
73
74 if (!service_heap)
75 {
76 slog(LG_INFO, "servtree_init(): Block allocator failed.");
77 exit(EXIT_FAILURE);
78 }
79
80 hook_add_event("config_ready");
81 hook_add_config_ready(servtree_update);
82 }
83
me_me_init(void)84 static void me_me_init(void)
85 {
86 if (me.numeric)
87 init_uid();
88 me.me = server_add(me.name, 0, NULL, me.numeric ? me.numeric : NULL, me.desc);
89 }
90
free_alias_string(const char * key,void * data,void * privdata)91 static void free_alias_string(const char *key, void *data, void *privdata)
92 {
93 free(data);
94 }
95
free_access_string(const char * key,void * data,void * privdata)96 static void free_access_string(const char *key, void *data, void *privdata)
97 {
98 free(data);
99 }
100
create_unique_service_nick(char * dest,size_t len)101 static void create_unique_service_nick(char *dest, size_t len)
102 {
103 unsigned int i = arc4random();
104
105 do
106 snprintf(dest, len, "ath%06x", (i++) & 0xFFFFFF);
107 while (service_find_nick(dest) || user_find_named(dest));
108 return;
109 }
110
conf_service_nick(mowgli_config_file_entry_t * ce)111 static int conf_service_nick(mowgli_config_file_entry_t *ce)
112 {
113 service_t *sptr;
114 char newnick[NICKLEN + 1];
115
116 if (!ce->vardata)
117 return -1;
118
119 sptr = service_find(ce->prevlevel->varname);
120 if (!sptr)
121 return -1;
122
123 mowgli_patricia_delete(services_nick, sptr->nick);
124 free(sptr->nick);
125
126 if (service_find_nick(ce->vardata))
127 {
128 create_unique_service_nick(newnick, sizeof newnick);
129 slog(LG_INFO, "conf_service_nick(): using nick %s for service %s due to duplication",
130 newnick, sptr->internal_name);
131 sptr->nick = sstrdup(newnick);
132 }
133 else
134 sptr->nick = sstrdup(ce->vardata);
135 mowgli_patricia_add(services_nick, sptr->nick, sptr);
136
137 return 0;
138 }
139
conf_service_user(mowgli_config_file_entry_t * ce)140 static int conf_service_user(mowgli_config_file_entry_t *ce)
141 {
142 service_t *sptr;
143
144 if (!ce->vardata)
145 return -1;
146
147 sptr = service_find(ce->prevlevel->varname);
148 if (!sptr)
149 return -1;
150
151 free(sptr->user);
152 sptr->user = sstrndup(ce->vardata, 10);
153
154 return 0;
155 }
156
conf_service_host(mowgli_config_file_entry_t * ce)157 static int conf_service_host(mowgli_config_file_entry_t *ce)
158 {
159 service_t *sptr;
160
161 if (!ce->vardata)
162 return -1;
163
164 if (!is_valid_host(ce->vardata))
165 {
166 conf_report_warning(ce, "invalid hostname: %s", ce->vardata);
167 return -1;
168 }
169
170 sptr = service_find(ce->prevlevel->varname);
171 if (!sptr)
172 return -1;
173
174 free(sptr->host);
175 sptr->host = sstrdup(ce->vardata);
176
177 return 0;
178 }
179
conf_service_real(mowgli_config_file_entry_t * ce)180 static int conf_service_real(mowgli_config_file_entry_t *ce)
181 {
182 service_t *sptr;
183
184 if (!ce->vardata)
185 return -1;
186
187 sptr = service_find(ce->prevlevel->varname);
188 if (!sptr)
189 return -1;
190
191 free(sptr->real);
192 sptr->real = sstrdup(ce->vardata);
193
194 return 0;
195 }
196
conf_service_aliases(mowgli_config_file_entry_t * ce)197 static int conf_service_aliases(mowgli_config_file_entry_t *ce)
198 {
199 service_t *sptr;
200 mowgli_config_file_entry_t *subce;
201
202 sptr = service_find(ce->prevlevel->varname);
203 if (!sptr)
204 return -1;
205
206 if (sptr->aliases)
207 mowgli_patricia_destroy(sptr->aliases, free_alias_string, NULL);
208
209 sptr->aliases = NULL;
210 if (!ce->entries)
211 return 0;
212
213 sptr->aliases = mowgli_patricia_create(strcasecanon);
214
215 MOWGLI_ITER_FOREACH(subce, ce->entries)
216 {
217 if (subce->vardata == NULL || subce->entries != NULL)
218 {
219 conf_report_warning(subce, "Invalid alias entry");
220 continue;
221 }
222
223 mowgli_patricia_add(sptr->aliases, subce->varname,
224 sstrdup(subce->vardata));
225 }
226
227 return 0;
228 }
229
conf_service_access(mowgli_config_file_entry_t * ce)230 static int conf_service_access(mowgli_config_file_entry_t *ce)
231 {
232 service_t *sptr;
233 mowgli_config_file_entry_t *subce;
234
235 sptr = service_find(ce->prevlevel->varname);
236 if (!sptr)
237 return -1;
238
239 if (sptr->access)
240 mowgli_patricia_destroy(sptr->access, free_access_string, NULL);
241
242 sptr->access = NULL;
243 if (!ce->entries)
244 return 0;
245
246 sptr->access = mowgli_patricia_create(strcasecanon);
247
248 MOWGLI_ITER_FOREACH(subce, ce->entries)
249 {
250 if (subce->vardata == NULL || subce->entries != NULL)
251 {
252 conf_report_warning(subce, "Invalid access entry");
253 continue;
254 }
255
256 mowgli_patricia_add(sptr->access, subce->varname,
257 sstrdup(subce->vardata));
258 }
259
260 return 0;
261 }
262
conf_service(mowgli_config_file_entry_t * ce)263 static int conf_service(mowgli_config_file_entry_t *ce)
264 {
265 service_t *sptr;
266
267 sptr = service_find(ce->varname);
268 if (!sptr)
269 return -1;
270
271 subblock_handler(ce, &sptr->conf_table);
272 return 0;
273 }
274
service_add(const char * name,void (* handler)(sourceinfo_t * si,int parc,char * parv[]))275 service_t *service_add(const char *name, void (*handler)(sourceinfo_t *si, int parc, char *parv[]))
276 {
277 service_t *sptr;
278 struct ConfTable *subblock;
279 const char *nick;
280 char newnick[NICKLEN];
281
282 return_val_if_fail(name != NULL, NULL);
283 return_val_if_fail(service_find(name) == NULL, NULL);
284
285 sptr = mowgli_heap_alloc(service_heap);
286
287 sptr->internal_name = sstrdup(name);
288 /* default these, to reasonably safe values */
289 nick = strchr(name, ':');
290 if (nick != NULL)
291 nick++;
292 else
293 nick = name;
294 mowgli_strlcpy(newnick, nick, sizeof newnick);
295 if (!is_valid_nick(newnick) || service_find_nick(newnick))
296 {
297 create_unique_service_nick(newnick, sizeof newnick);
298 slog(LG_INFO, "service_add(): using nick %s for service %s due to duplicate or invalid nickname",
299 newnick, name);
300 }
301 sptr->nick = sstrdup(newnick);
302 sptr->user = sstrndup(nick, 10);
303 sptr->host = sstrdup("services.int");
304 sptr->real = sstrndup(name, 50);
305 sptr->disp = sstrdup(sptr->nick);
306
307 if (handler != NULL)
308 sptr->handler = handler;
309 else
310 sptr->handler = service_default_handler;
311
312 sptr->notice_handler = dummy_handler;
313 sptr->aliases = NULL;
314 sptr->access = NULL;
315 sptr->chanmsg = false;
316
317 sptr->me = NULL;
318
319 mowgli_patricia_add(services_name, sptr->internal_name, sptr);
320 mowgli_patricia_add(services_nick, sptr->nick, sptr);
321
322 sptr->commands = mowgli_patricia_create(strcasecanon);
323
324 subblock = find_top_conf(name);
325 if (subblock == NULL)
326 add_top_conf(sptr->internal_name, conf_service);
327 add_conf_item("NICK", &sptr->conf_table, conf_service_nick);
328 add_conf_item("USER", &sptr->conf_table, conf_service_user);
329 add_conf_item("HOST", &sptr->conf_table, conf_service_host);
330 add_conf_item("REAL", &sptr->conf_table, conf_service_real);
331 add_conf_item("ALIASES", &sptr->conf_table, conf_service_aliases);
332 add_conf_item("ACCESS", &sptr->conf_table, conf_service_access);
333
334 return sptr;
335 }
336
service_delete(service_t * sptr)337 void service_delete(service_t *sptr)
338 {
339 struct ConfTable *subblock;
340
341 mowgli_patricia_delete(services_name, sptr->internal_name);
342 mowgli_patricia_delete(services_nick, sptr->nick);
343
344 del_conf_item("ACCESS", &sptr->conf_table);
345 del_conf_item("ALIASES", &sptr->conf_table);
346 del_conf_item("REAL", &sptr->conf_table);
347 del_conf_item("HOST", &sptr->conf_table);
348 del_conf_item("USER", &sptr->conf_table);
349 del_conf_item("NICK", &sptr->conf_table);
350 subblock = find_top_conf(sptr->internal_name);
351 if (subblock != NULL && conftable_get_conf_handler(subblock) == conf_service)
352 del_top_conf(sptr->internal_name);
353
354 if (sptr->me != NULL)
355 {
356 quit_sts(sptr->me, "Service unloaded.");
357 user_delete(sptr->me, "Service unloaded.");
358 sptr->me = NULL;
359 }
360 sptr->handler = NULL;
361 if (sptr->access)
362 mowgli_patricia_destroy(sptr->access, free_access_string, NULL);
363 if (sptr->aliases)
364 mowgli_patricia_destroy(sptr->aliases, free_alias_string, NULL);
365 if (sptr->commands)
366 mowgli_patricia_destroy(sptr->commands, NULL, NULL);
367 free(sptr->disp); /* service_name() does a malloc() */
368 free(sptr->internal_name);
369 free(sptr->nick);
370 free(sptr->user);
371 free(sptr->host);
372 free(sptr->real);
373
374 mowgli_heap_free(service_heap, sptr);
375 }
376
service_add_static(const char * name,const char * user,const char * host,const char * real,void (* handler)(sourceinfo_t * si,int parc,char * parv[]),service_t * logtarget)377 service_t *service_add_static(const char *name, const char *user, const char *host, const char *real, void (*handler)(sourceinfo_t *si, int parc, char *parv[]), service_t *logtarget)
378 {
379 service_t *sptr;
380 char internal_name[NICKLEN + 10];
381
382 snprintf(internal_name, sizeof internal_name, "static:%s", name);
383 sptr = service_add(internal_name, handler);
384
385 free(sptr->user);
386 free(sptr->host);
387 free(sptr->real);
388 sptr->user = sstrndup(user, 10);
389 sptr->host = sstrdup(host);
390 sptr->real = sstrdup(real);
391 sptr->botonly = true;
392 sptr->logtarget = logtarget;
393
394 servtree_update(NULL);
395
396 return sptr;
397 }
398
service_find_any(void)399 service_t *service_find_any(void)
400 {
401 service_t *sptr;
402 mowgli_patricia_iteration_state_t state;
403
404 MOWGLI_PATRICIA_FOREACH(sptr, &state, services_name)
405 return sptr;
406 return NULL;
407 }
408
service_find(const char * name)409 service_t *service_find(const char *name)
410 {
411 return mowgli_patricia_retrieve(services_name, name);
412 }
413
service_find_nick(const char * nick)414 service_t *service_find_nick(const char *nick)
415 {
416 return mowgli_patricia_retrieve(services_nick, nick);
417 }
418
servtree_update(void * dummy)419 void servtree_update(void *dummy)
420 {
421 service_t *sptr;
422 mowgli_patricia_iteration_state_t state;
423 user_t *u;
424
425 if (offline_mode)
426 return;
427
428 if (me.me == NULL)
429 me_me_init();
430 if (ircd->uses_uid && !me.numeric)
431 {
432 slog(LG_ERROR, "servtree_update(): ircd requires numeric, but none was specified in the configuration file");
433 exit(EXIT_FAILURE);
434 }
435
436 MOWGLI_PATRICIA_FOREACH(sptr, &state, services_name)
437 {
438 free(sptr->disp);
439 sptr->disp = service_name(sptr->nick);
440 if (sptr->me != NULL)
441 {
442 if (!strcmp(sptr->nick, sptr->me->nick) &&
443 !strcmp(sptr->user, sptr->me->user) &&
444 !strcmp(sptr->host, sptr->me->host) &&
445 !strcmp(sptr->real, sptr->me->gecos))
446 continue;
447 if (me.connected)
448 quit_sts(sptr->me, "Updating information");
449 u = user_find_named(sptr->nick);
450 if (u != NULL && u != sptr->me)
451 kill_user(NULL, u, "Nick taken by service");
452 user_changenick(sptr->me, sptr->nick, CURRTIME);
453 strshare_unref(sptr->me->user);
454 sptr->me->user = strshare_get(sptr->user);
455 strshare_unref(sptr->me->host);
456 sptr->me->host = strshare_get(sptr->host);
457 strshare_unref(sptr->me->chost);
458 sptr->me->chost = strshare_ref(sptr->me->host);
459 strshare_unref(sptr->me->vhost);
460 sptr->me->vhost = strshare_ref(sptr->me->host);
461 strshare_unref(sptr->me->gecos);
462 sptr->me->gecos = strshare_get(sptr->real);
463 if (me.connected)
464 reintroduce_user(sptr->me);
465 }
466 else
467 {
468 u = user_find_named(sptr->nick);
469 if (u != NULL)
470 kill_user(NULL, u, "Nick taken by service");
471 sptr->me = user_add(sptr->nick, sptr->user, sptr->host, NULL, NULL, ircd->uses_uid ? uid_get() : NULL, sptr->real, me.me, CURRTIME);
472 sptr->me->flags |= UF_IRCOP | UF_INVIS | UF_SERVICE;
473 if ((sptr == chansvs.me) && !chansvs.fantasy)
474 sptr->me->flags |= UF_DEAF;
475
476 if (me.connected)
477 {
478 /*
479 * Possibly send a kill for the service nick now
480 * - do not send a kill if we already sent
481 * one above
482 * - do not send a kill if we use UID, with
483 * UID we can always kill the other user
484 * in nick collisions and killing a nick
485 * with UID is likely to cause the desyncs
486 * UID is meant to avoid
487 */
488 if (u == NULL && !ircd->uses_uid)
489 kill_id_sts(NULL, sptr->nick, "Attempt to use service nick");
490 introduce_nick(sptr->me);
491 hook_call_service_introduce(sptr);
492 }
493 }
494 }
495 }
496
service_name(char * name)497 char *service_name(char *name)
498 {
499 char *str;
500
501 if (config_options.secure)
502 {
503 str = smalloc(strlen(name) + 1 + strlen(me.name) + 1);
504 sprintf(str, "%s@%s", name, me.name);
505 }
506 else
507 str = sstrdup(name);
508
509 return str;
510 }
511
service_set_chanmsg(service_t * service,bool chanmsg)512 void service_set_chanmsg(service_t *service, bool chanmsg)
513 {
514 return_if_fail(service != NULL);
515
516 service->chanmsg = chanmsg;
517 }
518
service_resolve_alias(service_t * sptr,const char * context,const char * cmd)519 const char *service_resolve_alias(service_t *sptr, const char *context, const char *cmd)
520 {
521 char fullname[256];
522 char *alias;
523
524 if (sptr->aliases == NULL)
525 return cmd;
526 fullname[0] = '\0';
527 if (context != NULL)
528 {
529 mowgli_strlcpy(fullname, context, sizeof fullname);
530 mowgli_strlcat(fullname, " ", sizeof fullname);
531 }
532 mowgli_strlcat(fullname, cmd, sizeof fullname);
533 alias = mowgli_patricia_retrieve(sptr->aliases, fullname);
534 return alias != NULL ? alias : cmd;
535 }
536
service_set_access(service_t * sptr,const char * cmd,const char * oldaccess)537 const char *service_set_access(service_t *sptr, const char *cmd, const char *oldaccess)
538 {
539 char *newaccess;
540
541 if (sptr->access == NULL)
542 return oldaccess;
543
544 newaccess = mowgli_patricia_retrieve(sptr->access, cmd);
545 return newaccess != NULL ? newaccess : oldaccess;
546 }
547
service_bind_command(service_t * sptr,command_t * cmd)548 void service_bind_command(service_t *sptr, command_t *cmd)
549 {
550 return_if_fail(sptr != NULL);
551 return_if_fail(cmd != NULL);
552
553 command_add(cmd, sptr->commands);
554 }
555
service_unbind_command(service_t * sptr,command_t * cmd)556 void service_unbind_command(service_t *sptr, command_t *cmd)
557 {
558 return_if_fail(sptr != NULL);
559 return_if_fail(cmd != NULL);
560
561 command_delete(cmd, sptr->commands);
562 }
563
service_named_bind_command(const char * svs,command_t * cmd)564 void service_named_bind_command(const char *svs, command_t *cmd)
565 {
566 service_t *sptr = NULL;
567
568 return_if_fail(svs != NULL);
569 return_if_fail(cmd != NULL);
570
571 sptr = service_find(svs);
572 if (sptr == NULL)
573 return;
574
575 service_bind_command(sptr, cmd);
576 }
577
service_named_unbind_command(const char * svs,command_t * cmd)578 void service_named_unbind_command(const char *svs, command_t *cmd)
579 {
580 service_t *sptr = NULL;
581
582 return_if_fail(svs != NULL);
583 return_if_fail(cmd != NULL);
584
585 sptr = service_find(svs);
586 if (sptr == NULL)
587 return;
588
589 service_unbind_command(sptr, cmd);
590 }
591
592 /* vim:cinoptions=>s,e0,n0,f0,{0,}0,^0,=s,ps,t0,c3,+s,(2s,us,)20,*30,gs,hs
593 * vim:ts=8
594 * vim:sw=8
595 * vim:noexpandtab
596 */
597