ascii-chat 0.11.33
Video chat in your terminal
Loading...
Searching...
No Matches
sqlite.c
Go to the documentation of this file.
1
11#include <sqlite3.h>
12#include <string.h>
13
17typedef struct {
18 sqlite3 *db;
20
21static asciichat_error_t sqlite_check(void *backend_data, const char *ip_address, rate_event_type_t event_type,
22 const rate_limit_config_t *config, bool *allowed) {
23 sqlite_backend_t *backend = (sqlite_backend_t *)backend_data;
24
25 // Use provided config or default
26 const rate_limit_config_t *limit = config ? config : &DEFAULT_RATE_LIMITS[event_type];
27
28 // Get current time in nanoseconds
30 uint64_t window_start_ns = now_ns - ((uint64_t)limit->window_secs * NS_PER_SEC_INT);
31
32 // Count events in the time window
33 const char *sql = "SELECT COUNT(*) FROM rate_events "
34 "WHERE ip_address = ? AND event_type = ? AND timestamp >= ?";
35
36 sqlite3_stmt *stmt = NULL;
37 int rc = sqlite3_prepare_v2(backend->db, sql, -1, &stmt, NULL);
38 if (rc != SQLITE_OK) {
39 return SET_ERRNO(ERROR_CONFIG, "Failed to prepare rate limit query: %s", sqlite3_errmsg(backend->db));
40 }
41
42 sqlite3_bind_text(stmt, 1, ip_address, -1, SQLITE_STATIC);
43 sqlite3_bind_text(stmt, 2, rate_limiter_event_type_string(event_type), -1, SQLITE_STATIC);
44 sqlite3_bind_int64(stmt, 3, (sqlite3_int64)window_start_ns);
45
46 rc = sqlite3_step(stmt);
47 if (rc != SQLITE_ROW) {
48 sqlite3_finalize(stmt);
49 return SET_ERRNO(ERROR_CONFIG, "Failed to execute rate limit query: %s", sqlite3_errmsg(backend->db));
50 }
51
52 uint32_t event_count = (uint32_t)sqlite3_column_int(stmt, 0);
53 sqlite3_finalize(stmt);
54
55 // Check if limit exceeded
56 *allowed = (event_count < limit->max_events);
57
58 if (!*allowed) {
59 log_warn("Rate limit exceeded for %s (event: %s, count: %u/%u)", ip_address,
60 rate_limiter_event_type_string(event_type), event_count, limit->max_events);
61 }
62
63 return ASCIICHAT_OK;
64}
65
66static asciichat_error_t sqlite_record(void *backend_data, const char *ip_address, rate_event_type_t event_type) {
67 sqlite_backend_t *backend = (sqlite_backend_t *)backend_data;
68
69 // Get current time in nanoseconds
71
72 // Insert event
73 const char *sql = "INSERT INTO rate_events (ip_address, event_type, timestamp) VALUES (?, ?, ?)";
74
75 sqlite3_stmt *stmt = NULL;
76 int rc = sqlite3_prepare_v2(backend->db, sql, -1, &stmt, NULL);
77 if (rc != SQLITE_OK) {
78 return SET_ERRNO(ERROR_CONFIG, "Failed to prepare rate limit insert: %s", sqlite3_errmsg(backend->db));
79 }
80
81 sqlite3_bind_text(stmt, 1, ip_address, -1, SQLITE_STATIC);
82 sqlite3_bind_text(stmt, 2, rate_limiter_event_type_string(event_type), -1, SQLITE_STATIC);
83 sqlite3_bind_int64(stmt, 3, (sqlite3_int64)now_ns);
84
85 rc = sqlite3_step(stmt);
86 sqlite3_finalize(stmt);
87
88 if (rc != SQLITE_DONE) {
89 return SET_ERRNO(ERROR_CONFIG, "Failed to record rate event: %s", sqlite3_errmsg(backend->db));
90 }
91
92 log_debug("Rate event recorded: %s - %s", ip_address, rate_limiter_event_type_string(event_type));
93 return ASCIICHAT_OK;
94}
95
96static asciichat_error_t sqlite_cleanup(void *backend_data, uint32_t max_age_secs) {
97 sqlite_backend_t *backend = (sqlite_backend_t *)backend_data;
98
99 // Default to 1 hour cleanup window
100 if (max_age_secs == 0) {
101 max_age_secs = SEC_PER_HOUR;
102 }
103
104 // Calculate cutoff time in nanoseconds
106 uint64_t cutoff_ns = now_ns - ((uint64_t)max_age_secs * NS_PER_SEC_INT);
107
108 // Delete old events
109 const char *sql = "DELETE FROM rate_events WHERE timestamp < ?";
110
111 sqlite3_stmt *stmt = NULL;
112 int rc = sqlite3_prepare_v2(backend->db, sql, -1, &stmt, NULL);
113 if (rc != SQLITE_OK) {
114 return SET_ERRNO(ERROR_CONFIG, "Failed to prepare rate limit cleanup: %s", sqlite3_errmsg(backend->db));
115 }
116
117 sqlite3_bind_int64(stmt, 1, (sqlite3_int64)cutoff_ns);
118
119 rc = sqlite3_step(stmt);
120 int changes = sqlite3_changes(backend->db);
121 sqlite3_finalize(stmt);
122
123 if (rc != SQLITE_DONE) {
124 return SET_ERRNO(ERROR_CONFIG, "Failed to cleanup rate events: %s", sqlite3_errmsg(backend->db));
125 }
126
127 if (changes > 0) {
128 log_debug("Cleaned up %d old rate events", changes);
129 }
130
131 return ASCIICHAT_OK;
132}
133
134static void sqlite_destroy(void *backend_data) {
135 sqlite_backend_t *backend = (sqlite_backend_t *)backend_data;
136 if (!backend) {
137 return;
138 }
139
140 // Note: We don't close the database here because it's owned by the caller
141 // (ACDS server manages the database lifecycle)
142 free(backend);
143}
144
145void *sqlite_backend_create(const char *db_path) {
146 (void)db_path; // Database is provided externally, not created here
147
148 sqlite_backend_t *backend = malloc(sizeof(sqlite_backend_t));
149 if (!backend) {
150 log_error("Failed to allocate SQLite backend");
151 return NULL;
152 }
153
154 memset(backend, 0, sizeof(*backend));
155
156 // Database handle will be set after creation by ACDS
157 // This is a placeholder backend that will be initialized later
158 log_debug("SQLite rate limiter backend allocated (database will be set externally)");
159 return backend;
160}
161
167void sqlite_backend_set_db(void *backend_data, sqlite3 *db) {
168 sqlite_backend_t *backend = (sqlite_backend_t *)backend_data;
169 if (backend) {
170 backend->db = db;
171 }
172}
173
175 .check = sqlite_check,
176 .record = sqlite_record,
177 .cleanup = sqlite_cleanup,
178 .destroy = sqlite_destroy,
179};
unsigned int uint32_t
Definition common.h:58
unsigned long long uint64_t
Definition common.h:59
#define SET_ERRNO(code, context_msg,...)
Set error code with custom context message and log it, returning the error code.
asciichat_error_t
Error and exit codes - unified status values (0-255)
Definition error_codes.h:49
@ ASCIICHAT_OK
Definition error_codes.h:51
@ ERROR_CONFIG
Definition error_codes.h:57
#define log_warn(...)
Log a WARN message.
Definition log/log.h:574
#define log_error(...)
Log an ERROR message.
Definition log/log.h:587
#define log_debug(...)
Log a DEBUG message.
Definition log/log.h:548
#define SEC_PER_HOUR
Definition time.h:168
uint64_t time_get_realtime_ns(void)
Get current wall-clock (real) time in nanoseconds.
Definition util/time.c:119
#define NS_PER_SEC_INT
Definition time.h:157
📝 Logging API with multiple log levels and terminal output control
const char * rate_limiter_event_type_string(rate_event_type_t event_type)
Get event type string for logging.
Definition rate_limit.c:176
const rate_limit_config_t DEFAULT_RATE_LIMITS[RATE_EVENT_MAX]
Default rate limits for each event type.
Definition rate_limit.c:30
rate_event_type_t
Rate limit event types.
Definition rate_limit.h:46
void sqlite_backend_set_db(void *backend_data, sqlite3 *db)
Set SQLite database handle for backend.
Definition sqlite.c:167
void * sqlite_backend_create(const char *db_path)
Create SQLite backend instance.
Definition sqlite.c:145
const rate_limiter_backend_ops_t sqlite_backend_ops
SQLite backend operations vtable.
Definition sqlite.c:174
💾 SQLite rate limiting backend interface
Rate limit configuration.
Definition rate_limit.h:66
uint32_t max_events
Maximum events allowed.
Definition rate_limit.h:67
uint32_t window_secs
Time window in seconds.
Definition rate_limit.h:68
Backend operations vtable.
Definition rate_limit.h:83
asciichat_error_t(* check)(void *backend_data, const char *ip_address, rate_event_type_t event_type, const rate_limit_config_t *config, bool *allowed)
Definition rate_limit.h:84
SQLite backend data.
Definition sqlite.c:17
sqlite3 * db
SQLite database handle.
Definition sqlite.c:18
⏱️ High-precision timing utilities using sokol_time.h and uthash