Fossil SCM

fossil-scm / src / cookies.c
Blame History Raw 312 lines
1
/*
2
** Copyright (c) 2017 D. Richard Hipp
3
**
4
** This program is free software; you can redistribute it and/or
5
** modify it under the terms of the Simplified BSD License (also
6
** known as the "2-Clause License" or "FreeBSD License".)
7
**
8
** This program is distributed in the hope that it will be useful,
9
** but without any warranty; without even the implied warranty of
10
** merchantability or fitness for a particular purpose.
11
**
12
** Author contact information:
13
** [email protected]
14
** http://www.hwaci.com/drh/
15
**
16
*******************************************************************************
17
**
18
** This file contains code used to manage a cookie that stores user-specific
19
** display preferences for the web interface.
20
**
21
** cookie_parse(void);
22
**
23
** Read and parse the display preferences cookie.
24
**
25
** cookie_read_parameter(zQP, zPName);
26
**
27
** If query parameter zQP does not exist but zPName does exist in
28
** the parsed cookie, then initialize zQP to hold the same value
29
** as the zPName element in the parsed cookie.
30
**
31
** cookie_write_parameter(zQP, zPName, zDefault);
32
**
33
** If query parameter zQP exists and if it has a different value from
34
** the zPName parameter in the parsed cookie, then replace the value of
35
** zPName with the value of zQP. If zQP exists but zPName does not
36
** exist, then zPName is created. If zQP does not exist or if it has
37
** the same value as zPName, then this routine is a no-op.
38
**
39
** cookie_link_parameter(zQP, zPName, zDefault);
40
**
41
** This does both cookie_read_parameter() and cookie_write_parameter()
42
** all at once.
43
**
44
** cookie_render();
45
**
46
** If any prior calls to cookie_write_parameter() have changed the
47
** value of the user preferences cookie, this routine will cause the
48
** new cookie value to be included in the HTTP header for the current
49
** web page. This routine is a destructor for this module and should
50
** be called once.
51
**
52
** char *cookie_value(zPName, zDefault);
53
**
54
** Look up the value of a cookie parameter zPName. Return zDefault if
55
** there is no display preferences cookie or if zPName does not exist.
56
*/
57
#include "config.h"
58
#include "cookies.h"
59
#include <assert.h>
60
#include <string.h>
61
62
#if INTERFACE
63
/* the standard name of the display settings cookie for fossil */
64
# define DISPLAY_SETTINGS_COOKIE "fossil_display_settings"
65
#endif
66
67
68
/*
69
** State information private to this module
70
*/
71
#define COOKIE_NPARAM 10
72
static struct {
73
char *zCookieValue; /* Value of the user preferences cookie */
74
int bChanged; /* True if any value has changed */
75
int bIsInit; /* True after initialization */
76
int nParam; /* Number of parameters in the cookie */
77
struct {
78
const char *zPName; /* Name of a parameter */
79
char *zPValue; /* Value of that parameter */
80
} aParam[COOKIE_NPARAM];
81
} cookies;
82
83
/* Initialize this module by parsing the content of the cookie named
84
** by DISPLAY_SETTINGS_COOKIE
85
*/
86
void cookie_parse(void){
87
char *z;
88
if( cookies.bIsInit ) return;
89
z = (char*)P(DISPLAY_SETTINGS_COOKIE);
90
if( z==0 ) z = "";
91
cookies.zCookieValue = z = fossil_strdup(z);
92
cookies.bIsInit = 1;
93
while( cookies.nParam<COOKIE_NPARAM ){
94
while( fossil_isspace(z[0]) ) z++;
95
if( z[0]==0 ) break;
96
cookies.aParam[cookies.nParam].zPName = z;
97
while( *z && *z!='=' && *z!=',' ){ z++; }
98
if( *z=='=' ){
99
*z = 0;
100
z++;
101
cookies.aParam[cookies.nParam].zPValue = z;
102
while( *z && *z!=',' ){ z++; }
103
if( *z ){
104
*z = 0;
105
z++;
106
}
107
dehttpize(cookies.aParam[cookies.nParam].zPValue);
108
}else{
109
if( *z ){ *z++ = 0; }
110
cookies.aParam[cookies.nParam].zPValue = "";
111
}
112
cookies.nParam++;
113
}
114
}
115
116
#define COOKIE_READ 1
117
#define COOKIE_WRITE 2
118
static void cookie_readwrite(
119
const char *zQP, /* Name of the query parameter */
120
const char *zPName, /* Name of the cooking setting */
121
const char *zDflt, /* Default value for the query parameter */
122
int flags /* READ or WRITE or both */
123
){
124
const char *zQVal = P(zQP);
125
int i;
126
cookie_parse();
127
for(i=0; i<cookies.nParam && strcmp(zPName,cookies.aParam[i].zPName); i++){}
128
if( zQVal==0 && (flags & COOKIE_READ)!=0 && i<cookies.nParam ){
129
cgi_set_parameter_nocopy(zQP, cookies.aParam[i].zPValue, 1);
130
return;
131
}
132
if( zQVal==0 ){
133
zQVal = zDflt;
134
if( flags & COOKIE_WRITE ) cgi_set_parameter_nocopy(zQP, zQVal, 1);
135
}
136
if( (flags & COOKIE_WRITE)!=0
137
&& i<COOKIE_NPARAM
138
&& (i==cookies.nParam || strcmp(zQVal, cookies.aParam[i].zPValue))
139
){
140
if( i==cookies.nParam ){
141
cookies.aParam[i].zPName = zPName;
142
cookies.nParam++;
143
}
144
cookies.aParam[i].zPValue = (char*)zQVal;
145
cookies.bChanged = 1;
146
}
147
}
148
149
/* If query parameter zQP is missing, initialize it using the zPName
150
** value from the user preferences cookie
151
*/
152
void cookie_read_parameter(const char *zQP, const char *zPName){
153
cookie_readwrite(zQP, zPName, 0, COOKIE_READ);
154
}
155
156
/* Update the zPName value of the user preference cookie to match
157
** the value of query parameter zQP.
158
*/
159
void cookie_write_parameter(
160
const char *zQP,
161
const char *zPName,
162
const char *zDflt
163
){
164
cookie_readwrite(zQP, zPName, zDflt, COOKIE_WRITE);
165
}
166
167
/* Use the zPName user preference value as a default for zQP and record
168
** any changes to the zQP value back into the cookie.
169
*/
170
void cookie_link_parameter(
171
const char *zQP, /* The query parameter */
172
const char *zPName, /* The name of the cookie value */
173
const char *zDflt /* Default value for the parameter */
174
){
175
cookie_readwrite(zQP, zPName, zDflt, COOKIE_READ|COOKIE_WRITE);
176
}
177
178
/* Update the user preferences cookie, if necessary, and shut down
179
** this module. The cookie is only emitted if its value has actually
180
** changed since the request started and the "udc" (Update Display
181
** Cookie) URL argument was provided.
182
**
183
** Historical note: from 2021-03-02 [71a2d68a7a113e7c] until
184
** 2023-01-16, the udc was not observed (it had been prior to that),
185
** and that led to the unfortunate side effect that a timeline link
186
** from the /reports page would end up persistently setting a user's
187
** timeline length preference to the number of items in that
188
** report. In a /chat discussion it was agreed that updating the
189
** cookie requires explicit opt-in via the udc argument or ?skin=...,
190
** which implies udc.
191
*/
192
void cookie_render(void){
193
if( cookies.bChanged && P("udc")!=0 ){
194
Blob new;
195
int i;
196
blob_init(&new, 0, 0);
197
for(i=0;i<cookies.nParam;i++){
198
if( i>0 ) blob_append(&new, ",", 1);
199
blob_appendf(&new, "%s=%T",
200
cookies.aParam[i].zPName, cookies.aParam[i].zPValue);
201
}
202
cgi_set_cookie(DISPLAY_SETTINGS_COOKIE, blob_str(&new), 0, 31536000);
203
}
204
cookies.bIsInit = 0;
205
}
206
207
/* Return the value of a preference cookie.
208
*/
209
const char *cookie_value(const char *zPName, const char *zDefault){
210
int i;
211
assert( zPName!=0 );
212
cookie_parse();
213
for(i=0; i<cookies.nParam && strcmp(zPName,cookies.aParam[i].zPName); i++){}
214
return i<cookies.nParam ? cookies.aParam[i].zPValue : zDefault;
215
}
216
217
/* Return the number of characters of hex in the prefix to the
218
** given string.
219
*/
220
static int hex_prefix_length(const char *z){
221
int i;
222
for(i=0; fossil_isXdigit(z[i]); i++){}
223
return i;
224
}
225
226
/*
227
** WEBPAGE: cookies
228
**
229
** Show all cookies associated with Fossil. This shows the text of the
230
** login cookie and is hence dangerous if an adversary is looking over
231
** your shoulder and is able to read and reproduce that cookie.
232
**
233
** WEBPAGE: fdscookie
234
**
235
** Show the current display settings contained in the
236
** "fossil_display_settings" cookie.
237
*/
238
void cookie_page(void){
239
int i;
240
int nCookie = 0;
241
const char *zName = 0;
242
const char *zValue = 0;
243
const char *zLoginCookie = login_cookie_name();
244
int isQP = 0;
245
int bFDSonly = strstr(g.zPath, "fdscookie")!=0;
246
cookie_parse();
247
if( bFDSonly ){
248
style_header("Display Preferences Cookie");
249
}else{
250
style_header("All Cookies");
251
}
252
@ <form method="POST">
253
@ <ol>
254
for(i=0; cgi_param_info(i, &zName, &zValue, &isQP); i++){
255
char *zDel;
256
if( isQP ) continue;
257
if( fossil_isupper(zName[0]) ) continue;
258
if( bFDSonly && strcmp(zName, "fossil_display_settings")!=0 ) continue;
259
zDel = mprintf("del%s",zName);
260
if( P(zDel)!=0 ){
261
const char *zPath = fossil_strcmp(ROBOT_COOKIE,zName)==0
262
? "/" : 0;
263
cgi_set_cookie(zName, "", zPath, -1);
264
cgi_redirect(g.zPath);
265
}
266
nCookie++;
267
@ <li><p><b>%h(zName)</b>: %h(zValue)
268
@ <input type="submit" name="%h(zDel)" value="Delete">
269
if( fossil_strcmp(zName, DISPLAY_SETTINGS_COOKIE)==0 && cookies.nParam>0 ){
270
int j;
271
@ <p>This cookie remembers your Fossil display preferences.
272
@ <ul>
273
for(j=0; j<cookies.nParam; j++){
274
@ <li>%h(cookies.aParam[j].zPName): "%h(cookies.aParam[j].zPValue)"
275
}
276
@ </ul>
277
}else
278
if( fossil_strcmp(zName, zLoginCookie)==0 ){
279
@ <p>This is your login cookie. If you delete this cookie, you will
280
@ be logged out.
281
}else
282
if( fossil_strncmp(zName, "fossil-", 7)==0
283
&& strlen(zName)==23
284
&& hex_prefix_length(&zName[7])==16
285
&& hex_prefix_length(zValue)>24
286
){
287
@ <p>This appears to be a login cookie for another Fossil repository
288
@ in the same website.
289
}else
290
if( fossil_strcmp(zName, ROBOT_COOKIE)==0 ){
291
@ <p>This cookie shows that your web-browser has been tested is
292
@ believed to be operated by a human, not a robot.
293
}
294
else {
295
@ <p>This cookie was not generated by Fossil. It might be something
296
@ from another program on the same website.
297
}
298
fossil_free(zDel);
299
}
300
@ </ol>
301
@ </form>
302
if( nCookie==0 ){
303
if( bFDSonly ){
304
@ <p><i>Your browser is not holding a "fossil_display_setting" cookie
305
@ for this website</i></p>
306
}else{
307
@ <p><i>Your browser is not holding any cookies for this website</i></p>
308
}
309
}
310
style_finish_page();
311
}
312

Keyboard Shortcuts

Open search /
Next entry (timeline) j
Previous entry (timeline) k
Open focused entry Enter
Show this help ?
Toggle theme Top nav button