00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00030 #ifndef lint
00031 static const char rcsid[] = "$Id: botnet.c,v 1.13 2007-09-13 22:20:55 sven Exp $";
00032 #endif
00033
00034 #include <eggdrop/eggdrop.h>
00035
00036 #define BAD_BOTNAME_CHARS " *?@:"
00037
00038 typedef struct {
00039 int priority;
00040 int min_prio;
00041 } botnet_search_prio_t;
00042
00043 static char *botname;
00044 static char *default_botname = "eggdrop";
00045
00046 static hash_table_t *bot_ht = NULL;
00047 static botnet_bot_t *bot_head, *localbot_head;
00048 static int nbots = 0;
00049 static int linking_bots = 0;
00050 static int botnet_cleanup(void *client_data);
00051 static void botnet_really_delete(botnet_bot_t *bot);
00052 static int botnet_clear_all(void);
00053
00054
00055
00056
00057
00058
00059
00060
00061
00076 static bind_table_t *BT_request_link;
00077
00091 static bind_table_t *BT_link;
00092
00110 static bind_table_t *BT_disc;
00111
00126 static bind_table_t *BT_bot;
00127
00130 static bind_table_t *BT_extension;
00137 int botnet_init()
00138 {
00139 nbots = 0;
00140 bot_head = NULL;
00141 localbot_head = NULL;
00142
00143 botname = default_botname;
00144 bot_ht = hash_table_create(NULL, NULL, 13, HASH_TABLE_STRINGS);
00145
00146 BT_request_link = bind_table_add(BTN_BOTNET_REQUEST_LINK, 2, "Us", 0, BIND_BREAKABLE);
00147 BT_link = bind_table_add(BTN_BOTNET_LINK, 1, "B", 0, BIND_STACKABLE);
00148 BT_disc = bind_table_add(BTN_BOTNET_DISC, 3, "BBs", 0, BIND_STACKABLE);
00149 BT_bot = bind_table_add(BTN_BOTNET_BOT, 3, "Bss", 0, 0);
00150 BT_extension = bind_table_add(BTN_BOTNET_EXTENSION, 3, "Ess", 0, 0);
00151 return 0;
00152 }
00153
00162 int botnet_shutdown(void)
00163 {
00164 int leaked;
00165
00166
00167
00168
00169 leaked = botnet_clear_all();
00170 if (leaked) putlog(LOG_MISC, "*", "Warning: Some botnet module leaked %d bots. Fix this!", leaked);
00171
00172 bind_table_del(BT_request_link);
00173 bind_table_del(BT_link);
00174 bind_table_del(BT_disc);
00175 bind_table_del(BT_bot);
00176 bind_table_del(BT_extension);
00177
00178
00179
00180
00181 garbage_run();
00182
00183 hash_table_delete(bot_ht);
00184 bot_ht = NULL;
00185
00186 if (botname != default_botname) free(botname);
00187 return 0;
00188 }
00189
00202 int botnet_set_name(const char *name)
00203 {
00204 if (bot_head || localbot_head || nbots || partymember_get_head()) return -1;
00205 if (botname != default_botname) free(botname);
00206 botname = strdup(name);
00207 return 0;
00208 }
00209
00216 const char *botnet_get_name()
00217 {
00218 return botname;
00219 }
00220
00230 const botnet_bot_t *botnet_get_head()
00231 {
00232 return bot_head;
00233 }
00234
00243 botnet_bot_t *botnet_lookup(const char *name)
00244 {
00245 botnet_bot_t *bot = NULL;
00246
00247 hash_table_find(bot_ht, name, &bot);
00248 if (bot && bot->flags & BOT_DELETED) return NULL;
00249 return bot;
00250 }
00251
00272 botnet_bot_t *botnet_new(const char *name, user_t *user, botnet_bot_t *uplink, botnet_bot_t *direction, xml_node_t *info, botnet_handler_t *handler, void *client_data, event_owner_t *owner, int netburst)
00273 {
00274 const char *temp;
00275 botnet_bot_t *bot, *tmp;
00276
00277 if (!uplink && (!user || !handler)) return 0;
00278 if ((uplink && !direction) || (!uplink && direction)) return 0;
00279 if (botnet_lookup(name)) return 0;
00280 if (!strcmp(botname, name)) return 0;
00281 for (temp = name; *temp; ++temp) if (*temp < 32 || strchr(BAD_BOTNAME_CHARS, *temp)) return 0;
00282
00283 bot = malloc(sizeof(*bot));
00284 if (!direction) direction = bot;
00285
00286 bot->name = strdup(name);
00287 bot->flags = 0;
00288 bot->user = user;
00289 bot->uplink = uplink;
00290 bot->direction = direction;
00291 bot->partys = NULL;
00292 bot->handler = handler;
00293 bot->client_data = client_data;
00294 bot->owner = owner;
00295 bot->info = info ? info : xml_node_new();
00296
00297 hash_table_insert(bot_ht, bot->name, bot);
00298
00299 bot->prev = 0;
00300 bot->next = bot_head;
00301 if (bot_head) bot_head->prev = bot;
00302 bot_head = bot;
00303
00304 bot->prev_local = 0;
00305 if (!uplink) {
00306 bot->next_local = localbot_head;
00307 if (localbot_head) localbot_head->prev_local = bot;
00308 localbot_head = bot;
00309 } else {
00310 bot->next_local = 0;
00311 }
00312
00313 ++nbots;
00314
00315 for (tmp = localbot_head; tmp; tmp = tmp->next_local) {
00316 if (tmp->flags & BOT_DELETED || tmp == bot->direction) continue;
00317 if (tmp->handler && tmp->handler->on_new_bot) tmp->handler->on_new_bot(tmp->client_data, bot, netburst);
00318 }
00319
00320 bind_check(BT_link, NULL, bot->name, bot);
00321
00322 if (!netburst) {
00323 if (!bot->uplink) putlog(LOG_MISC, "*", "Linked to %s.", bot->name);
00324 else putlog(LOG_MISC, "*", "(%s) Linked to %s.", bot->uplink->name, bot->name);
00325 }
00326
00327 return bot;
00328 }
00329
00343 static int botnet_clear_all()
00344 {
00345 int ret = 0;
00346 return ret;
00347 }
00348
00369 int botnet_unlink(botnet_entity_t *from, botnet_bot_t *bot, const char *reason)
00370 {
00371 char obuf[512];
00372 botnet_entity_t me = bot_entity((botnet_bot_t *) 0);
00373
00374 if (bot->direction != bot) {
00375 if (!bot->direction->handler || !bot->direction->handler->on_unlink) return -1;
00376 bot->direction->handler->on_unlink(bot->direction->client_data, from, bot, reason);
00377 return 0;
00378 }
00379
00380 snprintf(obuf, sizeof(obuf), "%s (%s)", reason, entity_full_name(from));
00381
00382 if (bot->handler && bot->handler->on_lost_bot) bot->handler->on_lost_bot(bot->client_data, bot, obuf);
00383 if (from->what == ENTITY_PARTYMEMBER) partymember_msgf(from->user, &me, _("Unlinked from %s."), bot->name);
00384 botnet_delete(bot, reason);
00385
00386 return 0;
00387 }
00388
00403 static void botnet_really_delete(botnet_bot_t *bot)
00404 {
00405 if (!(bot->flags & BOT_DELETED)) --nbots;
00406
00407 if (bot->prev) bot->prev->next = bot->next;
00408 else bot_head = bot->next;
00409 if (bot->next) bot->next->prev = bot->prev;
00410
00411 if (!bot->uplink) {
00412 if (bot->prev_local) bot->prev_local->next_local = bot->next_local;
00413 else localbot_head = bot->next_local;
00414 if (bot->next_local) bot->next_local->prev_local = bot->prev_local;
00415 }
00416 hash_table_remove(bot_ht, bot->name, NULL);
00417
00418 xml_node_delete(bot->info);
00419 free(bot->name);
00420 free(bot);
00421 }
00422
00435 static void botnet_recursive_delete(botnet_bot_t *bot, botnet_bot_t *root, const char *reason)
00436 {
00437 botnet_bot_t *tmp;
00438
00439 if (bot->flags & BOT_DELETED) return;
00440
00441
00442 for (tmp = bot_head; tmp; tmp = tmp->next) {
00443 if (tmp->flags & BOT_DELETED) continue;
00444 if (tmp->uplink == bot) botnet_recursive_delete(tmp, root, reason);
00445 }
00446
00447
00448 partymember_delete_by_bot(bot, root, reason);
00449
00450
00451 bind_check(BT_disc, NULL, bot->name, bot, root, reason);
00452 bot->flags |= BOT_DELETED;
00453 if (bot->user) bot->user->flags &= ~USER_LINKED_BOT;
00454 if (bot->owner && bot->owner->on_delete) bot->owner->on_delete(bot->owner, bot->client_data);
00455 --nbots;
00456 }
00457
00471 void botnet_count_subtree(botnet_bot_t *bot, int *bots, int *members)
00472 {
00473 int m;
00474 botnet_bot_t *tmp;
00475
00476 for (tmp = bot_head; tmp; tmp = tmp->next) {
00477 if (tmp->flags & BOT_DELETED) continue;
00478 if (tmp->uplink == bot) botnet_count_subtree(tmp, bots, members);
00479 }
00480
00481 m = partymember_count_by_bot(bot);
00482 if (bots) ++*bots;
00483 if (members) *members += m;
00484 }
00485
00498 int botnet_delete(botnet_bot_t *bot, const char *reason)
00499 {
00500 char obuf[512];
00501 const botnet_bot_t *tmp;
00502
00503 if (bot->flags & BOT_DELETED) return -1;
00504
00505 if (!bot->uplink) {
00506 int bots = 0, users = 0;
00507 botnet_count_subtree(bot, &bots, &users);
00508 snprintf(obuf, sizeof(obuf), "Unlinked from: %s (%s) (lost %d bots and %d users)", bot->name, reason, bots, users);
00509 reason = obuf;
00510 }
00511
00512 for (tmp = localbot_head; tmp; tmp = tmp->next_local) {
00513 if (tmp->flags & BOT_DELETED || tmp == bot->direction) continue;
00514 if (tmp->handler && tmp->handler->on_lost_bot) tmp->handler->on_lost_bot(tmp->client_data, bot, reason);
00515 }
00516
00517 botnet_recursive_delete(bot, bot, reason);
00518
00519 if (!bot->uplink) putlog(LOG_MISC, "*", "%s", reason);
00520 else putlog(LOG_MISC, "*", "(%s) %s", bot->uplink->name, reason);
00521
00522 if (bot->user && bot->user->flags & USER_LINKING_BOT) botnet_link_failed(bot->user, reason);
00523
00524 garbage_add(botnet_cleanup, NULL, GARBAGE_ONCE);
00525
00526 return 0;
00527 }
00528
00542 int botnet_delete_by_owner(struct egg_module *module, void *script)
00543 {
00544 int ret = 0;
00545 botnet_bot_t *tmp;
00546
00547 for (tmp = localbot_head; tmp; tmp = tmp->next_local) {
00548 if (tmp->flags & BOT_DELETED) continue;
00549 if (tmp->owner && tmp->owner->module == module && (!script || tmp->owner->client_data == script)) {
00550 ++ret;
00551 botnet_delete(tmp, _("Module unloaded"));
00552 }
00553 }
00554
00555 for (tmp = bot_head; tmp; tmp = tmp->next_local) {
00556 if (tmp->flags & BOT_DELETED) continue;
00557 if (tmp->owner && tmp->owner->module == module && (!script || tmp->owner->client_data == script)) {
00558 ++ret;
00559 botnet_delete(tmp, _("Module unloaded"));
00560 }
00561 }
00562
00563 return ret;
00564 }
00565
00572 static void botnet_recursive_replay_net(botnet_bot_t *dst, botnet_bot_t *start)
00573 {
00574 int i;
00575 botnet_bot_t *tmp;
00576 partymember_t *p;
00577
00578 if (start && dst->handler->on_new_bot) dst->handler->on_new_bot(dst->client_data, start, 1);
00579
00580 for (p = start ? start->partys : partymember_get_local_head(); p; p = p->next_on_bot) {
00581 if (p->flags & PARTY_DELETED) continue;
00582 if (dst->handler->on_login) dst->handler->on_login(dst->client_data, p);
00583 for (i = 0; i < p->nchannels; ++i) {
00584 if (dst->handler->on_join) dst->handler->on_join(dst->client_data, p->channels[i], p, 1);
00585 }
00586 }
00587
00588 for (tmp = bot_head; tmp; tmp = tmp->next) {
00589 if (tmp->flags & BOT_DELETED || tmp == dst) continue;
00590 if (tmp->uplink == start) botnet_recursive_replay_net(dst, tmp);
00591 }
00592 }
00593
00604 void botnet_replay_net(botnet_bot_t *dst)
00605 {
00606 botnet_recursive_replay_net(dst, NULL);
00607 }
00608
00619 void botnet_link_failed(user_t *user, const char *reason)
00620 {
00621 putlog(LOG_MISC, "*", _("Failed to link to %s (%s)."), user->handle, reason);
00622 --linking_bots;
00623 user->flags &= ~USER_LINKING_BOT;
00624 if (!linking_bots) botnet_autolink();
00625 }
00626
00636 void botnet_link_success(botnet_bot_t *bot)
00637 {
00638 if (linking_bots > 0 && bot->user->flags & USER_LINKING_BOT) --linking_bots;
00639 bot->user->flags &= ~USER_LINKING_BOT;
00640 bot->user->flags |= USER_LINKED_BOT;
00641 if (!linking_bots) botnet_autolink();
00642 }
00643
00657 static int botnet_find_min(const void *uid, void *userptr, void *param)
00658 {
00659 int p;
00660 char *prio;
00661 user_t *u = *(user_t **) userptr;
00662 botnet_search_prio_t *data = param;
00663
00664 if (!(u->flags & USER_BOT) || u->flags & USER_LINKED_BOT) return 0;
00665 if (u->flags & USER_LINKING_BOT) {
00666 putlog(LOG_MISC, "*", _("Warning: botnet_autolink() was called while still connecting to %s."), u->handle);
00667 if (linking_bots < 1) linking_bots = 1;
00668 return 0;
00669 }
00670 if (botnet_lookup(u->handle) || user_get_setting(u, NULL, "bot.link-priority", &prio)) return 0;
00671
00672 p = atoi(prio);
00673 if (p > data->priority && p < data->min_prio) data->min_prio = p;
00674
00675 return 0;
00676 }
00677
00691 static int botnet_link_prio(const void *uid, void *userptr, void *param)
00692 {
00693 char *prio;
00694 user_t *u = *(user_t **) userptr;
00695 int *priority = param;
00696 botnet_entity_t me = bot_entity((botnet_bot_t *) 0);
00697
00698 if (!(u->flags & USER_BOT) || u->flags & (USER_LINKING_BOT | USER_LINKED_BOT)) return 0;
00699 if (botnet_lookup(u->handle) || user_get_setting(u, NULL, "bot.link-priority", &prio)) return 0;
00700
00701 if (atoi(prio) == *priority) botnet_link(&me, NULL, u->handle);
00702
00703 return 0;
00704 }
00705
00727 void botnet_autolink()
00728 {
00729 static int priority = 0;
00730 botnet_search_prio_t data = {priority, INT_MAX};
00731
00732 if (linking_bots) return;
00733 user_walk(botnet_find_min, &data);
00734
00735 if (data.min_prio == INT_MAX) {
00736 priority = 0;
00737 return;
00738 }
00739
00740 priority = data.min_prio;
00741
00742 user_walk(botnet_link_prio, &priority);
00743 }
00744
00745 int botnet_check_direction(botnet_bot_t *direction, botnet_bot_t *src)
00746 {
00747 if (!src || src->direction != direction) {
00748 putlog(LOG_MISC, "*", _("Fake message from %s rejected! (direction != %s)."), direction->name, src ? src->name : _("non-existent bot"));
00749 return 1;
00750 }
00751 return 0;
00752 }
00753
00764 static int botnet_cleanup(void *client_data)
00765 {
00766 botnet_bot_t *bot, *next;
00767
00768 for (bot = bot_head; bot; bot = next) {
00769 next = bot->next;
00770 if (bot->flags & PARTY_DELETED) botnet_really_delete(bot);
00771 }
00772 return 0;
00773 }
00774
00775 int botnet_link(botnet_entity_t *src, botnet_bot_t *dst, const char *target)
00776 {
00777 char *type, *priority;
00778 user_t *user;
00779 botnet_entity_t entity_me = bot_entity((botnet_bot_t *) 0);
00780
00781 if (dst) {
00782 if (dst->direction->handler && dst->direction->handler->on_link)
00783 dst->direction->handler->on_link(dst->direction->client_data, src, dst, target);
00784 return 0;
00785 }
00786
00787 user = user_lookup_by_handle(target);
00788 if (!user || !(user->flags & USER_BOT)) {
00789 if (src->what == ENTITY_PARTYMEMBER) partymember_msg(src->user, &entity_me, _("Can't link there: No such bot."), -1);
00790 return -1;
00791 }
00792 if (user->flags & USER_LINKING_BOT || botnet_lookup(user->handle)) {
00793 if (src->what == ENTITY_PARTYMEMBER) partymember_msg(src->user, &entity_me, _("Can't link there: Already linked."), -1);
00794 return -2;
00795 }
00796 if (user_get_setting(user, NULL, "bot.type", &type)) type = "eggdrop";
00797 if (!user_get_setting(user, NULL, "bot.link-priority", &priority) && atoi(priority) < 0) {
00798 if (src->what == ENTITY_PARTYMEMBER) partymember_msg(src->user, &entity_me, _("Won't link there: Negative link priority."), -1);
00799 return -3;
00800 }
00801
00802 user->flags |= USER_LINKING_BOT;
00803 ++linking_bots;
00804 if (!bind_check(BT_request_link, NULL, type, user, type)) {
00805 user->flags &= ~USER_LINKING_BOT;
00806 --linking_bots;
00807 if (src->what == ENTITY_PARTYMEMBER) partymember_msg(src->user, &entity_me, _("Can't link there: Module not loaded."), -1);
00808 return -4;
00809 }
00810
00811 return 0;
00812 }
00813
00814 const char *botnet_get_info(botnet_bot_t *bot, const char *name)
00815 {
00816 char *ret;
00817
00818 xml_node_get_str(&ret, bot->info, name, 0, (void *) 0);
00819 return ret;
00820 }
00821
00822 void botnet_set_info(botnet_bot_t *bot, const char *name, const char *value)
00823 {
00824 xml_node_set_str(value, bot->info, name, 0, (void *) 0);
00825 }
00826
00827 void botnet_set_info_int(botnet_bot_t *bot, const char *name, int value)
00828 {
00829 xml_node_set_int(value, bot->info, name, 0, (void *) 0);
00830 }
00831
00847 void botnet_member_join(partychan_t *chan, partymember_t *p, int linking)
00848 {
00849 botnet_bot_t *tmp;
00850
00851 for (tmp = localbot_head; tmp; tmp = tmp->next_local) {
00852 if (tmp->flags & BOT_DELETED || (p->bot && tmp == p->bot->direction)) continue;
00853 if (tmp->handler && tmp->handler->on_join) tmp->handler->on_join(tmp->client_data, chan, p, linking);
00854 }
00855 }
00856
00870 void botnet_announce_login(partymember_t *p)
00871 {
00872 botnet_bot_t *tmp;
00873
00874 for (tmp = localbot_head; tmp; tmp = tmp->next_local) {
00875 if (tmp->flags & BOT_DELETED || (p->bot && tmp == p->bot->direction)) continue;
00876 if (tmp->handler && tmp->handler->on_login) tmp->handler->on_login(tmp->client_data, p);
00877 }
00878 }
00879
00880 void botnet_member_part(partychan_t *chan, partymember_t *p, const char *reason, int len)
00881 {
00882 botnet_bot_t *tmp;
00883
00884 for (tmp = localbot_head; tmp; tmp = tmp->next_local) {
00885 if (tmp->flags & BOT_DELETED || (p->bot && tmp == p->bot->direction)) continue;
00886 if (tmp->handler && tmp->handler->on_part) tmp->handler->on_part(tmp->client_data, chan, p, reason, len);
00887 }
00888 }
00889
00890 void botnet_chanmsg(partychan_t *chan, botnet_entity_t *src, const char *text, int len)
00891 {
00892 botnet_bot_t *tmp, *srcbot;
00893
00894 if (src->what == ENTITY_PARTYMEMBER) srcbot = src->user->bot;
00895 else srcbot = src->bot;
00896
00897 for (tmp = localbot_head; tmp; tmp = tmp->next_local) {
00898 if (tmp->flags & BOT_DELETED || (srcbot && tmp == srcbot->direction)) continue;
00899 if (tmp->handler && tmp->handler->on_chanmsg) tmp->handler->on_chanmsg(tmp->client_data, chan, src, text, len);
00900 }
00901 }
00902
00903 void botnet_broadcast(botnet_entity_t *src, const char *text, int len)
00904 {
00905 botnet_bot_t *tmp;
00906
00907 if (len < 0) len = strlen(text);
00908 for (tmp = localbot_head; tmp; tmp = tmp->next_local) {
00909 if (tmp->flags & BOT_DELETED || (src && src->bot && tmp == src->bot->direction)) continue;
00910 if (tmp->handler && tmp->handler->on_bcast) tmp->handler->on_bcast(tmp->client_data, src, text, len);
00911 }
00912 partymember_local_broadcast(src, text, len);
00913 }
00914
00915 void botnet_set_nick(partymember_t *p, const char *oldnick)
00916 {
00917 botnet_bot_t *tmp;
00918
00919 for (tmp = localbot_head; tmp; tmp = tmp->next_local) {
00920 if (tmp->flags & BOT_DELETED || (p->bot && tmp == p->bot->direction)) continue;
00921 if (tmp->handler && tmp->handler->on_nick) tmp->handler->on_nick(tmp->client_data, p, oldnick);
00922 }
00923 }
00924
00925 void botnet_member_quit(partymember_t *p, const char *reason, int len)
00926 {
00927 botnet_bot_t *tmp;
00928
00929 for (tmp = localbot_head; tmp; tmp = tmp->next_local) {
00930 if (tmp->flags & BOT_DELETED || (p->bot && tmp == p->bot->direction)) continue;
00931 if (tmp->handler && tmp->handler->on_quit) tmp->handler->on_quit(tmp->client_data, p, reason, len);
00932 }
00933 }
00934
00935 void botnet_botmsg(botnet_bot_t *src, botnet_bot_t *dst, const char *command, const char *text, int len)
00936 {
00937 if (!text) len = 0;
00938 else if (len < 0) len = strlen(text);
00939
00940 if (dst) {
00941 botnet_bot_t *dir = dst->direction;
00942 if (dir->handler && dir->handler->on_botmsg) dir->handler->on_botmsg(dir->client_data, src, dst, command, text, len);
00943 return;
00944 }
00945 bind_check(BT_bot, NULL, command, src, command, text);
00946 }
00947
00948 void botnet_botbroadcast(botnet_bot_t *src, const char *command, const char *text, int len)
00949 {
00950 botnet_bot_t *tmp;
00951
00952 if (!text) len = 0;
00953 else if (len < 0) len = strlen(text);
00954
00955 bind_check(BT_bot, NULL, command, src, command, text);
00956
00957 for (tmp = localbot_head; tmp; tmp = tmp->next_local) {
00958 if (tmp->flags & BOT_DELETED || (src && tmp == src->direction)) continue;
00959 if (tmp->handler && tmp->handler->on_botbroadcast) tmp->handler->on_botbroadcast(tmp->client_data, src, command, text, len);
00960 }
00961 }
00962
00963 void botnet_extension(int mode, botnet_entity_t *src, botnet_bot_t *dst, egg_module_t *mod, const char *cmd, const char *text, int len)
00964 {
00965 botnet_bot_t *tmp, *srcbot = src->what == ENTITY_BOT ? src->bot : src->user->bot;
00966
00967 if (!text) len = 0;
00968 else if (len < 0) len = strlen(text);
00969
00970 if (!dst) bind_check(BT_extension, NULL, cmd, src, cmd, text);
00971 if (mode == EXTENSION_ONE) {
00972 if (dst && dst->direction->handler && dst->direction->handler->on_extension)
00973 dst->direction->handler->on_extension(dst->direction->client_data, src, dst, cmd, text, len);
00974 return;
00975 }
00976
00977 for (tmp = localbot_head; tmp; tmp = tmp->next_local) {
00978 if (tmp->flags & BOT_DELETED || (srcbot && tmp == srcbot->direction)) continue;
00979 if (mod && !(tmp->owner && tmp->owner->module == mod)) continue;
00980 if (tmp->handler && tmp->handler->on_extension)
00981 tmp->handler->on_extension(tmp->client_data, src, mode == EXTENSION_ALL ? NULL : tmp, cmd, text, len);
00982 }
00983 }