cwist
Modern C web framework with libttak runtime
Loading...
Searching...
No Matches
cookie.c
Go to the documentation of this file.
1
5#define _POSIX_C_SOURCE 200809L
8#include <stdio.h>
9#include <stdlib.h>
10#include <string.h>
11#include <ctype.h>
12
19static int add_set_cookie(cwist_http_response *res, const char *value) {
20 cwist_error_t err = cwist_http_header_add(&res->headers, "Set-Cookie", value);
21 bool ok = cwist_error_is_ok(&err);
23 return ok ? 0 : -1;
24}
25
26/* URL-safe cookie characters: unreserved + !#$%&'()*+-./:<>?@[]^_`{|}~ */
27static int needs_url_encode(char c) {
28 if ((c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z') || (c >= '0' && c <= '9') || c == '-' ||
29 c == '_' || c == '.' || c == '~') {
30 return 0;
31 }
32 return 1;
33}
34
35char *cwist_cookie_encode(const char *value) {
36 if (!value) return NULL;
37 size_t len = strlen(value);
38 size_t max = len * 3 + 1;
39 char *out = cwist_alloc(max);
40 if (!out) return NULL;
41 size_t j = 0;
42 for (size_t i = 0; i < len; i++) {
43 unsigned char c = (unsigned char)value[i];
44 if (needs_url_encode((char)c)) {
45 snprintf(out + j, max - j, "%%%02X", c);
46 j += 3;
47 } else {
48 out[j++] = (char)c;
49 }
50 }
51 out[j] = '\0';
52 return out;
53}
54
55int cwist_cookie_decode(const char *in, char *out, size_t out_len) {
56 if (!in || !out || out_len == 0) return -1;
57 size_t len = strlen(in);
58 size_t j = 0;
59 for (size_t i = 0; i < len; i++) {
60 if (j >= out_len - 1) return -1;
61 if (in[i] == '%' && i + 2 < len && isxdigit((unsigned char)in[i + 1]) &&
62 isxdigit((unsigned char)in[i + 2])) {
63 unsigned int hex;
64 if (sscanf(in + i + 1, "%2x", &hex) != 1) {
65 out[j++] = in[i];
66 continue;
67 }
68 out[j++] = (char)hex;
69 i += 2;
70 } else if (in[i] == '+') {
71 out[j++] = ' ';
72 } else {
73 out[j++] = in[i];
74 }
75 }
76 out[j] = '\0';
77 return (int)j;
78}
79
80void cwist_cookie_parse(cwist_query_map *map, const char *header) {
81 if (!map || !header || !*header) return;
82
83 char *buf = cwist_strdup(header);
84 if (!buf) return;
85
86 char *save = NULL;
87 char *pair = strtok_r(buf, ";", &save);
88 while (pair) {
89 /* trim leading whitespace */
90 while (*pair == ' ' || *pair == '\t') pair++;
91 char *eq = strchr(pair, '=');
92 if (eq) {
93 *eq = '\0';
94 char *name = pair;
95 char *value = eq + 1;
96 /* trim trailing whitespace on name */
97 size_t nlen = strlen(name);
98 while (nlen > 0 && (name[nlen - 1] == ' ' || name[nlen - 1] == '\t')) {
99 name[--nlen] = '\0';
100 }
101
102 if (nlen > 0) {
103 char decoded[4096];
104 if (cwist_cookie_decode(value, decoded, sizeof(decoded)) >= 0) {
106 }
107 }
108 }
109 pair = strtok_r(NULL, ";", &save);
110 }
111 cwist_free(buf);
112}
113
114const char *cwist_cookie_get(cwist_query_map *map, const char *name) {
115 if (!map || !name) return NULL;
116 return cwist_query_map_get(map, name);
117}
118
119int cwist_cookie_set(cwist_http_response *res, const char *name, const char *value,
120 const cwist_cookie_options *opts) {
121 if (!res || !name) return -1;
122
123 char *encoded = value ? cwist_cookie_encode(value) : cwist_strdup("");
124 if (!encoded) return -1;
125
127 if (!cookie) {
129 return -1;
130 }
131
132 char buf[4096];
133 snprintf(buf, sizeof(buf), "%s=%s", name, encoded);
136
137 if (opts) {
138 if (opts->path) {
139 snprintf(buf, sizeof(buf), "; Path=%s", opts->path);
141 }
142 if (opts->domain) {
143 snprintf(buf, sizeof(buf), "; Domain=%s", opts->domain);
145 }
146 if (opts->max_age_seconds >= 0) {
147 snprintf(buf, sizeof(buf), "; Max-Age=%d", opts->max_age_seconds);
149 }
150 if (opts->http_only) {
151 cwist_sstring_append(cookie, "; HttpOnly");
152 }
153 if (opts->secure) {
154 cwist_sstring_append(cookie, "; Secure");
155 }
156 if (opts->same_site) {
157 snprintf(buf, sizeof(buf), "; SameSite=%s", opts->same_site);
159 }
160 }
161
162 int rc = add_set_cookie(res, cookie->data);
164 return rc;
165}
166
167int cwist_cookie_delete(cwist_http_response *res, const char *name) {
168 if (!res || !name) return -1;
169
171 if (!cookie) return -1;
172
173 char buf[256];
174 snprintf(buf, sizeof(buf), "%s=; Path=/; Max-Age=0", name);
176 int rc = add_set_cookie(res, cookie->data);
178 return rc;
179}
Memory allocation wrappers backed by libttak.
void cwist_free(void *ptr)
Free memory obtained via cwist_alloc/cwist_strdup/etc.
Definition alloc.c:52
char * cwist_strdup(const char *src)
Duplicate a string using libttak-backed storage.
Definition alloc.c:32
void * cwist_alloc(size_t size)
Allocate zeroed memory tracked by libttak.
Definition alloc.c:17
const char * cwist_cookie_get(cwist_query_map *map, const char *name)
Get a cookie value by name from a parsed map.
Definition cookie.c:114
int cwist_cookie_decode(const char *in, char *out, size_t out_len)
URL-decode a cookie value in-place into a caller-owned buffer.
Definition cookie.c:55
int cwist_cookie_set(cwist_http_response *res, const char *name, const char *value, const cwist_cookie_options *opts)
Add a Set-Cookie header to the response.
Definition cookie.c:119
int cwist_cookie_delete(cwist_http_response *res, const char *name)
Add a Set-Cookie header that deletes a cookie.
Definition cookie.c:167
char * cwist_cookie_encode(const char *value)
URL-encode a string for safe cookie values.
Definition cookie.c:35
void cwist_cookie_parse(cwist_query_map *map, const char *header)
Parse a Cookie header value into a query-style map.
Definition cookie.c:80
HTTP cookie parsing and construction helpers.
cwist_sstring * cwist_sstring_create(void)
Create a new sstring instance.
Definition sstring.c:802
cwist_error_t cwist_sstring_append(cwist_sstring *str, const char *data)
Append a C string.
Definition sstring.c:559
void cwist_sstring_destroy(cwist_sstring *str)
Destroy an sstring instance.
Definition sstring.c:823
cwist_error_t cwist_http_header_add(cwist_http_header_node **head, const char *key, const char *value)
Add a header to the list.
Definition http.c:1861
const char * cwist_query_map_get(cwist_query_map *map, const char *key)
Get a value by key.
Definition query.c:196
void cwist_query_map_set(cwist_query_map *map, const char *key, const char *value)
Set a key-value pair.
Definition query.c:146
size_t cap
Definition otel.c:56
Integrated error structure used throughout the system.
Definition cwist_err.h:72
HTTP Response Object. Supports standard string body or Zero-Copy pointer body.
Definition http.h:82
cwist_http_header_node * headers
Definition http.h:86
void cwist_error_dispose(cwist_error_t *err)
Release resources owned by an error value. Currently only CWIST_ERR_JSON owns memory (the cJSON paylo...
Definition error.c:43