summaryrefslogtreecommitdiff
path: root/src/core/session.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/core/session.c')
-rw-r--r--src/core/session.c2678
1 files changed, 2678 insertions, 0 deletions
diff --git a/src/core/session.c b/src/core/session.c
new file mode 100644
index 0000000..9f5d368
--- /dev/null
+++ b/src/core/session.c
@@ -0,0 +1,2678 @@
+#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 <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include <time.h>
+
+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;
+}
git clone -b <branch> https://cgit.mode42.com/<repo>.git
git clone -b <branch> git://cgit.mode42.com/<repo>.git

info@mode42.com