/* * uhub - A tiny ADC p2p connection hub * Copyright (C) 2007-2010, Jan Vidar Krey * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ #include "uhub.h" #ifdef DEBUG_SENDQ static const char* user_log_str(struct hub_user* user) { static char buf[128]; if (user) { snprintf(buf, 128, "user={ %p, \"%s\", %s/%s}", user, user->id.nick, sid_to_string(user->id.sid), user->id.cid); } else { snprintf(buf, 128, "user={ %p }", user); } return buf; } #endif struct hub_user* user_create(struct hub_info* hub, struct net_connection* con, struct ip_addr_encap* addr) { struct hub_user* user = NULL; LOG_TRACE("user_create(), hub=%p, con[sd=%d]", hub, net_con_get_sd(con)); user = (struct hub_user*) hub_malloc_zero(sizeof(struct hub_user)); if (user == NULL) return NULL; /* OOM */ user->send_queue = ioq_send_create(); user->recv_queue = ioq_recv_create(); user->connection = con; net_con_reinitialize(user->connection, net_event, user, NET_EVENT_READ); memcpy(&user->id.addr, addr, sizeof(struct ip_addr_encap)); user_set_state(user, state_protocol); user->type = user_type_client; flood_control_reset(&user->flood_chat); flood_control_reset(&user->flood_connect); flood_control_reset(&user->flood_search); flood_control_reset(&user->flood_update); flood_control_reset(&user->flood_extras); user->hub = hub; return user; } /// generate a semi-stable CID: base32_encode(tiger({hub name} + {nick})) static const char* generate_bot_cid(struct hub_info* hub, const char* nick) { static char result[MAX_CID_LEN+1]; char buf[(MAX_NICK_LEN*2)+1]; uint64_t tiger_res[3]; memset(buf, 0, sizeof(buf)); snprintf(buf, sizeof(buf), "%s%s", hub->config->hub_name, nick); tiger((uint64_t*) buf, sizeof(buf), (uint64_t*) tiger_res); base32_encode((unsigned char*) tiger_res, TIGERSIZE, result); result[MAX_CID_LEN] = 0; return result; } struct hub_user* user_create_bot(struct hub_info* hub, const char* nick, const char* description, bot_recv_msg msg_handler) { struct hub_user* user = NULL; LOG_TRACE("user_create_bot(), hub=%p, nick=\"%s\"", hub, nick); user = (struct hub_user*) hub_malloc_zero(sizeof(struct hub_user)); if (user == NULL) return NULL; /* OOM */ strcpy(user->id.nick, nick); uman_get_free_sid(hub->users, user); user_set_state(user, state_normal); user->type = user_type_bot; user->credentials = auth_cred_bot; // The message handler user->ptr = (void*) msg_handler; user->info = adc_msg_construct(ADC_CMD_BINF, 15); if (user->info) { adc_msg_add_argument(user->info, sid_to_string(user->id.sid)); adc_msg_add_named_argument(user->info, ADC_INF_FLAG_CLIENT_TYPE, ADC_CLIENT_TYPE_BOT); adc_msg_add_named_argument_string(user->info, ADC_INF_FLAG_USER_AGENT, PRODUCT_STRING); adc_msg_add_named_argument_string(user->info, ADC_INF_FLAG_NICK, nick); adc_msg_add_named_argument_string(user->info, ADC_INF_FLAG_DESCRIPTION, description); adc_msg_add_named_argument(user->info, ADC_INF_FLAG_CLIENT_ID, generate_bot_cid(hub, nick)); } user->hub = hub; return user; } 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 user_set_state(struct hub_user* user, enum user_state state) { if ((user->state == state_cleanup && state != state_disconnected) || (user->state == state_disconnected)) { return; } user->state = state; } 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 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); } static int convert_support_fourcc(int fourcc) { switch (fourcc) { case FOURCC('B','A','S','0'): return feature_bas0; case FOURCC('B','A','S','E'): return feature_base; case FOURCC('A','U','T','0'): return feature_auto; case FOURCC('U','C','M','0'): case FOURCC('U','C','M','D'): return feature_ucmd; case FOURCC('Z','L','I','F'): return feature_zlif; case FOURCC('B','B','S','0'): return feature_bbs; case FOURCC('T','I','G','R'): return feature_tiger; case FOURCC('B','L','O','M'): case FOURCC('B','L','O','0'): return feature_bloom; case FOURCC('P','I','N','G'): return feature_ping; case FOURCC('L','I','N','K'): return feature_link; case FOURCC('A','D','C','S'): return feature_adcs; // ignore these extensions, they are not useful for the hub. case FOURCC('D','H','T','0'): return 0; default: LOG_DEBUG("Unknown extension: %x", fourcc); return 0; } } void user_support_add(struct hub_user* user, int fourcc) { int feature_mask = convert_support_fourcc(fourcc); user->flags |= feature_mask; } int user_flag_get(struct hub_user* user, enum user_flags flag) { return user->flags & flag; } void user_flag_set(struct hub_user* user, enum user_flags flag) { user->flags |= flag; } void user_flag_unset(struct hub_user* user, enum user_flags flag) { user->flags &= ~flag; } void user_set_nat_override(struct hub_user* user) { user_flag_set(user, flag_nat); } int user_is_nat_override(struct hub_user* user) { return user_flag_get(user, flag_nat); } void user_support_remove(struct hub_user* user, int fourcc) { int feature_mask = convert_support_fourcc(fourcc); user->flags &= ~feature_mask; } int user_have_feature_cast_support(struct hub_user* user, char feature[4]) { char* tmp = list_get_first(user->feature_cast); while (tmp) { if (strncmp(tmp, feature, 4) == 0) return 1; tmp = list_get_next(user->feature_cast); } return 0; } int user_set_feature_cast_support(struct hub_user* u, char feature[4]) { if (!u->feature_cast) { u->feature_cast = list_create(); } if (!u->feature_cast) { return 0; /* OOM! */ } list_append(u->feature_cast, hub_strndup(feature, 4)); return 1; } void user_clear_feature_cast_support(struct hub_user* u) { if (u->feature_cast) { list_clear(u->feature_cast, &hub_free); list_destroy(u->feature_cast); u->feature_cast = 0; } } int user_is_logged_in(struct hub_user* user) { if (user->state == state_normal) return 1; return 0; } int user_is_connecting(struct hub_user* user) { if (user->state == state_protocol || user->state == state_identify || user->state == state_verify) return 1; return 0; } int user_is_protocol_negotiating(struct hub_user* user) { if (user->state == state_protocol) return 1; return 0; } int user_is_disconnecting(struct hub_user* user) { if (user->state == state_cleanup || user->state == state_disconnected) return 1; return 0; } int user_is_protected(struct hub_user* user) { return auth_cred_is_protected(user->credentials); } /** * Returns 1 if a user is registered. * Only registered users will be let in if the hub is configured for registered * users only. */ int user_is_registered(struct hub_user* user) { return auth_cred_is_registered(user->credentials); } void user_net_io_want_write(struct hub_user* user) { net_con_update(user->connection, NET_EVENT_READ | NET_EVENT_WRITE); } void user_net_io_want_read(struct hub_user* user) { net_con_update(user->connection, NET_EVENT_READ); } const char* user_get_quit_reason_string(enum user_quit_reason reason) { switch (reason) { case quit_unknown: return "unknown"; break; case quit_disconnected: return "disconnected"; break; case quit_kicked: return "kicked"; break; case quit_banned: return "banned"; break; case quit_timeout: return "timeout"; break; case quit_send_queue: return "send queue"; break; case quit_memory_error: return "out of memory"; break; case quit_socket_error: return "socket error"; break; case quit_protocol_error: return "protocol error"; break; case quit_logon_error: return "login error"; break; case quit_update_error: return "update error"; break; case quit_hub_disabled: return "hub disabled"; break; case quit_ghost_timeout: return "ghost"; break; } return "unknown"; } const char* user_get_address(struct hub_user* user) { return ip_convert_to_string(&user->id.addr); }