#include "hybbx/session.h" #include "hybbx/service.h" #include "hybbx/command.h" #include "hybbx/storage.h" #include "hybbx/texts.h" #include "hybbx/auth.h" #include "hybbx/chat.h" #include "hybbx/conference.h" #include "hybbx/mail.h" #include "hybbx/proxymail.h" #include "hybbx/proxychat.h" #include "hybbx/terminal.h" #include "hybbx/traffic.h" #include "hybbx/log.h" #include "hybbx/monitor.h" #include "hybbx/instance.h" #include "hybbx/hybbx.h" #include "hybbx/util.h" #include "hybbx/bandwidth_policy.h" #include "hybbx/instance.h" #include "hybbx/messages.h" #include #include #include #include #include static int session_str_ieq(const char *a, const char *b) { if (a == NULL || b == NULL) { return 0; } while (*a != '\0' && *b != '\0') { char ca = (char)(*a >= 'A' && *a <= 'Z' ? *a + 32 : *a); char cb = (char)(*b >= 'A' && *b <= 'Z' ? *b + 32 : *b); if (ca != cb) { return 0; } a++; b++; } return *a == '\0' && *b == '\0'; } typedef struct hybbx_session_core { hybbx_session_t pub; hybbx_session_record_t record; hybbx_user_record_t user; hybbx_service_t *service; char line_buf[HYBBX_LINE_MAX]; size_t line_len; size_t line_cursor; char history[HYBBX_HISTORY_MAX][HYBBX_LINE_MAX]; unsigned history_count; unsigned history_next; int history_view; char history_saved_line[HYBBX_LINE_MAX]; int expect_lf; int logged_in; int login_prompt; unsigned guest_slot; time_t guest_expires_at; hybbx_session_area_t area; hybbx_session_area_t area_stack[HYBBX_AREA_STACK_MAX]; unsigned area_depth; unsigned chat_channel; int chat_max_notice_shown; char conference_topic[HYBBX_CONFERENCE_TOPIC_MAX]; char conference_partner[HYBBX_USER_NAME_MAX]; int conference_invite_pending; char conference_invite_from[HYBBX_USER_NAME_MAX]; char conference_invite_topic[HYBBX_CONFERENCE_TOPIC_MAX]; time_t conference_invite_deadline; struct { char target[HYBBX_USER_NAME_MAX]; time_t sent_at[HYBBX_CONFERENCE_INVITE_MAX_PER_TARGET]; } conference_invite_rates[8]; int monitor_active; int mail_composing; char mail_compose_to[HYBBX_USER_NAME_MAX]; char mail_compose_subject[HYBBX_MAIL_SUBJECT_MAX + 1]; char mail_compose_body[HYBBX_MAIL_BODY_MAX + 1]; size_t mail_compose_body_len; int proxymail_composing; char proxymail_compose_to[HYBBX_PROXYMAIL_ADDRESS_MAX]; char proxymail_compose_subject[HYBBX_MAIL_SUBJECT_MAX + 1]; char proxymail_compose_body[HYBBX_MAIL_BODY_MAX + 1]; size_t proxymail_compose_body_len; unsigned out_col; int input_echo; int bandwidth_paused; int bandwidth_disconnect; unsigned char esc_state; char csi_buf[24]; size_t csi_len; } hybbx_session_core_t; #define SESSION_ESC_NONE 0u #define SESSION_ESC_ESC 1u #define SESSION_ESC_CSI 2u #define SESSION_ESC_SS3 3u #define SESSION_CSI_BUF_MAX 24u static hybbx_result_t session_handle_user_byte(hybbx_session_core_t *core, unsigned char byte); static size_t session_line_max_len(const hybbx_session_core_t *core); static unsigned session_chat_message_max(const hybbx_session_core_t *core); static hybbx_result_t session_write_transport(hybbx_session_core_t *core, const char *data, size_t len); static hybbx_result_t session_line_refresh_suffix(hybbx_session_core_t *core); static hybbx_result_t session_line_cursor_left(hybbx_session_core_t *core); static hybbx_result_t session_line_cursor_right(hybbx_session_core_t *core); static hybbx_result_t session_line_cursor_home(hybbx_session_core_t *core); static hybbx_result_t session_line_cursor_end(hybbx_session_core_t *core); static hybbx_result_t session_line_backspace(hybbx_session_core_t *core); static hybbx_result_t session_line_delete_forward(hybbx_session_core_t *core); static hybbx_result_t session_line_insert_char(hybbx_session_core_t *core, char ch); static hybbx_result_t session_history_up(hybbx_session_core_t *core); static hybbx_result_t session_history_down(hybbx_session_core_t *core); static void session_maybe_announce_login(hybbx_session_core_t *core); static void session_escape_reset(hybbx_session_core_t *core) { if (core == NULL) { return; } core->esc_state = SESSION_ESC_NONE; core->csi_len = 0; } static int session_csi_is_final(unsigned char ch) { return ch >= 0x40u && ch <= 0x7eu; } static size_t session_line_max_len(const hybbx_session_core_t *core) { size_t line_max = sizeof(core->line_buf) - 1; if (core == NULL) { return HYBBX_LINE_MAX - 1; } if (core->area == HYBBX_AREA_CHAT || core->area == HYBBX_AREA_CONFERENCE || core->area == HYBBX_AREA_PROXYCHAT) { line_max = session_chat_message_max(core); } else if (core->area == HYBBX_AREA_MAIL && core->mail_composing) { line_max = HYBBX_LINE_MAX - 1; } else if (core->area == HYBBX_AREA_PROXYMAIL && core->proxymail_composing) { line_max = HYBBX_LINE_MAX - 1; } return line_max; } static int session_char_echoable(char ch) { unsigned char byte = (unsigned char)ch; return byte >= 0x20 && byte != 0x7f; } static hybbx_result_t session_write_transport(hybbx_session_core_t *core, const char *data, size_t len) { if (core == NULL || data == NULL || len == 0) { return HYBBX_OK; } if (core->pub.transport != NULL && core->pub.transport->write != NULL) { return core->pub.transport->write(&core->pub, data, len); } return HYBBX_OK; } static hybbx_result_t session_line_move_back(hybbx_session_core_t *core, size_t count) { char buf[64]; size_t chunk; size_t sent = 0; if (core == NULL || count == 0) { return HYBBX_OK; } memset(buf, '\b', sizeof(buf)); while (sent < count) { hybbx_result_t rc; chunk = count - sent; if (chunk > sizeof(buf)) { chunk = sizeof(buf); } rc = session_write_transport(core, buf, chunk); if (rc != HYBBX_OK) { return rc; } sent += chunk; } return HYBBX_OK; } static hybbx_result_t session_line_refresh_suffix(hybbx_session_core_t *core) { size_t suffix_len; hybbx_result_t rc; if (core == NULL || !core->input_echo) { return HYBBX_OK; } suffix_len = core->line_len - core->line_cursor; if (suffix_len > 0) { rc = session_write_transport(core, core->line_buf + core->line_cursor, suffix_len); if (rc != HYBBX_OK) { return rc; } } rc = session_write_transport(core, "\x1b[K", 3); if (rc != HYBBX_OK) { return rc; } return session_line_move_back(core, suffix_len); } static hybbx_result_t session_line_cursor_left(hybbx_session_core_t *core) { if (core == NULL || core->line_cursor == 0) { return HYBBX_OK; } core->line_cursor--; if (!core->input_echo) { return HYBBX_OK; } return session_write_transport(core, "\b", 1); } static hybbx_result_t session_line_cursor_right(hybbx_session_core_t *core) { hybbx_result_t rc; if (core == NULL || core->line_cursor >= core->line_len) { return HYBBX_OK; } if (core->input_echo) { char out[2]; out[0] = core->line_buf[core->line_cursor]; out[1] = '\0'; rc = session_write_transport(core, out, 1); if (rc != HYBBX_OK) { return rc; } } core->line_cursor++; return HYBBX_OK; } static hybbx_result_t session_line_cursor_home(hybbx_session_core_t *core) { hybbx_result_t rc = HYBBX_OK; if (core == NULL) { return HYBBX_ERR_INVALID; } while (core->line_cursor > 0) { rc = session_line_cursor_left(core); if (rc != HYBBX_OK) { return rc; } } return HYBBX_OK; } static hybbx_result_t session_line_cursor_end(hybbx_session_core_t *core) { hybbx_result_t rc = HYBBX_OK; if (core == NULL) { return HYBBX_ERR_INVALID; } while (core->line_cursor < core->line_len) { rc = session_line_cursor_right(core); if (rc != HYBBX_OK) { return rc; } } return HYBBX_OK; } static hybbx_result_t session_line_backspace(hybbx_session_core_t *core) { hybbx_result_t rc; if (core == NULL || core->line_cursor == 0) { return HYBBX_OK; } core->line_cursor--; memmove(core->line_buf + core->line_cursor, core->line_buf + core->line_cursor + 1, core->line_len - core->line_cursor - 1); core->line_len--; core->line_buf[core->line_len] = '\0'; if (!core->input_echo) { return HYBBX_OK; } rc = session_write_transport(core, "\b", 1); if (rc != HYBBX_OK) { return rc; } return session_line_refresh_suffix(core); } static hybbx_result_t session_line_delete_forward(hybbx_session_core_t *core) { if (core == NULL || core->line_cursor >= core->line_len) { return HYBBX_OK; } memmove(core->line_buf + core->line_cursor, core->line_buf + core->line_cursor + 1, core->line_len - core->line_cursor - 1); core->line_len--; core->line_buf[core->line_len] = '\0'; if (!core->input_echo) { return HYBBX_OK; } return session_line_refresh_suffix(core); } static hybbx_result_t session_line_insert_char(hybbx_session_core_t *core, char ch) { size_t line_max; hybbx_result_t rc; char out[2]; if (core == NULL || !session_char_echoable(ch)) { return HYBBX_OK; } line_max = session_line_max_len(core); if (core->line_len >= line_max) { return HYBBX_OK; } if (core->line_cursor < core->line_len) { memmove(core->line_buf + core->line_cursor + 1, core->line_buf + core->line_cursor, core->line_len - core->line_cursor); } core->line_buf[core->line_cursor] = ch; core->line_len++; core->line_cursor++; core->line_buf[core->line_len] = '\0'; if (!core->input_echo) { return HYBBX_OK; } out[0] = ch; out[1] = '\0'; rc = session_write_transport(core, out, 1); if (rc != HYBBX_OK) { core->line_cursor--; core->line_len--; core->line_buf[core->line_len] = '\0'; return rc; } return session_line_refresh_suffix(core); } static int session_history_should_store(const char *line) { if (line == NULL || line[0] == '\0') { return 0; } return strncmp(line, "/login ", 7) != 0 && strncmp(line, "/register ", 10) != 0 && strncmp(line, "/changeme ", 10) != 0 && strncmp(line, "/userchange ", 12) != 0; } static void session_history_add(hybbx_session_core_t *core, const char *line) { if (core == NULL || !session_history_should_store(line)) { return; } hybbx_strlcpy(core->history[core->history_next], line, sizeof(core->history[0])); core->history_next = (core->history_next + 1u) % HYBBX_HISTORY_MAX; if (core->history_count < HYBBX_HISTORY_MAX) { core->history_count++; } core->history_view = -1; core->history_saved_line[0] = '\0'; } static hybbx_result_t session_line_replace(hybbx_session_core_t *core, const char *line) { size_t len; hybbx_result_t rc; if (core == NULL || line == NULL) { return HYBBX_ERR_INVALID; } len = strlen(line); if (len >= sizeof(core->line_buf)) { len = sizeof(core->line_buf) - 1; } if (core->input_echo) { rc = session_line_move_back(core, core->line_cursor); if (rc != HYBBX_OK) { return rc; } if (len > 0) { rc = session_write_transport(core, line, len); if (rc != HYBBX_OK) { return rc; } } rc = session_write_transport(core, "\x1b[K", 3); if (rc != HYBBX_OK) { return rc; } } memcpy(core->line_buf, line, len); core->line_buf[len] = '\0'; core->line_len = len; core->line_cursor = len; return HYBBX_OK; } static hybbx_result_t session_history_up(hybbx_session_core_t *core) { unsigned slot; if (core == NULL || core->history_count == 0) { return HYBBX_OK; } if (core->history_view < 0) { hybbx_strlcpy(core->history_saved_line, core->line_buf, sizeof(core->history_saved_line)); core->history_view = 0; } else if ((unsigned)(core->history_view + 1) < core->history_count) { core->history_view++; } else { return HYBBX_OK; } slot = (core->history_next + HYBBX_HISTORY_MAX - 1u - (unsigned)core->history_view) % HYBBX_HISTORY_MAX; return session_line_replace(core, core->history[slot]); } static hybbx_result_t session_history_down(hybbx_session_core_t *core) { unsigned slot; if (core == NULL || core->history_view < 0) { return HYBBX_OK; } if (core->history_view == 0) { core->history_view = -1; return session_line_replace(core, core->history_saved_line); } core->history_view--; slot = (core->history_next + HYBBX_HISTORY_MAX - 1u - (unsigned)core->history_view) % HYBBX_HISTORY_MAX; return session_line_replace(core, core->history[slot]); } static hybbx_result_t session_apply_csi(hybbx_session_core_t *core, const char *params, char final_ch) { if (core == NULL) { return HYBBX_ERR_INVALID; } if (final_ch == 'A') { return session_history_up(core); } if (final_ch == 'B') { return session_history_down(core); } if (final_ch == 'C') { return session_line_cursor_right(core); } if (final_ch == 'D') { return session_line_cursor_left(core); } if (final_ch == 'H') { return session_line_cursor_home(core); } if (final_ch == 'F') { return session_line_cursor_end(core); } if (final_ch == '~') { if (strcmp(params, "1") == 0 || strcmp(params, "7") == 0) { return session_line_cursor_home(core); } if (strcmp(params, "3") == 0) { return session_line_delete_forward(core); } if (strcmp(params, "4") == 0 || strcmp(params, "8") == 0) { return session_line_cursor_end(core); } return HYBBX_OK; } if (final_ch == 'K' || final_ch == 'P') { return HYBBX_OK; } return HYBBX_OK; } static int session_filter_escape_byte(hybbx_session_core_t *core, unsigned char byte) { if (core == NULL) { return 0; } if (core->esc_state == SESSION_ESC_NONE && byte == 0x9bu) { core->esc_state = SESSION_ESC_CSI; core->csi_len = 0; return 1; } if (core->esc_state == SESSION_ESC_NONE && byte == 0x1bu) { core->esc_state = SESSION_ESC_ESC; return 1; } if (core->esc_state == SESSION_ESC_ESC) { if (byte == '[') { core->esc_state = SESSION_ESC_CSI; core->csi_len = 0; return 1; } if (byte == 'O') { core->esc_state = SESSION_ESC_SS3; return 1; } session_escape_reset(core); return 0; } if (core->esc_state == SESSION_ESC_SS3) { hybbx_result_t rc = HYBBX_OK; if (byte == 'C') { rc = session_line_cursor_right(core); } else if (byte == 'D') { rc = session_line_cursor_left(core); } else if (byte == 'H') { rc = session_line_cursor_home(core); } else if (byte == 'F') { rc = session_line_cursor_end(core); } session_escape_reset(core); (void)rc; return 1; } if (core->esc_state == SESSION_ESC_CSI) { if (core->csi_len + 1 >= SESSION_CSI_BUF_MAX) { session_escape_reset(core); return 1; } core->csi_buf[core->csi_len++] = (char)byte; if (session_csi_is_final(byte)) { char final_ch = (char)byte; char params[SESSION_CSI_BUF_MAX]; if (core->csi_len > 1) { memcpy(params, core->csi_buf, core->csi_len - 1); params[core->csi_len - 1] = '\0'; } else { params[0] = '\0'; } session_escape_reset(core); (void)session_apply_csi(core, params, final_ch); return 1; } return 1; } return 0; } static unsigned session_chat_message_max(const hybbx_session_core_t *core) { const hybbx_chat_config_t *chat; if (core == NULL || core->service == NULL) { return HYBBX_CHAT_MESSAGE_MAX; } chat = hybbx_service_get_chat(core->service); if (chat == NULL) { return HYBBX_CHAT_MESSAGE_MAX; } return chat->message_max; } static void session_area_clear_state(hybbx_session_core_t *core, hybbx_session_area_t area) { if (core == NULL) { return; } if (area == HYBBX_AREA_CHAT) { core->chat_channel = 0; } if (area == HYBBX_AREA_CONFERENCE) { hybbx_conference_area_leaving(&core->pub); } if (area == HYBBX_AREA_MAIL) { core->mail_composing = 0; core->mail_compose_body[0] = '\0'; core->mail_compose_body_len = 0; core->mail_compose_to[0] = '\0'; core->mail_compose_subject[0] = '\0'; } if (area == HYBBX_AREA_PROXYMAIL) { core->proxymail_composing = 0; core->proxymail_compose_body[0] = '\0'; core->proxymail_compose_body_len = 0; core->proxymail_compose_to[0] = '\0'; core->proxymail_compose_subject[0] = '\0'; } } static void session_area_sync(hybbx_session_core_t *core) { if (core == NULL || core->area_depth == 0) { return; } core->area = core->area_stack[core->area_depth - 1]; } static hybbx_result_t session_area_push(hybbx_session_core_t *core, hybbx_session_area_t area) { if (core == NULL) { return HYBBX_ERR_INVALID; } if (core->area_depth >= HYBBX_AREA_STACK_MAX) { return HYBBX_ERR_BUSY; } if (core->area_depth > 0 && core->area_stack[core->area_depth - 1] == area) { core->area = area; return HYBBX_OK; } core->area_stack[core->area_depth] = area; core->area_depth++; core->area = area; return HYBBX_OK; } static void session_release_guest_slot(hybbx_session_core_t *core) { if (core == NULL || core->guest_slot == 0 || core->service == NULL) { return; } hybbx_service_guest_release(core->service, core->guest_slot); core->guest_slot = 0; } static void session_arm_guest_timer(hybbx_session_core_t *core) { unsigned timeout_sec; if (core == NULL || !hybbx_user_level_is_guest(core->user.level)) { if (core != NULL) { core->guest_expires_at = 0; } return; } timeout_sec = hybbx_service_guest_timeout_seconds(core->service); if (timeout_sec == 0) { core->guest_expires_at = 0; return; } core->guest_expires_at = time(NULL) + (time_t)timeout_sec; } static hybbx_result_t session_activate_guest(hybbx_session_core_t *core, const hybbx_user_record_t *guest, unsigned guest_slot) { hybbx_storage_t *storage; const char *transport_name; hybbx_result_t rc; if (core == NULL || guest == NULL || guest_slot < 1) { return HYBBX_ERR_INVALID; } storage = hybbx_service_get_storage(core->service); if (storage == NULL) { return HYBBX_ERR_INVALID; } if (core->guest_slot != 0 && core->guest_slot != guest_slot) { session_release_guest_slot(core); } transport_name = core->pub.transport != NULL ? core->pub.transport->name : "unknown"; if (core->record.session_id != 0) { hybbx_storage_session_end(storage, core->record.session_id); memset(&core->record, 0, sizeof(core->record)); } rc = hybbx_storage_session_begin(storage, guest, transport_name, &core->record); if (rc != HYBBX_OK) { hybbx_service_guest_release(core->service, guest_slot); return rc; } core->user = *guest; core->guest_slot = guest_slot; core->logged_in = 1; core->login_prompt = 0; session_arm_guest_timer(core); return HYBBX_OK; } static hybbx_result_t session_check_guest_expiry(hybbx_session_core_t *core) { time_t now; if (core == NULL || core->guest_expires_at == 0) { return HYBBX_OK; } now = time(NULL); if (now < core->guest_expires_at) { return HYBBX_OK; } (void)hybbx_msg_send_system(&core->pub, "Guest time limit. Goodbye."); return HYBBX_SESSION_END; } static void session_process_line(hybbx_session_core_t *core, const char *line); hybbx_result_t hybbx_session_write(hybbx_session_t *session, const char *text) { hybbx_session_core_t *core; if (session == NULL || text == NULL) { return HYBBX_ERR_INVALID; } core = (hybbx_session_core_t *)session->core_data; if (core != NULL && core->bandwidth_paused) { return HYBBX_OK; } return hybbx_traffic_emit(session, core != NULL ? &core->out_col : NULL, text, strlen(text)); } hybbx_result_t hybbx_session_write_line(hybbx_session_t *session, const char *text) { hybbx_result_t rc; if (text != NULL) { rc = hybbx_session_write(session, text); if (rc != HYBBX_OK) { return rc; } } return hybbx_session_write(session, "\n"); } hybbx_result_t hybbx_session_command_gap(hybbx_session_t *session) { return hybbx_session_write(session, "\n"); } static int session_accepts_input(const hybbx_session_core_t *core) { return core != NULL && (core->logged_in != 0 || core->login_prompt != 0); } hybbx_result_t hybbx_session_show_prompt(hybbx_session_t *session) { hybbx_session_core_t *core; const char *prompt; if (session == NULL) { return HYBBX_ERR_INVALID; } core = (hybbx_session_core_t *)session->core_data; if (core == NULL) { return HYBBX_ERR_INVALID; } if (!core->logged_in && !core->login_prompt) { return HYBBX_ERR_INVALID; } prompt = hybbx_service_get_prompt(core->service); if (prompt == NULL || prompt[0] == '\0') { return HYBBX_OK; } return hybbx_session_write(session, prompt); } hybbx_result_t hybbx_session_clear_terminal(hybbx_session_t *session) { hybbx_session_core_t *core; hybbx_result_t rc; if (session == NULL) { return HYBBX_ERR_INVALID; } core = (hybbx_session_core_t *)session->core_data; if (core == NULL || !session_accepts_input(core)) { return HYBBX_ERR_INVALID; } rc = hybbx_term_clear_screen(session); if (rc != HYBBX_OK) { return rc; } core->line_len = 0; core->line_cursor = 0; core->line_buf[0] = '\0'; core->out_col = 0; core->expect_lf = 0; return hybbx_session_show_prompt(session); } int hybbx_session_input_echo(const hybbx_session_t *session) { const hybbx_session_core_t *core; if (session == NULL) { return 0; } core = (const hybbx_session_core_t *)session->core_data; if (core == NULL) { return 0; } return core->input_echo != 0; } hybbx_result_t hybbx_session_set_input_echo(hybbx_session_t *session, int enabled) { hybbx_session_core_t *core; if (session == NULL) { return HYBBX_ERR_INVALID; } core = (hybbx_session_core_t *)session->core_data; if (core == NULL || !session_accepts_input(core)) { return HYBBX_ERR_INVALID; } core->input_echo = enabled ? 1 : 0; return HYBBX_OK; } static void session_submit_line(hybbx_session_core_t *core) { core->line_buf[core->line_len] = '\0'; session_history_add(core, core->line_buf); session_process_line(core, core->line_buf); core->line_len = 0; core->line_cursor = 0; core->history_view = -1; if ((core->logged_in || core->login_prompt) && core->area != HYBBX_AREA_CHAT && core->area != HYBBX_AREA_CONFERENCE && core->area != HYBBX_AREA_PROXYCHAT && !(core->area == HYBBX_AREA_MAIL && core->mail_composing) && !(core->area == HYBBX_AREA_PROXYMAIL && core->proxymail_composing)) { hybbx_session_show_prompt(&core->pub); } } static hybbx_result_t session_echo_newline(hybbx_session_core_t *core) { if (core == NULL || !core->input_echo) { return HYBBX_OK; } return hybbx_session_write(&core->pub, "\n"); } static hybbx_result_t session_handle_user_byte(hybbx_session_core_t *core, unsigned char byte) { char ch = (char)byte; if (ch == '\0') { return HYBBX_OK; } if (ch == '\b' || ch == 127) { return session_line_backspace(core); } if (ch == '\r') { { hybbx_result_t rc = session_echo_newline(core); if (rc != HYBBX_OK) { return rc; } } session_submit_line(core); if (!core->logged_in && !core->login_prompt) { return HYBBX_SESSION_END; } core->expect_lf = 1; return HYBBX_OK; } if (ch == '\n') { if (core->expect_lf) { core->expect_lf = 0; return HYBBX_OK; } { hybbx_result_t rc = session_echo_newline(core); if (rc != HYBBX_OK) { return rc; } } session_submit_line(core); if (!core->logged_in && !core->login_prompt) { return HYBBX_SESSION_END; } return HYBBX_OK; } core->expect_lf = 0; return session_line_insert_char(core, ch); } static void session_send_banner(hybbx_session_core_t *core) { const hybbx_texts_config_t *texts; const char *service_name; texts = hybbx_service_get_texts(core->service); if (texts == NULL) { return; } service_name = hybbx_service_get_name(core->service); hybbx_texts_send_banner(texts, &core->pub, HYBBX_VERSION_STRING, service_name); } static void session_process_line(hybbx_session_core_t *core, const char *line) { hybbx_parsed_command_t cmd; hybbx_command_scope_t scope; hybbx_result_t rc; if (line[0] == '\0') { return; } scope = hybbx_command_classify(line); if (scope == HYBBX_CMD_SCOPE_COMMENT) { return; } if (hybbx_conference_invite_pending(&core->pub) && scope == HYBBX_CMD_SCOPE_LOCAL) { if (hybbx_conference_reply_invite(core->service, &core->pub, line) == HYBBX_OK) { return; } hybbx_session_write_line(&core->pub, "Reply y/n or yes/no."); return; } if (core->area == HYBBX_AREA_MAIL && core->mail_composing && scope == HYBBX_CMD_SCOPE_LOCAL) { const hybbx_mail_config_t *mail; size_t line_len; size_t body_max; mail = hybbx_service_get_mail(core->service); body_max = mail != NULL ? mail->body_max : HYBBX_MAIL_BODY_MAX; line_len = strlen(line); if (core->mail_compose_body_len + line_len + 2 > body_max) { hybbx_session_write_line(&core->pub, "Message body too long."); return; } if (core->mail_compose_body_len > 0) { core->mail_compose_body[core->mail_compose_body_len++] = '\n'; } memcpy(core->mail_compose_body + core->mail_compose_body_len, line, line_len + 1); core->mail_compose_body_len += line_len; return; } if (core->area == HYBBX_AREA_PROXYMAIL && core->proxymail_composing && scope == HYBBX_CMD_SCOPE_LOCAL) { const hybbx_mail_config_t *mail; size_t line_len; size_t body_max; mail = hybbx_service_get_mail(core->service); body_max = mail != NULL ? mail->body_max : HYBBX_MAIL_BODY_MAX; line_len = strlen(line); if (core->proxymail_compose_body_len + line_len + 2 > body_max) { hybbx_session_write_line(&core->pub, "Message body too long."); return; } if (core->proxymail_compose_body_len > 0) { core->proxymail_compose_body[core->proxymail_compose_body_len++] = '\n'; } memcpy(core->proxymail_compose_body + core->proxymail_compose_body_len, line, line_len + 1); core->proxymail_compose_body_len += line_len; return; } if (core->area == HYBBX_AREA_PROXYCHAT && scope == HYBBX_CMD_SCOPE_LOCAL) { if (hybbx_session_is_guest(&core->pub)) { hybbx_session_write_line(&core->pub, "Guests cannot use proxychat."); return; } if (strlen(line) > session_chat_message_max(core)) { hybbx_session_write_line(&core->pub, "Message too long."); return; } (void)hybbx_proxychat_post(hybbx_session_service(&core->pub), &core->pub, line); return; } if (core->area == HYBBX_AREA_CHAT && scope == HYBBX_CMD_SCOPE_LOCAL) { if (hybbx_session_is_guest(&core->pub)) { hybbx_session_write_line(&core->pub, "Guests cannot use chat."); return; } if (strlen(line) > session_chat_message_max(core)) { hybbx_session_write_line(&core->pub, "Message too long."); return; } (void)hybbx_chat_post(core->service, &core->pub, line); return; } if (core->area == HYBBX_AREA_CONFERENCE && scope == HYBBX_CMD_SCOPE_LOCAL) { if (hybbx_session_is_guest(&core->pub)) { hybbx_session_write_line(&core->pub, "Guests cannot use conference."); return; } if (strlen(line) > session_chat_message_max(core)) { hybbx_session_write_line(&core->pub, "Message too long."); return; } (void)hybbx_conference_post(core->service, &core->pub, line); return; } if (scope == HYBBX_CMD_SCOPE_LOCAL) { return; } rc = hybbx_command_parse(line, &cmd); if (rc != HYBBX_OK) { hybbx_session_write_line(&core->pub, "Command parse error."); return; } rc = hybbx_command_dispatch(core->service, &core->pub, &cmd); if (rc == HYBBX_SESSION_END) { core->logged_in = 0; } else if (rc == HYBBX_ERR_DENIED) { /* access message already sent */ } else if (rc != HYBBX_OK && rc != HYBBX_ERR_NOT_FOUND) { hybbx_session_write_line(&core->pub, "Command failed."); } hybbx_command_free(&cmd); } hybbx_result_t hybbx_session_open(hybbx_service_t *service, const hybbx_transport_plugin_t *transport, void *transport_data, hybbx_session_t **out) { hybbx_session_core_t *core; hybbx_storage_t *storage; const hybbx_auth_config_t *auth; hybbx_result_t rc; int suppress_startup_text; int interactive; if (service == NULL || transport == NULL || out == NULL) { return HYBBX_ERR_INVALID; } interactive = (transport->kind == HYBBX_TRANSPORT_TELNET || transport->kind == HYBBX_TRANSPORT_SSH || transport->kind == HYBBX_TRANSPORT_WEBSOCKET); if (interactive && !hybbx_instance_offers_user_bbx()) { hybbx_log_warn("[session] reject %s on %s (no user BBX on this instance)", transport->name, hybbx_instance_role_name()); return HYBBX_ERR_DENIED; } rc = hybbx_service_acquire_node(service); if (rc != HYBBX_OK) { return rc; } storage = hybbx_service_get_storage(service); if (storage == NULL) { hybbx_service_release_node(service); return HYBBX_ERR_INVALID; } auth = hybbx_service_get_auth(service); if (auth == NULL) { hybbx_service_release_node(service); return HYBBX_ERR_INVALID; } core = calloc(1, sizeof(*core)); if (core == NULL) { hybbx_service_release_node(service); return HYBBX_ERR_NOMEM; } core->service = service; core->pub.transport = transport; core->pub.transport_data = transport_data; core->pub.core_data = core; core->area = HYBBX_AREA_MAIN; core->area_stack[0] = HYBBX_AREA_MAIN; core->area_depth = 1; { const hybbx_traffic_config_t *traffic = hybbx_traffic_config_get(); core->input_echo = traffic != NULL && traffic->input_echo; } suppress_startup_text = (transport->kind == HYBBX_TRANSPORT_CIRCUIT); (void)hybbx_term_init_session(&core->pub); if (!suppress_startup_text) { session_send_banner(core); } if (auth->auto_login) { unsigned guest_slot = 0; rc = hybbx_service_guest_assign(service, auth->guest_prefix, &core->user, &guest_slot); if (rc != HYBBX_OK) { free(core); hybbx_service_release_node(service); return rc; } rc = session_activate_guest(core, &core->user, guest_slot); if (rc != HYBBX_OK) { free(core); hybbx_service_release_node(service); return rc; } if (!suppress_startup_text) { hybbx_log_info("%s@%s connected (session %llu)", core->record.username, transport->name, (unsigned long long)core->record.session_id); hybbx_monitor_event(service, core->record.username, transport->name, "connected"); } if (!suppress_startup_text) { hybbx_session_show_prompt(&core->pub); } } else { core->login_prompt = 1; if (!suppress_startup_text) { hybbx_session_show_prompt(&core->pub); } } *out = &core->pub; if (hybbx_service_attach_session(service, &core->pub) != HYBBX_OK) { if (core->record.session_id != 0) { hybbx_storage_session_end(storage, core->record.session_id); } session_release_guest_slot(core); free(core); hybbx_service_release_node(service); return HYBBX_ERR_NOMEM; } return HYBBX_OK; } void hybbx_session_close(hybbx_session_t *session) { hybbx_session_core_t *core; hybbx_storage_t *storage; if (session == NULL) { return; } core = (hybbx_session_core_t *)session->core_data; if (core == NULL) { return; } if (hybbx_conference_invite_pending(session) || hybbx_session_area(session) == HYBBX_AREA_CONFERENCE) { hybbx_conference_session_closed(session); } storage = hybbx_service_get_storage(core->service); if (storage != NULL && core->record.session_id != 0) { const char *plugin = core->record.transport[0] != '\0' ? core->record.transport : (session->transport != NULL ? session->transport->name : "?"); hybbx_storage_session_end(storage, core->record.session_id); hybbx_log_info("%s@%s disconnected (session %llu)", core->record.username, plugin, (unsigned long long)core->record.session_id); hybbx_monitor_event(core->service, core->record.username, plugin, "disconnected"); } session_release_guest_slot(core); if (core->service != NULL) { hybbx_service_detach_session(core->service, session); hybbx_service_release_node(core->service); } free(core); } hybbx_result_t hybbx_session_switch_user(hybbx_session_t *session, const hybbx_user_record_t *user) { hybbx_session_core_t *core; hybbx_storage_t *storage; hybbx_session_record_t new_record; const char *transport_name; hybbx_result_t rc; if (session == NULL || user == NULL) { return HYBBX_ERR_INVALID; } if (hybbx_user_level_is_guest(user->level)) { return HYBBX_ERR_INVALID; } core = (hybbx_session_core_t *)session->core_data; if (core == NULL || core->service == NULL) { return HYBBX_ERR_INVALID; } session_release_guest_slot(core); storage = hybbx_service_get_storage(core->service); if (storage == NULL) { return HYBBX_ERR_INVALID; } transport_name = core->record.transport; if (transport_name[0] == '\0' && session->transport != NULL) { transport_name = session->transport->name; } if (transport_name == NULL || transport_name[0] == '\0') { return HYBBX_ERR_INVALID; } if (core->record.session_id != 0) { hybbx_storage_session_end(storage, core->record.session_id); } rc = hybbx_storage_session_begin(storage, user, transport_name, &new_record); if (rc != HYBBX_OK) { return rc; } core->user = *user; core->record = new_record; core->guest_expires_at = 0; core->logged_in = 1; core->login_prompt = 0; hybbx_log_info("%s@%s login (session %llu)", core->record.username, transport_name, (unsigned long long)core->record.session_id); hybbx_monitor_event(core->service, core->record.username, transport_name, "login"); session_maybe_announce_login(core); return HYBBX_OK; } hybbx_result_t hybbx_session_tick(hybbx_session_t *session) { hybbx_session_core_t *core; if (session == NULL) { return HYBBX_ERR_INVALID; } core = (hybbx_session_core_t *)session->core_data; if (core == NULL || !core->logged_in) { return HYBBX_ERR_INVALID; } if (core->bandwidth_disconnect) { return HYBBX_SESSION_END; } if (core->conference_invite_pending && core->conference_invite_deadline > 0) { hybbx_conference_invite_tick(core->service, session); } return session_check_guest_expiry(core); } hybbx_result_t hybbx_session_handle_input(hybbx_session_t *session, const uint8_t *data, size_t len) { hybbx_session_core_t *core; size_t i; hybbx_result_t expiry_rc; if (session == NULL || data == NULL) { return HYBBX_ERR_INVALID; } core = (hybbx_session_core_t *)session->core_data; if (core == NULL || !session_accepts_input(core)) { return HYBBX_ERR_INVALID; } if (core->bandwidth_paused) { return HYBBX_OK; } if (core->logged_in) { expiry_rc = session_check_guest_expiry(core); if (expiry_rc != HYBBX_OK) { return expiry_rc; } } for (i = 0; i < len; i++) { hybbx_result_t rc; if (session_filter_escape_byte(core, data[i])) { continue; } rc = session_handle_user_byte(core, data[i]); if (rc == HYBBX_SESSION_END) { return HYBBX_SESSION_END; } if (rc != HYBBX_OK) { return rc; } } return HYBBX_OK; } const char *hybbx_session_display_name(const hybbx_session_t *session) { const hybbx_session_core_t *core; if (session == NULL) { return ""; } core = (const hybbx_session_core_t *)session->core_data; if (core == NULL) { return ""; } return hybbx_user_display_name(&core->user); } hybbx_result_t hybbx_session_format_user_at_plugin(const hybbx_session_t *session, char *out, size_t out_len) { const hybbx_session_record_t *rec; const char *user; const char *plugin; if (session == NULL || out == NULL || out_len == 0) { return HYBBX_ERR_INVALID; } user = hybbx_session_display_name(session); if (user == NULL || user[0] == '\0') { user = hybbx_session_username(session); } if (user == NULL || user[0] == '\0') { user = "?"; } rec = hybbx_session_record(session); plugin = (rec != NULL && rec->transport[0] != '\0') ? rec->transport : NULL; if (plugin == NULL || plugin[0] == '\0') { plugin = (session->transport != NULL && session->transport->name != NULL) ? session->transport->name : "?"; } snprintf(out, out_len, "%s@%s", user, plugin); return HYBBX_OK; } typedef struct login_announce_ctx { const char *line; } login_announce_ctx_t; static void login_announce_visitor(hybbx_session_t *session, void *userdata) { login_announce_ctx_t *ctx = (login_announce_ctx_t *)userdata; if (session == NULL || ctx == NULL || ctx->line == NULL) { return; } if (!hybbx_session_is_interactive_user(session) || !hybbx_session_logged_in(session)) { return; } hybbx_session_write_line(session, ctx->line); } static void session_maybe_announce_login(hybbx_session_core_t *core) { hybbx_service_t *service; char user_at[96]; char line[128]; login_announce_ctx_t ctx; if (core == NULL || core->service == NULL) { return; } service = core->service; if (!hybbx_service_login_announce(service)) { return; } /* Guests: never announce. Hidden Sysop (invisible-sysop / monitor): skip. */ if (hybbx_user_level_is_guest(core->user.level) || hybbx_session_hidden_from_who(&core->pub)) { return; } if (hybbx_session_format_user_at_plugin(&core->pub, user_at, sizeof(user_at)) != HYBBX_OK) { return; } snprintf(line, sizeof(line), "*** User login: %s", user_at); ctx.line = line; hybbx_service_visit_sessions(service, login_announce_visitor, &ctx); } const char *hybbx_session_username(const hybbx_session_t *session) { const hybbx_session_core_t *core; if (session == NULL) { return ""; } core = (const hybbx_session_core_t *)session->core_data; if (core == NULL) { return ""; } return core->record.username; } uint64_t hybbx_session_id(const hybbx_session_t *session) { const hybbx_session_core_t *core; if (session == NULL) { return 0; } core = (const hybbx_session_core_t *)session->core_data; if (core == NULL) { return 0; } return core->record.session_id; } const hybbx_session_record_t *hybbx_session_record(const hybbx_session_t *session) { const hybbx_session_core_t *core; if (session == NULL) { return NULL; } core = (const hybbx_session_core_t *)session->core_data; if (core == NULL) { return NULL; } return &core->record; } hybbx_result_t hybbx_session_set_remote(hybbx_session_t *session, const char *remote) { hybbx_session_core_t *core; if (session == NULL || remote == NULL) { return HYBBX_ERR_INVALID; } core = (hybbx_session_core_t *)session->core_data; if (core == NULL) { return HYBBX_ERR_INVALID; } hybbx_strlcpy(core->record.remote, remote, sizeof(core->record.remote)); return HYBBX_OK; } hybbx_user_level_t hybbx_session_user_level(const hybbx_session_t *session) { const hybbx_session_core_t *core; if (session == NULL) { return HYBBX_LEVEL_GUEST; } core = (const hybbx_session_core_t *)session->core_data; if (core == NULL) { return HYBBX_LEVEL_GUEST; } return core->user.level; } int hybbx_session_is_guest(const hybbx_session_t *session) { return hybbx_user_level_is_guest(hybbx_session_user_level(session)); } int hybbx_session_is_interactive_user(const hybbx_session_t *session) { hybbx_transport_kind_t kind; if (session == NULL || session->transport == NULL) { return 0; } kind = session->transport->kind; return kind == HYBBX_TRANSPORT_TELNET || kind == HYBBX_TRANSPORT_SSH || kind == HYBBX_TRANSPORT_WEBSOCKET; } int hybbx_session_monitor_active(const hybbx_session_t *session) { const hybbx_session_core_t *core; if (session == NULL) { return 0; } core = (const hybbx_session_core_t *)session->core_data; return core != NULL && core->monitor_active != 0; } void hybbx_session_set_monitor_active(hybbx_session_t *session, int on) { hybbx_session_core_t *core; if (session == NULL) { return; } core = (hybbx_session_core_t *)session->core_data; if (core == NULL) { return; } core->monitor_active = on ? 1 : 0; } int hybbx_session_hidden_from_who(const hybbx_session_t *session) { if (session == NULL) { return 0; } if (!hybbx_user_level_is_sysop(hybbx_session_user_level(session))) { return 0; } if (hybbx_monitor_invisible_sysop()) { return 1; } return hybbx_session_monitor_active(session) ? 1 : 0; } int hybbx_session_logged_in(const hybbx_session_t *session) { const hybbx_session_core_t *core; if (session == NULL) { return 0; } core = (const hybbx_session_core_t *)session->core_data; if (core == NULL) { return 0; } return core->logged_in != 0; } int hybbx_session_login_prompt(const hybbx_session_t *session) { const hybbx_session_core_t *core; if (session == NULL) { return 0; } core = (const hybbx_session_core_t *)session->core_data; if (core == NULL) { return 0; } return core->login_prompt != 0; } hybbx_session_area_t hybbx_session_area(const hybbx_session_t *session) { const hybbx_session_core_t *core; if (session == NULL) { return HYBBX_AREA_MAIN; } core = (const hybbx_session_core_t *)session->core_data; if (core == NULL) { return HYBBX_AREA_MAIN; } return core->area; } const char *hybbx_session_area_name(hybbx_session_area_t area) { switch (area) { case HYBBX_AREA_MAIN: return "main"; case HYBBX_AREA_MAIL: return "mail"; case HYBBX_AREA_CHAT: return "chat"; case HYBBX_AREA_CONFERENCE: return "conference"; case HYBBX_AREA_PROXYMAIL: return "proxymail"; case HYBBX_AREA_PROXYCHAT: return "proxychat"; default: return "main"; } } hybbx_session_area_t hybbx_session_area_parse(const char *name) { if (name == NULL || name[0] == '\0') { return HYBBX_AREA_MAIN; } if (session_str_ieq(name, "main")) { return HYBBX_AREA_MAIN; } if (session_str_ieq(name, "mail")) { return HYBBX_AREA_MAIL; } if (session_str_ieq(name, "chat")) { return HYBBX_AREA_CHAT; } if (session_str_ieq(name, "conference")) { return HYBBX_AREA_CONFERENCE; } if (session_str_ieq(name, "proxymail")) { return HYBBX_AREA_PROXYMAIL; } if (session_str_ieq(name, "proxychat")) { return HYBBX_AREA_PROXYCHAT; } return HYBBX_AREA_MAIN; } hybbx_result_t hybbx_session_enter_area(hybbx_session_t *session, hybbx_session_area_t area) { hybbx_session_core_t *core; if (session == NULL) { return HYBBX_ERR_INVALID; } core = (hybbx_session_core_t *)session->core_data; if (core == NULL) { return HYBBX_ERR_INVALID; } return session_area_push(core, area); } hybbx_result_t hybbx_session_leave_area(hybbx_session_t *session) { hybbx_session_core_t *core; hybbx_session_area_t leaving; if (session == NULL) { return HYBBX_ERR_INVALID; } core = (hybbx_session_core_t *)session->core_data; if (core == NULL) { return HYBBX_ERR_INVALID; } if (core->area_depth <= 1) { return HYBBX_OK; } leaving = core->area_stack[core->area_depth - 1]; session_area_clear_state(core, leaving); core->area_depth--; session_area_sync(core); return HYBBX_OK; } hybbx_result_t hybbx_session_go_main(hybbx_session_t *session) { hybbx_session_core_t *core; if (session == NULL) { return HYBBX_ERR_INVALID; } core = (hybbx_session_core_t *)session->core_data; if (core == NULL) { return HYBBX_ERR_INVALID; } while (core->area_depth > 1) { hybbx_session_area_t leaving = core->area_stack[core->area_depth - 1]; session_area_clear_state(core, leaving); core->area_depth--; } core->area_stack[0] = HYBBX_AREA_MAIN; core->area_depth = 1; core->area = HYBBX_AREA_MAIN; return HYBBX_OK; } hybbx_result_t hybbx_session_join_chat_channel(hybbx_session_t *session, unsigned channel_index) { hybbx_session_core_t *core; const hybbx_chat_config_t *chat; const char *name; if (session == NULL || channel_index == 0) { return HYBBX_ERR_INVALID; } core = (hybbx_session_core_t *)session->core_data; if (core == NULL || core->service == NULL) { return HYBBX_ERR_INVALID; } chat = hybbx_service_get_chat(core->service); name = hybbx_chat_channel_name(chat, channel_index); if (name == NULL) { return HYBBX_ERR_NOT_FOUND; } if (core->area != HYBBX_AREA_CHAT) { hybbx_result_t push_rc = session_area_push(core, HYBBX_AREA_CHAT); if (push_rc != HYBBX_OK) { return push_rc; } } core->chat_channel = channel_index; if (!core->chat_max_notice_shown) { char notice[96]; snprintf(notice, sizeof(notice), "Max %u chars per message.", chat->message_max); hybbx_session_write_line(session, notice); core->chat_max_notice_shown = 1; } return HYBBX_OK; } unsigned hybbx_session_chat_channel(const hybbx_session_t *session) { const hybbx_session_core_t *core; if (session == NULL) { return 0; } core = (const hybbx_session_core_t *)session->core_data; if (core == NULL || core->area != HYBBX_AREA_CHAT) { return 0; } return core->chat_channel; } hybbx_service_t *hybbx_session_service(hybbx_session_t *session) { hybbx_session_core_t *core; if (session == NULL) { return NULL; } core = (hybbx_session_core_t *)session->core_data; if (core == NULL) { return NULL; } return core->service; } static int session_str_ieq_local(const char *a, const char *b) { if (a == NULL || b == NULL) { return 0; } while (*a != '\0' && *b != '\0') { char ca = (char)(*a >= 'A' && *a <= 'Z' ? *a + 32 : *a); char cb = (char)(*b >= 'A' && *b <= 'Z' ? *b + 32 : *b); if (ca != cb) { return 0; } a++; b++; } return *a == '\0' && *b == '\0'; } int hybbx_session_conference_may_invite(hybbx_session_t *session, const char *target) { hybbx_session_core_t *core; size_t s; size_t k; time_t now; unsigned count; if (session == NULL || target == NULL || target[0] == '\0') { return 0; } core = (hybbx_session_core_t *)session->core_data; if (core == NULL) { return 0; } now = time(NULL); for (s = 0; s < sizeof(core->conference_invite_rates) / sizeof(core->conference_invite_rates[0]); s++) { if (!session_str_ieq_local(core->conference_invite_rates[s].target, target)) { continue; } count = 0; for (k = 0; k < HYBBX_CONFERENCE_INVITE_MAX_PER_TARGET; k++) { time_t sent = core->conference_invite_rates[s].sent_at[k]; if (sent != 0 && (now - sent) < (time_t)HYBBX_CONFERENCE_INVITE_WINDOW_SEC) { count++; } } return count < HYBBX_CONFERENCE_INVITE_MAX_PER_TARGET; } return 1; } void hybbx_session_conference_invite_sent(hybbx_session_t *session, const char *target) { hybbx_session_core_t *core; size_t s; size_t slot = (size_t)-1; size_t empty = (size_t)-1; size_t k; time_t now; if (session == NULL || target == NULL || target[0] == '\0') { return; } core = (hybbx_session_core_t *)session->core_data; if (core == NULL) { return; } now = time(NULL); for (s = 0; s < sizeof(core->conference_invite_rates) / sizeof(core->conference_invite_rates[0]); s++) { if (core->conference_invite_rates[s].target[0] == '\0') { if (empty == (size_t)-1) { empty = s; } continue; } if (session_str_ieq_local(core->conference_invite_rates[s].target, target)) { slot = s; break; } } if (slot == (size_t)-1) { slot = empty; } if (slot == (size_t)-1) { slot = 0; } if (core->conference_invite_rates[slot].target[0] == '\0') { hybbx_strlcpy(core->conference_invite_rates[slot].target, target, sizeof(core->conference_invite_rates[slot].target)); } for (k = 0; k < HYBBX_CONFERENCE_INVITE_MAX_PER_TARGET - 1u; k++) { core->conference_invite_rates[slot].sent_at[k] = core->conference_invite_rates[slot].sent_at[k + 1]; } core->conference_invite_rates[slot].sent_at[ HYBBX_CONFERENCE_INVITE_MAX_PER_TARGET - 1u] = now; } void hybbx_session_set_conference_invite(hybbx_session_t *session, const char *from_username, const char *topic) { hybbx_session_core_t *core; if (session == NULL || from_username == NULL || topic == NULL) { return; } core = (hybbx_session_core_t *)session->core_data; if (core == NULL) { return; } core->conference_invite_pending = 1; core->conference_invite_deadline = 0; hybbx_strlcpy(core->conference_invite_from, from_username, sizeof(core->conference_invite_from)); hybbx_strlcpy(core->conference_invite_topic, topic, sizeof(core->conference_invite_topic)); } void hybbx_session_set_conference_invite_deadline(hybbx_session_t *session, time_t deadline) { hybbx_session_core_t *core; if (session == NULL) { return; } core = (hybbx_session_core_t *)session->core_data; if (core == NULL) { return; } core->conference_invite_deadline = deadline; } time_t hybbx_session_conference_invite_deadline(const hybbx_session_t *session) { const hybbx_session_core_t *core; if (session == NULL) { return 0; } core = (const hybbx_session_core_t *)session->core_data; if (core == NULL || !core->conference_invite_pending) { return 0; } return core->conference_invite_deadline; } void hybbx_session_clear_conference_invite(hybbx_session_t *session) { hybbx_session_core_t *core; if (session == NULL) { return; } core = (hybbx_session_core_t *)session->core_data; if (core == NULL) { return; } core->conference_invite_pending = 0; core->conference_invite_deadline = 0; core->conference_invite_from[0] = '\0'; core->conference_invite_topic[0] = '\0'; } int hybbx_conference_invite_pending(const hybbx_session_t *session) { const hybbx_session_core_t *core; if (session == NULL) { return 0; } core = (const hybbx_session_core_t *)session->core_data; if (core == NULL) { return 0; } return core->conference_invite_pending != 0; } const char *hybbx_session_conference_invite_from(const hybbx_session_t *session) { const hybbx_session_core_t *core; if (session == NULL) { return NULL; } core = (const hybbx_session_core_t *)session->core_data; if (core == NULL || !core->conference_invite_pending) { return NULL; } return core->conference_invite_from; } const char *hybbx_session_conference_invite_topic(const hybbx_session_t *session) { const hybbx_session_core_t *core; if (session == NULL) { return NULL; } core = (const hybbx_session_core_t *)session->core_data; if (core == NULL || !core->conference_invite_pending) { return NULL; } return core->conference_invite_topic; } hybbx_result_t hybbx_session_join_conference(hybbx_session_t *session, const char *topic, const char *partner_username) { hybbx_session_core_t *core; const hybbx_chat_config_t *chat; if (session == NULL || topic == NULL || partner_username == NULL || topic[0] == '\0' || partner_username[0] == '\0') { return HYBBX_ERR_INVALID; } core = (hybbx_session_core_t *)session->core_data; if (core == NULL || core->service == NULL) { return HYBBX_ERR_INVALID; } if (core->area != HYBBX_AREA_CONFERENCE) { hybbx_result_t push_rc = session_area_push(core, HYBBX_AREA_CONFERENCE); if (push_rc != HYBBX_OK) { return push_rc; } } hybbx_strlcpy(core->conference_topic, topic, sizeof(core->conference_topic)); hybbx_strlcpy(core->conference_partner, partner_username, sizeof(core->conference_partner)); chat = hybbx_service_get_chat(core->service); if (chat != NULL && !core->chat_max_notice_shown) { char notice[96]; snprintf(notice, sizeof(notice), "Max %u chars per message.", chat->message_max); hybbx_session_write_line(session, notice); core->chat_max_notice_shown = 1; } return HYBBX_OK; } void hybbx_session_clear_conference(hybbx_session_t *session) { hybbx_session_core_t *core; if (session == NULL) { return; } core = (hybbx_session_core_t *)session->core_data; if (core == NULL) { return; } core->conference_topic[0] = '\0'; core->conference_partner[0] = '\0'; } const char *hybbx_session_conference_partner(const hybbx_session_t *session) { const hybbx_session_core_t *core; if (session == NULL) { return NULL; } core = (const hybbx_session_core_t *)session->core_data; if (core == NULL || core->conference_partner[0] == '\0') { return NULL; } return core->conference_partner; } int hybbx_session_mail_composing(const hybbx_session_t *session) { const hybbx_session_core_t *core; if (session == NULL) { return 0; } core = (const hybbx_session_core_t *)session->core_data; if (core == NULL) { return 0; } return core->mail_composing; } hybbx_result_t hybbx_session_mail_compose_start(hybbx_session_t *session, const char *to_user, const char *subject) { hybbx_session_core_t *core; const hybbx_mail_config_t *mail; hybbx_user_record_t recipient; hybbx_storage_t *storage; size_t subject_len; if (session == NULL || to_user == NULL || subject == NULL) { return HYBBX_ERR_INVALID; } core = (hybbx_session_core_t *)session->core_data; if (core == NULL) { return HYBBX_ERR_INVALID; } mail = hybbx_service_get_mail(core->service); subject_len = strlen(subject); if (mail != NULL && subject_len > mail->subject_max) { return HYBBX_ERR_INVALID; } storage = hybbx_service_get_storage(core->service); if (storage != NULL && hybbx_storage_resolve_user(storage, to_user, &recipient) == HYBBX_OK) { hybbx_strlcpy(core->mail_compose_to, recipient.username, sizeof(core->mail_compose_to)); } else { hybbx_strlcpy(core->mail_compose_to, to_user, sizeof(core->mail_compose_to)); hybbx_username_normalize(core->mail_compose_to); } hybbx_strlcpy(core->mail_compose_subject, subject, sizeof(core->mail_compose_subject)); core->mail_compose_body[0] = '\0'; core->mail_compose_body_len = 0; core->mail_composing = 1; if (core->area != HYBBX_AREA_MAIL) { return session_area_push(core, HYBBX_AREA_MAIL); } return HYBBX_OK; } void hybbx_session_mail_compose_cancel(hybbx_session_t *session) { hybbx_session_core_t *core; if (session == NULL) { return; } core = (hybbx_session_core_t *)session->core_data; if (core == NULL) { return; } core->mail_composing = 0; core->mail_compose_body[0] = '\0'; core->mail_compose_body_len = 0; core->mail_compose_to[0] = '\0'; core->mail_compose_subject[0] = '\0'; } const char *hybbx_session_mail_compose_body(const hybbx_session_t *session) { const hybbx_session_core_t *core; if (session == NULL) { return ""; } core = (const hybbx_session_core_t *)session->core_data; if (core == NULL || !core->mail_composing) { return ""; } return core->mail_compose_body; } const char *hybbx_session_mail_compose_to(const hybbx_session_t *session) { const hybbx_session_core_t *core; if (session == NULL) { return ""; } core = (const hybbx_session_core_t *)session->core_data; if (core == NULL || !core->mail_composing) { return ""; } return core->mail_compose_to; } const char *hybbx_session_mail_compose_subject(const hybbx_session_t *session) { const hybbx_session_core_t *core; if (session == NULL) { return ""; } core = (const hybbx_session_core_t *)session->core_data; if (core == NULL || !core->mail_composing) { return ""; } return core->mail_compose_subject; } hybbx_result_t hybbx_session_enter_mail(hybbx_session_t *session) { return hybbx_session_enter_area(session, HYBBX_AREA_MAIL); } hybbx_result_t hybbx_session_enter_chat(hybbx_session_t *session) { return hybbx_session_enter_area(session, HYBBX_AREA_CHAT); } hybbx_result_t hybbx_session_enter_proxymail(hybbx_session_t *session) { hybbx_session_core_t *core; hybbx_result_t rc; if (session == NULL) { return HYBBX_ERR_INVALID; } core = (hybbx_session_core_t *)session->core_data; if (core == NULL) { return HYBBX_ERR_INVALID; } if (core->area != HYBBX_AREA_MAIL && core->area != HYBBX_AREA_PROXYMAIL) { rc = session_area_push(core, HYBBX_AREA_MAIL); if (rc != HYBBX_OK) { return rc; } } return session_area_push(core, HYBBX_AREA_PROXYMAIL); } hybbx_result_t hybbx_session_enter_proxychat(hybbx_session_t *session) { hybbx_session_core_t *core; hybbx_result_t rc; if (session == NULL) { return HYBBX_ERR_INVALID; } core = (hybbx_session_core_t *)session->core_data; if (core == NULL) { return HYBBX_ERR_INVALID; } if (core->area != HYBBX_AREA_CHAT && core->area != HYBBX_AREA_PROXYCHAT) { rc = session_area_push(core, HYBBX_AREA_CHAT); if (rc != HYBBX_OK) { return rc; } } return session_area_push(core, HYBBX_AREA_PROXYCHAT); } int hybbx_session_proxymail_composing(const hybbx_session_t *session) { const hybbx_session_core_t *core; if (session == NULL) { return 0; } core = (const hybbx_session_core_t *)session->core_data; if (core == NULL) { return 0; } return core->proxymail_composing; } hybbx_result_t hybbx_session_proxymail_compose_start(hybbx_session_t *session, const char *to_address, const char *subject) { hybbx_session_core_t *core; const hybbx_mail_config_t *mail; size_t subject_len; if (session == NULL || to_address == NULL || subject == NULL) { return HYBBX_ERR_INVALID; } if (!hybbx_proxymail_parse_address(to_address, NULL, 0, NULL, 0)) { return HYBBX_ERR_INVALID; } core = (hybbx_session_core_t *)session->core_data; if (core == NULL) { return HYBBX_ERR_INVALID; } mail = hybbx_service_get_mail(core->service); subject_len = strlen(subject); if (mail != NULL && subject_len > mail->subject_max) { return HYBBX_ERR_INVALID; } hybbx_strlcpy(core->proxymail_compose_to, to_address, sizeof(core->proxymail_compose_to)); hybbx_strlcpy(core->proxymail_compose_subject, subject, sizeof(core->proxymail_compose_subject)); core->proxymail_compose_body[0] = '\0'; core->proxymail_compose_body_len = 0; core->proxymail_composing = 1; return hybbx_session_enter_proxymail(session); } void hybbx_session_proxymail_compose_cancel(hybbx_session_t *session) { hybbx_session_core_t *core; if (session == NULL) { return; } core = (hybbx_session_core_t *)session->core_data; if (core == NULL) { return; } core->proxymail_composing = 0; core->proxymail_compose_body[0] = '\0'; core->proxymail_compose_body_len = 0; core->proxymail_compose_to[0] = '\0'; core->proxymail_compose_subject[0] = '\0'; } const char *hybbx_session_proxymail_compose_body(const hybbx_session_t *session) { const hybbx_session_core_t *core; if (session == NULL) { return ""; } core = (const hybbx_session_core_t *)session->core_data; if (core == NULL || !core->proxymail_composing) { return ""; } return core->proxymail_compose_body; } const char *hybbx_session_proxymail_compose_to(const hybbx_session_t *session) { const hybbx_session_core_t *core; if (session == NULL) { return ""; } core = (const hybbx_session_core_t *)session->core_data; if (core == NULL || !core->proxymail_composing) { return ""; } return core->proxymail_compose_to; } const char *hybbx_session_proxymail_compose_subject( const hybbx_session_t *session) { const hybbx_session_core_t *core; if (session == NULL) { return ""; } core = (const hybbx_session_core_t *)session->core_data; if (core == NULL || !core->proxymail_composing) { return ""; } return core->proxymail_compose_subject; } time_t hybbx_session_connected_at(const hybbx_session_t *session) { const hybbx_session_core_t *core; if (session == NULL) { return 0; } core = (const hybbx_session_core_t *)session->core_data; if (core == NULL) { return 0; } return core->record.connected_at; } int hybbx_session_bandwidth_paused(const hybbx_session_t *session) { const hybbx_session_core_t *core; if (session == NULL) { return 0; } core = (const hybbx_session_core_t *)session->core_data; if (core == NULL) { return 0; } return core->bandwidth_paused; } void hybbx_session_set_bandwidth_paused(hybbx_session_t *session, int paused) { hybbx_session_core_t *core; if (session == NULL) { return; } core = (hybbx_session_core_t *)session->core_data; if (core == NULL) { return; } core->bandwidth_paused = paused ? 1 : 0; } void hybbx_session_disconnect_bandwidth(hybbx_session_t *session) { hybbx_session_core_t *core; if (session == NULL) { return; } core = (hybbx_session_core_t *)session->core_data; if (core == NULL || !core->logged_in) { return; } core->bandwidth_paused = 0; (void)hybbx_traffic_emit(session, &core->out_col, HYBBX_BANDWIDTH_DISCONNECT_MSG, strlen(HYBBX_BANDWIDTH_DISCONNECT_MSG)); (void)hybbx_traffic_emit(session, &core->out_col, "\n", 1); core->bandwidth_disconnect = 1; }