void hub_free_variables(struct hub_info* hub) { hub_plugins_unload(hub); adc_msg_free(hub->command_info); adc_msg_free(hub->command_banner); adc_msg_free(hub->command_support); }
void ADC_client_destroy(struct ADC_client* client) { ADC_TRACE; ADC_client_disconnect(client); ioq_send_destroy(client->send_queue); ioq_recv_destroy(client->recv_queue); hub_free(client->timer); adc_msg_free(client->info); hub_free(client->nick); hub_free(client->desc); hub_free(client->address.hostname); hub_free(client); if (g_adc_client && g_adc_client->references > 0) { g_adc_client->references--; if (!g_adc_client->references) { #ifdef SSL_SUPPORT net_ssl_context_destroy(g_adc_client->ctx); g_adc_client->ctx = NULL; #endif hub_free(g_adc_client); g_adc_client = NULL; } } }
void user_update_info(struct hub_user* u, struct adc_message* cmd) { char prefix[2]; char* argument; size_t n = 0; struct adc_message* cmd_new = adc_msg_copy(u->info); if (!cmd_new) { /* FIXME: OOM! */ return; } /* * FIXME: Optimization potential: * * remove parts of cmd that do not really change anything in cmd_new. * this can save bandwidth if clients send multiple updates for information * that does not really change anything. */ argument = adc_msg_get_argument(cmd, n++); while (argument) { if (strlen(argument) >= 2) { prefix[0] = argument[0]; prefix[1] = argument[1]; adc_msg_replace_named_argument(cmd_new, prefix, argument+2); } hub_free(argument); argument = adc_msg_get_argument(cmd, n++); } user_set_info(u, cmd_new); adc_msg_free(cmd_new); }
int route_info_message(struct hub_info* hub, struct hub_user* u) { if (!user_is_nat_override(u)) { return route_to_all(hub, u->info); } else { struct adc_message* cmd = adc_msg_copy(u->info); const char* address = user_get_address(u); struct hub_user* user = 0; adc_msg_remove_named_argument(cmd, ADC_INF_FLAG_IPV4_ADDR); adc_msg_add_named_argument(cmd, ADC_INF_FLAG_IPV4_ADDR, address); user = (struct hub_user*) list_get_first(hub->users->list); while (user) { if (user_is_nat_override(user)) route_to_user(hub, user, cmd); else route_to_user(hub, user, u->info); user = (struct hub_user*) list_get_next(hub->users->list); } adc_msg_free(cmd); } return 0; }
void hub_free_variables(struct hub_info* hub) { #ifdef PLUGIN_SUPPORT hub_plugins_unload(hub); #endif adc_msg_free(hub->command_info); adc_msg_free(hub->command_banner); if (hub->command_motd) adc_msg_free(hub->command_motd); if (hub->command_rules) adc_msg_free(hub->command_rules); adc_msg_free(hub->command_support); }
void hub_send_password_challenge(struct hub_info* hub, struct hub_user* u) { struct adc_message* igpa; igpa = adc_msg_construct(ADC_CMD_IGPA, 38); adc_msg_add_argument(igpa, acl_password_generate_challenge(hub, u)); user_set_state(u, state_verify); route_to_user(hub, u, igpa); adc_msg_free(igpa); }
struct adc_message* adc_msg_copy(const struct adc_message* cmd) { char* tmp = 0; struct adc_message* copy = (struct adc_message*) msg_malloc_zero(sizeof(struct adc_message)); if (!copy) return NULL; /* OOM */ ADC_MSG_ASSERT(cmd); /* deep copy */ copy->cmd = cmd->cmd; copy->source = cmd->source; copy->target = cmd->target; copy->cache = 0; copy->length = cmd->length; copy->capacity = 0; copy->priority = cmd->priority; copy->references = 1; copy->feature_cast_include = 0; copy->feature_cast_exclude = 0; if (!adc_msg_grow(copy, copy->length)) { adc_msg_free(copy); return NULL; /* OOM */ } if (!copy->cache) { adc_msg_free(copy); return NULL; } memcpy(copy->cache, cmd->cache, cmd->length); copy->cache[copy->length] = 0; if (cmd->feature_cast_include) { copy->feature_cast_include = list_create(); LIST_FOREACH(char*, tmp, cmd->feature_cast_include, { list_append(copy->feature_cast_include, hub_strdup(tmp)); });
void hub_send_ping(struct hub_info* hub, struct hub_user* user) { /* This will just send a newline, despite appearing to do more below. */ struct adc_message* ping = adc_msg_construct(0, 0); ping->cache[0] = '\n'; ping->cache[1] = 0; ping->length = 1; ping->priority = 1; route_to_user(hub, user, ping); adc_msg_free(ping); }
static void ADC_client_send_handshake(struct ADC_client* client) { ADC_TRACE; struct adc_message* handshake = adc_msg_create(ADC_HANDSHAKE); client->callback(client, ADC_CLIENT_CONNECTED, 0); net_con_update(client->con, NET_EVENT_READ); ADC_client_send(client, handshake); ADC_client_set_state(client, ps_protocol); adc_msg_free(handshake); }
void user_set_info(struct hub_user* user, struct adc_message* cmd) { adc_msg_free(user->info); if (cmd) { user->info = adc_msg_incref(cmd); } else { user->info = 0; } }
void uman_send_quit_message(struct hub_info* hub, struct hub_user* leaving) { struct adc_message* command = adc_msg_construct(ADC_CMD_IQUI, 6); adc_msg_add_argument(command, (const char*) sid_to_string(leaving->id.sid)); if (leaving->quit_reason == quit_banned || leaving->quit_reason == quit_kicked) { adc_msg_add_argument(command, ADC_QUI_FLAG_DISCONNECT); } route_to_all(hub, command); adc_msg_free(command); }
void hub_send_sid(struct hub_info* hub, struct hub_user* u) { sid_t sid; struct adc_message* command; if (user_is_connecting(u)) { command = adc_msg_construct(ADC_CMD_ISID, 10); sid = uman_get_free_sid(hub, u); adc_msg_add_argument(command, (const char*) sid_to_string(sid)); route_to_user(hub, u, command); adc_msg_free(command); } }
void user_destroy(struct hub_user* user) { LOG_TRACE("user_destroy(), user=%p", user); ioq_recv_destroy(user->recv_queue); ioq_send_destroy(user->send_queue); if (user->connection) { LOG_TRACE("user_destory() -> net_con_close(%p)", user->connection); net_con_close(user->connection); } adc_msg_free(user->info); user_clear_feature_cast_support(user); hub_free(user); }
void hub_send_flood_warning(struct hub_info* hub, struct hub_user* u, const char* message) { struct adc_message* msg; char* tmp; if (user_flag_get(u, flag_flood)) return; msg = adc_msg_construct(ADC_CMD_ISTA, 128); if (msg) { tmp = adc_msg_escape(message); adc_msg_add_argument(msg, "110"); adc_msg_add_argument(msg, tmp); hub_free(tmp); route_to_user(hub, u, msg); user_flag_set(u, flag_flood); adc_msg_free(msg); } }
/** * @param hub The hub instance this message is sent from. * @param user The user this message is sent to. * @param msg See enum status_message * @param level See enum status_level */ void hub_send_status(struct hub_info* hub, struct hub_user* user, enum status_message msg, enum msg_status_level level) { struct hub_config* cfg = hub->config; struct adc_message* cmd = adc_msg_construct(ADC_CMD_ISTA, 6); struct adc_message* qui = adc_msg_construct(ADC_CMD_IQUI, 512); char code[4]; char buf[256]; const char* text = 0; const char* flag = 0; char* escaped_text = 0; int reconnect_time = 0; int redirect = 0; if (!cmd || !qui) { adc_msg_free(cmd); adc_msg_free(qui); return; } #define STATUS(CODE, MSG, FLAG, RCONTIME, REDIRECT) case status_ ## MSG : set_status_code(level, CODE, code); text = cfg->MSG; flag = FLAG; reconnect_time = RCONTIME; redirect = REDIRECT; break switch (msg) { STATUS(11, msg_hub_full, 0, 600, 1); /* FIXME: Proper timeout? */ STATUS(12, msg_hub_disabled, 0, -1, 1); STATUS(26, msg_hub_registered_users_only, 0, 0, 1); STATUS(43, msg_inf_error_nick_missing, 0, 0, 0); STATUS(43, msg_inf_error_nick_multiple, 0, 0, 0); STATUS(21, msg_inf_error_nick_invalid, 0, 0, 0); STATUS(21, msg_inf_error_nick_long, 0, 0, 0); STATUS(21, msg_inf_error_nick_short, 0, 0, 0); STATUS(21, msg_inf_error_nick_spaces, 0, 0, 0); STATUS(21, msg_inf_error_nick_bad_chars, 0, 0, 0); STATUS(21, msg_inf_error_nick_not_utf8, 0, 0, 0); STATUS(22, msg_inf_error_nick_taken, 0, 0, 0); STATUS(21, msg_inf_error_nick_restricted, 0, 0, 0); STATUS(43, msg_inf_error_cid_invalid, "FBID", 0, 0); STATUS(43, msg_inf_error_cid_missing, "FMID", 0, 0); STATUS(24, msg_inf_error_cid_taken, 0, 0, 0); STATUS(43, msg_inf_error_pid_missing, "FMPD", 0, 0); STATUS(27, msg_inf_error_pid_invalid, "FBPD", 0, 0); STATUS(31, msg_ban_permanently, 0, 0, 0); STATUS(32, msg_ban_temporarily, "TL600", 600, 0); /* FIXME: Proper timeout? */ STATUS(23, msg_auth_invalid_password, 0, 0, 0); STATUS(20, msg_auth_user_not_found, 0, 0, 0); STATUS(30, msg_error_no_memory, 0, 0, 0); STATUS(43, msg_user_share_size_low, "FB" ADC_INF_FLAG_SHARED_SIZE, 0, 1); STATUS(43, msg_user_share_size_high, "FB" ADC_INF_FLAG_SHARED_SIZE, 0, 1); STATUS(43, msg_user_slots_low, "FB" ADC_INF_FLAG_UPLOAD_SLOTS, 0, 1); STATUS(43, msg_user_slots_high, "FB" ADC_INF_FLAG_UPLOAD_SLOTS, 0, 1); STATUS(43, msg_user_hub_limit_low, 0, 0, 1); STATUS(43, msg_user_hub_limit_high, 0, 0, 1); STATUS(47, msg_proto_no_common_hash, 0, -1, 1); STATUS(40, msg_proto_obsolete_adc0, 0, -1, 1); } #undef STATUS escaped_text = adc_msg_escape(text); adc_msg_add_argument(cmd, code); adc_msg_add_argument(cmd, escaped_text); if (flag) { adc_msg_add_argument(cmd, flag); } route_to_user(hub, user, cmd); if (level >= status_level_fatal) { adc_msg_add_argument(qui, sid_to_string(user->id.sid)); snprintf(buf, 230, "MS%s", escaped_text); adc_msg_add_argument(qui, buf); if (reconnect_time != 0) { snprintf(buf, 10, "TL%d", reconnect_time); adc_msg_add_argument(qui, buf); } if (redirect && *hub->config->redirect_addr) { snprintf(buf, 255, "RD%s", hub->config->redirect_addr); adc_msg_add_argument(qui, buf); } route_to_user(hub, user, qui); } hub_free(escaped_text); adc_msg_free(cmd); adc_msg_free(qui); }
int hub_handle_message(struct hub_info* hub, struct hub_user* u, const char* line, size_t length) { int ret = 0; struct adc_message* cmd = 0; LOG_PROTO("recv %s: %s", sid_to_string(u->id.sid), line); if (user_is_disconnecting(u)) return -1; cmd = adc_msg_parse_verify(u, line, length); if (cmd) { switch (cmd->cmd) { case ADC_CMD_HSUP: CHECK_FLOOD(extras, 0); ret = hub_handle_support(hub, u, cmd); break; case ADC_CMD_HPAS: CHECK_FLOOD(extras, 0); ret = hub_handle_password(hub, u, cmd); break; case ADC_CMD_BINF: CHECK_FLOOD(update, 1); ret = hub_handle_info(hub, u, cmd); break; case ADC_CMD_DINF: case ADC_CMD_EINF: case ADC_CMD_FINF: case ADC_CMD_BQUI: case ADC_CMD_DQUI: case ADC_CMD_EQUI: case ADC_CMD_FQUI: /* these must never be allowed for security reasons, so we ignore them. */ CHECK_FLOOD(extras, 1); break; case ADC_CMD_EMSG: case ADC_CMD_DMSG: case ADC_CMD_BMSG: case ADC_CMD_FMSG: CHECK_FLOOD(chat, 1); ret = hub_handle_chat_message(hub, u, cmd); break; case ADC_CMD_BSCH: case ADC_CMD_DSCH: case ADC_CMD_ESCH: case ADC_CMD_FSCH: cmd->priority = -1; if (plugin_handle_search(hub, u, cmd->cache) == st_deny) break; CHECK_FLOOD(search, 1); ROUTE_MSG; case ADC_CMD_FRES: // spam case ADC_CMD_BRES: // spam case ADC_CMD_ERES: // pointless. CHECK_FLOOD(extras, 1); break; case ADC_CMD_DRES: cmd->priority = -1; if (plugin_handle_search_result(hub, u, uman_get_user_by_sid(hub->users, cmd->target), cmd->cache) == st_deny) break; /* CHECK_FLOOD(search, 0); */ ROUTE_MSG; case ADC_CMD_DRCM: cmd->priority = -1; if (plugin_handle_revconnect(hub, u, uman_get_user_by_sid(hub->users, cmd->target)) == st_deny) break; CHECK_FLOOD(connect, 1); ROUTE_MSG; case ADC_CMD_DCTM: cmd->priority = -1; if (plugin_handle_connect(hub, u, uman_get_user_by_sid(hub->users, cmd->target)) == st_deny) break; CHECK_FLOOD(connect, 1); ROUTE_MSG; case ADC_CMD_BCMD: case ADC_CMD_DCMD: case ADC_CMD_ECMD: case ADC_CMD_FCMD: case ADC_CMD_HCMD: CHECK_FLOOD(extras, 1); break; default: CHECK_FLOOD(extras, 1); ROUTE_MSG; } adc_msg_free(cmd); } else { if (!user_is_logged_in(u)) { ret = -1; } } return ret; }
void hub_send_hubinfo(struct hub_info* hub, struct hub_user* u) { struct adc_message* info = adc_msg_copy(hub->command_info); int value = 0; uint64_t size = 0; if (user_flag_get(u, feature_ping)) { /* FIXME: These are missing: HH - Hub Host address ( DNS or IP ) WS - Hub Website NE - Hub Network OW - Hub Owner name */ adc_msg_add_named_argument(info, "UC", uhub_itoa(hub_get_user_count(hub))); adc_msg_add_named_argument(info, "MC", uhub_itoa(hub_get_max_user_count(hub))); adc_msg_add_named_argument(info, "SS", uhub_ulltoa(hub_get_shared_size(hub))); adc_msg_add_named_argument(info, "SF", uhub_ulltoa(hub_get_shared_files(hub))); /* Maximum/minimum share size */ size = hub_get_max_share(hub); if (size) adc_msg_add_named_argument(info, "XS", uhub_ulltoa(size)); size = hub_get_min_share(hub); if (size) adc_msg_add_named_argument(info, "MS", uhub_ulltoa(size)); /* Maximum/minimum upload slots allowed per user */ value = hub_get_max_slots(hub); if (value) adc_msg_add_named_argument(info, "XL", uhub_itoa(value)); value = hub_get_min_slots(hub); if (value) adc_msg_add_named_argument(info, "ML", uhub_itoa(value)); /* guest users must be on min/max hubs */ value = hub_get_max_hubs_user(hub); if (value) adc_msg_add_named_argument(info, "XU", uhub_itoa(value)); value = hub_get_min_hubs_user(hub); if (value) adc_msg_add_named_argument(info, "MU", uhub_itoa(value)); /* registered users must be on min/max hubs */ value = hub_get_max_hubs_reg(hub); if (value) adc_msg_add_named_argument(info, "XR", uhub_itoa(value)); value = hub_get_min_hubs_reg(hub); if (value) adc_msg_add_named_argument(info, "MR", uhub_itoa(value)); /* operators must be on min/max hubs */ value = hub_get_max_hubs_op(hub); if (value) adc_msg_add_named_argument(info, "XO", uhub_itoa(value)); value = hub_get_min_hubs_op(hub); if (value) adc_msg_add_named_argument(info, "MO", uhub_itoa(value)); /* uptime in seconds */ adc_msg_add_named_argument(info, "UP", uhub_itoa((int) difftime(time(0), hub->tm_started))); } if (user_is_connecting(u) || user_is_logged_in(u)) { route_to_user(hub, u, info); } adc_msg_free(info); /* Only send banner when connecting */ if (hub->config->show_banner && user_is_connecting(u)) { route_to_user(hub, u, hub->command_banner); } }
int hub_handle_support(struct hub_info* hub, struct hub_user* u, struct adc_message* cmd) { int ret = 0; int index = 0; int ok = 1; char* arg = adc_msg_get_argument(cmd, index); if (hub->status == hub_status_disabled && u->state == state_protocol) { on_login_failure(hub, u, status_msg_hub_disabled); hub_free(arg); return -1; } while (arg) { if (strlen(arg) == 6) { fourcc_t fourcc = FOURCC(arg[2], arg[3], arg[4], arg[5]); if (strncmp(arg, ADC_SUP_FLAG_ADD, 2) == 0) { user_support_add(u, fourcc); } else if (strncmp(arg, ADC_SUP_FLAG_REMOVE, 2) == 0) { user_support_remove(u, fourcc); } else { ok = 0; } } else { ok = 0; } index++; hub_free(arg); arg = adc_msg_get_argument(cmd, index); } if (u->state == state_protocol) { if (index == 0) ok = 0; /* Need to support *SOMETHING*, at least BASE */ if (!ok) { /* disconnect user. Do not send crap during initial handshake! */ hub_disconnect_user(hub, u, quit_logon_error); return -1; } if (user_flag_get(u, feature_base)) { /* User supports ADC/1.0 and a hash we know */ if (user_flag_get(u, feature_tiger)) { hub_send_handshake(hub, u); /* TODO: timeouts for mux users */ if (u->connection) net_con_set_timeout(u->connection, TIMEOUT_HANDSHAKE); } else { // no common hash algorithm. hub_send_status(hub, u, status_msg_proto_no_common_hash, status_level_fatal); hub_disconnect_user(hub, u, quit_protocol_error); } } else if (user_flag_get(u, feature_bas0)) { if (hub->config->obsolete_clients) { hub_send_handshake(hub, u); /* TODO: timeouts for mux users */ if (u->connection) net_con_set_timeout(u->connection, TIMEOUT_HANDSHAKE); } else { /* disconnect user for using an obsolete client. */ char* tmp = adc_msg_escape(hub->config->msg_proto_obsolete_adc0); struct adc_message* message = adc_msg_construct(ADC_CMD_IMSG, 6 + strlen(tmp)); adc_msg_add_argument(message, tmp); hub_free(tmp); route_to_user(hub, u, message); adc_msg_free(message); hub_disconnect_user(hub, u, quit_protocol_error); } } else { /* Not speaking a compatible protocol - just disconnect. */ hub_disconnect_user(hub, u, quit_logon_error); } } return ret; }
int hub_handle_message(struct hub_info* hub, struct hub_user* u, const char* line, size_t length) { int ret = 0; struct adc_message* cmd = 0; LOG_PROTO("recv %s: %s", sid_to_string(u->id.sid), line); if (user_is_disconnecting(u)) return -1; cmd = adc_msg_parse_verify(u, line, length); if (cmd) { switch (cmd->cmd) { case ADC_CMD_HSUP: CHECK_FLOOD(extras, 0); ret = hub_handle_support(hub, u, cmd); break; case ADC_CMD_HPAS: CHECK_FLOOD(extras, 0); ret = hub_handle_password(hub, u, cmd); break; case ADC_CMD_BINF: CHECK_FLOOD(update, 1); ret = hub_handle_info(hub, u, cmd); break; case ADC_CMD_DINF: case ADC_CMD_EINF: case ADC_CMD_FINF: /* these must never be allowed for security reasons, so we ignore them. */ break; case ADC_CMD_EMSG: case ADC_CMD_DMSG: case ADC_CMD_BMSG: case ADC_CMD_FMSG: CHECK_FLOOD(chat, 1); ret = hub_handle_chat_message(hub, u, cmd); break; case ADC_CMD_BSCH: case ADC_CMD_DSCH: case ADC_CMD_ESCH: case ADC_CMD_FSCH: cmd->priority = -1; CHECK_CHAT_ONLY; CHECK_FLOOD(search, 1); ROUTE_MSG; case ADC_CMD_DRES: cmd->priority = -1; CHECK_CHAT_ONLY; /* CHECK_FLOOD(search, 0); */ ROUTE_MSG; case ADC_CMD_DRCM: case ADC_CMD_DCTM: cmd->priority = -1; CHECK_CHAT_ONLY; CHECK_FLOOD(connect, 1); ROUTE_MSG; default: CHECK_FLOOD(extras, 1); ROUTE_MSG; } adc_msg_free(cmd); } else { if (!user_is_logged_in(u)) { ret = -1; } } return ret; }
/* * If user is in the connecting state, we need to do fairly * strict checking of all arguments. * This means we disconnect users when they provide invalid data * during the login sequence. * When users are merely updating their data after successful login * we can just ignore any invalid data and not broadcast it. * * The data we need to check is: * - nick name (valid, not taken, etc) * - CID/PID (valid, not taken, etc). * - IP addresses (IPv4 and IPv6) */ int hub_handle_info(struct hub_info* hub, struct hub_user* user, const struct adc_message* cmd_unmodified) { int ret; struct adc_message* cmd = adc_msg_copy(cmd_unmodified); if (!cmd) return -1; /* OOM */ cmd->priority = 1; hub_handle_info_common(user, cmd); /* If user is logging in, perform more checks, otherwise only a few things need to be checked. */ if (user_is_connecting(user)) { /* * Don't allow the user to send multiple INF messages in this stage! * Since that can have serious side-effects. */ if (user->info) { adc_msg_free(cmd); return 0; } ret = hub_handle_info_login(hub, user, cmd); if (ret < 0) { on_login_failure(hub, user, ret); adc_msg_free(cmd); return -1; } else { /* Post a message, the user has joined */ struct event_data post; memset(&post, 0, sizeof(post)); post.id = UHUB_EVENT_USER_JOIN; post.ptr = user; post.flags = ret; /* 0 - all OK, 1 - need authentication */ event_queue_post(hub->queue, &post); adc_msg_free(cmd); return 0; } } else { /* These must not be allowed updated, let's remove them! */ adc_msg_remove_named_argument(cmd, ADC_INF_FLAG_PRIVATE_ID); adc_msg_remove_named_argument(cmd, ADC_INF_FLAG_CLIENT_ID); /* * If the nick is not accepted, do not relay it. * Otherwise, the nickname will be updated. */ if (adc_msg_has_named_argument(cmd, ADC_INF_FLAG_NICK)) { #ifdef ALLOW_CHANGE_NICK if (!check_nick(hub, user, cmd)) #endif adc_msg_remove_named_argument(cmd, ADC_INF_FLAG_NICK); } ret = check_limits(hub, user, cmd); if (ret < 0) { on_update_failure(hub, user, ret); adc_msg_free(cmd); return -1; } strip_network(user, cmd); hub_handle_info_low_bandwidth(hub, user, cmd); user_update_info(user, cmd); if (!adc_msg_is_empty(cmd)) { route_message(hub, user, cmd); } adc_msg_free(cmd); } return 0; }
static int ADC_client_on_recv_line(struct ADC_client* client, const char* line, size_t length) { struct ADC_chat_message chat; struct ADC_client_callback_data data; ADC_TRACE; #ifdef ADC_CLIENT_DEBUG_PROTO ADC_client_debug(client, "- LINE: '%s'", line); #endif /* Parse message */ struct adc_message* msg = adc_msg_parse(line, length); if (!msg) { ADC_client_debug(client, "WARNING: Message cannot be decoded: \"%s\"", line); return -1; } if (length < 4) { ADC_client_debug(client, "Unexpected response from hub: '%s'", line); return -1; } switch (msg->cmd) { case ADC_CMD_ISUP: break; case ADC_CMD_ISID: if (client->state == ps_protocol) { client->sid = string_to_sid(&line[5]); client->callback(client, ADC_CLIENT_LOGGING_IN, 0); ADC_client_set_state(client, ps_identify); ADC_client_send_info(client); } break; case ADC_CMD_BMSG: case ADC_CMD_EMSG: case ADC_CMD_DMSG: case ADC_CMD_IMSG: { chat.from_sid = msg->source; chat.to_sid = msg->target; data.chat = &chat; EXTRACT_POS_ARG(msg, 0, chat.message); chat.flags = 0; if (adc_msg_has_named_argument(msg, ADC_MSG_FLAG_ACTION)) chat.flags |= chat_flags_action; if (adc_msg_has_named_argument(msg, ADC_MSG_FLAG_PRIVATE)) chat.flags |= chat_flags_private; client->callback(client, ADC_CLIENT_MESSAGE, &data); hub_free(chat.message); break; } case ADC_CMD_IINF: { struct ADC_hub_info hubinfo; EXTRACT_NAMED_ARG(msg, "NI", hubinfo.name); EXTRACT_NAMED_ARG(msg, "DE", hubinfo.description); EXTRACT_NAMED_ARG(msg, "VE", hubinfo.version); struct ADC_client_callback_data data; data.hubinfo = &hubinfo; client->callback(client, ADC_CLIENT_HUB_INFO, &data); hub_free(hubinfo.name); hub_free(hubinfo.description); hub_free(hubinfo.version); break; } case ADC_CMD_BSCH: case ADC_CMD_FSCH: { client->callback(client, ADC_CLIENT_SEARCH_REQ, 0); break; } case ADC_CMD_BINF: { if (msg->source == client->sid) { if (client->state == ps_verify || client->state == ps_identify) { ADC_client_on_login(client); } } else { if (adc_msg_has_named_argument(msg, "ID")) { struct ADC_user user; user.sid = msg->source; EXTRACT_NAMED_ARG_X(msg, "NI", user.name, sizeof(user.name)); EXTRACT_NAMED_ARG_X(msg, "DE", user.description, sizeof(user.description)); EXTRACT_NAMED_ARG_X(msg, "VE", user.version, sizeof(user.version)); EXTRACT_NAMED_ARG_X(msg, "ID", user.cid, sizeof(user.cid)); EXTRACT_NAMED_ARG_X(msg, "I4", user.address, sizeof(user.address)); struct ADC_client_callback_data data; data.user = &user; client->callback(client, ADC_CLIENT_USER_JOIN, &data); } } } break; case ADC_CMD_IQUI: { struct ADC_client_quit_reason reason; memset(&reason, 0, sizeof(reason)); reason.sid = string_to_sid(&line[5]); if (adc_msg_has_named_argument(msg, ADC_QUI_FLAG_DISCONNECT)) reason.flags |= 1; data.quit = &reason; client->callback(client, ADC_CLIENT_USER_QUIT, &data); break; } case ADC_CMD_ISTA: /* if (strncmp(line, "ISTA 000", 8)) { ADC_client_debug(client, "status: '%s'\n", (start + 9)); } */ break; default: break; } adc_msg_free(msg); return 0; }