Fossil SCM

Improvements to privilege processing and the "Security Audit" page /secaudit0.

drh 2018-08-17 12:32 trunk
Commit 397d23c161aa500223a84969427b4f1447aef631873ba4e3c6eeb61db4523d15
+13 -12
--- src/capabilities.c
+++ src/capabilities.c
@@ -100,36 +100,37 @@
100100
void capability_expand(CapabilityString *pIn){
101101
static char *zNobody = 0;
102102
static char *zAnon = 0;
103103
static char *zReader = 0;
104104
static char *zDev = 0;
105
+ int doneV = 0;
105106
106107
if( pIn==0 ){
107108
fossil_free(zNobody); zNobody = 0;
108109
fossil_free(zAnon); zAnon = 0;
109110
fossil_free(zReader); zReader = 0;
110111
fossil_free(zDev); zDev = 0;
111112
return;
112113
}
113
- if( pIn->x['v'] ){
114
- if( zDev==0 ){
115
- zDev = db_text(0, "SELECT cap FROM user WHERE login='developer'");
116
- }
117
- pIn = capability_add(pIn, zDev);
118
- }
119
- if( pIn->x['u'] ){
120
- if( zReader==0 ){
121
- zReader = db_text(0, "SELECT cap FROM user WHERE login='reader'");
122
- }
123
- pIn = capability_add(pIn, zReader);
124
- }
125114
if( zNobody==0 ){
126115
zNobody = db_text(0, "SELECT cap FROM user WHERE login='nobody'");
127116
zAnon = db_text(0, "SELECT cap FROM user WHERE login='anonymous'");
117
+ zReader = db_text(0, "SELECT cap FROM user WHERE login='reader'");
118
+ zDev = db_text(0, "SELECT cap FROM user WHERE login='developer'");
128119
}
129120
pIn = capability_add(pIn, zAnon);
130121
pIn = capability_add(pIn, zNobody);
122
+ if( pIn->x['v'] ){
123
+ pIn = capability_add(pIn, zDev);
124
+ doneV = 1;
125
+ }
126
+ if( pIn->x['u'] ){
127
+ pIn = capability_add(pIn, zReader);
128
+ if( pIn->x['v'] && !doneV ){
129
+ pIn = capability_add(pIn, zDev);
130
+ }
131
+ }
131132
}
132133
133134
/*
134135
** Render a capability string in canonical string format. Space to hold
135136
** the returned string is obtained from fossil_malloc() can should be freed
136137
--- src/capabilities.c
+++ src/capabilities.c
@@ -100,36 +100,37 @@
100 void capability_expand(CapabilityString *pIn){
101 static char *zNobody = 0;
102 static char *zAnon = 0;
103 static char *zReader = 0;
104 static char *zDev = 0;
 
105
106 if( pIn==0 ){
107 fossil_free(zNobody); zNobody = 0;
108 fossil_free(zAnon); zAnon = 0;
109 fossil_free(zReader); zReader = 0;
110 fossil_free(zDev); zDev = 0;
111 return;
112 }
113 if( pIn->x['v'] ){
114 if( zDev==0 ){
115 zDev = db_text(0, "SELECT cap FROM user WHERE login='developer'");
116 }
117 pIn = capability_add(pIn, zDev);
118 }
119 if( pIn->x['u'] ){
120 if( zReader==0 ){
121 zReader = db_text(0, "SELECT cap FROM user WHERE login='reader'");
122 }
123 pIn = capability_add(pIn, zReader);
124 }
125 if( zNobody==0 ){
126 zNobody = db_text(0, "SELECT cap FROM user WHERE login='nobody'");
127 zAnon = db_text(0, "SELECT cap FROM user WHERE login='anonymous'");
 
 
128 }
129 pIn = capability_add(pIn, zAnon);
130 pIn = capability_add(pIn, zNobody);
 
 
 
 
 
 
 
 
 
 
131 }
132
133 /*
134 ** Render a capability string in canonical string format. Space to hold
135 ** the returned string is obtained from fossil_malloc() can should be freed
136
--- src/capabilities.c
+++ src/capabilities.c
@@ -100,36 +100,37 @@
100 void capability_expand(CapabilityString *pIn){
101 static char *zNobody = 0;
102 static char *zAnon = 0;
103 static char *zReader = 0;
104 static char *zDev = 0;
105 int doneV = 0;
106
107 if( pIn==0 ){
108 fossil_free(zNobody); zNobody = 0;
109 fossil_free(zAnon); zAnon = 0;
110 fossil_free(zReader); zReader = 0;
111 fossil_free(zDev); zDev = 0;
112 return;
113 }
 
 
 
 
 
 
 
 
 
 
 
 
114 if( zNobody==0 ){
115 zNobody = db_text(0, "SELECT cap FROM user WHERE login='nobody'");
116 zAnon = db_text(0, "SELECT cap FROM user WHERE login='anonymous'");
117 zReader = db_text(0, "SELECT cap FROM user WHERE login='reader'");
118 zDev = db_text(0, "SELECT cap FROM user WHERE login='developer'");
119 }
120 pIn = capability_add(pIn, zAnon);
121 pIn = capability_add(pIn, zNobody);
122 if( pIn->x['v'] ){
123 pIn = capability_add(pIn, zDev);
124 doneV = 1;
125 }
126 if( pIn->x['u'] ){
127 pIn = capability_add(pIn, zReader);
128 if( pIn->x['v'] && !doneV ){
129 pIn = capability_add(pIn, zDev);
130 }
131 }
132 }
133
134 /*
135 ** Render a capability string in canonical string format. Space to hold
136 ** the returned string is obtained from fossil_malloc() can should be freed
137
+8 -6
--- src/login.c
+++ src/login.c
@@ -1302,28 +1302,30 @@
13021302
13031303
case '7': p->EmailAlert = 1; break;
13041304
case 'A': p->Announce = 1; break;
13051305
case 'D': p->Debug = 1; break;
13061306
1307
- /* The "u" privileges is a little different. It recursively
1307
+ /* The "u" privilege recursively
13081308
** inherits all privileges of the user named "reader" */
13091309
case 'u': {
1310
- if( (flags & LOGIN_IGNORE_UV)==0 ){
1310
+ if( p->XReader==0 ){
13111311
const char *zUser;
1312
+ p->XReader = 1;
13121313
zUser = db_text("", "SELECT cap FROM user WHERE login='reader'");
1313
- login_set_capabilities(zUser, flags | LOGIN_IGNORE_UV);
1314
+ login_set_capabilities(zUser, flags);
13141315
}
13151316
break;
13161317
}
13171318
1318
- /* The "v" privileges is a little different. It recursively
1319
+ /* The "v" privilege recursively
13191320
** inherits all privileges of the user named "developer" */
13201321
case 'v': {
1321
- if( (flags & LOGIN_IGNORE_UV)==0 ){
1322
+ if( p->XDeveloper==0 ){
13221323
const char *zDev;
1324
+ p->XDeveloper = 1;
13231325
zDev = db_text("", "SELECT cap FROM user WHERE login='developer'");
1324
- login_set_capabilities(zDev, flags | LOGIN_IGNORE_UV);
1326
+ login_set_capabilities(zDev, flags);
13251327
}
13261328
break;
13271329
}
13281330
}
13291331
}
13301332
--- src/login.c
+++ src/login.c
@@ -1302,28 +1302,30 @@
1302
1303 case '7': p->EmailAlert = 1; break;
1304 case 'A': p->Announce = 1; break;
1305 case 'D': p->Debug = 1; break;
1306
1307 /* The "u" privileges is a little different. It recursively
1308 ** inherits all privileges of the user named "reader" */
1309 case 'u': {
1310 if( (flags & LOGIN_IGNORE_UV)==0 ){
1311 const char *zUser;
 
1312 zUser = db_text("", "SELECT cap FROM user WHERE login='reader'");
1313 login_set_capabilities(zUser, flags | LOGIN_IGNORE_UV);
1314 }
1315 break;
1316 }
1317
1318 /* The "v" privileges is a little different. It recursively
1319 ** inherits all privileges of the user named "developer" */
1320 case 'v': {
1321 if( (flags & LOGIN_IGNORE_UV)==0 ){
1322 const char *zDev;
 
1323 zDev = db_text("", "SELECT cap FROM user WHERE login='developer'");
1324 login_set_capabilities(zDev, flags | LOGIN_IGNORE_UV);
1325 }
1326 break;
1327 }
1328 }
1329 }
1330
--- src/login.c
+++ src/login.c
@@ -1302,28 +1302,30 @@
1302
1303 case '7': p->EmailAlert = 1; break;
1304 case 'A': p->Announce = 1; break;
1305 case 'D': p->Debug = 1; break;
1306
1307 /* The "u" privilege recursively
1308 ** inherits all privileges of the user named "reader" */
1309 case 'u': {
1310 if( p->XReader==0 ){
1311 const char *zUser;
1312 p->XReader = 1;
1313 zUser = db_text("", "SELECT cap FROM user WHERE login='reader'");
1314 login_set_capabilities(zUser, flags);
1315 }
1316 break;
1317 }
1318
1319 /* The "v" privilege recursively
1320 ** inherits all privileges of the user named "developer" */
1321 case 'v': {
1322 if( p->XDeveloper==0 ){
1323 const char *zDev;
1324 p->XDeveloper = 1;
1325 zDev = db_text("", "SELECT cap FROM user WHERE login='developer'");
1326 login_set_capabilities(zDev, flags);
1327 }
1328 break;
1329 }
1330 }
1331 }
1332
+3
--- src/main.c
+++ src/main.c
@@ -100,10 +100,13 @@
100100
char ModForum; /* 5: Moderate (approve or reject) forum posts */
101101
char AdminForum; /* 6: Set or remove capability 4 on other users */
102102
char EmailAlert; /* 7: Sign up for email notifications */
103103
char Announce; /* A: Send announcements */
104104
char Debug; /* D: show extra Fossil debugging features */
105
+ /* These last two are included to block infinite recursion */
106
+ char XReader; /* u: Inherit all privileges of "reader" */
107
+ char XDeveloper; /* v: Inherit all privileges of "developer" */
105108
};
106109
107110
#ifdef FOSSIL_ENABLE_TCL
108111
/*
109112
** All Tcl related context information is in this structure. This structure
110113
--- src/main.c
+++ src/main.c
@@ -100,10 +100,13 @@
100 char ModForum; /* 5: Moderate (approve or reject) forum posts */
101 char AdminForum; /* 6: Set or remove capability 4 on other users */
102 char EmailAlert; /* 7: Sign up for email notifications */
103 char Announce; /* A: Send announcements */
104 char Debug; /* D: show extra Fossil debugging features */
 
 
 
105 };
106
107 #ifdef FOSSIL_ENABLE_TCL
108 /*
109 ** All Tcl related context information is in this structure. This structure
110
--- src/main.c
+++ src/main.c
@@ -100,10 +100,13 @@
100 char ModForum; /* 5: Moderate (approve or reject) forum posts */
101 char AdminForum; /* 6: Set or remove capability 4 on other users */
102 char EmailAlert; /* 7: Sign up for email notifications */
103 char Announce; /* A: Send announcements */
104 char Debug; /* D: show extra Fossil debugging features */
105 /* These last two are included to block infinite recursion */
106 char XReader; /* u: Inherit all privileges of "reader" */
107 char XDeveloper; /* v: Inherit all privileges of "developer" */
108 };
109
110 #ifdef FOSSIL_ENABLE_TCL
111 /*
112 ** All Tcl related context information is in this structure. This structure
113
--- src/security_audit.c
+++ src/security_audit.c
@@ -58,12 +58,11 @@
5858
/* Step 1: Determine if the repository is public or private. "Public"
5959
** means that any anonymous user on the internet can access all content.
6060
** "Private" repos require (non-anonymous) login to access all content,
6161
** though some content may be accessible anonymously.
6262
*/
63
- zAnonCap = db_text("", "SELECT capunion(cap) FROM user"
64
- " WHERE login IN ('anonymous','nobody')");
63
+ zAnonCap = db_text("", "SELECT fullcap(NULL)");
6564
zPubPages = db_get("public-pages",0);
6665
if( hasAnyCap(zAnonCap,"as") ){
6766
@ <li><p>This repository is <big><b>Wildly INSECURE</b></big> because
6867
@ it grants administrator privileges to anonymous users. You
6968
@ should <a href="takeitprivate">take this repository private</a>
@@ -131,11 +130,11 @@
131130
if( hasAnyCap(zAnonCap, "e") ){
132131
@ <li><p><b>WARNING:</b>
133132
@ Anonymous users can view email addresses and other personally
134133
@ identifiable information on tickets.
135134
@ <p>Fix this by removing the "Email" privilege
136
- @ (<a href="setup_ucap_list">capability "e") from users
135
+ @ (<a href="setup_ucap_list">capability "e"</a>) from users
137136
@ "anonymous" and "nobody" on the
138137
@ <a href="setup_ulist">User Configuration</a> page.
139138
}
140139
141140
/* Anonymous users probably should not be allowed to push content
@@ -223,36 +222,42 @@
223222
224223
/* Administrative privilege should only be provided to
225224
** specific individuals, not to entire classes of people.
226225
** And not too many people should have administrator privilege.
227226
*/
228
- z = db_text(0, "SELECT group_concat(login,' AND ') FROM user"
229
- " WHERE cap GLOB '*[as]*'"
230
- " AND login in ('anonymous','nobody','reader','developer')");
227
+ z = db_text(0,
228
+ "SELECT group_concat("
229
+ "printf('<a href=''setup_uedit?id=%%d''>%%s</a>',uid,login),"
230
+ "' and ')"
231
+ " FROM user"
232
+ " WHERE cap GLOB '*[as]*'"
233
+ " AND login in ('anonymous','nobody','reader','developer')"
234
+ );
231235
if( z && z[0] ){
232
- @ <li><p>
233
- @ Administrative privilege is granted to an entire class of users
234
- @ (%h(z)). Ideally, the Write-Unver privilege should only be
236
+ @ <li><p><b>WARNING:</b>
237
+ @ Administrative privilege ('a' or 's')
238
+ @ is granted to an entire class of users: %s(z).
239
+ @ Administrative privilege should only be
235240
@ granted to specific individuals.
236241
}
237
- n = db_int(0,"SELECT count(*) FROM user WHERE cap GLOB '*[as]*'");
242
+ n = db_int(0,"SELECT count(*) FROM user WHERE fullcap(cap) GLOB '*[as]*'");
238243
if( n==0 ){
239244
@ <li><p>
240245
@ No users have administrator privilege.
241246
}else{
242247
z = db_text(0,
243248
"SELECT group_concat("
244249
"printf('<a href=''setup_uedit?id=%%d''>%%s</a>',uid,login),"
245250
"', ')"
246251
" FROM user"
247
- " WHERE cap GLOB '*[as]*'"
252
+ " WHERE fullcap(cap) GLOB '*[as]*'"
248253
);
249254
@ <li><p>
250255
@ Users with administrator privilege are: %s(z)
251256
fossil_free(z);
252257
if( n>3 ){
253
- @ <p><b>Caution</b>:
258
+ @ <li><p><b>WARNING:</b>
254259
@ Administrator privilege is granted to
255260
@ <a href='setup_ulist?with=as'>%d(n) users</a>.
256261
@ Ideally, administator privilege ('s' or 'a') should only
257262
@ be granted to one or two users.
258263
}
@@ -269,22 +274,21 @@
269274
" FROM user"
270275
" WHERE cap GLOB '*y*'"
271276
" AND login in ('anonymous','nobody','reader','developer')"
272277
);
273278
if( z && z[0] ){
274
- @ <li><p>
275
- @ The "Write-Unver" privilege is granted to an entire class of users
276
- @ (%s(z)). Ideally, the Write-Unver privilege should only be
277
- @ granted to specific individuals.
279
+ @ <li><p><b>WARNING:</b>
280
+ @ The "Write-Unver" privilege is granted to an entire class of users: %s(z).
281
+ @ The Write-Unver privilege should only be granted to specific individuals.
278282
fossil_free(z);
279283
}
280284
n = db_int(0,"SELECT count(*) FROM user WHERE cap GLOB '*y*'");
281285
if( n>0 ){
282286
z = db_text(0,
283287
"SELECT group_concat("
284288
"printf('<a href=''setup_uedit?id=%%d''>%%s</a>',uid,login),', ')"
285
- " FROM user WHERE cap GLOB '*y*'"
289
+ " FROM user WHERE fullcap(cap) GLOB '*y*'"
286290
);
287291
@ <li><p>
288292
@ Users with "Write-Unver" privilege: %s(z)
289293
fossil_free(z);
290294
if( n>3 ){
291295
--- src/security_audit.c
+++ src/security_audit.c
@@ -58,12 +58,11 @@
58 /* Step 1: Determine if the repository is public or private. "Public"
59 ** means that any anonymous user on the internet can access all content.
60 ** "Private" repos require (non-anonymous) login to access all content,
61 ** though some content may be accessible anonymously.
62 */
63 zAnonCap = db_text("", "SELECT capunion(cap) FROM user"
64 " WHERE login IN ('anonymous','nobody')");
65 zPubPages = db_get("public-pages",0);
66 if( hasAnyCap(zAnonCap,"as") ){
67 @ <li><p>This repository is <big><b>Wildly INSECURE</b></big> because
68 @ it grants administrator privileges to anonymous users. You
69 @ should <a href="takeitprivate">take this repository private</a>
@@ -131,11 +130,11 @@
131 if( hasAnyCap(zAnonCap, "e") ){
132 @ <li><p><b>WARNING:</b>
133 @ Anonymous users can view email addresses and other personally
134 @ identifiable information on tickets.
135 @ <p>Fix this by removing the "Email" privilege
136 @ (<a href="setup_ucap_list">capability "e") from users
137 @ "anonymous" and "nobody" on the
138 @ <a href="setup_ulist">User Configuration</a> page.
139 }
140
141 /* Anonymous users probably should not be allowed to push content
@@ -223,36 +222,42 @@
223
224 /* Administrative privilege should only be provided to
225 ** specific individuals, not to entire classes of people.
226 ** And not too many people should have administrator privilege.
227 */
228 z = db_text(0, "SELECT group_concat(login,' AND ') FROM user"
229 " WHERE cap GLOB '*[as]*'"
230 " AND login in ('anonymous','nobody','reader','developer')");
 
 
 
 
 
231 if( z && z[0] ){
232 @ <li><p>
233 @ Administrative privilege is granted to an entire class of users
234 @ (%h(z)). Ideally, the Write-Unver privilege should only be
 
235 @ granted to specific individuals.
236 }
237 n = db_int(0,"SELECT count(*) FROM user WHERE cap GLOB '*[as]*'");
238 if( n==0 ){
239 @ <li><p>
240 @ No users have administrator privilege.
241 }else{
242 z = db_text(0,
243 "SELECT group_concat("
244 "printf('<a href=''setup_uedit?id=%%d''>%%s</a>',uid,login),"
245 "', ')"
246 " FROM user"
247 " WHERE cap GLOB '*[as]*'"
248 );
249 @ <li><p>
250 @ Users with administrator privilege are: %s(z)
251 fossil_free(z);
252 if( n>3 ){
253 @ <p><b>Caution</b>:
254 @ Administrator privilege is granted to
255 @ <a href='setup_ulist?with=as'>%d(n) users</a>.
256 @ Ideally, administator privilege ('s' or 'a') should only
257 @ be granted to one or two users.
258 }
@@ -269,22 +274,21 @@
269 " FROM user"
270 " WHERE cap GLOB '*y*'"
271 " AND login in ('anonymous','nobody','reader','developer')"
272 );
273 if( z && z[0] ){
274 @ <li><p>
275 @ The "Write-Unver" privilege is granted to an entire class of users
276 @ (%s(z)). Ideally, the Write-Unver privilege should only be
277 @ granted to specific individuals.
278 fossil_free(z);
279 }
280 n = db_int(0,"SELECT count(*) FROM user WHERE cap GLOB '*y*'");
281 if( n>0 ){
282 z = db_text(0,
283 "SELECT group_concat("
284 "printf('<a href=''setup_uedit?id=%%d''>%%s</a>',uid,login),', ')"
285 " FROM user WHERE cap GLOB '*y*'"
286 );
287 @ <li><p>
288 @ Users with "Write-Unver" privilege: %s(z)
289 fossil_free(z);
290 if( n>3 ){
291
--- src/security_audit.c
+++ src/security_audit.c
@@ -58,12 +58,11 @@
58 /* Step 1: Determine if the repository is public or private. "Public"
59 ** means that any anonymous user on the internet can access all content.
60 ** "Private" repos require (non-anonymous) login to access all content,
61 ** though some content may be accessible anonymously.
62 */
63 zAnonCap = db_text("", "SELECT fullcap(NULL)");
 
64 zPubPages = db_get("public-pages",0);
65 if( hasAnyCap(zAnonCap,"as") ){
66 @ <li><p>This repository is <big><b>Wildly INSECURE</b></big> because
67 @ it grants administrator privileges to anonymous users. You
68 @ should <a href="takeitprivate">take this repository private</a>
@@ -131,11 +130,11 @@
130 if( hasAnyCap(zAnonCap, "e") ){
131 @ <li><p><b>WARNING:</b>
132 @ Anonymous users can view email addresses and other personally
133 @ identifiable information on tickets.
134 @ <p>Fix this by removing the "Email" privilege
135 @ (<a href="setup_ucap_list">capability "e"</a>) from users
136 @ "anonymous" and "nobody" on the
137 @ <a href="setup_ulist">User Configuration</a> page.
138 }
139
140 /* Anonymous users probably should not be allowed to push content
@@ -223,36 +222,42 @@
222
223 /* Administrative privilege should only be provided to
224 ** specific individuals, not to entire classes of people.
225 ** And not too many people should have administrator privilege.
226 */
227 z = db_text(0,
228 "SELECT group_concat("
229 "printf('<a href=''setup_uedit?id=%%d''>%%s</a>',uid,login),"
230 "' and ')"
231 " FROM user"
232 " WHERE cap GLOB '*[as]*'"
233 " AND login in ('anonymous','nobody','reader','developer')"
234 );
235 if( z && z[0] ){
236 @ <li><p><b>WARNING:</b>
237 @ Administrative privilege ('a' or 's')
238 @ is granted to an entire class of users: %s(z).
239 @ Administrative privilege should only be
240 @ granted to specific individuals.
241 }
242 n = db_int(0,"SELECT count(*) FROM user WHERE fullcap(cap) GLOB '*[as]*'");
243 if( n==0 ){
244 @ <li><p>
245 @ No users have administrator privilege.
246 }else{
247 z = db_text(0,
248 "SELECT group_concat("
249 "printf('<a href=''setup_uedit?id=%%d''>%%s</a>',uid,login),"
250 "', ')"
251 " FROM user"
252 " WHERE fullcap(cap) GLOB '*[as]*'"
253 );
254 @ <li><p>
255 @ Users with administrator privilege are: %s(z)
256 fossil_free(z);
257 if( n>3 ){
258 @ <li><p><b>WARNING:</b>
259 @ Administrator privilege is granted to
260 @ <a href='setup_ulist?with=as'>%d(n) users</a>.
261 @ Ideally, administator privilege ('s' or 'a') should only
262 @ be granted to one or two users.
263 }
@@ -269,22 +274,21 @@
274 " FROM user"
275 " WHERE cap GLOB '*y*'"
276 " AND login in ('anonymous','nobody','reader','developer')"
277 );
278 if( z && z[0] ){
279 @ <li><p><b>WARNING:</b>
280 @ The "Write-Unver" privilege is granted to an entire class of users: %s(z).
281 @ The Write-Unver privilege should only be granted to specific individuals.
 
282 fossil_free(z);
283 }
284 n = db_int(0,"SELECT count(*) FROM user WHERE cap GLOB '*y*'");
285 if( n>0 ){
286 z = db_text(0,
287 "SELECT group_concat("
288 "printf('<a href=''setup_uedit?id=%%d''>%%s</a>',uid,login),', ')"
289 " FROM user WHERE fullcap(cap) GLOB '*y*'"
290 );
291 @ <li><p>
292 @ Users with "Write-Unver" privilege: %s(z)
293 fossil_free(z);
294 if( n>3 ){
295
+1 -1
--- src/setup.c
+++ src/setup.c
@@ -250,11 +250,11 @@
250250
" FROM rcvfrom JOIN user USING(uid))"
251251
" GROUP BY 1;"
252252
);
253253
}
254254
if( zWith && zWith[0] ){
255
- zWith = mprintf(" AND cap GLOB '*[%q]*'", zWith);
255
+ zWith = mprintf(" AND fullcap(cap) GLOB '*[%q]*'", zWith);
256256
}else{
257257
zWith = "";
258258
}
259259
db_prepare(&s,
260260
"SELECT uid, login, cap, info, date(mtime,'unixepoch'),"
261261
--- src/setup.c
+++ src/setup.c
@@ -250,11 +250,11 @@
250 " FROM rcvfrom JOIN user USING(uid))"
251 " GROUP BY 1;"
252 );
253 }
254 if( zWith && zWith[0] ){
255 zWith = mprintf(" AND cap GLOB '*[%q]*'", zWith);
256 }else{
257 zWith = "";
258 }
259 db_prepare(&s,
260 "SELECT uid, login, cap, info, date(mtime,'unixepoch'),"
261
--- src/setup.c
+++ src/setup.c
@@ -250,11 +250,11 @@
250 " FROM rcvfrom JOIN user USING(uid))"
251 " GROUP BY 1;"
252 );
253 }
254 if( zWith && zWith[0] ){
255 zWith = mprintf(" AND fullcap(cap) GLOB '*[%q]*'", zWith);
256 }else{
257 zWith = "";
258 }
259 db_prepare(&s,
260 "SELECT uid, login, cap, info, date(mtime,'unixepoch'),"
261

Keyboard Shortcuts

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