|
1
|
/* |
|
2
|
** Copyright (c) 2008 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 SHUN table of the repository |
|
19
|
*/ |
|
20
|
#include "config.h" |
|
21
|
#include "shun.h" |
|
22
|
#include <assert.h> |
|
23
|
|
|
24
|
/* |
|
25
|
** Return true if the given artifact ID should be shunned. |
|
26
|
*/ |
|
27
|
int uuid_is_shunned(const char *zUuid){ |
|
28
|
static Stmt q; |
|
29
|
int rc; |
|
30
|
if( zUuid==0 || zUuid[0]==0 ) return 0; |
|
31
|
if( g.eHashPolicy==HPOLICY_SHUN_SHA1 && zUuid[HNAME_LEN_SHA1]==0 ) return 1; |
|
32
|
db_static_prepare(&q, "SELECT 1 FROM shun WHERE uuid=:uuid"); |
|
33
|
db_bind_text(&q, ":uuid", zUuid); |
|
34
|
rc = db_step(&q); |
|
35
|
db_reset(&q); |
|
36
|
return rc==SQLITE_ROW; |
|
37
|
} |
|
38
|
|
|
39
|
/* |
|
40
|
** WEBPAGE: shun |
|
41
|
** |
|
42
|
** View the hashes of all shunned artifacts. Add new hashes |
|
43
|
** to the shun set. Requires Admin privilege. |
|
44
|
*/ |
|
45
|
void shun_page(void){ |
|
46
|
Stmt q; |
|
47
|
int cnt = 0; |
|
48
|
const char *zUuid = P("uuid"); |
|
49
|
const char *zShun = P("shun"); |
|
50
|
const char *zAccept = P("accept"); |
|
51
|
const char *zRcvid = P("rcvid"); |
|
52
|
int reviewList = P("review")!=0; |
|
53
|
int nRcvid = 0; |
|
54
|
int numRows = 3; |
|
55
|
char *zCanonical = 0; |
|
56
|
|
|
57
|
login_check_credentials(); |
|
58
|
if( !g.perm.Admin ){ |
|
59
|
login_needed(0); |
|
60
|
return; |
|
61
|
} |
|
62
|
if( P("rebuild") ){ |
|
63
|
db_close(1); |
|
64
|
db_open_repository(g.zRepositoryName); |
|
65
|
db_begin_transaction(); |
|
66
|
rebuild_db(0, 0); |
|
67
|
admin_log("Rebuilt database."); |
|
68
|
db_end_transaction(0); |
|
69
|
} |
|
70
|
if( zUuid ){ |
|
71
|
char *p; |
|
72
|
int i = 0; |
|
73
|
int j = 0; |
|
74
|
zCanonical = fossil_malloc(strlen(zUuid)+2); |
|
75
|
while( zUuid[i] ){ |
|
76
|
if( fossil_isspace(zUuid[i]) ){ |
|
77
|
if( j && zCanonical[j-1] ){ |
|
78
|
zCanonical[j] = 0; |
|
79
|
j++; |
|
80
|
} |
|
81
|
}else{ |
|
82
|
zCanonical[j] = zUuid[i]; |
|
83
|
j++; |
|
84
|
} |
|
85
|
i++; |
|
86
|
} |
|
87
|
zCanonical[j+1] = zCanonical[j] = 0; |
|
88
|
p = zCanonical; |
|
89
|
while( *p ){ |
|
90
|
int nUuid = strlen(p); |
|
91
|
if( !(reviewList || hname_validate(p, nUuid)) ){ |
|
92
|
@ <p class="generalError">Error: Bad artifact IDs.</p> |
|
93
|
fossil_free(zCanonical); |
|
94
|
zCanonical = 0; |
|
95
|
break; |
|
96
|
}else{ |
|
97
|
canonical16(p, nUuid); |
|
98
|
p += nUuid+1; |
|
99
|
} |
|
100
|
} |
|
101
|
zUuid = zCanonical; |
|
102
|
} |
|
103
|
style_header("Shunned Artifacts"); |
|
104
|
if( zUuid && P("sub") && cgi_csrf_safe(2) ){ |
|
105
|
const char *p = zUuid; |
|
106
|
int allExist = 1; |
|
107
|
while( *p ){ |
|
108
|
db_multi_exec("DELETE FROM shun WHERE uuid=%Q", p); |
|
109
|
if( !db_exists("SELECT 1 FROM blob WHERE uuid=%Q", p) ){ |
|
110
|
allExist = 0; |
|
111
|
} |
|
112
|
admin_log("Unshunned %Q", p); |
|
113
|
p += strlen(p)+1; |
|
114
|
} |
|
115
|
if( allExist ){ |
|
116
|
@ <p class="noMoreShun">Artifact(s)<br> |
|
117
|
for( p = zUuid ; *p ; p += strlen(p)+1 ){ |
|
118
|
@ <a href="%R/artifact/%s(p)">%s(p)</a><br> |
|
119
|
} |
|
120
|
@ are no longer being shunned.</p> |
|
121
|
}else{ |
|
122
|
@ <p class="noMoreShun">Artifact(s)<br> |
|
123
|
for( p = zUuid ; *p ; p += strlen(p)+1 ){ |
|
124
|
@ %s(p)<br> |
|
125
|
} |
|
126
|
@ will no longer be shunned but they may not exist in the repository. |
|
127
|
@ It may be necessary to rebuild the repository |
|
128
|
@ before the artifact content can be pulled in |
|
129
|
@ from other repositories.</p> |
|
130
|
} |
|
131
|
} |
|
132
|
if( zUuid && P("add") && cgi_csrf_safe(2) ){ |
|
133
|
const char *p = zUuid; |
|
134
|
int rid, tagid; |
|
135
|
while( *p ){ |
|
136
|
db_multi_exec( |
|
137
|
"INSERT OR IGNORE INTO shun(uuid,mtime)" |
|
138
|
" VALUES(%Q, now())", p); |
|
139
|
db_multi_exec("DELETE FROM attachment WHERE src=%Q", p); |
|
140
|
rid = db_int(0, "SELECT rid FROM blob WHERE uuid=%Q", p); |
|
141
|
if( rid ){ |
|
142
|
db_multi_exec("DELETE FROM event WHERE objid=%d", rid); |
|
143
|
} |
|
144
|
tagid = db_int(0, "SELECT tagid FROM tag WHERE tagname='tkt-%q'", p); |
|
145
|
if( tagid ){ |
|
146
|
db_multi_exec("DELETE FROM ticket WHERE tkt_uuid=%Q", p); |
|
147
|
db_multi_exec("DELETE FROM tag WHERE tagid=%d", tagid); |
|
148
|
db_multi_exec("DELETE FROM tagxref WHERE tagid=%d", tagid); |
|
149
|
} |
|
150
|
admin_log("Shunned %Q", p); |
|
151
|
p += strlen(p)+1; |
|
152
|
} |
|
153
|
@ <p class="shunned">Artifact(s)<br> |
|
154
|
for( p = zUuid ; *p ; p += strlen(p)+1 ){ |
|
155
|
@ <a href="%R/artifact/%s(p)">%s(p)</a><br> |
|
156
|
} |
|
157
|
@ have been shunned. They will no longer be pushed. |
|
158
|
@ They will be removed from the repository the next time the repository |
|
159
|
@ is rebuilt using the <b>fossil rebuild</b> command-line</p> |
|
160
|
} |
|
161
|
if( zUuid && reviewList ){ |
|
162
|
const char *p; |
|
163
|
int nTotal = 0; |
|
164
|
int nOk = 0; |
|
165
|
@ <table class="shun-review"><tbody><tr><td> |
|
166
|
for( p = zUuid ; *p ; p += strlen(p)+1 ){ |
|
167
|
int rid = symbolic_name_to_rid(p, 0); |
|
168
|
nTotal++; |
|
169
|
if( rid < 0 ){ |
|
170
|
@ Ambiguous<br> |
|
171
|
}else if( rid == 0 ){ |
|
172
|
if( !hname_validate(p, strlen(p)) ){ |
|
173
|
@ Bad artifact<br> |
|
174
|
}else if(db_int(0, "SELECT 1 FROM shun WHERE uuid=%Q", p)){ |
|
175
|
@ Already shunned<br> |
|
176
|
}else{ |
|
177
|
@ Unknown<br> |
|
178
|
} |
|
179
|
}else{ |
|
180
|
char *zCmpUuid = db_text(0, |
|
181
|
"SELECT uuid" |
|
182
|
" FROM blob, rcvfrom" |
|
183
|
" WHERE rid=%d" |
|
184
|
" AND rcvfrom.rcvid=blob.rcvid", |
|
185
|
rid); |
|
186
|
if( fossil_strcmp(p, zCmpUuid)==0 ){ |
|
187
|
nOk++; |
|
188
|
@ OK</br> |
|
189
|
}else{ |
|
190
|
@ Abbreviated<br> |
|
191
|
} |
|
192
|
} |
|
193
|
} |
|
194
|
@ </td><td> |
|
195
|
for( p = zUuid ; *p ; p += strlen(p)+1 ){ |
|
196
|
int rid = symbolic_name_to_rid(p, 0); |
|
197
|
if( rid > 0 ){ |
|
198
|
@ <a href="%R/artifact/%s(p)">%s(p)</a><br> |
|
199
|
}else{ |
|
200
|
@ %s(p)<br> |
|
201
|
} |
|
202
|
} |
|
203
|
@ </td></tr></tbody></table> |
|
204
|
@ <p class="shunned"> |
|
205
|
if( nOk < nTotal){ |
|
206
|
@ <b>Warning:</b> Not all artifacts |
|
207
|
}else if( nTotal==1 ){ |
|
208
|
@ The artifact is present and |
|
209
|
}else{ |
|
210
|
@ All %i(nOk) artifacts are present and |
|
211
|
} |
|
212
|
@ can be shunned with its hash above.</p> |
|
213
|
} |
|
214
|
if( zRcvid ){ |
|
215
|
nRcvid = atoi(zRcvid); |
|
216
|
numRows = db_int(0, "SELECT min(count(), 10) FROM blob WHERE rcvid=%d", |
|
217
|
nRcvid); |
|
218
|
} |
|
219
|
@ <p>A shunned artifact will not be pushed nor accepted in a pull and the |
|
220
|
@ artifact content will be purged from the repository the next time the |
|
221
|
@ repository is rebuilt. A list of shunned artifacts can be seen at the |
|
222
|
@ bottom of this page.</p> |
|
223
|
@ |
|
224
|
@ <a name="addshun"></a> |
|
225
|
@ <p>To shun artifacts, enter their artifact hashes (the 40- or |
|
226
|
@ 64-character lowercase hexadecimal hash of the artifact content) in the |
|
227
|
@ following box and press the "Shun" button. This will cause the artifacts |
|
228
|
@ to be removed from the repository and will prevent the artifacts from being |
|
229
|
@ readded to the repository by subsequent sync operation.</p> |
|
230
|
@ |
|
231
|
@ <p>Note that you must enter full artifact hashes, not abbreviations |
|
232
|
@ or symbolic tags.</p> |
|
233
|
@ |
|
234
|
@ <p>Warning: Shunning should only be used to remove inappropriate content |
|
235
|
@ from the repository. Inappropriate content includes such things as |
|
236
|
@ spam added to Wiki, files that violate copyright or patent agreements, |
|
237
|
@ or artifacts that by design or accident interfere with the processing |
|
238
|
@ of the repository. Do not shun artifacts merely to remove them from |
|
239
|
@ sight - set the "hidden" tag on such artifacts instead.</p> |
|
240
|
@ |
|
241
|
@ <blockquote> |
|
242
|
@ <form method="post" action="%R/%s(g.zPath)"><div> |
|
243
|
login_insert_csrf_secret(); |
|
244
|
@ <textarea class="fullsize-text" cols="70" rows="%d(numRows)" name="uuid"> |
|
245
|
if( zShun ){ |
|
246
|
if( strlen(zShun) ){ |
|
247
|
@ %h(zShun) |
|
248
|
}else if( nRcvid ){ |
|
249
|
db_prepare(&q, "SELECT uuid FROM blob WHERE rcvid=%d", nRcvid); |
|
250
|
while( db_step(&q)==SQLITE_ROW ){ |
|
251
|
@ %s(db_column_text(&q, 0)) |
|
252
|
} |
|
253
|
db_finalize(&q); |
|
254
|
} |
|
255
|
}else if( zUuid && reviewList ){ |
|
256
|
const char *p; |
|
257
|
for( p = zUuid ; *p ; p += strlen(p)+1 ){ |
|
258
|
@ %s(p) |
|
259
|
} |
|
260
|
} |
|
261
|
@ </textarea> |
|
262
|
@ <input type="submit" name="add" value="Shun"> |
|
263
|
@ <input type="submit" name="review" value="Review"> |
|
264
|
@ </div></form> |
|
265
|
@ </blockquote> |
|
266
|
@ |
|
267
|
@ <a name="delshun"></a> |
|
268
|
@ <p>Enter the UUIDs of previously shunned artifacts to cause them to be |
|
269
|
@ accepted again in the repository. The artifacts content is not |
|
270
|
@ restored because the content is unknown. The only change is that |
|
271
|
@ the formerly shunned artifacts will be accepted on subsequent sync |
|
272
|
@ operations.</p> |
|
273
|
@ |
|
274
|
@ <blockquote> |
|
275
|
@ <form method="post" action="%R/%s(g.zPath)"><div> |
|
276
|
login_insert_csrf_secret(); |
|
277
|
@ <textarea class="fullsize-text" cols="70" rows="%d(numRows)" name="uuid"> |
|
278
|
if( zAccept ){ |
|
279
|
if( strlen(zAccept) ){ |
|
280
|
@ %h(zAccept) |
|
281
|
}else if( nRcvid ){ |
|
282
|
db_prepare(&q, "SELECT uuid FROM blob WHERE rcvid=%d", nRcvid); |
|
283
|
while( db_step(&q)==SQLITE_ROW ){ |
|
284
|
@ %s(db_column_text(&q, 0)) |
|
285
|
} |
|
286
|
db_finalize(&q); |
|
287
|
} |
|
288
|
} |
|
289
|
@ </textarea> |
|
290
|
@ <input type="submit" name="sub" value="Accept"> |
|
291
|
@ </div></form> |
|
292
|
@ </blockquote> |
|
293
|
@ |
|
294
|
@ <p>Press the Rebuild button below to rebuild the repository. The |
|
295
|
@ content of newly shunned artifacts is not purged until the repository |
|
296
|
@ is rebuilt. On larger repositories, the rebuild may take minute or |
|
297
|
@ two, so be patient after pressing the button.</p> |
|
298
|
@ |
|
299
|
@ <blockquote> |
|
300
|
@ <form method="post" action="%R/%s(g.zPath)"><div> |
|
301
|
login_insert_csrf_secret(); |
|
302
|
@ <input type="submit" name="rebuild" value="Rebuild"> |
|
303
|
@ </div></form> |
|
304
|
@ </blockquote> |
|
305
|
@ |
|
306
|
@ <hr><p>Shunned Artifacts:</p> |
|
307
|
@ <blockquote><p> |
|
308
|
db_prepare(&q, |
|
309
|
"SELECT uuid, EXISTS(SELECT 1 FROM blob WHERE blob.uuid=shun.uuid)" |
|
310
|
" FROM shun ORDER BY uuid"); |
|
311
|
while( db_step(&q)==SQLITE_ROW ){ |
|
312
|
const char *zUuid = db_column_text(&q, 0); |
|
313
|
int stillExists = db_column_int(&q, 1); |
|
314
|
cnt++; |
|
315
|
if( stillExists ){ |
|
316
|
@ <b><a href="%R/artifact/%s(zUuid)">%s(zUuid)</a></b><br> |
|
317
|
}else{ |
|
318
|
@ <b>%s(zUuid)</b><br> |
|
319
|
} |
|
320
|
} |
|
321
|
if( cnt==0 ){ |
|
322
|
@ <i>no artifacts are shunned on this server</i> |
|
323
|
} |
|
324
|
db_finalize(&q); |
|
325
|
@ </p></blockquote> |
|
326
|
style_finish_page(); |
|
327
|
fossil_free(zCanonical); |
|
328
|
} |
|
329
|
|
|
330
|
/* |
|
331
|
** Remove from the BLOB table all artifacts that are in the SHUN table. |
|
332
|
*/ |
|
333
|
void shun_artifacts(void){ |
|
334
|
Stmt q; |
|
335
|
db_multi_exec( |
|
336
|
"CREATE TEMP TABLE toshun(rid INTEGER PRIMARY KEY);" |
|
337
|
"INSERT INTO toshun SELECT rid FROM blob, shun WHERE blob.uuid=shun.uuid;" |
|
338
|
); |
|
339
|
db_prepare(&q, |
|
340
|
"SELECT rid FROM delta WHERE srcid IN toshun" |
|
341
|
); |
|
342
|
while( db_step(&q)==SQLITE_ROW ){ |
|
343
|
int srcid = db_column_int(&q, 0); |
|
344
|
content_undelta(srcid); |
|
345
|
} |
|
346
|
db_finalize(&q); |
|
347
|
db_multi_exec( |
|
348
|
"DELETE FROM delta WHERE rid IN toshun;" |
|
349
|
"DELETE FROM blob WHERE rid IN toshun;" |
|
350
|
"DROP TABLE toshun;" |
|
351
|
"DELETE FROM private " |
|
352
|
" WHERE NOT EXISTS (SELECT 1 FROM blob WHERE rid=private.rid);" |
|
353
|
); |
|
354
|
} |
|
355
|
|
|
356
|
/* |
|
357
|
** WEBPAGE: rcvfromlist |
|
358
|
** |
|
359
|
** Show a listing of RCVFROM table entries. |
|
360
|
** |
|
361
|
** The RCVFROM table records where this repository received each |
|
362
|
** artifact, including the time of receipt, user, and IP address. |
|
363
|
** |
|
364
|
** Access requires Admin privilege. |
|
365
|
*/ |
|
366
|
void rcvfromlist_page(void){ |
|
367
|
int ofst = atoi(PD("ofst","0")); |
|
368
|
int showAll = P("all")!=0; |
|
369
|
int cnt; |
|
370
|
Stmt q; |
|
371
|
const int perScreen = 500; /* RCVIDs per page */ |
|
372
|
|
|
373
|
login_check_credentials(); |
|
374
|
if( !g.perm.Admin ){ |
|
375
|
login_needed(0); |
|
376
|
return; |
|
377
|
} |
|
378
|
style_header("Xfer Log"); |
|
379
|
style_submenu_element("Log-Menu", "setup-logmenu"); |
|
380
|
if( showAll ){ |
|
381
|
ofst = 0; |
|
382
|
}else{ |
|
383
|
style_submenu_element("All", "rcvfromlist?all=1"); |
|
384
|
} |
|
385
|
if( ofst>0 ){ |
|
386
|
style_submenu_element("Newer", "rcvfromlist?ofst=%d", |
|
387
|
ofst>perScreen ? ofst-perScreen : 0); |
|
388
|
} |
|
389
|
style_submenu_element("Artifacts", "bloblist"); |
|
390
|
style_submenu_element("Top-250", "bigbloblist"); |
|
391
|
db_multi_exec( |
|
392
|
"CREATE TEMP TABLE rcvidUsed(x INTEGER PRIMARY KEY);" |
|
393
|
"CREATE TEMP TABLE rcvidSha1(x INTEGER PRIMARY KEY);" |
|
394
|
"CREATE TEMP TABLE rcvidSha3(x INTEGER PRIMARY KEY);" |
|
395
|
"INSERT OR IGNORE INTO rcvidUsed(x) SELECT rcvid FROM blob;" |
|
396
|
"INSERT OR IGNORE INTO rcvidSha1(x)" |
|
397
|
" SELECT rcvid FROM blob WHERE length(uuid)==40;" |
|
398
|
"INSERT OR IGNORE INTO rcvidSha3(x)" |
|
399
|
" SELECT rcvid FROM blob WHERE length(uuid)==64;" |
|
400
|
); |
|
401
|
if( db_table_exists("repository","unversioned") ){ |
|
402
|
db_multi_exec( |
|
403
|
"INSERT OR IGNORE INTO rcvidUsed(x) SELECT rcvid FROM unversioned;" |
|
404
|
"INSERT OR IGNORE INTO rcvidSha1(x)" |
|
405
|
" SELECT rcvid FROM unversioned WHERE length(hash)==40;" |
|
406
|
"INSERT OR IGNORE INTO rcvidSha3(x)" |
|
407
|
" SELECT rcvid FROM unversioned WHERE length(hash)==64;" |
|
408
|
); |
|
409
|
} |
|
410
|
db_prepare(&q, |
|
411
|
"SELECT rcvid, login, datetime(rcvfrom.mtime), rcvfrom.ipaddr," |
|
412
|
" EXISTS(SELECT 1 FROM rcvidUsed WHERE x=rcvfrom.rcvid)," |
|
413
|
" EXISTS(SELECT 1 FROM rcvidSha1 WHERE x=rcvfrom.rcvid)," |
|
414
|
" EXISTS(SELECT 1 FROM rcvidSha3 WHERE x=rcvfrom.rcvid)" |
|
415
|
" FROM rcvfrom LEFT JOIN user USING(uid)" |
|
416
|
" ORDER BY rcvid DESC LIMIT %d OFFSET %d", |
|
417
|
showAll ? -1 : perScreen+1, ofst |
|
418
|
); |
|
419
|
@ <p>Whenever new artifacts are added to the repository, either by |
|
420
|
@ push or using the web interface or by "fossil commit" or similar, |
|
421
|
@ an entry is made in the RCVFROM table |
|
422
|
@ to record the source of those artifacts. This log facilitates |
|
423
|
@ finding and fixing attempts to inject illicit content into the |
|
424
|
@ repository.</p> |
|
425
|
@ |
|
426
|
@ <p>Click on the "rcvid" to show a list of specific artifacts received |
|
427
|
@ by a transaction. After identifying illicit artifacts, remove them |
|
428
|
@ using the "Shun" button. If an "rcvid" is not hyperlinked, that means |
|
429
|
@ all artifacts associated with that rcvid have already been shunned |
|
430
|
@ or purged.</p> |
|
431
|
@ |
|
432
|
@ <table cellpadding="0" cellspacing="0" border="0"> |
|
433
|
@ <tr><th style="padding-right: 15px;text-align: right;">rcvid</th> |
|
434
|
@ <th style="padding-right: 15px;text-align: left;">Date</th> |
|
435
|
@ <th style="padding-right: 15px;text-align: left;">User</th> |
|
436
|
@ <th style="padding-right: 15px;text-align: left;">Hash</th> |
|
437
|
@ <th style="text-align: left;">IP Address</th></tr> |
|
438
|
cnt = 0; |
|
439
|
while( db_step(&q)==SQLITE_ROW ){ |
|
440
|
int rcvid = db_column_int(&q, 0); |
|
441
|
const char *zUser = db_column_text(&q, 1); |
|
442
|
const char *zDate = db_column_text(&q, 2); |
|
443
|
const char *zIpAddr = db_column_text(&q, 3); |
|
444
|
int usesSha1 = db_column_int(&q, 5)!=0; |
|
445
|
int usesSha3 = db_column_int(&q, 6)!=0; |
|
446
|
static const char *const zHashType[] = { "", "sha1", "sha3", "both" }; |
|
447
|
const char *zHash = zHashType[usesSha1+usesSha3*2]; |
|
448
|
if( cnt==perScreen && !showAll ){ |
|
449
|
style_submenu_element("Older", "rcvfromlist?ofst=%d", ofst+perScreen); |
|
450
|
}else{ |
|
451
|
cnt++; |
|
452
|
@ <tr> |
|
453
|
if( db_column_int(&q,4) ){ |
|
454
|
@ <td style="padding-right: 15px;text-align: right;"> |
|
455
|
@ <a href="rcvfrom?rcvid=%d(rcvid)">%d(rcvid)</a></td> |
|
456
|
}else{ |
|
457
|
@ <td style="padding-right: 15px;text-align: right;">%d(rcvid)</td> |
|
458
|
} |
|
459
|
@ <td style="padding-right: 15px;text-align: left;">%s(zDate)</td> |
|
460
|
@ <td style="padding-right: 15px;text-align: left;">%h(zUser)</td> |
|
461
|
@ <td style="padding-right: 15px;text-align: left;">%s(zHash)</td> |
|
462
|
@ <td style="text-align: left;">%s(zIpAddr)</td> |
|
463
|
@ </tr> |
|
464
|
} |
|
465
|
} |
|
466
|
db_finalize(&q); |
|
467
|
@ </table> |
|
468
|
style_finish_page(); |
|
469
|
} |
|
470
|
|
|
471
|
/* |
|
472
|
** WEBPAGE: rcvfrom |
|
473
|
** |
|
474
|
** Show a single RCVFROM table entry identified by the rcvid= query |
|
475
|
** parameters. Requires Admin privilege. |
|
476
|
*/ |
|
477
|
void rcvfrom_page(void){ |
|
478
|
int rcvid = atoi(PD("rcvid","0")); |
|
479
|
Stmt q; |
|
480
|
int cnt; |
|
481
|
|
|
482
|
login_check_credentials(); |
|
483
|
if( !g.perm.Admin ){ |
|
484
|
login_needed(0); |
|
485
|
return; |
|
486
|
} |
|
487
|
style_header("Artifact Receipt %d", rcvid); |
|
488
|
if( db_exists( |
|
489
|
"SELECT 1 FROM blob WHERE rcvid=%d AND" |
|
490
|
" NOT EXISTS (SELECT 1 FROM shun WHERE shun.uuid=blob.uuid)", rcvid) |
|
491
|
){ |
|
492
|
style_submenu_element("Shun All", "shun?shun&rcvid=%d#addshun", rcvid); |
|
493
|
} |
|
494
|
if( db_exists( |
|
495
|
"SELECT 1 FROM blob WHERE rcvid=%d AND" |
|
496
|
" EXISTS (SELECT 1 FROM shun WHERE shun.uuid=blob.uuid)", rcvid) |
|
497
|
){ |
|
498
|
style_submenu_element("Unshun All", "shun?accept&rcvid=%d#delshun", rcvid); |
|
499
|
} |
|
500
|
db_prepare(&q, |
|
501
|
"SELECT login, datetime(rcvfrom.mtime), rcvfrom.ipaddr" |
|
502
|
" FROM rcvfrom LEFT JOIN user USING(uid)" |
|
503
|
" WHERE rcvid=%d", |
|
504
|
rcvid |
|
505
|
); |
|
506
|
@ <table cellspacing="15" cellpadding="0" border="0"> |
|
507
|
@ <tr><th valign="top" align="right">rcvid:</th> |
|
508
|
@ <td valign="top">%d(rcvid)</td></tr> |
|
509
|
if( db_step(&q)==SQLITE_ROW ){ |
|
510
|
const char *zUser = db_column_text(&q, 0); |
|
511
|
const char *zDate = db_column_text(&q, 1); |
|
512
|
const char *zIpAddr = db_column_text(&q, 2); |
|
513
|
@ <tr><th valign="top" align="right">User:</th> |
|
514
|
@ <td valign="top">%s(zUser)</td></tr> |
|
515
|
@ <tr><th valign="top" align="right">Date:</th> |
|
516
|
@ <td valign="top">%s(zDate)</td></tr> |
|
517
|
@ <tr><th valign="top" align="right">IP Address:</th> |
|
518
|
@ <td valign="top">%s(zIpAddr)</td></tr> |
|
519
|
} |
|
520
|
db_finalize(&q); |
|
521
|
db_multi_exec( |
|
522
|
"CREATE TEMP TABLE toshow(rid INTEGER PRIMARY KEY);" |
|
523
|
"INSERT INTO toshow SELECT rid FROM blob WHERE rcvid=%d", rcvid |
|
524
|
); |
|
525
|
describe_artifacts("IN toshow"); |
|
526
|
db_prepare(&q, |
|
527
|
"SELECT blob.rid, blob.uuid, blob.size, description.summary\n" |
|
528
|
" FROM blob LEFT JOIN description ON (blob.rid=description.rid)" |
|
529
|
" WHERE blob.rcvid=%d", rcvid |
|
530
|
); |
|
531
|
cnt = 0; |
|
532
|
while( db_step(&q)==SQLITE_ROW ){ |
|
533
|
const char *zUuid = db_column_text(&q, 1); |
|
534
|
int size = db_column_int(&q, 2); |
|
535
|
const char *zDesc = db_column_text(&q, 3); |
|
536
|
if( zDesc==0 ) zDesc = ""; |
|
537
|
if( cnt==0 ){ |
|
538
|
@ <tr><th valign="top" align="right">Artifacts:</th> |
|
539
|
@ <td valign="top"> |
|
540
|
} |
|
541
|
cnt++; |
|
542
|
@ <a href="%R/info/%s(zUuid)">%s(zUuid)</a> |
|
543
|
@ %h(zDesc) (size: %d(size))<br> |
|
544
|
} |
|
545
|
if( cnt>0 ){ |
|
546
|
@ <p> |
|
547
|
if( db_exists( |
|
548
|
"SELECT 1 FROM blob WHERE rcvid=%d AND" |
|
549
|
" NOT EXISTS (SELECT 1 FROM shun WHERE shun.uuid=blob.uuid)", rcvid) |
|
550
|
){ |
|
551
|
@ <form action='%R/shun'> |
|
552
|
@ <input type="hidden" name="shun"> |
|
553
|
@ <input type="hidden" name="rcvid" value='%d(rcvid)'> |
|
554
|
@ <input type="submit" value="Shun All These Artifacts"> |
|
555
|
@ </form> |
|
556
|
} |
|
557
|
if( db_exists( |
|
558
|
"SELECT 1 FROM blob WHERE rcvid=%d AND" |
|
559
|
" EXISTS (SELECT 1 FROM shun WHERE shun.uuid=blob.uuid)", rcvid) |
|
560
|
){ |
|
561
|
@ <form action='%R/shun'> |
|
562
|
@ <input type="hidden" name="unshun"> |
|
563
|
@ <input type="hidden" name="rcvid" value='%d(rcvid)'> |
|
564
|
@ <input type="submit" value="Unshun All These Artifacts"> |
|
565
|
@ </form> |
|
566
|
} |
|
567
|
@ </td></tr> |
|
568
|
} |
|
569
|
if( db_table_exists("repository","unversioned") ){ |
|
570
|
cnt = 0; |
|
571
|
if( PB("uvdelete") && PB("confirmdelete") ){ |
|
572
|
db_multi_exec( |
|
573
|
"DELETE FROM unversioned WHERE rcvid=%d", rcvid |
|
574
|
); |
|
575
|
} |
|
576
|
db_finalize(&q); |
|
577
|
db_prepare(&q, |
|
578
|
"SELECT name, hash, sz\n" |
|
579
|
" FROM unversioned " |
|
580
|
" WHERE rcvid=%d", rcvid |
|
581
|
); |
|
582
|
while( db_step(&q)==SQLITE_ROW ){ |
|
583
|
const char *zName = db_column_text(&q,0); |
|
584
|
const char *zHash = db_column_text(&q,1); |
|
585
|
int size = db_column_int(&q,2); |
|
586
|
int isDeleted = zHash==0; |
|
587
|
if( cnt==0 ){ |
|
588
|
@ <tr><th valign="top" align="right">Unversioned Files:</th> |
|
589
|
@ <td valign="top"> |
|
590
|
} |
|
591
|
cnt++; |
|
592
|
if( isDeleted ){ |
|
593
|
@ %h(zName) (deleted)<br> |
|
594
|
}else{ |
|
595
|
@ <a href="%R/uv/%h(zName)">%h(zName)</a> (size: %d(size))<br> |
|
596
|
} |
|
597
|
} |
|
598
|
if( cnt>0 ){ |
|
599
|
@ <p><form action='%R/rcvfrom'> |
|
600
|
@ <input type="hidden" name="rcvid" value='%d(rcvid)'> |
|
601
|
@ <input type="hidden" name="uvdelete" value="1"> |
|
602
|
if( PB("uvdelete") ){ |
|
603
|
@ <input type="hidden" name="confirmdelete" value="1"> |
|
604
|
@ <input type="submit" value="Confirm Deletion of These Files"> |
|
605
|
}else{ |
|
606
|
@ <input type="submit" value="Delete These Unversioned Files"> |
|
607
|
} |
|
608
|
@ </form> |
|
609
|
@ </td></tr> |
|
610
|
} |
|
611
|
} |
|
612
|
@ </table> |
|
613
|
db_finalize(&q); |
|
614
|
style_finish_page(); |
|
615
|
} |
|
616
|
|