Nilorea Library
C utilities for networking, threading, graphics
Loading...
Searching...
No Matches
n_cookies.c
Go to the documentation of this file.
1/*
2 * Nilorea Library
3 * Copyright (C) 2005-2026 Castagnier Mickael
4 *
5 * Licensed under the Apache License, Version 2.0 (the "License");
6 * you may not use this file except in compliance with the License.
7 * You may obtain a copy of the License at
8 *
9 * http://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
14 * implied. See the License for the specific language governing
15 * permissions and limitations under the License.
16 *
17 * SPDX-License-Identifier: Apache-2.0
18 */
19
27#include "nilorea/n_cookies.h"
28
29#include "nilorea/n_common.h"
30#include "nilorea/n_log.h"
31
32#include <stdlib.h>
33#include <string.h>
34#include <strings.h>
35#include <time.h>
36
37/* Free an N_COOKIE stored in a LIST. */
38static void n_cookie_free(void* ptr) {
39 N_COOKIE* c = (N_COOKIE*)ptr;
40 if (!c)
41 return;
42 FreeNoLog(c->name);
43 FreeNoLog(c->value);
44 FreeNoLog(c->domain);
45 FreeNoLog(c->path);
46 FreeNoLog(c);
47}
48
50 N_COOKIE_JAR* jar = NULL;
51 Malloc(jar, N_COOKIE_JAR, 1);
52 if (!jar)
53 return NULL;
54 jar->cookies = new_generic_list(0);
55 if (!jar->cookies) {
56 Free(jar);
57 return NULL;
58 }
59 init_lock(jar->lock);
60 return jar;
61}
62
64 if (!jar || !*jar)
65 return;
66 if ((*jar)->cookies)
67 list_destroy(&(*jar)->cookies);
68 rw_lock_destroy((*jar)->lock);
69 Free(*jar);
70}
71
72/* Does the cookie's domain match the request domain? */
73static int n_cookie_domain_matches(const char* cookie_domain, const char* request_domain) {
74 if (!cookie_domain || !request_domain)
75 return 0;
76 if (strcasecmp(cookie_domain, request_domain) == 0)
77 return 1;
78 if (cookie_domain[0] == '.') {
79 size_t cd = strlen(cookie_domain);
80 size_t rd = strlen(request_domain);
81 if (rd >= cd - 1) {
82 const char* suffix = request_domain + rd - (cd - 1);
83 if (strcasecmp(suffix, cookie_domain + 1) == 0)
84 return 1;
85 }
86 }
87 return 0;
88}
89
90/* Does the request path fall under the cookie's path prefix? */
91static int n_cookie_path_matches(const char* cookie_path, const char* request_path) {
92 if (!cookie_path || !request_path)
93 return 1;
94 return strncmp(request_path, cookie_path, strlen(cookie_path)) == 0;
95}
96
97void n_cookiejar_add_from_header(N_COOKIE_JAR* jar, const char* set_cookie_value, const char* request_domain) {
98 char* dup;
99 char* saveptr = NULL;
100 char* token;
101 char* eq;
102 const char* n;
103 N_COOKIE* cookie = NULL;
104 if (!jar || !set_cookie_value || !request_domain)
105 return;
106
107 dup = strdup(set_cookie_value);
108 if (!dup)
109 return;
110 token = strtok_r(dup, ";", &saveptr);
111 if (!token) {
112 free(dup);
113 return;
114 }
115 eq = strchr(token, '=');
116 if (!eq) {
117 free(dup);
118 return;
119 }
120 Malloc(cookie, N_COOKIE, 1);
121 if (!cookie) {
122 free(dup);
123 return;
124 }
125 *eq = '\0';
126 n = token;
127 while (*n == ' ')
128 n++;
129 cookie->name = strdup(n);
130 cookie->value = strdup(eq + 1);
131 cookie->domain = strdup(request_domain);
132 cookie->path = strdup("/");
133 cookie->secure = 0;
134 cookie->http_only = 0;
135 cookie->expires = 0;
136
137 while ((token = strtok_r(NULL, ";", &saveptr)) != NULL) {
138 while (*token == ' ')
139 token++;
140 if (strncasecmp(token, "domain=", 7) == 0) {
141 free(cookie->domain);
142 cookie->domain = strdup(token + 7);
143 } else if (strncasecmp(token, "path=", 5) == 0) {
144 free(cookie->path);
145 cookie->path = strdup(token + 5);
146 } else if (strncasecmp(token, "secure", 6) == 0) {
147 cookie->secure = 1;
148 } else if (strncasecmp(token, "httponly", 8) == 0) {
149 cookie->http_only = 1;
150 } else if (strncasecmp(token, "max-age=", 8) == 0) {
151 long ma = strtol(token + 8, NULL, 10);
152 cookie->expires = (long)time(NULL) + ma;
153 }
154 }
155 free(dup);
156
157 write_lock(jar->lock);
158 /* replace an existing cookie with the same name+domain in place */
159 if (jar->cookies) {
160 list_foreach(cnode, jar->cookies) {
161 N_COOKIE* ex = (N_COOKIE*)cnode->ptr;
162 if (ex && ex->name && cookie->name && strcmp(ex->name, cookie->name) == 0 &&
163 ex->domain && cookie->domain && strcasecmp(ex->domain, cookie->domain) == 0) {
164 free(ex->value);
165 ex->value = cookie->value ? strdup(cookie->value) : NULL;
166 free(ex->path);
167 ex->path = cookie->path ? strdup(cookie->path) : NULL;
168 ex->secure = cookie->secure;
169 ex->http_only = cookie->http_only;
170 ex->expires = cookie->expires;
171 unlock(jar->lock);
172 n_cookie_free(cookie);
173 return;
174 }
175 }
176 }
177 list_push(jar->cookies, cookie, n_cookie_free);
178 unlock(jar->lock);
179}
180
181N_STR* n_cookiejar_build_header(N_COOKIE_JAR* jar, const char* domain, const char* path, int is_secure) {
182 N_STR* header;
183 time_t now;
184 int first = 1;
185 if (!jar || !jar->cookies || !domain)
186 return NULL;
187 header = new_nstr(256);
188 if (!header)
189 return NULL;
190 now = time(NULL);
191
192 read_lock(jar->lock);
193 list_foreach(node, jar->cookies) {
194 N_COOKIE* c = (N_COOKIE*)node->ptr;
195 if (!c || !c->name || !c->value)
196 continue;
197 if (c->expires > 0 && c->expires < (long)now)
198 continue;
199 if (!n_cookie_domain_matches(c->domain, domain))
200 continue;
201 if (!n_cookie_path_matches(c->path, path))
202 continue;
203 if (c->secure && !is_secure)
204 continue;
205 if (!first)
206 nstrprintf_cat(header, "; ");
207 nstrprintf_cat(header, "%s=%s", c->name, c->value);
208 first = 0;
209 }
210 unlock(jar->lock);
211
212 if (header->written == 0) {
213 free_nstr(&header);
214 return NULL;
215 }
216 return header;
217}
218
220 size_t n;
221 if (!jar || !jar->cookies)
222 return 0;
223 read_lock(jar->lock);
224 n = jar->cookies->nb_items;
225 unlock(jar->lock);
226 return n;
227}
228
230 if (!jar || !jar->cookies)
231 return;
232 write_lock(jar->lock);
233 while (jar->cookies->start) {
235 n_cookie_free(c);
236 }
237 unlock(jar->lock);
238}
#define init_lock(__rwlock_mutex)
Macro for initializing a rwlock.
Definition n_common.h:350
#define FreeNoLog(__ptr)
Free Handler without log.
Definition n_common.h:272
#define Malloc(__ptr, __struct, __size)
Malloc Handler to get errors and set to 0.
Definition n_common.h:204
#define rw_lock_destroy(__rwlock_mutex)
Macro to destroy rwlock mutex.
Definition n_common.h:409
#define unlock(__rwlock_mutex)
Macro for releasing read/write lock a rwlock mutex.
Definition n_common.h:396
#define write_lock(__rwlock_mutex)
Macro for acquiring a write lock on a rwlock mutex.
Definition n_common.h:382
#define Free(__ptr)
Free Handler to get errors.
Definition n_common.h:263
#define read_lock(__rwlock_mutex)
Macro for acquiring a read lock on a rwlock mutex.
Definition n_common.h:368
LIST_NODE * start
pointer to the start of the list
Definition n_list.h:66
size_t nb_items
number of item currently in the list
Definition n_list.h:61
int list_push(LIST *list, void *ptr, void(*destructor)(void *ptr))
Add a pointer to the end of the list.
Definition n_list.c:228
#define list_foreach(__ITEM_, __LIST_)
ForEach macro helper, safe for node removal during iteration.
Definition n_list.h:89
int list_destroy(LIST **list)
Empty and Free a list container.
Definition n_list.c:548
void * remove_list_node_f(LIST *list, LIST_NODE *node)
Internal function called each time we need to get a node out of a list.
Definition n_list.c:76
LIST * new_generic_list(size_t max_items)
Initialiaze a generic list container to max_items pointers.
Definition n_list.c:37
long expires
unix expiry timestamp, 0 = session cookie
Definition n_cookies.h:52
char * path
path prefix (e.g.
Definition n_cookies.h:49
char * domain
domain (e.g.
Definition n_cookies.h:48
char * value
cookie value
Definition n_cookies.h:47
int http_only
1 = HttpOnly flag was set
Definition n_cookies.h:51
LIST * cookies
list of N_COOKIE*
Definition n_cookies.h:57
int secure
1 = only sent over HTTPS
Definition n_cookies.h:50
char * name
cookie name
Definition n_cookies.h:46
pthread_rwlock_t lock
guards cookies for concurrent use
Definition n_cookies.h:58
void n_cookiejar_free(N_COOKIE_JAR **jar)
free a cookie jar and all its cookies, then NULL the pointer
Definition n_cookies.c:63
void n_cookiejar_add_from_header(N_COOKIE_JAR *jar, const char *set_cookie_value, const char *request_domain)
parse one Set-Cookie value (without the "Set-Cookie:" name) and store it; request_domain/path supply ...
Definition n_cookies.c:97
N_STR * n_cookiejar_build_header(N_COOKIE_JAR *jar, const char *domain, const char *path, int is_secure)
build a "name=value; name2=value2" Cookie header value for a request (domain/path/secure/expiry match...
Definition n_cookies.c:181
N_COOKIE_JAR * n_cookiejar_new(void)
create an empty cookie jar; free with n_cookiejar_free.
Definition n_cookies.c:49
void n_cookiejar_clear(N_COOKIE_JAR *jar)
drop every stored cookie
Definition n_cookies.c:229
size_t n_cookiejar_count(N_COOKIE_JAR *jar)
number of cookies currently stored
Definition n_cookies.c:219
a single stored cookie
Definition n_cookies.h:45
a thread-safe cookie jar
Definition n_cookies.h:56
size_t written
number of meaningful bytes in data, excluding the null terminator; the size including the null termin...
Definition n_str.h:68
#define free_nstr(__ptr)
free a N_STR structure and set the pointer to NULL
Definition n_str.h:203
#define nstrprintf_cat(__nstr_var, __format,...)
Macro to quickly allocate and sprintf and cat to a N_STR.
Definition n_str.h:121
N_STR * new_nstr(NSTRBYTE size)
create a new N_STR string
Definition n_str.c:207
A box including a string and his lenght.
Definition n_str.h:61
Common headers and low-level functions & define.
static int n_cookie_domain_matches(const char *cookie_domain, const char *request_domain)
Definition n_cookies.c:73
static int n_cookie_path_matches(const char *cookie_path, const char *request_path)
Definition n_cookies.c:91
static void n_cookie_free(void *ptr)
Definition n_cookies.c:38
Domain-aware HTTP cookie jar: Set-Cookie parsing and Cookie header building.
Generic log system.