1d4afb5ceSopenharmony_ci/*
2d4afb5ceSopenharmony_ci * libwebsockets - small server side websockets and web server implementation
3d4afb5ceSopenharmony_ci *
4d4afb5ceSopenharmony_ci * Copyright (C) 2010 - 2021 Andy Green <andy@warmcat.com>
5d4afb5ceSopenharmony_ci *
6d4afb5ceSopenharmony_ci * Permission is hereby granted, free of charge, to any person obtaining a copy
7d4afb5ceSopenharmony_ci * of this software and associated documentation files (the "Software"), to
8d4afb5ceSopenharmony_ci * deal in the Software without restriction, including without limitation the
9d4afb5ceSopenharmony_ci * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
10d4afb5ceSopenharmony_ci * sell copies of the Software, and to permit persons to whom the Software is
11d4afb5ceSopenharmony_ci * furnished to do so, subject to the following conditions:
12d4afb5ceSopenharmony_ci *
13d4afb5ceSopenharmony_ci * The above copyright notice and this permission notice shall be included in
14d4afb5ceSopenharmony_ci * all copies or substantial portions of the Software.
15d4afb5ceSopenharmony_ci *
16d4afb5ceSopenharmony_ci * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17d4afb5ceSopenharmony_ci * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18d4afb5ceSopenharmony_ci * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19d4afb5ceSopenharmony_ci * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20d4afb5ceSopenharmony_ci * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
21d4afb5ceSopenharmony_ci * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
22d4afb5ceSopenharmony_ci * IN THE SOFTWARE.
23d4afb5ceSopenharmony_ci *
24d4afb5ceSopenharmony_ci * Implements a cache backing store compatible with netscape cookies.txt format
25d4afb5ceSopenharmony_ci * There is one entry per "line", and fields are tab-delimited
26d4afb5ceSopenharmony_ci *
27d4afb5ceSopenharmony_ci * We need to know the format here, because while the unique cookie tag consists
28d4afb5ceSopenharmony_ci * of "hostname|urlpath|cookiename", that does not appear like that in the file;
29d4afb5ceSopenharmony_ci * we have to go parse the fields and synthesize the corresponding tag.
30d4afb5ceSopenharmony_ci *
31d4afb5ceSopenharmony_ci * We rely on all the fields except the cookie value fitting in a 256 byte
32d4afb5ceSopenharmony_ci * buffer, and allow eating multiple buffers to get a huge cookie values.
33d4afb5ceSopenharmony_ci *
34d4afb5ceSopenharmony_ci * Because the cookie file is a device-wide asset, although lws will change it
35d4afb5ceSopenharmony_ci * from the lws thread without conflict, there may be other processes that will
36d4afb5ceSopenharmony_ci * change it by removal and regenerating the file asynchronously.  For that
37d4afb5ceSopenharmony_ci * reason, file handles are opened fresh each time we want to use the file, so
38d4afb5ceSopenharmony_ci * we always get the latest version.
39d4afb5ceSopenharmony_ci *
40d4afb5ceSopenharmony_ci * When updating the file ourselves, we use a lockfile to ensure our process
41d4afb5ceSopenharmony_ci * has exclusive access.
42d4afb5ceSopenharmony_ci *
43d4afb5ceSopenharmony_ci *
44d4afb5ceSopenharmony_ci * Tag Matching rules
45d4afb5ceSopenharmony_ci *
46d4afb5ceSopenharmony_ci * There are three kinds of tag matching rules
47d4afb5ceSopenharmony_ci *
48d4afb5ceSopenharmony_ci * 1) specific - tag strigs must be the same
49d4afb5ceSopenharmony_ci * 2) wilcard - tags matched using optional wildcards
50d4afb5ceSopenharmony_ci * 3) wildcard + lookup - wildcard, but path part matches using cookie scope rules
51d4afb5ceSopenharmony_ci *
52d4afb5ceSopenharmony_ci */
53d4afb5ceSopenharmony_ci
54d4afb5ceSopenharmony_ci#include <private-lib-core.h>
55d4afb5ceSopenharmony_ci#include "private-lib-misc-cache-ttl.h"
56d4afb5ceSopenharmony_ci
57d4afb5ceSopenharmony_citypedef enum nsc_iterator_ret {
58d4afb5ceSopenharmony_ci	NIR_CONTINUE		= 0,
59d4afb5ceSopenharmony_ci	NIR_FINISH_OK		= 1,
60d4afb5ceSopenharmony_ci	NIR_FINISH_ERROR	= -1
61d4afb5ceSopenharmony_ci} nsc_iterator_ret_t;
62d4afb5ceSopenharmony_ci
63d4afb5ceSopenharmony_citypedef enum cbreason {
64d4afb5ceSopenharmony_ci	LCN_SOL			= (1 << 0),
65d4afb5ceSopenharmony_ci	LCN_EOL			= (1 << 1)
66d4afb5ceSopenharmony_ci} cbreason_t;
67d4afb5ceSopenharmony_ci
68d4afb5ceSopenharmony_citypedef int (*nsc_cb_t)(lws_cache_nscookiejar_t *cache, void *opaque, int flags,
69d4afb5ceSopenharmony_ci			const char *buf, size_t size);
70d4afb5ceSopenharmony_ci
71d4afb5ceSopenharmony_cistatic void
72d4afb5ceSopenharmony_ciexpiry_cb(lws_sorted_usec_list_t *sul);
73d4afb5ceSopenharmony_ci
74d4afb5ceSopenharmony_cistatic int
75d4afb5ceSopenharmony_cinsc_backing_open_lock(lws_cache_nscookiejar_t *cache, int mode, const char *par)
76d4afb5ceSopenharmony_ci{
77d4afb5ceSopenharmony_ci	int sanity = 50;
78d4afb5ceSopenharmony_ci	char lock[128];
79d4afb5ceSopenharmony_ci	int fd_lock, fd;
80d4afb5ceSopenharmony_ci
81d4afb5ceSopenharmony_ci	lwsl_debug("%s: %s\n", __func__, par);
82d4afb5ceSopenharmony_ci
83d4afb5ceSopenharmony_ci	lws_snprintf(lock, sizeof(lock), "%s.LCK",
84d4afb5ceSopenharmony_ci			cache->cache.info.u.nscookiejar.filepath);
85d4afb5ceSopenharmony_ci
86d4afb5ceSopenharmony_ci	do {
87d4afb5ceSopenharmony_ci		fd_lock = open(lock, LWS_O_CREAT | O_EXCL, 0600);
88d4afb5ceSopenharmony_ci		if (fd_lock >= 0) {
89d4afb5ceSopenharmony_ci			close(fd_lock);
90d4afb5ceSopenharmony_ci			break;
91d4afb5ceSopenharmony_ci		}
92d4afb5ceSopenharmony_ci
93d4afb5ceSopenharmony_ci		if (!sanity--) {
94d4afb5ceSopenharmony_ci			lwsl_warn("%s: unable to lock %s: errno %d\n", __func__,
95d4afb5ceSopenharmony_ci					lock, errno);
96d4afb5ceSopenharmony_ci			return -1;
97d4afb5ceSopenharmony_ci		}
98d4afb5ceSopenharmony_ci
99d4afb5ceSopenharmony_ci#if defined(WIN32)
100d4afb5ceSopenharmony_ci		Sleep(100);
101d4afb5ceSopenharmony_ci#else
102d4afb5ceSopenharmony_ci		usleep(100000);
103d4afb5ceSopenharmony_ci#endif
104d4afb5ceSopenharmony_ci	} while (1);
105d4afb5ceSopenharmony_ci
106d4afb5ceSopenharmony_ci	fd = open(cache->cache.info.u.nscookiejar.filepath,
107d4afb5ceSopenharmony_ci		      LWS_O_CREAT | mode, 0600);
108d4afb5ceSopenharmony_ci
109d4afb5ceSopenharmony_ci	if (fd == -1) {
110d4afb5ceSopenharmony_ci		lwsl_warn("%s: unable to open or create %s\n", __func__,
111d4afb5ceSopenharmony_ci				cache->cache.info.u.nscookiejar.filepath);
112d4afb5ceSopenharmony_ci		unlink(lock);
113d4afb5ceSopenharmony_ci	}
114d4afb5ceSopenharmony_ci
115d4afb5ceSopenharmony_ci	return fd;
116d4afb5ceSopenharmony_ci}
117d4afb5ceSopenharmony_ci
118d4afb5ceSopenharmony_cistatic void
119d4afb5ceSopenharmony_cinsc_backing_close_unlock(lws_cache_nscookiejar_t *cache, int fd)
120d4afb5ceSopenharmony_ci{
121d4afb5ceSopenharmony_ci	char lock[128];
122d4afb5ceSopenharmony_ci
123d4afb5ceSopenharmony_ci	lwsl_debug("%s\n", __func__);
124d4afb5ceSopenharmony_ci
125d4afb5ceSopenharmony_ci	lws_snprintf(lock, sizeof(lock), "%s.LCK",
126d4afb5ceSopenharmony_ci			cache->cache.info.u.nscookiejar.filepath);
127d4afb5ceSopenharmony_ci	if (fd >= 0)
128d4afb5ceSopenharmony_ci		close(fd);
129d4afb5ceSopenharmony_ci	unlink(lock);
130d4afb5ceSopenharmony_ci}
131d4afb5ceSopenharmony_ci
132d4afb5ceSopenharmony_ci/*
133d4afb5ceSopenharmony_ci * We're going to call the callback with chunks of the file with flags
134d4afb5ceSopenharmony_ci * indicating we're giving it the start of a line and / or giving it the end
135d4afb5ceSopenharmony_ci * of a line.
136d4afb5ceSopenharmony_ci *
137d4afb5ceSopenharmony_ci * It's like this because the cookie value may be huge (and to a lesser extent
138d4afb5ceSopenharmony_ci * the path may also be big).
139d4afb5ceSopenharmony_ci *
140d4afb5ceSopenharmony_ci * If it's the start of a line (flags on the cb has LCN_SOL), then the buffer
141d4afb5ceSopenharmony_ci * contains up to the first 256 chars of the line, it's enough to match with.
142d4afb5ceSopenharmony_ci *
143d4afb5ceSopenharmony_ci * We cannot hold the file open inbetweentimes, since other processes may
144d4afb5ceSopenharmony_ci * regenerate it, so we need to bind to a new inode.  We open it with an
145d4afb5ceSopenharmony_ci * exclusive flock() so other processes can't replace conflicting changes
146d4afb5ceSopenharmony_ci * while we also write changes, without having to wait and see our changes.
147d4afb5ceSopenharmony_ci */
148d4afb5ceSopenharmony_ci
149d4afb5ceSopenharmony_cistatic int
150d4afb5ceSopenharmony_cinscookiejar_iterate(lws_cache_nscookiejar_t *cache, int fd,
151d4afb5ceSopenharmony_ci		    nsc_cb_t cb, void *opaque)
152d4afb5ceSopenharmony_ci{
153d4afb5ceSopenharmony_ci	int m = 0, n = 0, e, r = LCN_SOL, ignore = 0, ret = 0;
154d4afb5ceSopenharmony_ci	char temp[256], eof = 0;
155d4afb5ceSopenharmony_ci
156d4afb5ceSopenharmony_ci	if (lseek(fd, 0, SEEK_SET) == (off_t)-1)
157d4afb5ceSopenharmony_ci		return -1;
158d4afb5ceSopenharmony_ci
159d4afb5ceSopenharmony_ci	do { /* for as many buffers in the file */
160d4afb5ceSopenharmony_ci
161d4afb5ceSopenharmony_ci		int n1;
162d4afb5ceSopenharmony_ci
163d4afb5ceSopenharmony_ci		lwsl_debug("%s: n %d, m %d\n", __func__, n, m);
164d4afb5ceSopenharmony_ci
165d4afb5ceSopenharmony_ciread:
166d4afb5ceSopenharmony_ci		n1 = (int)read(fd, temp + n, sizeof(temp) - (size_t)n);
167d4afb5ceSopenharmony_ci
168d4afb5ceSopenharmony_ci		lwsl_debug("%s: n1 %d\n", __func__, n1);
169d4afb5ceSopenharmony_ci
170d4afb5ceSopenharmony_ci		if (n1 <= 0) {
171d4afb5ceSopenharmony_ci			eof = 1;
172d4afb5ceSopenharmony_ci			if (m == n)
173d4afb5ceSopenharmony_ci				continue;
174d4afb5ceSopenharmony_ci		} else
175d4afb5ceSopenharmony_ci			n += n1;
176d4afb5ceSopenharmony_ci
177d4afb5ceSopenharmony_ci		while (m < n) {
178d4afb5ceSopenharmony_ci
179d4afb5ceSopenharmony_ci			m++;
180d4afb5ceSopenharmony_ci
181d4afb5ceSopenharmony_ci			if (temp[m - 1] != '\n')
182d4afb5ceSopenharmony_ci				continue;
183d4afb5ceSopenharmony_ci
184d4afb5ceSopenharmony_ci			/* ie, we hit EOL */
185d4afb5ceSopenharmony_ci
186d4afb5ceSopenharmony_ci			if (temp[0] == '#')
187d4afb5ceSopenharmony_ci				/* lines starting with # are comments */
188d4afb5ceSopenharmony_ci				e = 0;
189d4afb5ceSopenharmony_ci			else
190d4afb5ceSopenharmony_ci				e = cb(cache, opaque, r | LCN_EOL, temp,
191d4afb5ceSopenharmony_ci				       (size_t)m - 1);
192d4afb5ceSopenharmony_ci			r = LCN_SOL;
193d4afb5ceSopenharmony_ci			ignore = 0;
194d4afb5ceSopenharmony_ci			/*
195d4afb5ceSopenharmony_ci			 * Move back remainder and prefill the gap that opened
196d4afb5ceSopenharmony_ci			 * up: we want to pass enough in the start chunk so the
197d4afb5ceSopenharmony_ci			 * cb can classify it even if it can't get all the
198d4afb5ceSopenharmony_ci			 * value part in one go
199d4afb5ceSopenharmony_ci			 */
200d4afb5ceSopenharmony_ci			memmove(temp, temp + m, (size_t)(n - m));
201d4afb5ceSopenharmony_ci			n -= m;
202d4afb5ceSopenharmony_ci			m = 0;
203d4afb5ceSopenharmony_ci
204d4afb5ceSopenharmony_ci			if (e) {
205d4afb5ceSopenharmony_ci				ret = e;
206d4afb5ceSopenharmony_ci				goto bail;
207d4afb5ceSopenharmony_ci			}
208d4afb5ceSopenharmony_ci
209d4afb5ceSopenharmony_ci			goto read;
210d4afb5ceSopenharmony_ci		}
211d4afb5ceSopenharmony_ci
212d4afb5ceSopenharmony_ci		if (m) {
213d4afb5ceSopenharmony_ci			/* we ran out of buffer */
214d4afb5ceSopenharmony_ci			if (ignore || (r == LCN_SOL && n && temp[0] == '#')) {
215d4afb5ceSopenharmony_ci				e = 0;
216d4afb5ceSopenharmony_ci				ignore = 1;
217d4afb5ceSopenharmony_ci			} else {
218d4afb5ceSopenharmony_ci				e = cb(cache, opaque,
219d4afb5ceSopenharmony_ci				       r | (n == m && eof ? LCN_EOL : 0),
220d4afb5ceSopenharmony_ci				       temp, (size_t)m);
221d4afb5ceSopenharmony_ci
222d4afb5ceSopenharmony_ci				m = 0;
223d4afb5ceSopenharmony_ci				n = 0;
224d4afb5ceSopenharmony_ci			}
225d4afb5ceSopenharmony_ci
226d4afb5ceSopenharmony_ci			if (e) {
227d4afb5ceSopenharmony_ci				/*
228d4afb5ceSopenharmony_ci				 * We have to call off the whole thing if any
229d4afb5ceSopenharmony_ci				 * step, eg, OOMs
230d4afb5ceSopenharmony_ci				 */
231d4afb5ceSopenharmony_ci				ret = e;
232d4afb5ceSopenharmony_ci				goto bail;
233d4afb5ceSopenharmony_ci			}
234d4afb5ceSopenharmony_ci			r = 0;
235d4afb5ceSopenharmony_ci		}
236d4afb5ceSopenharmony_ci
237d4afb5ceSopenharmony_ci	} while (!eof || n != m);
238d4afb5ceSopenharmony_ci
239d4afb5ceSopenharmony_ci	ret = 0;
240d4afb5ceSopenharmony_ci
241d4afb5ceSopenharmony_cibail:
242d4afb5ceSopenharmony_ci
243d4afb5ceSopenharmony_ci	return ret;
244d4afb5ceSopenharmony_ci}
245d4afb5ceSopenharmony_ci
246d4afb5ceSopenharmony_ci/*
247d4afb5ceSopenharmony_ci * lookup() just handles wildcard resolution, it doesn't deal with moving the
248d4afb5ceSopenharmony_ci * hits to L1.  That has to be done individually by non-wildcard names.
249d4afb5ceSopenharmony_ci */
250d4afb5ceSopenharmony_ci
251d4afb5ceSopenharmony_cienum {
252d4afb5ceSopenharmony_ci	NSC_COL_HOST		= 0, /* wc idx 0 */
253d4afb5ceSopenharmony_ci	NSC_COL_PATH		= 2, /* wc idx 1 */
254d4afb5ceSopenharmony_ci	NSC_COL_EXPIRY		= 4,
255d4afb5ceSopenharmony_ci	NSC_COL_NAME		= 5, /* wc idx 2 */
256d4afb5ceSopenharmony_ci
257d4afb5ceSopenharmony_ci	NSC_COL_COUNT		= 6
258d4afb5ceSopenharmony_ci};
259d4afb5ceSopenharmony_ci
260d4afb5ceSopenharmony_ci/*
261d4afb5ceSopenharmony_ci * This performs the specialized wildcard that knows about cookie path match
262d4afb5ceSopenharmony_ci * rules.
263d4afb5ceSopenharmony_ci *
264d4afb5ceSopenharmony_ci * To defeat the lookup path matching, lie to it about idx being NSC_COL_PATH
265d4afb5ceSopenharmony_ci */
266d4afb5ceSopenharmony_ci
267d4afb5ceSopenharmony_cistatic int
268d4afb5ceSopenharmony_cinsc_match(const char *wc, size_t wc_len, const char *col, size_t col_len,
269d4afb5ceSopenharmony_ci	  int idx)
270d4afb5ceSopenharmony_ci{
271d4afb5ceSopenharmony_ci	size_t n = 0;
272d4afb5ceSopenharmony_ci
273d4afb5ceSopenharmony_ci	if (idx != NSC_COL_PATH)
274d4afb5ceSopenharmony_ci		return lws_strcmp_wildcard(wc, wc_len, col, col_len);
275d4afb5ceSopenharmony_ci
276d4afb5ceSopenharmony_ci	/*
277d4afb5ceSopenharmony_ci	 * Cookie path match is special, if we lookup on a path like /my/path,
278d4afb5ceSopenharmony_ci	 * we must match on cookie paths for every dir level including /, so
279d4afb5ceSopenharmony_ci	 * match on /, /my, and /my/path.  But we must not match on /m or
280d4afb5ceSopenharmony_ci	 * /my/pa etc.  If we lookup on /, we must not match /my/path
281d4afb5ceSopenharmony_ci	 *
282d4afb5ceSopenharmony_ci	 * Let's go through wc checking at / and for every complete subpath if
283d4afb5ceSopenharmony_ci	 * it is an explicit match
284d4afb5ceSopenharmony_ci	 */
285d4afb5ceSopenharmony_ci
286d4afb5ceSopenharmony_ci	if (!strcmp(col, wc))
287d4afb5ceSopenharmony_ci		return 0; /* exact hit */
288d4afb5ceSopenharmony_ci
289d4afb5ceSopenharmony_ci	while (n <= wc_len) {
290d4afb5ceSopenharmony_ci		if (n == wc_len || wc[n] == '/') {
291d4afb5ceSopenharmony_ci			if (n && col_len <= n && !strncmp(wc, col, n))
292d4afb5ceSopenharmony_ci				return 0; /* hit */
293d4afb5ceSopenharmony_ci
294d4afb5ceSopenharmony_ci			if (n != wc_len && col_len <= n + 1 &&
295d4afb5ceSopenharmony_ci			    !strncmp(wc, col, n + 1)) /* check for trailing / */
296d4afb5ceSopenharmony_ci				return 0; /* hit */
297d4afb5ceSopenharmony_ci		}
298d4afb5ceSopenharmony_ci		n++;
299d4afb5ceSopenharmony_ci	}
300d4afb5ceSopenharmony_ci
301d4afb5ceSopenharmony_ci	return 1; /* fail */
302d4afb5ceSopenharmony_ci}
303d4afb5ceSopenharmony_ci
304d4afb5ceSopenharmony_cistatic const uint8_t nsc_cols[] = { NSC_COL_HOST, NSC_COL_PATH, NSC_COL_NAME };
305d4afb5ceSopenharmony_ci
306d4afb5ceSopenharmony_cistatic int
307d4afb5ceSopenharmony_cilws_cache_nscookiejar_tag_match(struct lws_cache_ttl_lru *cache,
308d4afb5ceSopenharmony_ci				const char *wc, const char *tag, char lookup)
309d4afb5ceSopenharmony_ci{
310d4afb5ceSopenharmony_ci	const char *wc_end = wc + strlen(wc), *tag_end = tag + strlen(tag),
311d4afb5ceSopenharmony_ci			*start_wc, *start_tag;
312d4afb5ceSopenharmony_ci	int n = 0;
313d4afb5ceSopenharmony_ci
314d4afb5ceSopenharmony_ci	lwsl_cache("%s: '%s' vs '%s'\n", __func__, wc, tag);
315d4afb5ceSopenharmony_ci
316d4afb5ceSopenharmony_ci	/*
317d4afb5ceSopenharmony_ci	 * Given a well-formed host|path|name tag and a wildcard term,
318d4afb5ceSopenharmony_ci	 * make the determination if the tag matches the wildcard or not,
319d4afb5ceSopenharmony_ci	 * using lookup rules that apply at this cache level.
320d4afb5ceSopenharmony_ci	 */
321d4afb5ceSopenharmony_ci
322d4afb5ceSopenharmony_ci	while (n < 3) {
323d4afb5ceSopenharmony_ci		start_wc = wc;
324d4afb5ceSopenharmony_ci		while (wc < wc_end && *wc != LWSCTAG_SEP)
325d4afb5ceSopenharmony_ci			wc++;
326d4afb5ceSopenharmony_ci
327d4afb5ceSopenharmony_ci		start_tag = tag;
328d4afb5ceSopenharmony_ci		while (tag < tag_end && *tag != LWSCTAG_SEP)
329d4afb5ceSopenharmony_ci			tag++;
330d4afb5ceSopenharmony_ci
331d4afb5ceSopenharmony_ci		lwsl_cache("%s:   '%.*s' vs '%.*s'\n", __func__,
332d4afb5ceSopenharmony_ci				lws_ptr_diff(wc, start_wc), start_wc,
333d4afb5ceSopenharmony_ci				lws_ptr_diff(tag, start_tag), start_tag);
334d4afb5ceSopenharmony_ci		if (nsc_match(start_wc, lws_ptr_diff_size_t(wc, start_wc),
335d4afb5ceSopenharmony_ci			      start_tag, lws_ptr_diff_size_t(tag, start_tag),
336d4afb5ceSopenharmony_ci			      lookup ? nsc_cols[n] : NSC_COL_HOST)) {
337d4afb5ceSopenharmony_ci			lwsl_cache("%s: fail\n", __func__);
338d4afb5ceSopenharmony_ci			return 1;
339d4afb5ceSopenharmony_ci		}
340d4afb5ceSopenharmony_ci
341d4afb5ceSopenharmony_ci		if (wc < wc_end)
342d4afb5ceSopenharmony_ci			wc++;
343d4afb5ceSopenharmony_ci		if (tag < tag_end)
344d4afb5ceSopenharmony_ci			tag++;
345d4afb5ceSopenharmony_ci
346d4afb5ceSopenharmony_ci		n++;
347d4afb5ceSopenharmony_ci	}
348d4afb5ceSopenharmony_ci
349d4afb5ceSopenharmony_ci	lwsl_cache("%s: hit\n", __func__);
350d4afb5ceSopenharmony_ci
351d4afb5ceSopenharmony_ci	return 0; /* match */
352d4afb5ceSopenharmony_ci}
353d4afb5ceSopenharmony_ci
354d4afb5ceSopenharmony_ci/*
355d4afb5ceSopenharmony_ci * Converts the start of a cookie file line into a tag
356d4afb5ceSopenharmony_ci */
357d4afb5ceSopenharmony_ci
358d4afb5ceSopenharmony_cistatic int
359d4afb5ceSopenharmony_cinsc_line_to_tag(const char *buf, size_t size, char *tag, size_t max_tag,
360d4afb5ceSopenharmony_ci		lws_usec_t *pexpiry)
361d4afb5ceSopenharmony_ci{
362d4afb5ceSopenharmony_ci	int n, idx = 0, tl = 0;
363d4afb5ceSopenharmony_ci	lws_usec_t expiry = 0;
364d4afb5ceSopenharmony_ci	size_t bn = 0;
365d4afb5ceSopenharmony_ci	char col[64];
366d4afb5ceSopenharmony_ci
367d4afb5ceSopenharmony_ci	if (size < 3)
368d4afb5ceSopenharmony_ci		return 1;
369d4afb5ceSopenharmony_ci
370d4afb5ceSopenharmony_ci	while (bn < size && idx <= NSC_COL_NAME) {
371d4afb5ceSopenharmony_ci
372d4afb5ceSopenharmony_ci		n = 0;
373d4afb5ceSopenharmony_ci		while (bn < size && n < (int)sizeof(col) - 1 &&
374d4afb5ceSopenharmony_ci		       buf[bn] != '\t')
375d4afb5ceSopenharmony_ci			col[n++] = buf[bn++];
376d4afb5ceSopenharmony_ci		col[n] = '\0';
377d4afb5ceSopenharmony_ci		if (buf[bn] == '\t')
378d4afb5ceSopenharmony_ci			bn++;
379d4afb5ceSopenharmony_ci
380d4afb5ceSopenharmony_ci		switch (idx) {
381d4afb5ceSopenharmony_ci		case NSC_COL_EXPIRY:
382d4afb5ceSopenharmony_ci			expiry = (lws_usec_t)((unsigned long long)atoll(col) *
383d4afb5ceSopenharmony_ci					(lws_usec_t)LWS_US_PER_SEC);
384d4afb5ceSopenharmony_ci			break;
385d4afb5ceSopenharmony_ci
386d4afb5ceSopenharmony_ci		case NSC_COL_HOST:
387d4afb5ceSopenharmony_ci		case NSC_COL_PATH:
388d4afb5ceSopenharmony_ci		case NSC_COL_NAME:
389d4afb5ceSopenharmony_ci
390d4afb5ceSopenharmony_ci			/*
391d4afb5ceSopenharmony_ci			 * As we match the pieces of the wildcard,
392d4afb5ceSopenharmony_ci			 * compose the matches into a specific tag
393d4afb5ceSopenharmony_ci			 */
394d4afb5ceSopenharmony_ci
395d4afb5ceSopenharmony_ci			if (tl + n + 2 > (int)max_tag)
396d4afb5ceSopenharmony_ci				return 1;
397d4afb5ceSopenharmony_ci			if (tl)
398d4afb5ceSopenharmony_ci				tag[tl++] = LWSCTAG_SEP;
399d4afb5ceSopenharmony_ci			memcpy(tag + tl, col, (size_t)n);
400d4afb5ceSopenharmony_ci			tl += n;
401d4afb5ceSopenharmony_ci			tag[tl] = '\0';
402d4afb5ceSopenharmony_ci			break;
403d4afb5ceSopenharmony_ci		default:
404d4afb5ceSopenharmony_ci			break;
405d4afb5ceSopenharmony_ci		}
406d4afb5ceSopenharmony_ci
407d4afb5ceSopenharmony_ci		idx++;
408d4afb5ceSopenharmony_ci	}
409d4afb5ceSopenharmony_ci
410d4afb5ceSopenharmony_ci	if (pexpiry)
411d4afb5ceSopenharmony_ci		*pexpiry = expiry;
412d4afb5ceSopenharmony_ci
413d4afb5ceSopenharmony_ci	lwsl_info("%s: %.*s: tag '%s'\n", __func__, (int)size, buf, tag);
414d4afb5ceSopenharmony_ci
415d4afb5ceSopenharmony_ci	return 0;
416d4afb5ceSopenharmony_ci}
417d4afb5ceSopenharmony_ci
418d4afb5ceSopenharmony_cistruct nsc_lookup_ctx {
419d4afb5ceSopenharmony_ci	const char		*wildcard_key;
420d4afb5ceSopenharmony_ci	lws_dll2_owner_t	*results_owner;
421d4afb5ceSopenharmony_ci	lws_cache_match_t	*match; /* current match if any */
422d4afb5ceSopenharmony_ci	size_t			wklen;
423d4afb5ceSopenharmony_ci};
424d4afb5ceSopenharmony_ci
425d4afb5ceSopenharmony_ci
426d4afb5ceSopenharmony_cistatic int
427d4afb5ceSopenharmony_cinsc_lookup_cb(lws_cache_nscookiejar_t *cache, void *opaque, int flags,
428d4afb5ceSopenharmony_ci	      const char *buf, size_t size)
429d4afb5ceSopenharmony_ci{
430d4afb5ceSopenharmony_ci	struct nsc_lookup_ctx *ctx = (struct nsc_lookup_ctx *)opaque;
431d4afb5ceSopenharmony_ci	lws_usec_t expiry;
432d4afb5ceSopenharmony_ci	char tag[200];
433d4afb5ceSopenharmony_ci	int tl;
434d4afb5ceSopenharmony_ci
435d4afb5ceSopenharmony_ci	if (!(flags & LCN_SOL)) {
436d4afb5ceSopenharmony_ci		if (ctx->match)
437d4afb5ceSopenharmony_ci			ctx->match->payload_size += size;
438d4afb5ceSopenharmony_ci
439d4afb5ceSopenharmony_ci		return NIR_CONTINUE;
440d4afb5ceSopenharmony_ci	}
441d4afb5ceSopenharmony_ci
442d4afb5ceSopenharmony_ci	/*
443d4afb5ceSopenharmony_ci	 * There should be enough in buf to match or reject it... let's
444d4afb5ceSopenharmony_ci	 * synthesize a tag from the text "line" and then check the tags for
445d4afb5ceSopenharmony_ci	 * a match
446d4afb5ceSopenharmony_ci	 */
447d4afb5ceSopenharmony_ci
448d4afb5ceSopenharmony_ci	ctx->match = NULL; /* new SOL means stop tracking payload len */
449d4afb5ceSopenharmony_ci
450d4afb5ceSopenharmony_ci	if (nsc_line_to_tag(buf, size, tag, sizeof(tag), &expiry))
451d4afb5ceSopenharmony_ci		return NIR_CONTINUE;
452d4afb5ceSopenharmony_ci
453d4afb5ceSopenharmony_ci	if (lws_cache_nscookiejar_tag_match(&cache->cache,
454d4afb5ceSopenharmony_ci					    ctx->wildcard_key, tag, 1))
455d4afb5ceSopenharmony_ci		return NIR_CONTINUE;
456d4afb5ceSopenharmony_ci
457d4afb5ceSopenharmony_ci	tl = (int)strlen(tag);
458d4afb5ceSopenharmony_ci
459d4afb5ceSopenharmony_ci	/*
460d4afb5ceSopenharmony_ci	 * ... it looks like a match then... create new match
461d4afb5ceSopenharmony_ci	 * object with the specific tag, and add it to the owner list
462d4afb5ceSopenharmony_ci	 */
463d4afb5ceSopenharmony_ci
464d4afb5ceSopenharmony_ci	ctx->match = lws_fi(&cache->cache.info.cx->fic, "cache_lookup_oom") ? NULL :
465d4afb5ceSopenharmony_ci			lws_malloc(sizeof(*ctx->match) + (unsigned int)tl + 1u,
466d4afb5ceSopenharmony_ci				__func__);
467d4afb5ceSopenharmony_ci	if (!ctx->match)
468d4afb5ceSopenharmony_ci		/* caller of lookup will clean results list on fail */
469d4afb5ceSopenharmony_ci		return NIR_FINISH_ERROR;
470d4afb5ceSopenharmony_ci
471d4afb5ceSopenharmony_ci	ctx->match->payload_size = size;
472d4afb5ceSopenharmony_ci	ctx->match->tag_size = (size_t)tl;
473d4afb5ceSopenharmony_ci	ctx->match->expiry = expiry;
474d4afb5ceSopenharmony_ci
475d4afb5ceSopenharmony_ci	memset(&ctx->match->list, 0, sizeof(ctx->match->list));
476d4afb5ceSopenharmony_ci	memcpy(&ctx->match[1], tag, (size_t)tl + 1u);
477d4afb5ceSopenharmony_ci	lws_dll2_add_tail(&ctx->match->list, ctx->results_owner);
478d4afb5ceSopenharmony_ci
479d4afb5ceSopenharmony_ci	return NIR_CONTINUE;
480d4afb5ceSopenharmony_ci}
481d4afb5ceSopenharmony_ci
482d4afb5ceSopenharmony_cistatic int
483d4afb5ceSopenharmony_cilws_cache_nscookiejar_lookup(struct lws_cache_ttl_lru *_c,
484d4afb5ceSopenharmony_ci			     const char *wildcard_key,
485d4afb5ceSopenharmony_ci			     lws_dll2_owner_t *results_owner)
486d4afb5ceSopenharmony_ci{
487d4afb5ceSopenharmony_ci	lws_cache_nscookiejar_t *cache = (lws_cache_nscookiejar_t *)_c;
488d4afb5ceSopenharmony_ci	struct nsc_lookup_ctx ctx;
489d4afb5ceSopenharmony_ci	int ret, fd;
490d4afb5ceSopenharmony_ci
491d4afb5ceSopenharmony_ci	fd = nsc_backing_open_lock(cache, LWS_O_RDONLY, __func__);
492d4afb5ceSopenharmony_ci	if (fd < 0)
493d4afb5ceSopenharmony_ci		return 1;
494d4afb5ceSopenharmony_ci
495d4afb5ceSopenharmony_ci	ctx.wildcard_key = wildcard_key;
496d4afb5ceSopenharmony_ci	ctx.results_owner = results_owner;
497d4afb5ceSopenharmony_ci	ctx.wklen = strlen(wildcard_key);
498d4afb5ceSopenharmony_ci	ctx.match = 0;
499d4afb5ceSopenharmony_ci
500d4afb5ceSopenharmony_ci	ret = nscookiejar_iterate(cache, fd, nsc_lookup_cb, &ctx);
501d4afb5ceSopenharmony_ci		/*
502d4afb5ceSopenharmony_ci		 * The cb can fail, eg, with OOM, making the whole lookup
503d4afb5ceSopenharmony_ci		 * invalid and returning fail.  Caller will clean
504d4afb5ceSopenharmony_ci		 * results_owner on fail.
505d4afb5ceSopenharmony_ci		 */
506d4afb5ceSopenharmony_ci	nsc_backing_close_unlock(cache, fd);
507d4afb5ceSopenharmony_ci
508d4afb5ceSopenharmony_ci	return ret == NIR_FINISH_ERROR;
509d4afb5ceSopenharmony_ci}
510d4afb5ceSopenharmony_ci
511d4afb5ceSopenharmony_ci/*
512d4afb5ceSopenharmony_ci * It's pretty horrible having to implement add or remove individual items by
513d4afb5ceSopenharmony_ci * file regeneration, but if we don't want to keep it all in heap, and we want
514d4afb5ceSopenharmony_ci * this cookie jar format, that is what we are into.
515d4afb5ceSopenharmony_ci *
516d4afb5ceSopenharmony_ci * Allow to optionally add a "line", optionally wildcard delete tags, and always
517d4afb5ceSopenharmony_ci * delete expired entries.
518d4afb5ceSopenharmony_ci *
519d4afb5ceSopenharmony_ci * Although we can rely on the lws thread to be doing this, multiple processes
520d4afb5ceSopenharmony_ci * may be using the cookie jar and can tread on each other.  So we use flock()
521d4afb5ceSopenharmony_ci * (linux only) to get exclusive access while we are processing this.
522d4afb5ceSopenharmony_ci *
523d4afb5ceSopenharmony_ci * We leave the existing file alone and generate a new one alongside it, with a
524d4afb5ceSopenharmony_ci * fixed name.tmp format so it can't leak, if that went OK then we unlink the
525d4afb5ceSopenharmony_ci * old and rename the new.
526d4afb5ceSopenharmony_ci */
527d4afb5ceSopenharmony_ci
528d4afb5ceSopenharmony_cistruct nsc_regen_ctx {
529d4afb5ceSopenharmony_ci	const char		*wildcard_key_delete;
530d4afb5ceSopenharmony_ci	const void		*add_data;
531d4afb5ceSopenharmony_ci	lws_usec_t		curr;
532d4afb5ceSopenharmony_ci	size_t			add_size;
533d4afb5ceSopenharmony_ci	int			fdt;
534d4afb5ceSopenharmony_ci	char			drop;
535d4afb5ceSopenharmony_ci};
536d4afb5ceSopenharmony_ci
537d4afb5ceSopenharmony_ci/* only used by nsc_regen() */
538d4afb5ceSopenharmony_ci
539d4afb5ceSopenharmony_cistatic int
540d4afb5ceSopenharmony_cinsc_regen_cb(lws_cache_nscookiejar_t *cache, void *opaque, int flags,
541d4afb5ceSopenharmony_ci	      const char *buf, size_t size)
542d4afb5ceSopenharmony_ci{
543d4afb5ceSopenharmony_ci	struct nsc_regen_ctx *ctx = (struct nsc_regen_ctx *)opaque;
544d4afb5ceSopenharmony_ci	char tag[256];
545d4afb5ceSopenharmony_ci	lws_usec_t expiry;
546d4afb5ceSopenharmony_ci
547d4afb5ceSopenharmony_ci	if (flags & LCN_SOL) {
548d4afb5ceSopenharmony_ci
549d4afb5ceSopenharmony_ci		ctx->drop = 0;
550d4afb5ceSopenharmony_ci
551d4afb5ceSopenharmony_ci		if (nsc_line_to_tag(buf, size, tag, sizeof(tag), &expiry))
552d4afb5ceSopenharmony_ci			/* filter it out if it is unparseable */
553d4afb5ceSopenharmony_ci			goto drop;
554d4afb5ceSopenharmony_ci
555d4afb5ceSopenharmony_ci		/* routinely track the earliest expiry */
556d4afb5ceSopenharmony_ci
557d4afb5ceSopenharmony_ci		if (!cache->earliest_expiry ||
558d4afb5ceSopenharmony_ci		    (expiry && cache->earliest_expiry > expiry))
559d4afb5ceSopenharmony_ci			cache->earliest_expiry = expiry;
560d4afb5ceSopenharmony_ci
561d4afb5ceSopenharmony_ci		if (expiry && expiry < ctx->curr)
562d4afb5ceSopenharmony_ci			/* routinely strip anything beyond its expiry */
563d4afb5ceSopenharmony_ci			goto drop;
564d4afb5ceSopenharmony_ci
565d4afb5ceSopenharmony_ci		if (ctx->wildcard_key_delete)
566d4afb5ceSopenharmony_ci			lwsl_cache("%s: %s vs %s\n", __func__,
567d4afb5ceSopenharmony_ci					tag, ctx->wildcard_key_delete);
568d4afb5ceSopenharmony_ci		if (ctx->wildcard_key_delete &&
569d4afb5ceSopenharmony_ci		    !lws_cache_nscookiejar_tag_match(&cache->cache,
570d4afb5ceSopenharmony_ci						     ctx->wildcard_key_delete,
571d4afb5ceSopenharmony_ci						     tag, 0)) {
572d4afb5ceSopenharmony_ci			lwsl_cache("%s: %s matches wc delete %s\n", __func__,
573d4afb5ceSopenharmony_ci					tag, ctx->wildcard_key_delete);
574d4afb5ceSopenharmony_ci			goto drop;
575d4afb5ceSopenharmony_ci		}
576d4afb5ceSopenharmony_ci	}
577d4afb5ceSopenharmony_ci
578d4afb5ceSopenharmony_ci	if (ctx->drop)
579d4afb5ceSopenharmony_ci		return 0;
580d4afb5ceSopenharmony_ci
581d4afb5ceSopenharmony_ci	cache->cache.current_footprint += (uint64_t)size;
582d4afb5ceSopenharmony_ci
583d4afb5ceSopenharmony_ci	if (write(ctx->fdt, buf, /*msvc*/(unsigned int)size) != (ssize_t)size)
584d4afb5ceSopenharmony_ci		return NIR_FINISH_ERROR;
585d4afb5ceSopenharmony_ci
586d4afb5ceSopenharmony_ci	if (flags & LCN_EOL)
587d4afb5ceSopenharmony_ci		if ((size_t)write(ctx->fdt, "\n", 1) != 1)
588d4afb5ceSopenharmony_ci			return NIR_FINISH_ERROR;
589d4afb5ceSopenharmony_ci
590d4afb5ceSopenharmony_ci	return 0;
591d4afb5ceSopenharmony_ci
592d4afb5ceSopenharmony_cidrop:
593d4afb5ceSopenharmony_ci	ctx->drop = 1;
594d4afb5ceSopenharmony_ci
595d4afb5ceSopenharmony_ci	return NIR_CONTINUE;
596d4afb5ceSopenharmony_ci}
597d4afb5ceSopenharmony_ci
598d4afb5ceSopenharmony_cistatic int
599d4afb5ceSopenharmony_cinsc_regen(lws_cache_nscookiejar_t *cache, const char *wc_delete,
600d4afb5ceSopenharmony_ci	  const void *pay, size_t pay_size)
601d4afb5ceSopenharmony_ci{
602d4afb5ceSopenharmony_ci	struct nsc_regen_ctx ctx;
603d4afb5ceSopenharmony_ci	char filepath[128];
604d4afb5ceSopenharmony_ci	int fd, ret = 1;
605d4afb5ceSopenharmony_ci
606d4afb5ceSopenharmony_ci	fd = nsc_backing_open_lock(cache, LWS_O_RDONLY, __func__);
607d4afb5ceSopenharmony_ci	if (fd < 0)
608d4afb5ceSopenharmony_ci		return 1;
609d4afb5ceSopenharmony_ci
610d4afb5ceSopenharmony_ci	lws_snprintf(filepath, sizeof(filepath), "%s.tmp",
611d4afb5ceSopenharmony_ci			cache->cache.info.u.nscookiejar.filepath);
612d4afb5ceSopenharmony_ci	unlink(filepath);
613d4afb5ceSopenharmony_ci
614d4afb5ceSopenharmony_ci	if (lws_fi(&cache->cache.info.cx->fic, "cache_regen_temp_open"))
615d4afb5ceSopenharmony_ci		goto bail;
616d4afb5ceSopenharmony_ci
617d4afb5ceSopenharmony_ci	ctx.fdt = open(filepath, LWS_O_CREAT | LWS_O_WRONLY, 0600);
618d4afb5ceSopenharmony_ci	if (ctx.fdt < 0)
619d4afb5ceSopenharmony_ci		goto bail;
620d4afb5ceSopenharmony_ci
621d4afb5ceSopenharmony_ci	/* magic header */
622d4afb5ceSopenharmony_ci
623d4afb5ceSopenharmony_ci	if (lws_fi(&cache->cache.info.cx->fic, "cache_regen_temp_write") ||
624d4afb5ceSopenharmony_ci	/* other consumers insist to see this at start of cookie jar */
625d4afb5ceSopenharmony_ci	    write(ctx.fdt, "# Netscape HTTP Cookie File\n", 28) != 28)
626d4afb5ceSopenharmony_ci		goto bail1;
627d4afb5ceSopenharmony_ci
628d4afb5ceSopenharmony_ci	/* if we are adding something, put it first */
629d4afb5ceSopenharmony_ci
630d4afb5ceSopenharmony_ci	if (pay &&
631d4afb5ceSopenharmony_ci	    write(ctx.fdt, pay, /*msvc*/(unsigned int)pay_size) !=
632d4afb5ceSopenharmony_ci						    (ssize_t)pay_size)
633d4afb5ceSopenharmony_ci		goto bail1;
634d4afb5ceSopenharmony_ci	if (pay && write(ctx.fdt, "\n", 1u) != (ssize_t)1)
635d4afb5ceSopenharmony_ci		goto bail1;
636d4afb5ceSopenharmony_ci
637d4afb5ceSopenharmony_ci	cache->cache.current_footprint = 0;
638d4afb5ceSopenharmony_ci
639d4afb5ceSopenharmony_ci	ctx.wildcard_key_delete = wc_delete;
640d4afb5ceSopenharmony_ci	ctx.add_data = pay;
641d4afb5ceSopenharmony_ci	ctx.add_size = pay_size;
642d4afb5ceSopenharmony_ci	ctx.curr = lws_now_usecs();
643d4afb5ceSopenharmony_ci	ctx.drop = 0;
644d4afb5ceSopenharmony_ci
645d4afb5ceSopenharmony_ci	cache->earliest_expiry = 0;
646d4afb5ceSopenharmony_ci
647d4afb5ceSopenharmony_ci	if (lws_fi(&cache->cache.info.cx->fic, "cache_regen_iter_fail") ||
648d4afb5ceSopenharmony_ci	    nscookiejar_iterate(cache, fd, nsc_regen_cb, &ctx))
649d4afb5ceSopenharmony_ci		goto bail1;
650d4afb5ceSopenharmony_ci
651d4afb5ceSopenharmony_ci	close(ctx.fdt);
652d4afb5ceSopenharmony_ci	ctx.fdt = -1;
653d4afb5ceSopenharmony_ci
654d4afb5ceSopenharmony_ci	if (unlink(cache->cache.info.u.nscookiejar.filepath) == -1)
655d4afb5ceSopenharmony_ci		lwsl_info("%s: unlink %s failed\n", __func__,
656d4afb5ceSopenharmony_ci			  cache->cache.info.u.nscookiejar.filepath);
657d4afb5ceSopenharmony_ci	if (rename(filepath, cache->cache.info.u.nscookiejar.filepath) == -1)
658d4afb5ceSopenharmony_ci		lwsl_info("%s: rename %s failed\n", __func__,
659d4afb5ceSopenharmony_ci			  cache->cache.info.u.nscookiejar.filepath);
660d4afb5ceSopenharmony_ci
661d4afb5ceSopenharmony_ci	if (cache->earliest_expiry)
662d4afb5ceSopenharmony_ci		lws_cache_schedule(&cache->cache, expiry_cb,
663d4afb5ceSopenharmony_ci				   cache->earliest_expiry);
664d4afb5ceSopenharmony_ci
665d4afb5ceSopenharmony_ci	ret = 0;
666d4afb5ceSopenharmony_ci	goto bail;
667d4afb5ceSopenharmony_ci
668d4afb5ceSopenharmony_cibail1:
669d4afb5ceSopenharmony_ci	if (ctx.fdt >= 0)
670d4afb5ceSopenharmony_ci		close(ctx.fdt);
671d4afb5ceSopenharmony_cibail:
672d4afb5ceSopenharmony_ci	unlink(filepath);
673d4afb5ceSopenharmony_ci
674d4afb5ceSopenharmony_ci	nsc_backing_close_unlock(cache, fd);
675d4afb5ceSopenharmony_ci
676d4afb5ceSopenharmony_ci	return ret;
677d4afb5ceSopenharmony_ci}
678d4afb5ceSopenharmony_ci
679d4afb5ceSopenharmony_cistatic void
680d4afb5ceSopenharmony_ciexpiry_cb(lws_sorted_usec_list_t *sul)
681d4afb5ceSopenharmony_ci{
682d4afb5ceSopenharmony_ci	lws_cache_nscookiejar_t *cache = lws_container_of(sul,
683d4afb5ceSopenharmony_ci					lws_cache_nscookiejar_t, cache.sul);
684d4afb5ceSopenharmony_ci
685d4afb5ceSopenharmony_ci	/*
686d4afb5ceSopenharmony_ci	 * regen the cookie jar without changes, so expired are removed and
687d4afb5ceSopenharmony_ci	 * new earliest expired computed
688d4afb5ceSopenharmony_ci	 */
689d4afb5ceSopenharmony_ci	if (nsc_regen(cache, NULL, NULL, 0))
690d4afb5ceSopenharmony_ci		return;
691d4afb5ceSopenharmony_ci
692d4afb5ceSopenharmony_ci	if (cache->earliest_expiry)
693d4afb5ceSopenharmony_ci		lws_cache_schedule(&cache->cache, expiry_cb,
694d4afb5ceSopenharmony_ci				   cache->earliest_expiry);
695d4afb5ceSopenharmony_ci}
696d4afb5ceSopenharmony_ci
697d4afb5ceSopenharmony_ci
698d4afb5ceSopenharmony_ci/* specific_key and expiry are ignored, since it must be encoded in payload */
699d4afb5ceSopenharmony_ci
700d4afb5ceSopenharmony_cistatic int
701d4afb5ceSopenharmony_cilws_cache_nscookiejar_write(struct lws_cache_ttl_lru *_c,
702d4afb5ceSopenharmony_ci			    const char *specific_key, const uint8_t *source,
703d4afb5ceSopenharmony_ci			    size_t size, lws_usec_t expiry, void **ppvoid)
704d4afb5ceSopenharmony_ci{
705d4afb5ceSopenharmony_ci	lws_cache_nscookiejar_t *cache = (lws_cache_nscookiejar_t *)_c;
706d4afb5ceSopenharmony_ci	char tag[128];
707d4afb5ceSopenharmony_ci
708d4afb5ceSopenharmony_ci	lwsl_cache("%s: %s: len %d\n", __func__, _c->info.name, (int)size);
709d4afb5ceSopenharmony_ci
710d4afb5ceSopenharmony_ci	assert(source);
711d4afb5ceSopenharmony_ci
712d4afb5ceSopenharmony_ci	if (nsc_line_to_tag((const char *)source, size, tag, sizeof(tag), NULL))
713d4afb5ceSopenharmony_ci		return 1;
714d4afb5ceSopenharmony_ci
715d4afb5ceSopenharmony_ci	if (ppvoid)
716d4afb5ceSopenharmony_ci		*ppvoid = NULL;
717d4afb5ceSopenharmony_ci
718d4afb5ceSopenharmony_ci	if (nsc_regen(cache, tag, source, size)) {
719d4afb5ceSopenharmony_ci		lwsl_err("%s: regen failed\n", __func__);
720d4afb5ceSopenharmony_ci
721d4afb5ceSopenharmony_ci		return 1;
722d4afb5ceSopenharmony_ci	}
723d4afb5ceSopenharmony_ci
724d4afb5ceSopenharmony_ci	return 0;
725d4afb5ceSopenharmony_ci}
726d4afb5ceSopenharmony_ci
727d4afb5ceSopenharmony_cistruct nsc_get_ctx {
728d4afb5ceSopenharmony_ci	struct lws_buflist	*buflist;
729d4afb5ceSopenharmony_ci	const char		*specific_key;
730d4afb5ceSopenharmony_ci	const void		**pdata;
731d4afb5ceSopenharmony_ci	size_t			*psize;
732d4afb5ceSopenharmony_ci	lws_cache_ttl_lru_t	*l1;
733d4afb5ceSopenharmony_ci	lws_usec_t		expiry;
734d4afb5ceSopenharmony_ci};
735d4afb5ceSopenharmony_ci
736d4afb5ceSopenharmony_ci/*
737d4afb5ceSopenharmony_ci * We're looking for a specific key, if found, we want to make an entry for it
738d4afb5ceSopenharmony_ci * in L1 and return information about that
739d4afb5ceSopenharmony_ci */
740d4afb5ceSopenharmony_ci
741d4afb5ceSopenharmony_cistatic int
742d4afb5ceSopenharmony_cinsc_get_cb(lws_cache_nscookiejar_t *cache, void *opaque, int flags,
743d4afb5ceSopenharmony_ci	   const char *buf, size_t size)
744d4afb5ceSopenharmony_ci{
745d4afb5ceSopenharmony_ci	struct nsc_get_ctx *ctx = (struct nsc_get_ctx *)opaque;
746d4afb5ceSopenharmony_ci	char tag[200];
747d4afb5ceSopenharmony_ci	uint8_t *q;
748d4afb5ceSopenharmony_ci
749d4afb5ceSopenharmony_ci	if (ctx->buflist)
750d4afb5ceSopenharmony_ci		goto collect;
751d4afb5ceSopenharmony_ci
752d4afb5ceSopenharmony_ci	if (!(flags & LCN_SOL))
753d4afb5ceSopenharmony_ci		return NIR_CONTINUE;
754d4afb5ceSopenharmony_ci
755d4afb5ceSopenharmony_ci	if (nsc_line_to_tag(buf, size, tag, sizeof(tag), &ctx->expiry)) {
756d4afb5ceSopenharmony_ci		lwsl_err("%s: can't get tag\n", __func__);
757d4afb5ceSopenharmony_ci		return NIR_CONTINUE;
758d4afb5ceSopenharmony_ci	}
759d4afb5ceSopenharmony_ci
760d4afb5ceSopenharmony_ci	lwsl_cache("%s: %s %s\n", __func__, ctx->specific_key, tag);
761d4afb5ceSopenharmony_ci
762d4afb5ceSopenharmony_ci	if (strcmp(ctx->specific_key, tag)) {
763d4afb5ceSopenharmony_ci		lwsl_cache("%s: no match\n", __func__);
764d4afb5ceSopenharmony_ci		return NIR_CONTINUE;
765d4afb5ceSopenharmony_ci	}
766d4afb5ceSopenharmony_ci
767d4afb5ceSopenharmony_ci	/* it's a match */
768d4afb5ceSopenharmony_ci
769d4afb5ceSopenharmony_ci	lwsl_cache("%s: IS match\n", __func__);
770d4afb5ceSopenharmony_ci
771d4afb5ceSopenharmony_ci	if (!(flags & LCN_EOL))
772d4afb5ceSopenharmony_ci		goto collect;
773d4afb5ceSopenharmony_ci
774d4afb5ceSopenharmony_ci	/* it all fit in the buffer, let's create it in L1 now */
775d4afb5ceSopenharmony_ci
776d4afb5ceSopenharmony_ci	*ctx->psize = size;
777d4afb5ceSopenharmony_ci	if (ctx->l1->info.ops->write(ctx->l1,
778d4afb5ceSopenharmony_ci				     ctx->specific_key, (const uint8_t *)buf,
779d4afb5ceSopenharmony_ci				     size, ctx->expiry, (void **)ctx->pdata))
780d4afb5ceSopenharmony_ci		return NIR_FINISH_ERROR;
781d4afb5ceSopenharmony_ci
782d4afb5ceSopenharmony_ci	return NIR_FINISH_OK;
783d4afb5ceSopenharmony_ci
784d4afb5ceSopenharmony_cicollect:
785d4afb5ceSopenharmony_ci	/*
786d4afb5ceSopenharmony_ci	 * it's bigger than one buffer-load, we have to stash what we're getting
787d4afb5ceSopenharmony_ci	 * on a buflist and create it when we have it all
788d4afb5ceSopenharmony_ci	 */
789d4afb5ceSopenharmony_ci
790d4afb5ceSopenharmony_ci	if (lws_buflist_append_segment(&ctx->buflist, (const uint8_t *)buf,
791d4afb5ceSopenharmony_ci				       size))
792d4afb5ceSopenharmony_ci		goto cleanup;
793d4afb5ceSopenharmony_ci
794d4afb5ceSopenharmony_ci	if (!(flags & LCN_EOL))
795d4afb5ceSopenharmony_ci		return NIR_CONTINUE;
796d4afb5ceSopenharmony_ci
797d4afb5ceSopenharmony_ci	/* we have all the payload, create the L1 entry without payload yet */
798d4afb5ceSopenharmony_ci
799d4afb5ceSopenharmony_ci	*ctx->psize = size;
800d4afb5ceSopenharmony_ci	if (ctx->l1->info.ops->write(ctx->l1, ctx->specific_key, NULL,
801d4afb5ceSopenharmony_ci				     lws_buflist_total_len(&ctx->buflist),
802d4afb5ceSopenharmony_ci				     ctx->expiry, (void **)&q))
803d4afb5ceSopenharmony_ci		goto cleanup;
804d4afb5ceSopenharmony_ci	*ctx->pdata = q;
805d4afb5ceSopenharmony_ci
806d4afb5ceSopenharmony_ci	/* dump the buflist into the L1 cache entry */
807d4afb5ceSopenharmony_ci
808d4afb5ceSopenharmony_ci	do {
809d4afb5ceSopenharmony_ci		uint8_t *p;
810d4afb5ceSopenharmony_ci		size_t len = lws_buflist_next_segment_len(&ctx->buflist, &p);
811d4afb5ceSopenharmony_ci
812d4afb5ceSopenharmony_ci		memcpy(q, p, len);
813d4afb5ceSopenharmony_ci		q += len;
814d4afb5ceSopenharmony_ci
815d4afb5ceSopenharmony_ci		lws_buflist_use_segment(&ctx->buflist, len);
816d4afb5ceSopenharmony_ci	} while (ctx->buflist);
817d4afb5ceSopenharmony_ci
818d4afb5ceSopenharmony_ci	return NIR_FINISH_OK;
819d4afb5ceSopenharmony_ci
820d4afb5ceSopenharmony_cicleanup:
821d4afb5ceSopenharmony_ci	lws_buflist_destroy_all_segments(&ctx->buflist);
822d4afb5ceSopenharmony_ci
823d4afb5ceSopenharmony_ci	return NIR_FINISH_ERROR;
824d4afb5ceSopenharmony_ci}
825d4afb5ceSopenharmony_ci
826d4afb5ceSopenharmony_cistatic int
827d4afb5ceSopenharmony_cilws_cache_nscookiejar_get(struct lws_cache_ttl_lru *_c,
828d4afb5ceSopenharmony_ci			  const char *specific_key, const void **pdata,
829d4afb5ceSopenharmony_ci			  size_t *psize)
830d4afb5ceSopenharmony_ci{
831d4afb5ceSopenharmony_ci	lws_cache_nscookiejar_t *cache = (lws_cache_nscookiejar_t *)_c;
832d4afb5ceSopenharmony_ci	struct nsc_get_ctx ctx;
833d4afb5ceSopenharmony_ci	int ret, fd;
834d4afb5ceSopenharmony_ci
835d4afb5ceSopenharmony_ci	fd = nsc_backing_open_lock(cache, LWS_O_RDONLY, __func__);
836d4afb5ceSopenharmony_ci	if (fd < 0)
837d4afb5ceSopenharmony_ci		return 1;
838d4afb5ceSopenharmony_ci
839d4afb5ceSopenharmony_ci	/* get a pointer to l1 */
840d4afb5ceSopenharmony_ci	ctx.l1 = &cache->cache;
841d4afb5ceSopenharmony_ci	while (ctx.l1->child)
842d4afb5ceSopenharmony_ci		ctx.l1 = ctx.l1->child;
843d4afb5ceSopenharmony_ci
844d4afb5ceSopenharmony_ci	ctx.pdata = pdata;
845d4afb5ceSopenharmony_ci	ctx.psize = psize;
846d4afb5ceSopenharmony_ci	ctx.specific_key = specific_key;
847d4afb5ceSopenharmony_ci	ctx.buflist = NULL;
848d4afb5ceSopenharmony_ci	ctx.expiry = 0;
849d4afb5ceSopenharmony_ci
850d4afb5ceSopenharmony_ci	ret = nscookiejar_iterate(cache, fd, nsc_get_cb, &ctx);
851d4afb5ceSopenharmony_ci
852d4afb5ceSopenharmony_ci	nsc_backing_close_unlock(cache, fd);
853d4afb5ceSopenharmony_ci
854d4afb5ceSopenharmony_ci	return ret != NIR_FINISH_OK;
855d4afb5ceSopenharmony_ci}
856d4afb5ceSopenharmony_ci
857d4afb5ceSopenharmony_cistatic int
858d4afb5ceSopenharmony_cilws_cache_nscookiejar_invalidate(struct lws_cache_ttl_lru *_c,
859d4afb5ceSopenharmony_ci				 const char *wc_key)
860d4afb5ceSopenharmony_ci{
861d4afb5ceSopenharmony_ci	lws_cache_nscookiejar_t *cache = (lws_cache_nscookiejar_t *)_c;
862d4afb5ceSopenharmony_ci
863d4afb5ceSopenharmony_ci	return nsc_regen(cache, wc_key, NULL, 0);
864d4afb5ceSopenharmony_ci}
865d4afb5ceSopenharmony_ci
866d4afb5ceSopenharmony_cistatic struct lws_cache_ttl_lru *
867d4afb5ceSopenharmony_cilws_cache_nscookiejar_create(const struct lws_cache_creation_info *info)
868d4afb5ceSopenharmony_ci{
869d4afb5ceSopenharmony_ci	lws_cache_nscookiejar_t *cache;
870d4afb5ceSopenharmony_ci
871d4afb5ceSopenharmony_ci	cache = lws_fi(&info->cx->fic, "cache_createfail") ? NULL :
872d4afb5ceSopenharmony_ci					lws_zalloc(sizeof(*cache), __func__);
873d4afb5ceSopenharmony_ci	if (!cache)
874d4afb5ceSopenharmony_ci		return NULL;
875d4afb5ceSopenharmony_ci
876d4afb5ceSopenharmony_ci	cache->cache.info = *info;
877d4afb5ceSopenharmony_ci
878d4afb5ceSopenharmony_ci	/*
879d4afb5ceSopenharmony_ci	 * We need to scan the file, if it exists, and find the earliest
880d4afb5ceSopenharmony_ci	 * expiry while cleaning out any expired entries
881d4afb5ceSopenharmony_ci	 */
882d4afb5ceSopenharmony_ci	expiry_cb(&cache->cache.sul);
883d4afb5ceSopenharmony_ci
884d4afb5ceSopenharmony_ci	lwsl_notice("%s: create %s\n", __func__, info->name ? info->name : "?");
885d4afb5ceSopenharmony_ci
886d4afb5ceSopenharmony_ci	return (struct lws_cache_ttl_lru *)cache;
887d4afb5ceSopenharmony_ci}
888d4afb5ceSopenharmony_ci
889d4afb5ceSopenharmony_cistatic int
890d4afb5ceSopenharmony_cilws_cache_nscookiejar_expunge(struct lws_cache_ttl_lru *_c)
891d4afb5ceSopenharmony_ci{
892d4afb5ceSopenharmony_ci	lws_cache_nscookiejar_t *cache = (lws_cache_nscookiejar_t *)_c;
893d4afb5ceSopenharmony_ci	int r;
894d4afb5ceSopenharmony_ci
895d4afb5ceSopenharmony_ci	if (!cache)
896d4afb5ceSopenharmony_ci		return 0;
897d4afb5ceSopenharmony_ci
898d4afb5ceSopenharmony_ci	r = unlink(cache->cache.info.u.nscookiejar.filepath);
899d4afb5ceSopenharmony_ci	if (r)
900d4afb5ceSopenharmony_ci		lwsl_warn("%s: failed to unlink %s\n", __func__,
901d4afb5ceSopenharmony_ci				cache->cache.info.u.nscookiejar.filepath);
902d4afb5ceSopenharmony_ci
903d4afb5ceSopenharmony_ci	return r;
904d4afb5ceSopenharmony_ci}
905d4afb5ceSopenharmony_ci
906d4afb5ceSopenharmony_cistatic void
907d4afb5ceSopenharmony_cilws_cache_nscookiejar_destroy(struct lws_cache_ttl_lru **_pc)
908d4afb5ceSopenharmony_ci{
909d4afb5ceSopenharmony_ci	lws_cache_nscookiejar_t *cache = (lws_cache_nscookiejar_t *)*_pc;
910d4afb5ceSopenharmony_ci
911d4afb5ceSopenharmony_ci	if (!cache)
912d4afb5ceSopenharmony_ci		return;
913d4afb5ceSopenharmony_ci
914d4afb5ceSopenharmony_ci	lws_sul_cancel(&cache->cache.sul);
915d4afb5ceSopenharmony_ci
916d4afb5ceSopenharmony_ci	lws_free_set_NULL(*_pc);
917d4afb5ceSopenharmony_ci}
918d4afb5ceSopenharmony_ci
919d4afb5ceSopenharmony_ci#if defined(_DEBUG)
920d4afb5ceSopenharmony_ci
921d4afb5ceSopenharmony_cistatic int
922d4afb5ceSopenharmony_cinsc_dump_cb(lws_cache_nscookiejar_t *cache, void *opaque, int flags,
923d4afb5ceSopenharmony_ci	      const char *buf, size_t size)
924d4afb5ceSopenharmony_ci{
925d4afb5ceSopenharmony_ci	lwsl_hexdump_cache(buf, size);
926d4afb5ceSopenharmony_ci
927d4afb5ceSopenharmony_ci	return 0;
928d4afb5ceSopenharmony_ci}
929d4afb5ceSopenharmony_ci
930d4afb5ceSopenharmony_cistatic void
931d4afb5ceSopenharmony_cilws_cache_nscookiejar_debug_dump(struct lws_cache_ttl_lru *_c)
932d4afb5ceSopenharmony_ci{
933d4afb5ceSopenharmony_ci	lws_cache_nscookiejar_t *cache = (lws_cache_nscookiejar_t *)_c;
934d4afb5ceSopenharmony_ci	int fd = nsc_backing_open_lock(cache, LWS_O_RDONLY, __func__);
935d4afb5ceSopenharmony_ci
936d4afb5ceSopenharmony_ci	if (fd < 0)
937d4afb5ceSopenharmony_ci		return;
938d4afb5ceSopenharmony_ci
939d4afb5ceSopenharmony_ci	lwsl_cache("%s: %s\n", __func__, _c->info.name);
940d4afb5ceSopenharmony_ci
941d4afb5ceSopenharmony_ci	nscookiejar_iterate(cache, fd, nsc_dump_cb, NULL);
942d4afb5ceSopenharmony_ci
943d4afb5ceSopenharmony_ci	nsc_backing_close_unlock(cache, fd);
944d4afb5ceSopenharmony_ci}
945d4afb5ceSopenharmony_ci#endif
946d4afb5ceSopenharmony_ci
947d4afb5ceSopenharmony_ciconst struct lws_cache_ops lws_cache_ops_nscookiejar = {
948d4afb5ceSopenharmony_ci	.create			= lws_cache_nscookiejar_create,
949d4afb5ceSopenharmony_ci	.destroy		= lws_cache_nscookiejar_destroy,
950d4afb5ceSopenharmony_ci	.expunge		= lws_cache_nscookiejar_expunge,
951d4afb5ceSopenharmony_ci
952d4afb5ceSopenharmony_ci	.write			= lws_cache_nscookiejar_write,
953d4afb5ceSopenharmony_ci	.tag_match		= lws_cache_nscookiejar_tag_match,
954d4afb5ceSopenharmony_ci	.lookup			= lws_cache_nscookiejar_lookup,
955d4afb5ceSopenharmony_ci	.invalidate		= lws_cache_nscookiejar_invalidate,
956d4afb5ceSopenharmony_ci	.get			= lws_cache_nscookiejar_get,
957d4afb5ceSopenharmony_ci#if defined(_DEBUG)
958d4afb5ceSopenharmony_ci	.debug_dump		= lws_cache_nscookiejar_debug_dump,
959d4afb5ceSopenharmony_ci#endif
960d4afb5ceSopenharmony_ci};
961