Fossil SCM

Merge changes from trunk

george 2021-03-23 13:31 wiki-history merge
Commit 9ff7d9e5e3b75658f6d7d19a00b0f27da3a94dd3d1d4d9ab68300253733149cd
--- .dockerignore
+++ .dockerignore
@@ -6,10 +6,11 @@
66
bld
77
compat
88
debian
99
fossil
1010
fossil.exe
11
+msvcbld
1112
setup
1213
src
1314
test
1415
tools
1516
win
1617
--- .dockerignore
+++ .dockerignore
@@ -6,10 +6,11 @@
6 bld
7 compat
8 debian
9 fossil
10 fossil.exe
 
11 setup
12 src
13 test
14 tools
15 win
16
--- .dockerignore
+++ .dockerignore
@@ -6,10 +6,11 @@
6 bld
7 compat
8 debian
9 fossil
10 fossil.exe
11 msvcbld
12 setup
13 src
14 test
15 tools
16 win
17
--- .fossil-settings/clean-glob
+++ .fossil-settings/clean-glob
@@ -8,10 +8,11 @@
88
Makefile
99
autosetup/jimsh0
1010
autosetup/jimsh0.exe
1111
bld/*
1212
msvcbld/*
13
+wbld/*
1314
win/*.c
1415
win/*.h
1516
win/*.exe
1617
win/headers
1718
win/linkopts
1819
--- .fossil-settings/clean-glob
+++ .fossil-settings/clean-glob
@@ -8,10 +8,11 @@
8 Makefile
9 autosetup/jimsh0
10 autosetup/jimsh0.exe
11 bld/*
12 msvcbld/*
 
13 win/*.c
14 win/*.h
15 win/*.exe
16 win/headers
17 win/linkopts
18
--- .fossil-settings/clean-glob
+++ .fossil-settings/clean-glob
@@ -8,10 +8,11 @@
8 Makefile
9 autosetup/jimsh0
10 autosetup/jimsh0.exe
11 bld/*
12 msvcbld/*
13 wbld/*
14 win/*.c
15 win/*.h
16 win/*.exe
17 win/headers
18 win/linkopts
19
--- .fossil-settings/ignore-glob
+++ .fossil-settings/ignore-glob
@@ -1,10 +1,7 @@
11
compat/openssl*
22
compat/tcl*
3
-compat/zlib*
43
fossil
54
fossil.exe
65
win/fossil.exe
76
*shell-see.*
87
*sqlite3-see.*
9
-bld/*
10
-msvcbld/*
118
--- .fossil-settings/ignore-glob
+++ .fossil-settings/ignore-glob
@@ -1,10 +1,7 @@
1 compat/openssl*
2 compat/tcl*
3 compat/zlib*
4 fossil
5 fossil.exe
6 win/fossil.exe
7 *shell-see.*
8 *sqlite3-see.*
9 bld/*
10 msvcbld/*
11
--- .fossil-settings/ignore-glob
+++ .fossil-settings/ignore-glob
@@ -1,10 +1,7 @@
1 compat/openssl*
2 compat/tcl*
 
3 fossil
4 fossil.exe
5 win/fossil.exe
6 *shell-see.*
7 *sqlite3-see.*
 
 
8
+3
--- auto.def
+++ auto.def
@@ -235,10 +235,13 @@
235235
}
236236
237237
if {[opt-bool no-opt]} {
238238
define CFLAGS {-g -O0 -Wall}
239239
msg-result "Builting without compiler optimization"
240
+ if {[opt-bool fossil-debug]} {
241
+ define-append CFLAGS -DFOSSIL_DEBUG
242
+ }
240243
}
241244
242245
if {[opt-bool with-mman]} {
243246
define-append EXTRA_CFLAGS -DUSE_MMAN_H
244247
define USE_MMAN_H 1
245248
--- auto.def
+++ auto.def
@@ -235,10 +235,13 @@
235 }
236
237 if {[opt-bool no-opt]} {
238 define CFLAGS {-g -O0 -Wall}
239 msg-result "Builting without compiler optimization"
 
 
 
240 }
241
242 if {[opt-bool with-mman]} {
243 define-append EXTRA_CFLAGS -DUSE_MMAN_H
244 define USE_MMAN_H 1
245
--- auto.def
+++ auto.def
@@ -235,10 +235,13 @@
235 }
236
237 if {[opt-bool no-opt]} {
238 define CFLAGS {-g -O0 -Wall}
239 msg-result "Builting without compiler optimization"
240 if {[opt-bool fossil-debug]} {
241 define-append CFLAGS -DFOSSIL_DEBUG
242 }
243 }
244
245 if {[opt-bool with-mman]} {
246 define-append EXTRA_CFLAGS -DUSE_MMAN_H
247 define USE_MMAN_H 1
248
+13 -3
--- src/backoffice.c
+++ src/backoffice.c
@@ -364,20 +364,32 @@
364364
}
365365
366366
/*
367367
** COMMAND: test-backoffice-lease
368368
**
369
-** Usage: %fossil test-backoffice-lease
369
+** Usage: %fossil test-backoffice-lease ?--reset?
370370
**
371371
** Print out information about the backoffice "lease" entry in the
372372
** config table that controls whether or not backoffice should run.
373
+**
374
+** If the --reset option is given, the backoffice lease is reset.
375
+** The use of the --reset option can be disruptive. It can cause two
376
+** or more backoffice processes to be run simultaneously. Use it with
377
+** caution.
373378
*/
374379
void test_backoffice_lease(void){
375380
sqlite3_int64 tmNow = time(0);
376381
Lease x;
377382
const char *zLease;
378383
db_find_and_open_repository(0,0);
384
+ if( find_option("reset",0,0)!=0 ){
385
+ db_unprotect(PROTECT_CONFIG);
386
+ db_multi_exec(
387
+ "DELETE FROM repository.config WHERE name='backoffice'"
388
+ );
389
+ db_protect_pop();
390
+ }
379391
verify_all_options();
380392
zLease = db_get("backoffice","");
381393
fossil_print("now: %lld\n", tmNow);
382394
fossil_print("lease: \"%s\"\n", zLease);
383395
backofficeReadLease(&x);
@@ -630,12 +642,10 @@
630642
}
631643
632644
/* Here is where the actual work of the backoffice happens */
633645
nThis = alert_backoffice(0);
634646
if( nThis ){ backoffice_log("%d alerts", nThis); nTotal += nThis; }
635
- nThis = smtp_cleanup();
636
- if( nThis ){ backoffice_log("%d SMTPs", nThis); nTotal += nThis; }
637647
nThis = hook_backoffice();
638648
if( nThis ){ backoffice_log("%d hooks", nThis); nTotal += nThis; }
639649
640650
/* Close the log */
641651
if( backofficeFILE ){
642652
--- src/backoffice.c
+++ src/backoffice.c
@@ -364,20 +364,32 @@
364 }
365
366 /*
367 ** COMMAND: test-backoffice-lease
368 **
369 ** Usage: %fossil test-backoffice-lease
370 **
371 ** Print out information about the backoffice "lease" entry in the
372 ** config table that controls whether or not backoffice should run.
 
 
 
 
 
373 */
374 void test_backoffice_lease(void){
375 sqlite3_int64 tmNow = time(0);
376 Lease x;
377 const char *zLease;
378 db_find_and_open_repository(0,0);
 
 
 
 
 
 
 
379 verify_all_options();
380 zLease = db_get("backoffice","");
381 fossil_print("now: %lld\n", tmNow);
382 fossil_print("lease: \"%s\"\n", zLease);
383 backofficeReadLease(&x);
@@ -630,12 +642,10 @@
630 }
631
632 /* Here is where the actual work of the backoffice happens */
633 nThis = alert_backoffice(0);
634 if( nThis ){ backoffice_log("%d alerts", nThis); nTotal += nThis; }
635 nThis = smtp_cleanup();
636 if( nThis ){ backoffice_log("%d SMTPs", nThis); nTotal += nThis; }
637 nThis = hook_backoffice();
638 if( nThis ){ backoffice_log("%d hooks", nThis); nTotal += nThis; }
639
640 /* Close the log */
641 if( backofficeFILE ){
642
--- src/backoffice.c
+++ src/backoffice.c
@@ -364,20 +364,32 @@
364 }
365
366 /*
367 ** COMMAND: test-backoffice-lease
368 **
369 ** Usage: %fossil test-backoffice-lease ?--reset?
370 **
371 ** Print out information about the backoffice "lease" entry in the
372 ** config table that controls whether or not backoffice should run.
373 **
374 ** If the --reset option is given, the backoffice lease is reset.
375 ** The use of the --reset option can be disruptive. It can cause two
376 ** or more backoffice processes to be run simultaneously. Use it with
377 ** caution.
378 */
379 void test_backoffice_lease(void){
380 sqlite3_int64 tmNow = time(0);
381 Lease x;
382 const char *zLease;
383 db_find_and_open_repository(0,0);
384 if( find_option("reset",0,0)!=0 ){
385 db_unprotect(PROTECT_CONFIG);
386 db_multi_exec(
387 "DELETE FROM repository.config WHERE name='backoffice'"
388 );
389 db_protect_pop();
390 }
391 verify_all_options();
392 zLease = db_get("backoffice","");
393 fossil_print("now: %lld\n", tmNow);
394 fossil_print("lease: \"%s\"\n", zLease);
395 backofficeReadLease(&x);
@@ -630,12 +642,10 @@
642 }
643
644 /* Here is where the actual work of the backoffice happens */
645 nThis = alert_backoffice(0);
646 if( nThis ){ backoffice_log("%d alerts", nThis); nTotal += nThis; }
 
 
647 nThis = hook_backoffice();
648 if( nThis ){ backoffice_log("%d hooks", nThis); nTotal += nThis; }
649
650 /* Close the log */
651 if( backofficeFILE ){
652
+1 -1
--- src/blob.c
+++ src/blob.c
@@ -998,11 +998,11 @@
998998
#endif
999999
}else{
10001000
file_mkfolder(zFilename, ExtFILE, 1, 0);
10011001
out = fossil_fopen(zFilename, "wb");
10021002
if( out==0 ){
1003
-#if _WIN32
1003
+#if defined(_WIN32)
10041004
const char *zReserved = file_is_win_reserved(zFilename);
10051005
if( zReserved ){
10061006
fossil_fatal("cannot open \"%s\" because \"%s\" is "
10071007
"a reserved name on Windows", zFilename, zReserved);
10081008
}
10091009
--- src/blob.c
+++ src/blob.c
@@ -998,11 +998,11 @@
998 #endif
999 }else{
1000 file_mkfolder(zFilename, ExtFILE, 1, 0);
1001 out = fossil_fopen(zFilename, "wb");
1002 if( out==0 ){
1003 #if _WIN32
1004 const char *zReserved = file_is_win_reserved(zFilename);
1005 if( zReserved ){
1006 fossil_fatal("cannot open \"%s\" because \"%s\" is "
1007 "a reserved name on Windows", zFilename, zReserved);
1008 }
1009
--- src/blob.c
+++ src/blob.c
@@ -998,11 +998,11 @@
998 #endif
999 }else{
1000 file_mkfolder(zFilename, ExtFILE, 1, 0);
1001 out = fossil_fopen(zFilename, "wb");
1002 if( out==0 ){
1003 #if defined(_WIN32)
1004 const char *zReserved = file_is_win_reserved(zFilename);
1005 if( zReserved ){
1006 fossil_fatal("cannot open \"%s\" because \"%s\" is "
1007 "a reserved name on Windows", zFilename, zReserved);
1008 }
1009
+1 -1
--- src/chat.c
+++ src/chat.c
@@ -186,11 +186,11 @@
186186
@ <div id='chat-messages-wrapper'>
187187
/* New chat messages get inserted immediately after this element */
188188
@ <span id='message-inject-point'></span>
189189
@ </div>
190190
191
- builtin_fossil_js_bundle_or("popupwidget", "storage", NULL);
191
+ builtin_fossil_js_bundle_or("popupwidget", "storage", "fetch", NULL);
192192
/* Always in-line the javascript for the chat page */
193193
@ <script nonce="%h(style_nonce())">/* chat.c:%d(__LINE__) */
194194
/* We need an onload handler to ensure that window.fossil is
195195
initialized before the chat init code runs. */
196196
@ window.addEventListener('load', function(){
197197
--- src/chat.c
+++ src/chat.c
@@ -186,11 +186,11 @@
186 @ <div id='chat-messages-wrapper'>
187 /* New chat messages get inserted immediately after this element */
188 @ <span id='message-inject-point'></span>
189 @ </div>
190
191 builtin_fossil_js_bundle_or("popupwidget", "storage", NULL);
192 /* Always in-line the javascript for the chat page */
193 @ <script nonce="%h(style_nonce())">/* chat.c:%d(__LINE__) */
194 /* We need an onload handler to ensure that window.fossil is
195 initialized before the chat init code runs. */
196 @ window.addEventListener('load', function(){
197
--- src/chat.c
+++ src/chat.c
@@ -186,11 +186,11 @@
186 @ <div id='chat-messages-wrapper'>
187 /* New chat messages get inserted immediately after this element */
188 @ <span id='message-inject-point'></span>
189 @ </div>
190
191 builtin_fossil_js_bundle_or("popupwidget", "storage", "fetch", NULL);
192 /* Always in-line the javascript for the chat page */
193 @ <script nonce="%h(style_nonce())">/* chat.c:%d(__LINE__) */
194 /* We need an onload handler to ensure that window.fossil is
195 initialized before the chat init code runs. */
196 @ window.addEventListener('load', function(){
197
+87 -66
--- src/chat.js
+++ src/chat.js
@@ -397,10 +397,12 @@
397397
this.playNewMessageSound.uri = uri;
398398
this.settings.set('audible-alert', !!uri);
399399
return this;
400400
}
401401
};
402
+ F.fetch.beforesend = ()=>cs.ajaxStart();
403
+ F.fetch.aftersend = ()=>cs.ajaxEnd();
402404
cs.e.inputCurrent = cs.e.inputSingle;
403405
/* Install default settings... */
404406
Object.keys(cs.settings.defaults).forEach(function(k){
405407
const v = cs.settings.get(k,cs);
406408
if(cs===v) cs.settings.set(k,cs.settings.defaults[k]);
@@ -538,18 +540,15 @@
538540
e = this.getMessageElemById(id);
539541
}
540542
if(!(e instanceof HTMLElement)) return;
541543
if(this.userMayDelete(e)){
542544
this.ajaxStart();
543
- fetch("chat-delete/" + id)
544
- .then(function(response){
545
- if(!response.ok) throw cs._newResponseError(response);
546
- return response;
547
- })
548
- .then(()=>this.deleteMessageElem(e))
549
- .catch(err=>this.reportErrorAsMessage(err))
550
- .finally(()=>this.ajaxEnd());
545
+ F.fetch("chat-delete/" + id, {
546
+ responseType: 'json',
547
+ onload:(r)=>this.deleteMessageElem(r),
548
+ onerror:(err)=>this.reportErrorAsMessage(err)
549
+ });
551550
}else{
552551
this.deleteMessageElem(id);
553552
}
554553
};
555554
document.addEventListener('visibilitychange', function(ev){
@@ -878,27 +877,25 @@
878877
const file = BlobXferState.blob || this.e.inputFile.files[0];
879878
if(file) fd.set("file", file);
880879
if( !msg && !file ) return;
881880
const self = this;
882881
fd.set("lmtime", localTime8601(new Date()));
883
- fetch("chat-send",{
884
- method: 'POST',
885
- body: fd
886
- }).then((x)=>{
887
- if(x.ok) return x.text();
888
- else throw Chat._newResponseError(x);
889
- }).then(function(txt){
882
+ F.fetch("chat-send",{
883
+ payload: fd,
884
+ responseType: 'text',
885
+ onerror:(err)=>this.reportErrorAsMessage(err),
886
+ onload:function(txt){
890887
if(!txt) return/*success response*/;
891888
try{
892889
const json = JSON.parse(txt);
893890
self.newContent({msgs:[json]});
894891
}catch(e){
895892
self.reportError(e);
896893
return;
897894
}
898
- })
899
- .catch((e)=>this.reportErrorAsMessage(e));
895
+ }
896
+ });
900897
BlobXferState.clear();
901898
Chat.inputValue("").inputFocus();
902899
};
903900
904901
Chat.e.inputSingle.addEventListener('keydown',function(ev){
@@ -1155,35 +1152,36 @@
11551152
D.fieldset(loadLegend), "id", "load-msg-toolbar"
11561153
);
11571154
Chat.disableDuringAjax.push(toolbar);
11581155
/* Loads the next n oldest messages, or all previous history if n is negative. */
11591156
const loadOldMessages = function(n){
1160
- Chat.ajaxStart();
11611157
Chat.e.messagesWrapper.classList.add('loading');
11621158
Chat._isBatchLoading = true;
1163
- var gotMessages = false;
11641159
const scrollHt = Chat.e.messagesWrapper.scrollHeight,
11651160
scrollTop = Chat.e.messagesWrapper.scrollTop;
1166
- fetch("chat-poll?before="+Chat.mnMsg+"&n="+n)
1167
- .then(Chat._fetchJsonOrError)
1168
- .then(function(x){
1169
- gotMessages = x.msgs.length;
1161
+ F.fetch("chat-poll",{
1162
+ urlParams:{
1163
+ before: Chat.mnMsg,
1164
+ n: n
1165
+ },
1166
+ responseType: 'json',
1167
+ onerror:function(err){
1168
+ Chat.reportErrorAsMessage(err);
1169
+ Chat._isBatchLoading = false;
1170
+ },
1171
+ onload:function(x){
1172
+ let gotMessages = x.msgs.length;
11701173
newcontent(x,true);
1171
- })
1172
- .catch(e=>Chat.reportErrorAsMessage(e))
1173
- .finally(function(){
11741174
Chat._isBatchLoading = false;
1175
- Chat.e.messagesWrapper.classList.remove('loading');
1176
- Chat.ajaxEnd();
11771175
if(Chat._gotServerError){
1178
- F.toast.error("Got an error response from the server. ",
1179
- "See message for details.");
1176
+ Chat._gotServerError = false;
11801177
return;
1181
- }else if(n<0/*we asked for all history*/
1178
+ }
1179
+ if(n<0/*we asked for all history*/
11821180
|| 0===gotMessages/*we found no history*/
11831181
|| (n>0 && gotMessages<n /*we got fewer history entries than requested*/)
1184
- || (false!==gotMessages && n===0 && gotMessages<Chat.loadMessageCount
1182
+ || (n===0 && gotMessages<Chat.loadMessageCount
11851183
/*we asked for default amount and got fewer than that.*/)){
11861184
/* We've loaded all history. Permanently disable the
11871185
history-load toolbar and keep it from being re-enabled
11881186
via the ajaxStart()/ajaxEnd() mechanism... */
11891187
const div = Chat.e.loadOlderToolbar.querySelector('div');
@@ -1199,11 +1197,16 @@
11991197
was requested, per user request */
12001198
Chat.e.messagesWrapper.scrollTo(
12011199
0, Chat.e.messagesWrapper.scrollHeight - scrollHt + scrollTop
12021200
);
12031201
}
1204
- });
1202
+ },
1203
+ aftersend:function(){
1204
+ Chat.e.messagesWrapper.classList.remove('loading');
1205
+ Chat.ajaxEnd();
1206
+ }
1207
+ });
12051208
};
12061209
const wrapper = D.div(); /* browsers don't all properly handle >1 child in a fieldset */;
12071210
D.append(toolbar, wrapper);
12081211
var btn = D.button("Previous "+Chat.loadMessageCount+" messages");
12091212
D.append(wrapper, btn);
@@ -1213,47 +1216,65 @@
12131216
btn.addEventListener('click',()=>loadOldMessages(-1));
12141217
D.append(Chat.e.messagesWrapper, toolbar);
12151218
toolbar.disabled = true /*will be enabled when msg load finishes */;
12161219
})()/*end history loading widget setup*/;
12171220
1218
- async function poll(isFirstCall){
1219
- if(poll.running) return;
1220
- poll.running = true;
1221
- if(isFirstCall){
1221
+ const afterFetch = function f(){
1222
+ if(true===f.isFirstCall){
1223
+ f.isFirstCall = false;
1224
+ Chat.ajaxEnd();
1225
+ Chat.e.messagesWrapper.classList.remove('loading');
1226
+ setTimeout(function(){
1227
+ Chat.scrollMessagesTo(1);
1228
+ }, 250);
1229
+ }
1230
+ if(Chat._gotServerError && Chat.intervalTimer){
1231
+ clearInterval(Chat.intervalTimer);
1232
+ Chat.reportErrorAsMessage(
1233
+ "Shutting down chat poller due to server-side error. ",
1234
+ "Reload this page to reactivate it.");
1235
+ delete Chat.intervalTimer;
1236
+ }
1237
+ poll.running = false;
1238
+ };
1239
+ afterFetch.isFirstCall = true;
1240
+ const poll = async function f(){
1241
+ if(f.running) return;
1242
+ f.running = true;
1243
+ Chat._isBatchLoading = f.isFirstCall;
1244
+ if(true===f.isFirstCall){
1245
+ f.isFirstCall = false;
12221246
Chat.ajaxStart();
12231247
Chat.e.messagesWrapper.classList.add('loading');
12241248
}
1225
- Chat._isBatchLoading = isFirstCall;
1226
- var p = fetch("chat-poll?name=" + Chat.mxMsg);
1227
- p.then(Chat._fetchJsonOrError)
1228
- .then(y=>newcontent(y))
1229
- .catch(e=>console.error(e))
1230
- /* ^^^ we don't use Chat.reportError(e) here b/c the polling
1231
- fails exepectedly when it times out, but is then immediately
1232
- resumed, and reportError() produces a loud error message. */
1233
- .finally(function(){
1234
- if(isFirstCall){
1235
- Chat._isBatchLoading = false;
1236
- Chat.ajaxEnd();
1237
- Chat.e.messagesWrapper.classList.remove('loading');
1238
- setTimeout(function(){
1239
- Chat.scrollMessagesTo(1);
1240
- }, 250);
1241
- }
1242
- if(Chat._gotServerError && Chat.intervalTimer){
1243
- clearInterval(Chat.intervalTimer);
1244
- delete Chat.intervalTimer;
1245
- }
1246
- poll.running=false;
1247
- });
1248
- }
1249
+ F.fetch("chat-poll",{
1250
+ timeout: 420 * 1000/*FIXME: get the value from the server*/,
1251
+ urlParams:{
1252
+ name: Chat.mxMsg
1253
+ },
1254
+ responseType: "json",
1255
+ // Disable the ajax start/end handling for this long-polling op:
1256
+ beforesend: function(){},
1257
+ aftersend: function(){},
1258
+ onerror:function(err){
1259
+ Chat._isBatchLoading = false;
1260
+ console.error(err);
1261
+ /* ^^^ we don't use Chat.reportError() here b/c the polling
1262
+ fails exepectedly when it times out, but is then immediately
1263
+ resumed, and reportError() produces a loud error message. */
1264
+ afterFetch();
1265
+ },
1266
+ onload:function(y){
1267
+ newcontent(y);
1268
+ Chat._isBatchLoading = false;
1269
+ afterFetch();
1270
+ }
1271
+ });
1272
+ };
1273
+ poll.isFirstCall = true;
12491274
Chat._gotServerError = poll.running = false;
1250
- poll(true);
1251
- if(!Chat._gotServerError){
1252
- Chat.intervalTimer = setInterval(poll, 1000);
1253
- }
12541275
if( window.fossil.config.chat.fromcli ){
12551276
Chat.chatOnlyMode(true);
12561277
}
1257
-
1278
+ Chat.intervalTimer = setInterval(poll, 1000);
12581279
F.page.chat = Chat/* enables testing the APIs via the dev tools */;
12591280
})();
12601281
--- src/chat.js
+++ src/chat.js
@@ -397,10 +397,12 @@
397 this.playNewMessageSound.uri = uri;
398 this.settings.set('audible-alert', !!uri);
399 return this;
400 }
401 };
 
 
402 cs.e.inputCurrent = cs.e.inputSingle;
403 /* Install default settings... */
404 Object.keys(cs.settings.defaults).forEach(function(k){
405 const v = cs.settings.get(k,cs);
406 if(cs===v) cs.settings.set(k,cs.settings.defaults[k]);
@@ -538,18 +540,15 @@
538 e = this.getMessageElemById(id);
539 }
540 if(!(e instanceof HTMLElement)) return;
541 if(this.userMayDelete(e)){
542 this.ajaxStart();
543 fetch("chat-delete/" + id)
544 .then(function(response){
545 if(!response.ok) throw cs._newResponseError(response);
546 return response;
547 })
548 .then(()=>this.deleteMessageElem(e))
549 .catch(err=>this.reportErrorAsMessage(err))
550 .finally(()=>this.ajaxEnd());
551 }else{
552 this.deleteMessageElem(id);
553 }
554 };
555 document.addEventListener('visibilitychange', function(ev){
@@ -878,27 +877,25 @@
878 const file = BlobXferState.blob || this.e.inputFile.files[0];
879 if(file) fd.set("file", file);
880 if( !msg && !file ) return;
881 const self = this;
882 fd.set("lmtime", localTime8601(new Date()));
883 fetch("chat-send",{
884 method: 'POST',
885 body: fd
886 }).then((x)=>{
887 if(x.ok) return x.text();
888 else throw Chat._newResponseError(x);
889 }).then(function(txt){
890 if(!txt) return/*success response*/;
891 try{
892 const json = JSON.parse(txt);
893 self.newContent({msgs:[json]});
894 }catch(e){
895 self.reportError(e);
896 return;
897 }
898 })
899 .catch((e)=>this.reportErrorAsMessage(e));
900 BlobXferState.clear();
901 Chat.inputValue("").inputFocus();
902 };
903
904 Chat.e.inputSingle.addEventListener('keydown',function(ev){
@@ -1155,35 +1152,36 @@
1155 D.fieldset(loadLegend), "id", "load-msg-toolbar"
1156 );
1157 Chat.disableDuringAjax.push(toolbar);
1158 /* Loads the next n oldest messages, or all previous history if n is negative. */
1159 const loadOldMessages = function(n){
1160 Chat.ajaxStart();
1161 Chat.e.messagesWrapper.classList.add('loading');
1162 Chat._isBatchLoading = true;
1163 var gotMessages = false;
1164 const scrollHt = Chat.e.messagesWrapper.scrollHeight,
1165 scrollTop = Chat.e.messagesWrapper.scrollTop;
1166 fetch("chat-poll?before="+Chat.mnMsg+"&n="+n)
1167 .then(Chat._fetchJsonOrError)
1168 .then(function(x){
1169 gotMessages = x.msgs.length;
 
 
 
 
 
 
 
 
1170 newcontent(x,true);
1171 })
1172 .catch(e=>Chat.reportErrorAsMessage(e))
1173 .finally(function(){
1174 Chat._isBatchLoading = false;
1175 Chat.e.messagesWrapper.classList.remove('loading');
1176 Chat.ajaxEnd();
1177 if(Chat._gotServerError){
1178 F.toast.error("Got an error response from the server. ",
1179 "See message for details.");
1180 return;
1181 }else if(n<0/*we asked for all history*/
 
1182 || 0===gotMessages/*we found no history*/
1183 || (n>0 && gotMessages<n /*we got fewer history entries than requested*/)
1184 || (false!==gotMessages && n===0 && gotMessages<Chat.loadMessageCount
1185 /*we asked for default amount and got fewer than that.*/)){
1186 /* We've loaded all history. Permanently disable the
1187 history-load toolbar and keep it from being re-enabled
1188 via the ajaxStart()/ajaxEnd() mechanism... */
1189 const div = Chat.e.loadOlderToolbar.querySelector('div');
@@ -1199,11 +1197,16 @@
1199 was requested, per user request */
1200 Chat.e.messagesWrapper.scrollTo(
1201 0, Chat.e.messagesWrapper.scrollHeight - scrollHt + scrollTop
1202 );
1203 }
1204 });
 
 
 
 
 
1205 };
1206 const wrapper = D.div(); /* browsers don't all properly handle >1 child in a fieldset */;
1207 D.append(toolbar, wrapper);
1208 var btn = D.button("Previous "+Chat.loadMessageCount+" messages");
1209 D.append(wrapper, btn);
@@ -1213,47 +1216,65 @@
1213 btn.addEventListener('click',()=>loadOldMessages(-1));
1214 D.append(Chat.e.messagesWrapper, toolbar);
1215 toolbar.disabled = true /*will be enabled when msg load finishes */;
1216 })()/*end history loading widget setup*/;
1217
1218 async function poll(isFirstCall){
1219 if(poll.running) return;
1220 poll.running = true;
1221 if(isFirstCall){
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1222 Chat.ajaxStart();
1223 Chat.e.messagesWrapper.classList.add('loading');
1224 }
1225 Chat._isBatchLoading = isFirstCall;
1226 var p = fetch("chat-poll?name=" + Chat.mxMsg);
1227 p.then(Chat._fetchJsonOrError)
1228 .then(y=>newcontent(y))
1229 .catch(e=>console.error(e))
1230 /* ^^^ we don't use Chat.reportError(e) here b/c the polling
1231 fails exepectedly when it times out, but is then immediately
1232 resumed, and reportError() produces a loud error message. */
1233 .finally(function(){
1234 if(isFirstCall){
1235 Chat._isBatchLoading = false;
1236 Chat.ajaxEnd();
1237 Chat.e.messagesWrapper.classList.remove('loading');
1238 setTimeout(function(){
1239 Chat.scrollMessagesTo(1);
1240 }, 250);
1241 }
1242 if(Chat._gotServerError && Chat.intervalTimer){
1243 clearInterval(Chat.intervalTimer);
1244 delete Chat.intervalTimer;
1245 }
1246 poll.running=false;
1247 });
1248 }
 
1249 Chat._gotServerError = poll.running = false;
1250 poll(true);
1251 if(!Chat._gotServerError){
1252 Chat.intervalTimer = setInterval(poll, 1000);
1253 }
1254 if( window.fossil.config.chat.fromcli ){
1255 Chat.chatOnlyMode(true);
1256 }
1257
1258 F.page.chat = Chat/* enables testing the APIs via the dev tools */;
1259 })();
1260
--- src/chat.js
+++ src/chat.js
@@ -397,10 +397,12 @@
397 this.playNewMessageSound.uri = uri;
398 this.settings.set('audible-alert', !!uri);
399 return this;
400 }
401 };
402 F.fetch.beforesend = ()=>cs.ajaxStart();
403 F.fetch.aftersend = ()=>cs.ajaxEnd();
404 cs.e.inputCurrent = cs.e.inputSingle;
405 /* Install default settings... */
406 Object.keys(cs.settings.defaults).forEach(function(k){
407 const v = cs.settings.get(k,cs);
408 if(cs===v) cs.settings.set(k,cs.settings.defaults[k]);
@@ -538,18 +540,15 @@
540 e = this.getMessageElemById(id);
541 }
542 if(!(e instanceof HTMLElement)) return;
543 if(this.userMayDelete(e)){
544 this.ajaxStart();
545 F.fetch("chat-delete/" + id, {
546 responseType: 'json',
547 onload:(r)=>this.deleteMessageElem(r),
548 onerror:(err)=>this.reportErrorAsMessage(err)
549 });
 
 
 
550 }else{
551 this.deleteMessageElem(id);
552 }
553 };
554 document.addEventListener('visibilitychange', function(ev){
@@ -878,27 +877,25 @@
877 const file = BlobXferState.blob || this.e.inputFile.files[0];
878 if(file) fd.set("file", file);
879 if( !msg && !file ) return;
880 const self = this;
881 fd.set("lmtime", localTime8601(new Date()));
882 F.fetch("chat-send",{
883 payload: fd,
884 responseType: 'text',
885 onerror:(err)=>this.reportErrorAsMessage(err),
886 onload:function(txt){
 
 
887 if(!txt) return/*success response*/;
888 try{
889 const json = JSON.parse(txt);
890 self.newContent({msgs:[json]});
891 }catch(e){
892 self.reportError(e);
893 return;
894 }
895 }
896 });
897 BlobXferState.clear();
898 Chat.inputValue("").inputFocus();
899 };
900
901 Chat.e.inputSingle.addEventListener('keydown',function(ev){
@@ -1155,35 +1152,36 @@
1152 D.fieldset(loadLegend), "id", "load-msg-toolbar"
1153 );
1154 Chat.disableDuringAjax.push(toolbar);
1155 /* Loads the next n oldest messages, or all previous history if n is negative. */
1156 const loadOldMessages = function(n){
 
1157 Chat.e.messagesWrapper.classList.add('loading');
1158 Chat._isBatchLoading = true;
 
1159 const scrollHt = Chat.e.messagesWrapper.scrollHeight,
1160 scrollTop = Chat.e.messagesWrapper.scrollTop;
1161 F.fetch("chat-poll",{
1162 urlParams:{
1163 before: Chat.mnMsg,
1164 n: n
1165 },
1166 responseType: 'json',
1167 onerror:function(err){
1168 Chat.reportErrorAsMessage(err);
1169 Chat._isBatchLoading = false;
1170 },
1171 onload:function(x){
1172 let gotMessages = x.msgs.length;
1173 newcontent(x,true);
 
 
 
1174 Chat._isBatchLoading = false;
 
 
1175 if(Chat._gotServerError){
1176 Chat._gotServerError = false;
 
1177 return;
1178 }
1179 if(n<0/*we asked for all history*/
1180 || 0===gotMessages/*we found no history*/
1181 || (n>0 && gotMessages<n /*we got fewer history entries than requested*/)
1182 || (n===0 && gotMessages<Chat.loadMessageCount
1183 /*we asked for default amount and got fewer than that.*/)){
1184 /* We've loaded all history. Permanently disable the
1185 history-load toolbar and keep it from being re-enabled
1186 via the ajaxStart()/ajaxEnd() mechanism... */
1187 const div = Chat.e.loadOlderToolbar.querySelector('div');
@@ -1199,11 +1197,16 @@
1197 was requested, per user request */
1198 Chat.e.messagesWrapper.scrollTo(
1199 0, Chat.e.messagesWrapper.scrollHeight - scrollHt + scrollTop
1200 );
1201 }
1202 },
1203 aftersend:function(){
1204 Chat.e.messagesWrapper.classList.remove('loading');
1205 Chat.ajaxEnd();
1206 }
1207 });
1208 };
1209 const wrapper = D.div(); /* browsers don't all properly handle >1 child in a fieldset */;
1210 D.append(toolbar, wrapper);
1211 var btn = D.button("Previous "+Chat.loadMessageCount+" messages");
1212 D.append(wrapper, btn);
@@ -1213,47 +1216,65 @@
1216 btn.addEventListener('click',()=>loadOldMessages(-1));
1217 D.append(Chat.e.messagesWrapper, toolbar);
1218 toolbar.disabled = true /*will be enabled when msg load finishes */;
1219 })()/*end history loading widget setup*/;
1220
1221 const afterFetch = function f(){
1222 if(true===f.isFirstCall){
1223 f.isFirstCall = false;
1224 Chat.ajaxEnd();
1225 Chat.e.messagesWrapper.classList.remove('loading');
1226 setTimeout(function(){
1227 Chat.scrollMessagesTo(1);
1228 }, 250);
1229 }
1230 if(Chat._gotServerError && Chat.intervalTimer){
1231 clearInterval(Chat.intervalTimer);
1232 Chat.reportErrorAsMessage(
1233 "Shutting down chat poller due to server-side error. ",
1234 "Reload this page to reactivate it.");
1235 delete Chat.intervalTimer;
1236 }
1237 poll.running = false;
1238 };
1239 afterFetch.isFirstCall = true;
1240 const poll = async function f(){
1241 if(f.running) return;
1242 f.running = true;
1243 Chat._isBatchLoading = f.isFirstCall;
1244 if(true===f.isFirstCall){
1245 f.isFirstCall = false;
1246 Chat.ajaxStart();
1247 Chat.e.messagesWrapper.classList.add('loading');
1248 }
1249 F.fetch("chat-poll",{
1250 timeout: 420 * 1000/*FIXME: get the value from the server*/,
1251 urlParams:{
1252 name: Chat.mxMsg
1253 },
1254 responseType: "json",
1255 // Disable the ajax start/end handling for this long-polling op:
1256 beforesend: function(){},
1257 aftersend: function(){},
1258 onerror:function(err){
1259 Chat._isBatchLoading = false;
1260 console.error(err);
1261 /* ^^^ we don't use Chat.reportError() here b/c the polling
1262 fails exepectedly when it times out, but is then immediately
1263 resumed, and reportError() produces a loud error message. */
1264 afterFetch();
1265 },
1266 onload:function(y){
1267 newcontent(y);
1268 Chat._isBatchLoading = false;
1269 afterFetch();
1270 }
1271 });
1272 };
1273 poll.isFirstCall = true;
1274 Chat._gotServerError = poll.running = false;
 
 
 
 
1275 if( window.fossil.config.chat.fromcli ){
1276 Chat.chatOnlyMode(true);
1277 }
1278 Chat.intervalTimer = setInterval(poll, 1000);
1279 F.page.chat = Chat/* enables testing the APIs via the dev tools */;
1280 })();
1281
+22 -1
--- src/file.c
+++ src/file.c
@@ -2059,11 +2059,11 @@
20592059
}else{
20602060
FILE * p;
20612061
file_mkfolder(zFilename, ExtFILE, 1, 0);
20622062
p = fossil_fopen(zFilename, "wb");
20632063
if( p==0 ){
2064
-#if _WIN32
2064
+#if defined(_WIN32)
20652065
const char *zReserved = file_is_win_reserved(zFilename);
20662066
if( zReserved ){
20672067
fossil_fatal("cannot open \"%s\" because \"%s\" is "
20682068
"a reserved name on Windows", zFilename,
20692069
zReserved);
@@ -2565,5 +2565,26 @@
25652565
default:{
25662566
return 0;
25672567
}
25682568
}
25692569
}
2570
+
2571
+/*
2572
+** COMMAND: test-is-reserved-name
2573
+**
2574
+** Usage: %fossil test-is-reserved-name FILENAMES...
2575
+**
2576
+** Passes each given name to file_is_reserved_name() and outputs one
2577
+** line per file: the result value of that function followed by the
2578
+** name.
2579
+*/
2580
+void test_is_reserved_name_cmd(void){
2581
+ int i;
2582
+
2583
+ if(g.argc<3){
2584
+ usage("FILENAME_1 [...FILENAME_N]");
2585
+ }
2586
+ for( i = 2; i < g.argc; ++i ){
2587
+ const int check = file_is_reserved_name(g.argv[i], -1);
2588
+ fossil_print("%d %s\n", check, g.argv[i]);
2589
+ }
2590
+}
25702591
--- src/file.c
+++ src/file.c
@@ -2059,11 +2059,11 @@
2059 }else{
2060 FILE * p;
2061 file_mkfolder(zFilename, ExtFILE, 1, 0);
2062 p = fossil_fopen(zFilename, "wb");
2063 if( p==0 ){
2064 #if _WIN32
2065 const char *zReserved = file_is_win_reserved(zFilename);
2066 if( zReserved ){
2067 fossil_fatal("cannot open \"%s\" because \"%s\" is "
2068 "a reserved name on Windows", zFilename,
2069 zReserved);
@@ -2565,5 +2565,26 @@
2565 default:{
2566 return 0;
2567 }
2568 }
2569 }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2570
--- src/file.c
+++ src/file.c
@@ -2059,11 +2059,11 @@
2059 }else{
2060 FILE * p;
2061 file_mkfolder(zFilename, ExtFILE, 1, 0);
2062 p = fossil_fopen(zFilename, "wb");
2063 if( p==0 ){
2064 #if defined(_WIN32)
2065 const char *zReserved = file_is_win_reserved(zFilename);
2066 if( zReserved ){
2067 fossil_fatal("cannot open \"%s\" because \"%s\" is "
2068 "a reserved name on Windows", zFilename,
2069 zReserved);
@@ -2565,5 +2565,26 @@
2565 default:{
2566 return 0;
2567 }
2568 }
2569 }
2570
2571 /*
2572 ** COMMAND: test-is-reserved-name
2573 **
2574 ** Usage: %fossil test-is-reserved-name FILENAMES...
2575 **
2576 ** Passes each given name to file_is_reserved_name() and outputs one
2577 ** line per file: the result value of that function followed by the
2578 ** name.
2579 */
2580 void test_is_reserved_name_cmd(void){
2581 int i;
2582
2583 if(g.argc<3){
2584 usage("FILENAME_1 [...FILENAME_N]");
2585 }
2586 for( i = 2; i < g.argc; ++i ){
2587 const int check = file_is_reserved_name(g.argv[i], -1);
2588 fossil_print("%d %s\n", check, g.argv[i]);
2589 }
2590 }
2591
+3 -3
--- src/forum.c
+++ src/forum.c
@@ -481,16 +481,16 @@
481481
** * The post is unedited
482482
** * The post was last edited by the original author
483483
** * The post was last edited by a different person
484484
*/
485485
if( p->pEditHead ){
486
- zDate = db_text(0, "SELECT datetime(%.17g)", p->pEditHead->rDate);
486
+ zDate = db_text(0, "SELECT datetime(%.17g,toLocal())", p->pEditHead->rDate);
487487
}else{
488488
zPosterName = forum_post_display_name(p, pManifest);
489489
zEditorName = zPosterName;
490490
}
491
- zDate = db_text(0, "SELECT datetime(%.17g)", p->rDate);
491
+ zDate = db_text(0, "SELECT datetime(%.17g,toLocal())", p->rDate);
492492
if( p->pEditPrev ){
493493
zPosterName = forum_post_display_name(p->pEditHead, 0);
494494
zEditorName = forum_post_display_name(p, pManifest);
495495
zHist = bHist ? "" : "&hist";
496496
@ <h3 class='forumPostHdr'>(%d(p->sid)\
@@ -1287,11 +1287,11 @@
12871287
style_header("Reply");
12881288
if( pRootPost->zThreadTitle ){
12891289
@ <h1>Thread: %h(pRootPost->zThreadTitle)</h1>
12901290
}
12911291
@ <h2>Replying To:</h2>
1292
- zDate = db_text(0, "SELECT datetime(%.17g)", pPost->rDate);
1292
+ zDate = db_text(0, "SELECT datetime(%.17g,toLocal())", pPost->rDate);
12931293
zDisplayName = display_name_from_login(pPost->zUser);
12941294
@ <h3 class='forumPostHdr'>By %h(zDisplayName) on %h(zDate)</h3>
12951295
fossil_free(zDisplayName);
12961296
fossil_free(zDate);
12971297
forum_render(0, pPost->zMimetype, pPost->zWiki, "forumEdit", 1);
12981298
--- src/forum.c
+++ src/forum.c
@@ -481,16 +481,16 @@
481 ** * The post is unedited
482 ** * The post was last edited by the original author
483 ** * The post was last edited by a different person
484 */
485 if( p->pEditHead ){
486 zDate = db_text(0, "SELECT datetime(%.17g)", p->pEditHead->rDate);
487 }else{
488 zPosterName = forum_post_display_name(p, pManifest);
489 zEditorName = zPosterName;
490 }
491 zDate = db_text(0, "SELECT datetime(%.17g)", p->rDate);
492 if( p->pEditPrev ){
493 zPosterName = forum_post_display_name(p->pEditHead, 0);
494 zEditorName = forum_post_display_name(p, pManifest);
495 zHist = bHist ? "" : "&hist";
496 @ <h3 class='forumPostHdr'>(%d(p->sid)\
@@ -1287,11 +1287,11 @@
1287 style_header("Reply");
1288 if( pRootPost->zThreadTitle ){
1289 @ <h1>Thread: %h(pRootPost->zThreadTitle)</h1>
1290 }
1291 @ <h2>Replying To:</h2>
1292 zDate = db_text(0, "SELECT datetime(%.17g)", pPost->rDate);
1293 zDisplayName = display_name_from_login(pPost->zUser);
1294 @ <h3 class='forumPostHdr'>By %h(zDisplayName) on %h(zDate)</h3>
1295 fossil_free(zDisplayName);
1296 fossil_free(zDate);
1297 forum_render(0, pPost->zMimetype, pPost->zWiki, "forumEdit", 1);
1298
--- src/forum.c
+++ src/forum.c
@@ -481,16 +481,16 @@
481 ** * The post is unedited
482 ** * The post was last edited by the original author
483 ** * The post was last edited by a different person
484 */
485 if( p->pEditHead ){
486 zDate = db_text(0, "SELECT datetime(%.17g,toLocal())", p->pEditHead->rDate);
487 }else{
488 zPosterName = forum_post_display_name(p, pManifest);
489 zEditorName = zPosterName;
490 }
491 zDate = db_text(0, "SELECT datetime(%.17g,toLocal())", p->rDate);
492 if( p->pEditPrev ){
493 zPosterName = forum_post_display_name(p->pEditHead, 0);
494 zEditorName = forum_post_display_name(p, pManifest);
495 zHist = bHist ? "" : "&hist";
496 @ <h3 class='forumPostHdr'>(%d(p->sid)\
@@ -1287,11 +1287,11 @@
1287 style_header("Reply");
1288 if( pRootPost->zThreadTitle ){
1289 @ <h1>Thread: %h(pRootPost->zThreadTitle)</h1>
1290 }
1291 @ <h2>Replying To:</h2>
1292 zDate = db_text(0, "SELECT datetime(%.17g,toLocal())", pPost->rDate);
1293 zDisplayName = display_name_from_login(pPost->zUser);
1294 @ <h3 class='forumPostHdr'>By %h(zDisplayName) on %h(zDate)</h3>
1295 fossil_free(zDisplayName);
1296 fossil_free(zDate);
1297 forum_render(0, pPost->zMimetype, pPost->zWiki, "forumEdit", 1);
1298
--- src/fossil.fetch.js
+++ src/fossil.fetch.js
@@ -86,11 +86,11 @@
8686
- timeout: integer in milliseconds specifying the XHR timeout
8787
duration. Default = fossil.fetch.timeout.
8888
8989
When an options object does not provide
9090
onload/onerror/beforesend/aftersend handlers of its own, this
91
- function falls to defaults which are member properties of this
91
+ function falls back to defaults which are member properties of this
9292
function with the same name, e.g. fossil.fetch.onload(). The
9393
default onload/onerror implementations route the data through the
9494
dev console and (for onerror()) through fossil.error(). The default
9595
beforesend/aftersend are no-ops. Individual pages may overwrite
9696
those members to provide default implementations suitable for the
@@ -166,10 +166,22 @@
166166
opt.onerror(new Error("XHR timeout of "+x.timeout+"ms expired."));
167167
};
168168
x.onreadystatechange = function(){
169169
if(XMLHttpRequest.DONE !== x.readyState) return;
170170
try{opt.aftersend()}catch(e){/*ignore*/}
171
+ if(false && 0===x.status){
172
+ /* For reasons unknown, we _sometimes_ trigger x.status==0 in FF
173
+ when the /chat page starts up, but not in Chrome nor in other
174
+ apps. Insofar as has been determined, this happens before a
175
+ request is actually sent and it appears to have no
176
+ side-effects on the app other than to generate an error
177
+ (i.e. no requests/responses are missing). This is a silly
178
+ workaround which may or may not bite us later. If so, it can
179
+ be removed at the cost of an unsightly console error message
180
+ in FF. */
181
+ return;
182
+ }
171183
if(200!==x.status){
172184
let err;
173185
try{
174186
const j = JSON.parse(x.response);
175187
if(j.error) err = new Error(j.error);
176188
--- src/fossil.fetch.js
+++ src/fossil.fetch.js
@@ -86,11 +86,11 @@
86 - timeout: integer in milliseconds specifying the XHR timeout
87 duration. Default = fossil.fetch.timeout.
88
89 When an options object does not provide
90 onload/onerror/beforesend/aftersend handlers of its own, this
91 function falls to defaults which are member properties of this
92 function with the same name, e.g. fossil.fetch.onload(). The
93 default onload/onerror implementations route the data through the
94 dev console and (for onerror()) through fossil.error(). The default
95 beforesend/aftersend are no-ops. Individual pages may overwrite
96 those members to provide default implementations suitable for the
@@ -166,10 +166,22 @@
166 opt.onerror(new Error("XHR timeout of "+x.timeout+"ms expired."));
167 };
168 x.onreadystatechange = function(){
169 if(XMLHttpRequest.DONE !== x.readyState) return;
170 try{opt.aftersend()}catch(e){/*ignore*/}
 
 
 
 
 
 
 
 
 
 
 
 
171 if(200!==x.status){
172 let err;
173 try{
174 const j = JSON.parse(x.response);
175 if(j.error) err = new Error(j.error);
176
--- src/fossil.fetch.js
+++ src/fossil.fetch.js
@@ -86,11 +86,11 @@
86 - timeout: integer in milliseconds specifying the XHR timeout
87 duration. Default = fossil.fetch.timeout.
88
89 When an options object does not provide
90 onload/onerror/beforesend/aftersend handlers of its own, this
91 function falls back to defaults which are member properties of this
92 function with the same name, e.g. fossil.fetch.onload(). The
93 default onload/onerror implementations route the data through the
94 dev console and (for onerror()) through fossil.error(). The default
95 beforesend/aftersend are no-ops. Individual pages may overwrite
96 those members to provide default implementations suitable for the
@@ -166,10 +166,22 @@
166 opt.onerror(new Error("XHR timeout of "+x.timeout+"ms expired."));
167 };
168 x.onreadystatechange = function(){
169 if(XMLHttpRequest.DONE !== x.readyState) return;
170 try{opt.aftersend()}catch(e){/*ignore*/}
171 if(false && 0===x.status){
172 /* For reasons unknown, we _sometimes_ trigger x.status==0 in FF
173 when the /chat page starts up, but not in Chrome nor in other
174 apps. Insofar as has been determined, this happens before a
175 request is actually sent and it appears to have no
176 side-effects on the app other than to generate an error
177 (i.e. no requests/responses are missing). This is a silly
178 workaround which may or may not bite us later. If so, it can
179 be removed at the cost of an unsightly console error message
180 in FF. */
181 return;
182 }
183 if(200!==x.status){
184 let err;
185 try{
186 const j = JSON.parse(x.response);
187 if(j.error) err = new Error(j.error);
188
+2 -1
--- src/info.c
+++ src/info.c
@@ -1662,11 +1662,12 @@
16621662
** * The "preferred-diff-type" setting
16631663
** * 1 for mobile and 2 for desktop, based on the UserAgent
16641664
*/
16651665
int preferred_diff_type(void){
16661666
int dflt;
1667
- char zDflt[2];
1667
+ static char zDflt[2]
1668
+ /*static b/c cookie_link_parameter() does not copy it!*/;
16681669
dflt = db_get_int("preferred-diff-type",-99);
16691670
if( dflt<=0 ) dflt = user_agent_is_likely_mobile() ? 1 : 2;
16701671
zDflt[0] = dflt + '0';
16711672
zDflt[1] = 0;
16721673
cookie_link_parameter("diff","diff", zDflt);
16731674
--- src/info.c
+++ src/info.c
@@ -1662,11 +1662,12 @@
1662 ** * The "preferred-diff-type" setting
1663 ** * 1 for mobile and 2 for desktop, based on the UserAgent
1664 */
1665 int preferred_diff_type(void){
1666 int dflt;
1667 char zDflt[2];
 
1668 dflt = db_get_int("preferred-diff-type",-99);
1669 if( dflt<=0 ) dflt = user_agent_is_likely_mobile() ? 1 : 2;
1670 zDflt[0] = dflt + '0';
1671 zDflt[1] = 0;
1672 cookie_link_parameter("diff","diff", zDflt);
1673
--- src/info.c
+++ src/info.c
@@ -1662,11 +1662,12 @@
1662 ** * The "preferred-diff-type" setting
1663 ** * 1 for mobile and 2 for desktop, based on the UserAgent
1664 */
1665 int preferred_diff_type(void){
1666 int dflt;
1667 static char zDflt[2]
1668 /*static b/c cookie_link_parameter() does not copy it!*/;
1669 dflt = db_get_int("preferred-diff-type",-99);
1670 if( dflt<=0 ) dflt = user_agent_is_likely_mobile() ? 1 : 2;
1671 zDflt[0] = dflt + '0';
1672 zDflt[1] = 0;
1673 cookie_link_parameter("diff","diff", zDflt);
1674
--- src/json_detail.h
+++ src/json_detail.h
@@ -16,11 +16,13 @@
1616
** [email protected]
1717
** http://www.hwaci.com/drh/
1818
**
1919
*/
2020
21
+#if !defined(_RC_COMPILE_)
2122
#include "cson_amalgamation.h"
23
+#endif /* !defined(_RC_COMPILE_) */
2224
2325
/**
2426
FOSSIL_JSON_API_VERSION holds the date (YYYYMMDD) of the latest
2527
"significant" change to the JSON API (a change in an interface or
2628
new functionality). It is sent as part of the /json/version
@@ -53,10 +55,11 @@
5355
** Maintenance reminder: when entries are added to this list, update
5456
** the code in json_page_resultCodes() and json_err_cstr() (both in
5557
** json.c)!
5658
**
5759
*/
60
+#if !defined(_RC_COMPILE_)
5861
enum FossilJsonCodes {
5962
FSL_JSON_W_START = 0,
6063
FSL_JSON_W_UNKNOWN /*+1*/,
6164
FSL_JSON_W_ROW_TO_JSON_FAILED /*+2*/,
6265
FSL_JSON_W_COL_TO_JSON_FAILED /*+3*/,
@@ -263,7 +266,8 @@
263266
264267
enum json_get_changed_files_flags {
265268
json_get_changed_files_ELIDE_PARENT = 1 << 0
266269
};
267270
271
+#endif /* !defined(_RC_COMPILE_) */
268272
#endif/*FOSSIL_JSON_DETAIL_H_INCLUDED*/
269273
#endif /* FOSSIL_ENABLE_JSON */
270274
--- src/json_detail.h
+++ src/json_detail.h
@@ -16,11 +16,13 @@
16 ** [email protected]
17 ** http://www.hwaci.com/drh/
18 **
19 */
20
 
21 #include "cson_amalgamation.h"
 
22
23 /**
24 FOSSIL_JSON_API_VERSION holds the date (YYYYMMDD) of the latest
25 "significant" change to the JSON API (a change in an interface or
26 new functionality). It is sent as part of the /json/version
@@ -53,10 +55,11 @@
53 ** Maintenance reminder: when entries are added to this list, update
54 ** the code in json_page_resultCodes() and json_err_cstr() (both in
55 ** json.c)!
56 **
57 */
 
58 enum FossilJsonCodes {
59 FSL_JSON_W_START = 0,
60 FSL_JSON_W_UNKNOWN /*+1*/,
61 FSL_JSON_W_ROW_TO_JSON_FAILED /*+2*/,
62 FSL_JSON_W_COL_TO_JSON_FAILED /*+3*/,
@@ -263,7 +266,8 @@
263
264 enum json_get_changed_files_flags {
265 json_get_changed_files_ELIDE_PARENT = 1 << 0
266 };
267
 
268 #endif/*FOSSIL_JSON_DETAIL_H_INCLUDED*/
269 #endif /* FOSSIL_ENABLE_JSON */
270
--- src/json_detail.h
+++ src/json_detail.h
@@ -16,11 +16,13 @@
16 ** [email protected]
17 ** http://www.hwaci.com/drh/
18 **
19 */
20
21 #if !defined(_RC_COMPILE_)
22 #include "cson_amalgamation.h"
23 #endif /* !defined(_RC_COMPILE_) */
24
25 /**
26 FOSSIL_JSON_API_VERSION holds the date (YYYYMMDD) of the latest
27 "significant" change to the JSON API (a change in an interface or
28 new functionality). It is sent as part of the /json/version
@@ -53,10 +55,11 @@
55 ** Maintenance reminder: when entries are added to this list, update
56 ** the code in json_page_resultCodes() and json_err_cstr() (both in
57 ** json.c)!
58 **
59 */
60 #if !defined(_RC_COMPILE_)
61 enum FossilJsonCodes {
62 FSL_JSON_W_START = 0,
63 FSL_JSON_W_UNKNOWN /*+1*/,
64 FSL_JSON_W_ROW_TO_JSON_FAILED /*+2*/,
65 FSL_JSON_W_COL_TO_JSON_FAILED /*+3*/,
@@ -263,7 +266,8 @@
266
267 enum json_get_changed_files_flags {
268 json_get_changed_files_ELIDE_PARENT = 1 << 0
269 };
270
271 #endif /* !defined(_RC_COMPILE_) */
272 #endif/*FOSSIL_JSON_DETAIL_H_INCLUDED*/
273 #endif /* FOSSIL_ENABLE_JSON */
274
+8 -1
--- src/main.c
+++ src/main.c
@@ -349,11 +349,11 @@
349349
/*
350350
** Zero, unlock, and free the saved database encryption key now.
351351
*/
352352
db_unsave_encryption_key();
353353
#endif
354
-#if defined(_WIN32) || defined(__BIONIC__) && !defined(FOSSIL_HAVE_GETPASS)
354
+#if defined(_WIN32) || (defined(__BIONIC__) && !defined(FOSSIL_HAVE_GETPASS))
355355
/*
356356
** Free the secure getpass() buffer now.
357357
*/
358358
freepass();
359359
#endif
@@ -388,10 +388,17 @@
388388
*/
389389
if( fossil_getenv("TH1_DELETE_INTERP")!=0 ){
390390
if( g.interp ){
391391
Th_DeleteInterp(g.interp); g.interp = 0;
392392
}
393
+#if defined(TH_MEMDEBUG)
394
+ if( Th_GetOutstandingMalloc()!=0 ){
395
+ fossil_print("Th_GetOutstandingMalloc() => %d\n",
396
+ Th_GetOutstandingMalloc());
397
+ }
398
+ assert( Th_GetOutstandingMalloc()==0 );
399
+#endif
393400
}
394401
}
395402
396403
/*
397404
** Convert all arguments from mbcs (or unicode) to UTF-8. Then
398405
--- src/main.c
+++ src/main.c
@@ -349,11 +349,11 @@
349 /*
350 ** Zero, unlock, and free the saved database encryption key now.
351 */
352 db_unsave_encryption_key();
353 #endif
354 #if defined(_WIN32) || defined(__BIONIC__) && !defined(FOSSIL_HAVE_GETPASS)
355 /*
356 ** Free the secure getpass() buffer now.
357 */
358 freepass();
359 #endif
@@ -388,10 +388,17 @@
388 */
389 if( fossil_getenv("TH1_DELETE_INTERP")!=0 ){
390 if( g.interp ){
391 Th_DeleteInterp(g.interp); g.interp = 0;
392 }
 
 
 
 
 
 
 
393 }
394 }
395
396 /*
397 ** Convert all arguments from mbcs (or unicode) to UTF-8. Then
398
--- src/main.c
+++ src/main.c
@@ -349,11 +349,11 @@
349 /*
350 ** Zero, unlock, and free the saved database encryption key now.
351 */
352 db_unsave_encryption_key();
353 #endif
354 #if defined(_WIN32) || (defined(__BIONIC__) && !defined(FOSSIL_HAVE_GETPASS))
355 /*
356 ** Free the secure getpass() buffer now.
357 */
358 freepass();
359 #endif
@@ -388,10 +388,17 @@
388 */
389 if( fossil_getenv("TH1_DELETE_INTERP")!=0 ){
390 if( g.interp ){
391 Th_DeleteInterp(g.interp); g.interp = 0;
392 }
393 #if defined(TH_MEMDEBUG)
394 if( Th_GetOutstandingMalloc()!=0 ){
395 fossil_print("Th_GetOutstandingMalloc() => %d\n",
396 Th_GetOutstandingMalloc());
397 }
398 assert( Th_GetOutstandingMalloc()==0 );
399 #endif
400 }
401 }
402
403 /*
404 ** Convert all arguments from mbcs (or unicode) to UTF-8. Then
405
-12
--- src/main.mk
+++ src/main.mk
@@ -154,11 +154,10 @@
154154
$(SRCDIR)/user.c \
155155
$(SRCDIR)/utf8.c \
156156
$(SRCDIR)/util.c \
157157
$(SRCDIR)/verify.c \
158158
$(SRCDIR)/vfile.c \
159
- $(SRCDIR)/webmail.c \
160159
$(SRCDIR)/wiki.c \
161160
$(SRCDIR)/wikiformat.c \
162161
$(SRCDIR)/winfile.c \
163162
$(SRCDIR)/winhttp.c \
164163
$(SRCDIR)/xfer.c \
@@ -411,11 +410,10 @@
411410
$(OBJDIR)/user_.c \
412411
$(OBJDIR)/utf8_.c \
413412
$(OBJDIR)/util_.c \
414413
$(OBJDIR)/verify_.c \
415414
$(OBJDIR)/vfile_.c \
416
- $(OBJDIR)/webmail_.c \
417415
$(OBJDIR)/wiki_.c \
418416
$(OBJDIR)/wikiformat_.c \
419417
$(OBJDIR)/winfile_.c \
420418
$(OBJDIR)/winhttp_.c \
421419
$(OBJDIR)/xfer_.c \
@@ -561,11 +559,10 @@
561559
$(OBJDIR)/user.o \
562560
$(OBJDIR)/utf8.o \
563561
$(OBJDIR)/util.o \
564562
$(OBJDIR)/verify.o \
565563
$(OBJDIR)/vfile.o \
566
- $(OBJDIR)/webmail.o \
567564
$(OBJDIR)/wiki.o \
568565
$(OBJDIR)/wikiformat.o \
569566
$(OBJDIR)/winfile.o \
570567
$(OBJDIR)/winhttp.o \
571568
$(OBJDIR)/xfer.o \
@@ -901,11 +898,10 @@
901898
$(OBJDIR)/user_.c:$(OBJDIR)/user.h \
902899
$(OBJDIR)/utf8_.c:$(OBJDIR)/utf8.h \
903900
$(OBJDIR)/util_.c:$(OBJDIR)/util.h \
904901
$(OBJDIR)/verify_.c:$(OBJDIR)/verify.h \
905902
$(OBJDIR)/vfile_.c:$(OBJDIR)/vfile.h \
906
- $(OBJDIR)/webmail_.c:$(OBJDIR)/webmail.h \
907903
$(OBJDIR)/wiki_.c:$(OBJDIR)/wiki.h \
908904
$(OBJDIR)/wikiformat_.c:$(OBJDIR)/wikiformat.h \
909905
$(OBJDIR)/winfile_.c:$(OBJDIR)/winfile.h \
910906
$(OBJDIR)/winhttp_.c:$(OBJDIR)/winhttp.h \
911907
$(OBJDIR)/xfer_.c:$(OBJDIR)/xfer.h \
@@ -2036,18 +2032,10 @@
20362032
$(OBJDIR)/vfile.o: $(OBJDIR)/vfile_.c $(OBJDIR)/vfile.h $(SRCDIR)/config.h
20372033
$(XTCC) -o $(OBJDIR)/vfile.o -c $(OBJDIR)/vfile_.c
20382034
20392035
$(OBJDIR)/vfile.h: $(OBJDIR)/headers
20402036
2041
-$(OBJDIR)/webmail_.c: $(SRCDIR)/webmail.c $(OBJDIR)/translate
2042
- $(OBJDIR)/translate $(SRCDIR)/webmail.c >$@
2043
-
2044
-$(OBJDIR)/webmail.o: $(OBJDIR)/webmail_.c $(OBJDIR)/webmail.h $(SRCDIR)/config.h
2045
- $(XTCC) -o $(OBJDIR)/webmail.o -c $(OBJDIR)/webmail_.c
2046
-
2047
-$(OBJDIR)/webmail.h: $(OBJDIR)/headers
2048
-
20492037
$(OBJDIR)/wiki_.c: $(SRCDIR)/wiki.c $(OBJDIR)/translate
20502038
$(OBJDIR)/translate $(SRCDIR)/wiki.c >$@
20512039
20522040
$(OBJDIR)/wiki.o: $(OBJDIR)/wiki_.c $(OBJDIR)/wiki.h $(SRCDIR)/config.h
20532041
$(XTCC) -o $(OBJDIR)/wiki.o -c $(OBJDIR)/wiki_.c
20542042
--- src/main.mk
+++ src/main.mk
@@ -154,11 +154,10 @@
154 $(SRCDIR)/user.c \
155 $(SRCDIR)/utf8.c \
156 $(SRCDIR)/util.c \
157 $(SRCDIR)/verify.c \
158 $(SRCDIR)/vfile.c \
159 $(SRCDIR)/webmail.c \
160 $(SRCDIR)/wiki.c \
161 $(SRCDIR)/wikiformat.c \
162 $(SRCDIR)/winfile.c \
163 $(SRCDIR)/winhttp.c \
164 $(SRCDIR)/xfer.c \
@@ -411,11 +410,10 @@
411 $(OBJDIR)/user_.c \
412 $(OBJDIR)/utf8_.c \
413 $(OBJDIR)/util_.c \
414 $(OBJDIR)/verify_.c \
415 $(OBJDIR)/vfile_.c \
416 $(OBJDIR)/webmail_.c \
417 $(OBJDIR)/wiki_.c \
418 $(OBJDIR)/wikiformat_.c \
419 $(OBJDIR)/winfile_.c \
420 $(OBJDIR)/winhttp_.c \
421 $(OBJDIR)/xfer_.c \
@@ -561,11 +559,10 @@
561 $(OBJDIR)/user.o \
562 $(OBJDIR)/utf8.o \
563 $(OBJDIR)/util.o \
564 $(OBJDIR)/verify.o \
565 $(OBJDIR)/vfile.o \
566 $(OBJDIR)/webmail.o \
567 $(OBJDIR)/wiki.o \
568 $(OBJDIR)/wikiformat.o \
569 $(OBJDIR)/winfile.o \
570 $(OBJDIR)/winhttp.o \
571 $(OBJDIR)/xfer.o \
@@ -901,11 +898,10 @@
901 $(OBJDIR)/user_.c:$(OBJDIR)/user.h \
902 $(OBJDIR)/utf8_.c:$(OBJDIR)/utf8.h \
903 $(OBJDIR)/util_.c:$(OBJDIR)/util.h \
904 $(OBJDIR)/verify_.c:$(OBJDIR)/verify.h \
905 $(OBJDIR)/vfile_.c:$(OBJDIR)/vfile.h \
906 $(OBJDIR)/webmail_.c:$(OBJDIR)/webmail.h \
907 $(OBJDIR)/wiki_.c:$(OBJDIR)/wiki.h \
908 $(OBJDIR)/wikiformat_.c:$(OBJDIR)/wikiformat.h \
909 $(OBJDIR)/winfile_.c:$(OBJDIR)/winfile.h \
910 $(OBJDIR)/winhttp_.c:$(OBJDIR)/winhttp.h \
911 $(OBJDIR)/xfer_.c:$(OBJDIR)/xfer.h \
@@ -2036,18 +2032,10 @@
2036 $(OBJDIR)/vfile.o: $(OBJDIR)/vfile_.c $(OBJDIR)/vfile.h $(SRCDIR)/config.h
2037 $(XTCC) -o $(OBJDIR)/vfile.o -c $(OBJDIR)/vfile_.c
2038
2039 $(OBJDIR)/vfile.h: $(OBJDIR)/headers
2040
2041 $(OBJDIR)/webmail_.c: $(SRCDIR)/webmail.c $(OBJDIR)/translate
2042 $(OBJDIR)/translate $(SRCDIR)/webmail.c >$@
2043
2044 $(OBJDIR)/webmail.o: $(OBJDIR)/webmail_.c $(OBJDIR)/webmail.h $(SRCDIR)/config.h
2045 $(XTCC) -o $(OBJDIR)/webmail.o -c $(OBJDIR)/webmail_.c
2046
2047 $(OBJDIR)/webmail.h: $(OBJDIR)/headers
2048
2049 $(OBJDIR)/wiki_.c: $(SRCDIR)/wiki.c $(OBJDIR)/translate
2050 $(OBJDIR)/translate $(SRCDIR)/wiki.c >$@
2051
2052 $(OBJDIR)/wiki.o: $(OBJDIR)/wiki_.c $(OBJDIR)/wiki.h $(SRCDIR)/config.h
2053 $(XTCC) -o $(OBJDIR)/wiki.o -c $(OBJDIR)/wiki_.c
2054
--- src/main.mk
+++ src/main.mk
@@ -154,11 +154,10 @@
154 $(SRCDIR)/user.c \
155 $(SRCDIR)/utf8.c \
156 $(SRCDIR)/util.c \
157 $(SRCDIR)/verify.c \
158 $(SRCDIR)/vfile.c \
 
159 $(SRCDIR)/wiki.c \
160 $(SRCDIR)/wikiformat.c \
161 $(SRCDIR)/winfile.c \
162 $(SRCDIR)/winhttp.c \
163 $(SRCDIR)/xfer.c \
@@ -411,11 +410,10 @@
410 $(OBJDIR)/user_.c \
411 $(OBJDIR)/utf8_.c \
412 $(OBJDIR)/util_.c \
413 $(OBJDIR)/verify_.c \
414 $(OBJDIR)/vfile_.c \
 
415 $(OBJDIR)/wiki_.c \
416 $(OBJDIR)/wikiformat_.c \
417 $(OBJDIR)/winfile_.c \
418 $(OBJDIR)/winhttp_.c \
419 $(OBJDIR)/xfer_.c \
@@ -561,11 +559,10 @@
559 $(OBJDIR)/user.o \
560 $(OBJDIR)/utf8.o \
561 $(OBJDIR)/util.o \
562 $(OBJDIR)/verify.o \
563 $(OBJDIR)/vfile.o \
 
564 $(OBJDIR)/wiki.o \
565 $(OBJDIR)/wikiformat.o \
566 $(OBJDIR)/winfile.o \
567 $(OBJDIR)/winhttp.o \
568 $(OBJDIR)/xfer.o \
@@ -901,11 +898,10 @@
898 $(OBJDIR)/user_.c:$(OBJDIR)/user.h \
899 $(OBJDIR)/utf8_.c:$(OBJDIR)/utf8.h \
900 $(OBJDIR)/util_.c:$(OBJDIR)/util.h \
901 $(OBJDIR)/verify_.c:$(OBJDIR)/verify.h \
902 $(OBJDIR)/vfile_.c:$(OBJDIR)/vfile.h \
 
903 $(OBJDIR)/wiki_.c:$(OBJDIR)/wiki.h \
904 $(OBJDIR)/wikiformat_.c:$(OBJDIR)/wikiformat.h \
905 $(OBJDIR)/winfile_.c:$(OBJDIR)/winfile.h \
906 $(OBJDIR)/winhttp_.c:$(OBJDIR)/winhttp.h \
907 $(OBJDIR)/xfer_.c:$(OBJDIR)/xfer.h \
@@ -2036,18 +2032,10 @@
2032 $(OBJDIR)/vfile.o: $(OBJDIR)/vfile_.c $(OBJDIR)/vfile.h $(SRCDIR)/config.h
2033 $(XTCC) -o $(OBJDIR)/vfile.o -c $(OBJDIR)/vfile_.c
2034
2035 $(OBJDIR)/vfile.h: $(OBJDIR)/headers
2036
 
 
 
 
 
 
 
 
2037 $(OBJDIR)/wiki_.c: $(SRCDIR)/wiki.c $(OBJDIR)/translate
2038 $(OBJDIR)/translate $(SRCDIR)/wiki.c >$@
2039
2040 $(OBJDIR)/wiki.o: $(OBJDIR)/wiki_.c $(OBJDIR)/wiki.h $(SRCDIR)/config.h
2041 $(XTCC) -o $(OBJDIR)/wiki.o -c $(OBJDIR)/wiki_.c
2042
-12
--- src/main.mk
+++ src/main.mk
@@ -154,11 +154,10 @@
154154
$(SRCDIR)/user.c \
155155
$(SRCDIR)/utf8.c \
156156
$(SRCDIR)/util.c \
157157
$(SRCDIR)/verify.c \
158158
$(SRCDIR)/vfile.c \
159
- $(SRCDIR)/webmail.c \
160159
$(SRCDIR)/wiki.c \
161160
$(SRCDIR)/wikiformat.c \
162161
$(SRCDIR)/winfile.c \
163162
$(SRCDIR)/winhttp.c \
164163
$(SRCDIR)/xfer.c \
@@ -411,11 +410,10 @@
411410
$(OBJDIR)/user_.c \
412411
$(OBJDIR)/utf8_.c \
413412
$(OBJDIR)/util_.c \
414413
$(OBJDIR)/verify_.c \
415414
$(OBJDIR)/vfile_.c \
416
- $(OBJDIR)/webmail_.c \
417415
$(OBJDIR)/wiki_.c \
418416
$(OBJDIR)/wikiformat_.c \
419417
$(OBJDIR)/winfile_.c \
420418
$(OBJDIR)/winhttp_.c \
421419
$(OBJDIR)/xfer_.c \
@@ -561,11 +559,10 @@
561559
$(OBJDIR)/user.o \
562560
$(OBJDIR)/utf8.o \
563561
$(OBJDIR)/util.o \
564562
$(OBJDIR)/verify.o \
565563
$(OBJDIR)/vfile.o \
566
- $(OBJDIR)/webmail.o \
567564
$(OBJDIR)/wiki.o \
568565
$(OBJDIR)/wikiformat.o \
569566
$(OBJDIR)/winfile.o \
570567
$(OBJDIR)/winhttp.o \
571568
$(OBJDIR)/xfer.o \
@@ -901,11 +898,10 @@
901898
$(OBJDIR)/user_.c:$(OBJDIR)/user.h \
902899
$(OBJDIR)/utf8_.c:$(OBJDIR)/utf8.h \
903900
$(OBJDIR)/util_.c:$(OBJDIR)/util.h \
904901
$(OBJDIR)/verify_.c:$(OBJDIR)/verify.h \
905902
$(OBJDIR)/vfile_.c:$(OBJDIR)/vfile.h \
906
- $(OBJDIR)/webmail_.c:$(OBJDIR)/webmail.h \
907903
$(OBJDIR)/wiki_.c:$(OBJDIR)/wiki.h \
908904
$(OBJDIR)/wikiformat_.c:$(OBJDIR)/wikiformat.h \
909905
$(OBJDIR)/winfile_.c:$(OBJDIR)/winfile.h \
910906
$(OBJDIR)/winhttp_.c:$(OBJDIR)/winhttp.h \
911907
$(OBJDIR)/xfer_.c:$(OBJDIR)/xfer.h \
@@ -2036,18 +2032,10 @@
20362032
$(OBJDIR)/vfile.o: $(OBJDIR)/vfile_.c $(OBJDIR)/vfile.h $(SRCDIR)/config.h
20372033
$(XTCC) -o $(OBJDIR)/vfile.o -c $(OBJDIR)/vfile_.c
20382034
20392035
$(OBJDIR)/vfile.h: $(OBJDIR)/headers
20402036
2041
-$(OBJDIR)/webmail_.c: $(SRCDIR)/webmail.c $(OBJDIR)/translate
2042
- $(OBJDIR)/translate $(SRCDIR)/webmail.c >$@
2043
-
2044
-$(OBJDIR)/webmail.o: $(OBJDIR)/webmail_.c $(OBJDIR)/webmail.h $(SRCDIR)/config.h
2045
- $(XTCC) -o $(OBJDIR)/webmail.o -c $(OBJDIR)/webmail_.c
2046
-
2047
-$(OBJDIR)/webmail.h: $(OBJDIR)/headers
2048
-
20492037
$(OBJDIR)/wiki_.c: $(SRCDIR)/wiki.c $(OBJDIR)/translate
20502038
$(OBJDIR)/translate $(SRCDIR)/wiki.c >$@
20512039
20522040
$(OBJDIR)/wiki.o: $(OBJDIR)/wiki_.c $(OBJDIR)/wiki.h $(SRCDIR)/config.h
20532041
$(XTCC) -o $(OBJDIR)/wiki.o -c $(OBJDIR)/wiki_.c
20542042
--- src/main.mk
+++ src/main.mk
@@ -154,11 +154,10 @@
154 $(SRCDIR)/user.c \
155 $(SRCDIR)/utf8.c \
156 $(SRCDIR)/util.c \
157 $(SRCDIR)/verify.c \
158 $(SRCDIR)/vfile.c \
159 $(SRCDIR)/webmail.c \
160 $(SRCDIR)/wiki.c \
161 $(SRCDIR)/wikiformat.c \
162 $(SRCDIR)/winfile.c \
163 $(SRCDIR)/winhttp.c \
164 $(SRCDIR)/xfer.c \
@@ -411,11 +410,10 @@
411 $(OBJDIR)/user_.c \
412 $(OBJDIR)/utf8_.c \
413 $(OBJDIR)/util_.c \
414 $(OBJDIR)/verify_.c \
415 $(OBJDIR)/vfile_.c \
416 $(OBJDIR)/webmail_.c \
417 $(OBJDIR)/wiki_.c \
418 $(OBJDIR)/wikiformat_.c \
419 $(OBJDIR)/winfile_.c \
420 $(OBJDIR)/winhttp_.c \
421 $(OBJDIR)/xfer_.c \
@@ -561,11 +559,10 @@
561 $(OBJDIR)/user.o \
562 $(OBJDIR)/utf8.o \
563 $(OBJDIR)/util.o \
564 $(OBJDIR)/verify.o \
565 $(OBJDIR)/vfile.o \
566 $(OBJDIR)/webmail.o \
567 $(OBJDIR)/wiki.o \
568 $(OBJDIR)/wikiformat.o \
569 $(OBJDIR)/winfile.o \
570 $(OBJDIR)/winhttp.o \
571 $(OBJDIR)/xfer.o \
@@ -901,11 +898,10 @@
901 $(OBJDIR)/user_.c:$(OBJDIR)/user.h \
902 $(OBJDIR)/utf8_.c:$(OBJDIR)/utf8.h \
903 $(OBJDIR)/util_.c:$(OBJDIR)/util.h \
904 $(OBJDIR)/verify_.c:$(OBJDIR)/verify.h \
905 $(OBJDIR)/vfile_.c:$(OBJDIR)/vfile.h \
906 $(OBJDIR)/webmail_.c:$(OBJDIR)/webmail.h \
907 $(OBJDIR)/wiki_.c:$(OBJDIR)/wiki.h \
908 $(OBJDIR)/wikiformat_.c:$(OBJDIR)/wikiformat.h \
909 $(OBJDIR)/winfile_.c:$(OBJDIR)/winfile.h \
910 $(OBJDIR)/winhttp_.c:$(OBJDIR)/winhttp.h \
911 $(OBJDIR)/xfer_.c:$(OBJDIR)/xfer.h \
@@ -2036,18 +2032,10 @@
2036 $(OBJDIR)/vfile.o: $(OBJDIR)/vfile_.c $(OBJDIR)/vfile.h $(SRCDIR)/config.h
2037 $(XTCC) -o $(OBJDIR)/vfile.o -c $(OBJDIR)/vfile_.c
2038
2039 $(OBJDIR)/vfile.h: $(OBJDIR)/headers
2040
2041 $(OBJDIR)/webmail_.c: $(SRCDIR)/webmail.c $(OBJDIR)/translate
2042 $(OBJDIR)/translate $(SRCDIR)/webmail.c >$@
2043
2044 $(OBJDIR)/webmail.o: $(OBJDIR)/webmail_.c $(OBJDIR)/webmail.h $(SRCDIR)/config.h
2045 $(XTCC) -o $(OBJDIR)/webmail.o -c $(OBJDIR)/webmail_.c
2046
2047 $(OBJDIR)/webmail.h: $(OBJDIR)/headers
2048
2049 $(OBJDIR)/wiki_.c: $(SRCDIR)/wiki.c $(OBJDIR)/translate
2050 $(OBJDIR)/translate $(SRCDIR)/wiki.c >$@
2051
2052 $(OBJDIR)/wiki.o: $(OBJDIR)/wiki_.c $(OBJDIR)/wiki.h $(SRCDIR)/config.h
2053 $(XTCC) -o $(OBJDIR)/wiki.o -c $(OBJDIR)/wiki_.c
2054
--- src/main.mk
+++ src/main.mk
@@ -154,11 +154,10 @@
154 $(SRCDIR)/user.c \
155 $(SRCDIR)/utf8.c \
156 $(SRCDIR)/util.c \
157 $(SRCDIR)/verify.c \
158 $(SRCDIR)/vfile.c \
 
159 $(SRCDIR)/wiki.c \
160 $(SRCDIR)/wikiformat.c \
161 $(SRCDIR)/winfile.c \
162 $(SRCDIR)/winhttp.c \
163 $(SRCDIR)/xfer.c \
@@ -411,11 +410,10 @@
410 $(OBJDIR)/user_.c \
411 $(OBJDIR)/utf8_.c \
412 $(OBJDIR)/util_.c \
413 $(OBJDIR)/verify_.c \
414 $(OBJDIR)/vfile_.c \
 
415 $(OBJDIR)/wiki_.c \
416 $(OBJDIR)/wikiformat_.c \
417 $(OBJDIR)/winfile_.c \
418 $(OBJDIR)/winhttp_.c \
419 $(OBJDIR)/xfer_.c \
@@ -561,11 +559,10 @@
559 $(OBJDIR)/user.o \
560 $(OBJDIR)/utf8.o \
561 $(OBJDIR)/util.o \
562 $(OBJDIR)/verify.o \
563 $(OBJDIR)/vfile.o \
 
564 $(OBJDIR)/wiki.o \
565 $(OBJDIR)/wikiformat.o \
566 $(OBJDIR)/winfile.o \
567 $(OBJDIR)/winhttp.o \
568 $(OBJDIR)/xfer.o \
@@ -901,11 +898,10 @@
898 $(OBJDIR)/user_.c:$(OBJDIR)/user.h \
899 $(OBJDIR)/utf8_.c:$(OBJDIR)/utf8.h \
900 $(OBJDIR)/util_.c:$(OBJDIR)/util.h \
901 $(OBJDIR)/verify_.c:$(OBJDIR)/verify.h \
902 $(OBJDIR)/vfile_.c:$(OBJDIR)/vfile.h \
 
903 $(OBJDIR)/wiki_.c:$(OBJDIR)/wiki.h \
904 $(OBJDIR)/wikiformat_.c:$(OBJDIR)/wikiformat.h \
905 $(OBJDIR)/winfile_.c:$(OBJDIR)/winfile.h \
906 $(OBJDIR)/winhttp_.c:$(OBJDIR)/winhttp.h \
907 $(OBJDIR)/xfer_.c:$(OBJDIR)/xfer.h \
@@ -2036,18 +2032,10 @@
2032 $(OBJDIR)/vfile.o: $(OBJDIR)/vfile_.c $(OBJDIR)/vfile.h $(SRCDIR)/config.h
2033 $(XTCC) -o $(OBJDIR)/vfile.o -c $(OBJDIR)/vfile_.c
2034
2035 $(OBJDIR)/vfile.h: $(OBJDIR)/headers
2036
 
 
 
 
 
 
 
 
2037 $(OBJDIR)/wiki_.c: $(SRCDIR)/wiki.c $(OBJDIR)/translate
2038 $(OBJDIR)/translate $(SRCDIR)/wiki.c >$@
2039
2040 $(OBJDIR)/wiki.o: $(OBJDIR)/wiki_.c $(OBJDIR)/wiki.h $(SRCDIR)/config.h
2041 $(XTCC) -o $(OBJDIR)/wiki.o -c $(OBJDIR)/wiki_.c
2042
+38 -11
--- src/makemake.tcl
+++ src/makemake.tcl
@@ -1,23 +1,34 @@
11
#!/usr/bin/tclsh
22
#
3
-# Run this Tcl script to generate the various makefiles for a variety
4
-# of platforms. Files generated include:
3
+# ### Run this Tcl script EVERY time you modify it in any way! ###
4
+#
5
+# This Tcl script generates make files for various platforms. The makefiles
6
+# then need to be committed.
7
+#
8
+# If you modify this file then:
9
+#
10
+# 1. cd src; tclsh makemake.tcl
11
+#
12
+# 2. if errors are reported, fix them and go to step 1
13
+#
14
+# 3. if "fossil diff" reports changes in any of the generated
15
+# files, commit the changed files to the repo
16
+#
17
+# Files generated include:
518
#
619
# src/main.mk # makefile for all unix systems
720
# win/Makefile.mingw # makefile for mingw on windows
821
# win/Makefile.* # makefiles for other windows compilers
922
#
10
-# Run this script while in the "src" subdirectory. Like this:
11
-#
12
-# tclsh makemake.tcl
13
-#
1423
# Add new source files by listing the files (without their .c suffix)
1524
# in the "src" variable. Add new resource files to the "extra_files"
1625
# variable. There are other variables that you can alter, down to
1726
# the "STOP HERE" comment. The stuff below "STOP HERE" should rarely need
18
-# to change.
27
+# to change. After modification, go to step 1 above.
28
+#
29
+# Delete unused source files in the "src" variable, then go to step 1 above.
1930
#
2031
#############################################################################
2132
2233
# Basenames of all source files that get preprocessed using
2334
# "translate" and "makeheaders". To add new C-language source files to the
@@ -164,11 +175,10 @@
164175
user
165176
utf8
166177
util
167178
verify
168179
vfile
169
- webmail
170180
wiki
171181
wikiformat
172182
winfile
173183
winhttp
174184
xfer
@@ -1468,11 +1478,11 @@
14681478
OBJDIR = $(T)
14691479
OX = $(OBJDIR)
14701480
O = .obj
14711481
E = .exe
14721482
P = .pdb
1473
-OPTLEVEL= /Os
1483
+DBGOPTS = /Od
14741484
14751485
INSTALLDIR = .
14761486
!ifdef DESTDIR
14771487
INSTALLDIR = $(DESTDIR)\$(INSTALLDIR)
14781488
!endif
@@ -1491,10 +1501,15 @@
14911501
# Perl is only necessary if OpenSSL support is enabled and it is built from
14921502
# source code. The PERLDIR environment variable, if it exists, should point
14931503
# to the directory containing the main Perl executable specified here (i.e.
14941504
# "perl.exe").
14951505
PERL = perl.exe
1506
+
1507
+# Enable use of available compiler optimizations?
1508
+!ifndef OPTIMIZATIONS
1509
+OPTIMIZATIONS = 2
1510
+!endif
14961511
14971512
# Enable debugging symbols?
14981513
!ifndef DEBUG
14991514
DEBUG = 0
15001515
!endif
@@ -1663,16 +1678,28 @@
16631678
CRTFLAGS = /MTd
16641679
!else
16651680
CRTFLAGS = /MT
16661681
!endif
16671682
!endif
1683
+
1684
+!if $(OPTIMIZATIONS)>3
1685
+RELOPTS = /Os
1686
+!elseif $(OPTIMIZATIONS)>2
1687
+RELOPTS = /Ox
1688
+!elseif $(OPTIMIZATIONS)>1
1689
+RELOPTS = /O2
1690
+!elseif $(OPTIMIZATIONS)>0
1691
+RELOPTS = /O1
1692
+!else
1693
+RELOPTS =
1694
+!endif
16681695
16691696
!if $(DEBUG)!=0
1670
-CFLAGS = $(CFLAGS) /Zi $(CRTFLAGS) /Od /DFOSSIL_DEBUG
1697
+CFLAGS = $(CFLAGS) /Zi $(CRTFLAGS) $(DBGOPTS) /DFOSSIL_DEBUG /DTH_MEMDEBUG
16711698
LDFLAGS = $(LDFLAGS) /DEBUG
16721699
!else
1673
-CFLAGS = $(CFLAGS) $(CRTFLAGS) $(OPTLEVEL)
1700
+CFLAGS = $(CFLAGS) $(CRTFLAGS) $(RELOPTS)
16741701
!endif
16751702
16761703
BCC = $(CC) $(CFLAGS)
16771704
TCC = $(CC) /c $(CFLAGS) $(MSCDEF) $(INCL)
16781705
RCC = $(RC) /D_WIN32 /D_MSC_VER $(MSCDEF) $(INCL)
16791706
--- src/makemake.tcl
+++ src/makemake.tcl
@@ -1,23 +1,34 @@
1 #!/usr/bin/tclsh
2 #
3 # Run this Tcl script to generate the various makefiles for a variety
4 # of platforms. Files generated include:
 
 
 
 
 
 
 
 
 
 
 
 
 
5 #
6 # src/main.mk # makefile for all unix systems
7 # win/Makefile.mingw # makefile for mingw on windows
8 # win/Makefile.* # makefiles for other windows compilers
9 #
10 # Run this script while in the "src" subdirectory. Like this:
11 #
12 # tclsh makemake.tcl
13 #
14 # Add new source files by listing the files (without their .c suffix)
15 # in the "src" variable. Add new resource files to the "extra_files"
16 # variable. There are other variables that you can alter, down to
17 # the "STOP HERE" comment. The stuff below "STOP HERE" should rarely need
18 # to change.
 
 
19 #
20 #############################################################################
21
22 # Basenames of all source files that get preprocessed using
23 # "translate" and "makeheaders". To add new C-language source files to the
@@ -164,11 +175,10 @@
164 user
165 utf8
166 util
167 verify
168 vfile
169 webmail
170 wiki
171 wikiformat
172 winfile
173 winhttp
174 xfer
@@ -1468,11 +1478,11 @@
1468 OBJDIR = $(T)
1469 OX = $(OBJDIR)
1470 O = .obj
1471 E = .exe
1472 P = .pdb
1473 OPTLEVEL= /Os
1474
1475 INSTALLDIR = .
1476 !ifdef DESTDIR
1477 INSTALLDIR = $(DESTDIR)\$(INSTALLDIR)
1478 !endif
@@ -1491,10 +1501,15 @@
1491 # Perl is only necessary if OpenSSL support is enabled and it is built from
1492 # source code. The PERLDIR environment variable, if it exists, should point
1493 # to the directory containing the main Perl executable specified here (i.e.
1494 # "perl.exe").
1495 PERL = perl.exe
 
 
 
 
 
1496
1497 # Enable debugging symbols?
1498 !ifndef DEBUG
1499 DEBUG = 0
1500 !endif
@@ -1663,16 +1678,28 @@
1663 CRTFLAGS = /MTd
1664 !else
1665 CRTFLAGS = /MT
1666 !endif
1667 !endif
 
 
 
 
 
 
 
 
 
 
 
 
1668
1669 !if $(DEBUG)!=0
1670 CFLAGS = $(CFLAGS) /Zi $(CRTFLAGS) /Od /DFOSSIL_DEBUG
1671 LDFLAGS = $(LDFLAGS) /DEBUG
1672 !else
1673 CFLAGS = $(CFLAGS) $(CRTFLAGS) $(OPTLEVEL)
1674 !endif
1675
1676 BCC = $(CC) $(CFLAGS)
1677 TCC = $(CC) /c $(CFLAGS) $(MSCDEF) $(INCL)
1678 RCC = $(RC) /D_WIN32 /D_MSC_VER $(MSCDEF) $(INCL)
1679
--- src/makemake.tcl
+++ src/makemake.tcl
@@ -1,23 +1,34 @@
1 #!/usr/bin/tclsh
2 #
3 # ### Run this Tcl script EVERY time you modify it in any way! ###
4 #
5 # This Tcl script generates make files for various platforms. The makefiles
6 # then need to be committed.
7 #
8 # If you modify this file then:
9 #
10 # 1. cd src; tclsh makemake.tcl
11 #
12 # 2. if errors are reported, fix them and go to step 1
13 #
14 # 3. if "fossil diff" reports changes in any of the generated
15 # files, commit the changed files to the repo
16 #
17 # Files generated include:
18 #
19 # src/main.mk # makefile for all unix systems
20 # win/Makefile.mingw # makefile for mingw on windows
21 # win/Makefile.* # makefiles for other windows compilers
22 #
 
 
 
 
23 # Add new source files by listing the files (without their .c suffix)
24 # in the "src" variable. Add new resource files to the "extra_files"
25 # variable. There are other variables that you can alter, down to
26 # the "STOP HERE" comment. The stuff below "STOP HERE" should rarely need
27 # to change. After modification, go to step 1 above.
28 #
29 # Delete unused source files in the "src" variable, then go to step 1 above.
30 #
31 #############################################################################
32
33 # Basenames of all source files that get preprocessed using
34 # "translate" and "makeheaders". To add new C-language source files to the
@@ -164,11 +175,10 @@
175 user
176 utf8
177 util
178 verify
179 vfile
 
180 wiki
181 wikiformat
182 winfile
183 winhttp
184 xfer
@@ -1468,11 +1478,11 @@
1478 OBJDIR = $(T)
1479 OX = $(OBJDIR)
1480 O = .obj
1481 E = .exe
1482 P = .pdb
1483 DBGOPTS = /Od
1484
1485 INSTALLDIR = .
1486 !ifdef DESTDIR
1487 INSTALLDIR = $(DESTDIR)\$(INSTALLDIR)
1488 !endif
@@ -1491,10 +1501,15 @@
1501 # Perl is only necessary if OpenSSL support is enabled and it is built from
1502 # source code. The PERLDIR environment variable, if it exists, should point
1503 # to the directory containing the main Perl executable specified here (i.e.
1504 # "perl.exe").
1505 PERL = perl.exe
1506
1507 # Enable use of available compiler optimizations?
1508 !ifndef OPTIMIZATIONS
1509 OPTIMIZATIONS = 2
1510 !endif
1511
1512 # Enable debugging symbols?
1513 !ifndef DEBUG
1514 DEBUG = 0
1515 !endif
@@ -1663,16 +1678,28 @@
1678 CRTFLAGS = /MTd
1679 !else
1680 CRTFLAGS = /MT
1681 !endif
1682 !endif
1683
1684 !if $(OPTIMIZATIONS)>3
1685 RELOPTS = /Os
1686 !elseif $(OPTIMIZATIONS)>2
1687 RELOPTS = /Ox
1688 !elseif $(OPTIMIZATIONS)>1
1689 RELOPTS = /O2
1690 !elseif $(OPTIMIZATIONS)>0
1691 RELOPTS = /O1
1692 !else
1693 RELOPTS =
1694 !endif
1695
1696 !if $(DEBUG)!=0
1697 CFLAGS = $(CFLAGS) /Zi $(CRTFLAGS) $(DBGOPTS) /DFOSSIL_DEBUG /DTH_MEMDEBUG
1698 LDFLAGS = $(LDFLAGS) /DEBUG
1699 !else
1700 CFLAGS = $(CFLAGS) $(CRTFLAGS) $(RELOPTS)
1701 !endif
1702
1703 BCC = $(CC) $(CFLAGS)
1704 TCC = $(CC) /c $(CFLAGS) $(MSCDEF) $(INCL)
1705 RCC = $(RC) /D_WIN32 /D_MSC_VER $(MSCDEF) $(INCL)
1706
+2 -2
--- src/printf.c
+++ src/printf.c
@@ -1100,11 +1100,11 @@
11001100
/*
11011101
** Avoid calling into the JSON support subsystem if it
11021102
** has not yet been initialized, e.g. early SQLite log
11031103
** messages, etc.
11041104
*/
1105
- assert(json_is_bootstrapped_early());
1105
+ if( !json_is_bootstrapped_early() ) json_bootstrap_early();
11061106
json_err( 0, z, 1 );
11071107
if( g.isHTTP && !g.json.preserveRc ){
11081108
rc = 0 /* avoid HTTP 500 */;
11091109
}
11101110
if( g.cgiOutput==1 ){
@@ -1230,11 +1230,11 @@
12301230
/*
12311231
** Avoid calling into the JSON support subsystem if it
12321232
** has not yet been initialized, e.g. early SQLite log
12331233
** messages, etc.
12341234
*/
1235
- assert(json_is_bootstrapped_early());
1235
+ if( !json_is_bootstrapped_early() ) json_bootstrap_early();
12361236
json_warn( FSL_JSON_W_UNKNOWN, "%s", z );
12371237
}else
12381238
#endif
12391239
{
12401240
if( g.cgiOutput==1 ){
12411241
--- src/printf.c
+++ src/printf.c
@@ -1100,11 +1100,11 @@
1100 /*
1101 ** Avoid calling into the JSON support subsystem if it
1102 ** has not yet been initialized, e.g. early SQLite log
1103 ** messages, etc.
1104 */
1105 assert(json_is_bootstrapped_early());
1106 json_err( 0, z, 1 );
1107 if( g.isHTTP && !g.json.preserveRc ){
1108 rc = 0 /* avoid HTTP 500 */;
1109 }
1110 if( g.cgiOutput==1 ){
@@ -1230,11 +1230,11 @@
1230 /*
1231 ** Avoid calling into the JSON support subsystem if it
1232 ** has not yet been initialized, e.g. early SQLite log
1233 ** messages, etc.
1234 */
1235 assert(json_is_bootstrapped_early());
1236 json_warn( FSL_JSON_W_UNKNOWN, "%s", z );
1237 }else
1238 #endif
1239 {
1240 if( g.cgiOutput==1 ){
1241
--- src/printf.c
+++ src/printf.c
@@ -1100,11 +1100,11 @@
1100 /*
1101 ** Avoid calling into the JSON support subsystem if it
1102 ** has not yet been initialized, e.g. early SQLite log
1103 ** messages, etc.
1104 */
1105 if( !json_is_bootstrapped_early() ) json_bootstrap_early();
1106 json_err( 0, z, 1 );
1107 if( g.isHTTP && !g.json.preserveRc ){
1108 rc = 0 /* avoid HTTP 500 */;
1109 }
1110 if( g.cgiOutput==1 ){
@@ -1230,11 +1230,11 @@
1230 /*
1231 ** Avoid calling into the JSON support subsystem if it
1232 ** has not yet been initialized, e.g. early SQLite log
1233 ** messages, etc.
1234 */
1235 if( !json_is_bootstrapped_early() ) json_bootstrap_early();
1236 json_warn( FSL_JSON_W_UNKNOWN, "%s", z );
1237 }else
1238 #endif
1239 {
1240 if( g.cgiOutput==1 ){
1241
+103 -91
--- src/shell.c
+++ src/shell.c
@@ -3662,12 +3662,12 @@
36623662
** set, then a new database is appended to the already existing file.
36633663
**
36643664
** (5) Otherwise, SQLITE_CANTOPEN is returned.
36653665
**
36663666
** To avoid unnecessary complications with the PENDING_BYTE, the size of
3667
-** the file containing the database is limited to 1GB. (1000013824 bytes)
3668
-** This VFS will not read or write past the 1GB mark. This restriction
3667
+** the file containing the database is limited to 1GiB. (1073741824 bytes)
3668
+** This VFS will not read or write past the 1GiB mark. This restriction
36693669
** might be lifted in future versions. For now, if you need a larger
36703670
** database, then keep it in a separate file.
36713671
**
36723672
** If the file being opened is a plain database (not an appended one), then
36733673
** this shim is a pass-through into the default underlying VFS. (rule 3)
@@ -3692,18 +3692,20 @@
36923692
36933693
/*
36943694
** Maximum size of the combined prefix + database + append-mark. This
36953695
** must be less than 0x40000000 to avoid locking issues on Windows.
36963696
*/
3697
-#define APND_MAX_SIZE (65536*15259)
3697
+#define APND_MAX_SIZE (0x40000000)
36983698
36993699
/*
3700
-** Size of storage page upon which to align appendvfs portion.
3700
+** Try to align the database to an even multiple of APND_ROUNDUP bytes.
37013701
*/
3702
-#ifndef APND_ROUNDUP_BITS
3703
-#define APND_ROUNDUP_BITS 12
3702
+#ifndef APND_ROUNDUP
3703
+#define APND_ROUNDUP 4096
37043704
#endif
3705
+#define APND_ALIGN_MASK ((sqlite3_int64)(APND_ROUNDUP-1))
3706
+#define APND_START_ROUNDUP(fsz) (((fsz)+APND_ALIGN_MASK) & ~APND_ALIGN_MASK)
37053707
37063708
/*
37073709
** Forward declaration of objects used by this utility
37083710
*/
37093711
typedef struct sqlite3_vfs ApndVfs;
@@ -3713,43 +3715,49 @@
37133715
** access to randomness, etc.
37143716
*/
37153717
#define ORIGVFS(p) ((sqlite3_vfs*)((p)->pAppData))
37163718
#define ORIGFILE(p) ((sqlite3_file*)(((ApndFile*)(p))+1))
37173719
3718
-/* Invariants for an open appendvfs file:
3719
- * Once an appendvfs file is opened, it will be in one of three states:
3720
- * State 0: Never written. Underlying file (if any) is unaltered.
3721
- * State 1: Append mark is persisted, content write is in progress.
3722
- * State 2: Append mark is persisted, content writes are complete.
3723
- *
3724
- * State 0 is persistent in the sense that nothing will have been done
3725
- * to the underlying file, including any attempt to convert it to an
3726
- * appendvfs file.
3727
- *
3728
- * State 1 is normally transitory. However, if a write operation ends
3729
- * abnormally (disk full, power loss, process kill, etc.), then State 1
3730
- * may be persistent on disk with an incomplete content write-out. This
3731
- * is logically equivalent to an interrupted write to an ordinary file,
3732
- * where some unknown portion of to-be-written data is persisted while
3733
- * the remainder is not. Database integrity in such cases is maintained
3734
- * (or not) by the same measures available for ordinary file access.
3735
- *
3736
- * State 2 is persistent under normal circumstances (when there is no
3737
- * abnormal termination of a write operation such that data provided
3738
- * to the underlying VFS write method has not yet reached storage.)
3739
- *
3740
- * In order to maintain the state invariant, the append mark is written
3741
- * in advance of content writes where any part of such content would
3742
- * overwrite an existing (or yet to be written) append mark.
3743
- */
3720
+/* An open appendvfs file
3721
+**
3722
+** An instance of this structure describes the appended database file.
3723
+** A separate sqlite3_file object is always appended. The appended
3724
+** sqlite3_file object (which can be accessed using ORIGFILE()) describes
3725
+** the entire file, including the prefix, the database, and the
3726
+** append-mark.
3727
+**
3728
+** The structure of an AppendVFS database is like this:
3729
+**
3730
+** +-------------+---------+----------+-------------+
3731
+** | prefix-file | padding | database | append-mark |
3732
+** +-------------+---------+----------+-------------+
3733
+** ^ ^
3734
+** | |
3735
+** iPgOne iMark
3736
+**
3737
+**
3738
+** "prefix file" - file onto which the database has been appended.
3739
+** "padding" - zero or more bytes inserted so that "database"
3740
+** starts on an APND_ROUNDUP boundary
3741
+** "database" - The SQLite database file
3742
+** "append-mark" - The 25-byte "Start-Of-SQLite3-NNNNNNNN" that indicates
3743
+** the offset from the start of prefix-file to the start
3744
+** of "database".
3745
+**
3746
+** The size of the database is iMark - iPgOne.
3747
+**
3748
+** The NNNNNNNN in the "Start-Of-SQLite3-NNNNNNNN" suffix is the value
3749
+** of iPgOne stored as a big-ending 64-bit integer.
3750
+**
3751
+** iMark will be the size of the underlying file minus 25 (APND_MARKSIZE).
3752
+** Or, iMark is -1 to indicate that it has not yet been written.
3753
+*/
37443754
struct ApndFile {
3745
- /* Access to IO methods of the underlying file */
3746
- sqlite3_file base;
3747
- /* File offset to beginning of appended content (unchanging) */
3748
- sqlite3_int64 iPgOne;
3749
- /* File offset of written append-mark, or -1 if unwritten */
3750
- sqlite3_int64 iMark;
3755
+ sqlite3_file base; /* Subclass. MUST BE FIRST! */
3756
+ sqlite3_int64 iPgOne; /* Offset to the start of the database */
3757
+ sqlite3_int64 iMark; /* Offset of the append mark. -1 if unwritten */
3758
+ /* Always followed by another sqlite3_file that describes the whole file */
37513759
};
37523760
37533761
/*
37543762
** Methods for ApndFile
37553763
*/
@@ -3875,11 +3883,11 @@
38753883
unsigned char a[APND_MARK_SIZE];
38763884
int i = APND_MARK_FOS_SZ;
38773885
int rc;
38783886
assert(pFile == ORIGFILE(paf));
38793887
memcpy(a, APND_MARK_PREFIX, APND_MARK_PREFIX_SZ);
3880
- while (--i >= 0) {
3888
+ while( --i >= 0 ){
38813889
a[APND_MARK_PREFIX_SZ+i] = (unsigned char)(iPgOne & 0xff);
38823890
iPgOne >>= 8;
38833891
}
38843892
iWriteEnd += paf->iPgOne;
38853893
if( SQLITE_OK==(rc = pFile->pMethods->xWrite
@@ -3903,12 +3911,11 @@
39033911
if( iWriteEnd>=APND_MAX_SIZE ) return SQLITE_FULL;
39043912
pFile = ORIGFILE(pFile);
39053913
/* If append-mark is absent or will be overwritten, write it. */
39063914
if( paf->iMark < 0 || paf->iPgOne + iWriteEnd > paf->iMark ){
39073915
int rc = apndWriteMark(paf, pFile, iWriteEnd);
3908
- if( SQLITE_OK!=rc )
3909
- return rc;
3916
+ if( SQLITE_OK!=rc ) return rc;
39103917
}
39113918
return pFile->pMethods->xWrite(pFile, zBuf, iAmt, paf->iPgOne+iOfst);
39123919
}
39133920
39143921
/*
@@ -3916,12 +3923,11 @@
39163923
*/
39173924
static int apndTruncate(sqlite3_file *pFile, sqlite_int64 size){
39183925
ApndFile *paf = (ApndFile *)pFile;
39193926
pFile = ORIGFILE(pFile);
39203927
/* The append mark goes out first so truncate failure does not lose it. */
3921
- if( SQLITE_OK!=apndWriteMark(paf, pFile, size) )
3922
- return SQLITE_IOERR;
3928
+ if( SQLITE_OK!=apndWriteMark(paf, pFile, size) ) return SQLITE_IOERR;
39233929
/* Truncate underlying file just past append mark */
39243930
return pFile->pMethods->xTruncate(pFile, paf->iMark+APND_MARK_SIZE);
39253931
}
39263932
39273933
/*
@@ -4033,12 +4039,13 @@
40334039
sqlite3_int64 iOfst,
40344040
int iAmt,
40354041
void **pp
40364042
){
40374043
ApndFile *p = (ApndFile *)pFile;
4038
- if( p->iMark < 0 || iOfst+iAmt > p->iMark)
4044
+ if( p->iMark < 0 || iOfst+iAmt > p->iMark ){
40394045
return SQLITE_IOERR; /* Cannot read what is not yet there. */
4046
+ }
40404047
pFile = ORIGFILE(pFile);
40414048
return pFile->pMethods->xFetch(pFile, iOfst+p->iPgOne, iAmt, pp);
40424049
}
40434050
40444051
/* Release a memory-mapped page */
@@ -4085,18 +4092,22 @@
40854092
static int apndIsAppendvfsDatabase(sqlite3_int64 sz, sqlite3_file *pFile){
40864093
int rc;
40874094
char zHdr[16];
40884095
sqlite3_int64 iMark = apndReadMark(sz, pFile);
40894096
if( iMark>=0 ){
4090
- /* If file has right end-marker, the expected odd size, and the
4091
- * SQLite DB type marker where the end-marker puts it, then it
4092
- * is an appendvfs database (to be treated as such.)
4093
- */
4097
+ /* If file has the correct end-marker, the expected odd size, and the
4098
+ ** SQLite DB type marker where the end-marker puts it, then it
4099
+ ** is an appendvfs database.
4100
+ */
40944101
rc = pFile->pMethods->xRead(pFile, zHdr, sizeof(zHdr), iMark);
4095
- if( SQLITE_OK==rc && memcmp(zHdr, apvfsSqliteHdr, sizeof(zHdr))==0
4096
- && (sz & 0x1ff)== APND_MARK_SIZE && sz>=512+APND_MARK_SIZE )
4102
+ if( SQLITE_OK==rc
4103
+ && memcmp(zHdr, apvfsSqliteHdr, sizeof(zHdr))==0
4104
+ && (sz & 0x1ff) == APND_MARK_SIZE
4105
+ && sz>=512+APND_MARK_SIZE
4106
+ ){
40974107
return 1; /* It's an appendvfs database */
4108
+ }
40984109
}
40994110
return 0;
41004111
}
41014112
41024113
/*
@@ -4114,70 +4125,69 @@
41144125
}else{
41154126
return 1;
41164127
}
41174128
}
41184129
4119
-/* Round-up used to get appendvfs portion to begin at a page boundary. */
4120
-#define APND_ALIGN_MASK(nbits) ((1<<nbits)-1)
4121
-#define APND_START_ROUNDUP(fsz, nbits) \
4122
- ( ((fsz)+APND_ALIGN_MASK(nbits)) & ~(sqlite3_int64)APND_ALIGN_MASK(nbits) )
4123
-
41244130
/*
41254131
** Open an apnd file handle.
41264132
*/
41274133
static int apndOpen(
4128
- sqlite3_vfs *pVfs,
4134
+ sqlite3_vfs *pApndVfs,
41294135
const char *zName,
41304136
sqlite3_file *pFile,
41314137
int flags,
41324138
int *pOutFlags
41334139
){
4134
- ApndFile *p;
4135
- sqlite3_file *pSubFile;
4136
- sqlite3_vfs *pSubVfs;
4140
+ ApndFile *pApndFile = (ApndFile*)pFile;
4141
+ sqlite3_file *pBaseFile = ORIGFILE(pFile);
4142
+ sqlite3_vfs *pBaseVfs = ORIGVFS(pApndVfs);
41374143
int rc;
4138
- sqlite3_int64 sz;
4139
- pSubVfs = ORIGVFS(pVfs);
4144
+ sqlite3_int64 sz = 0;
41404145
if( (flags & SQLITE_OPEN_MAIN_DB)==0 ){
4141
- /* The appendvfs is not to be used for transient or temporary databases. */
4142
- return pSubVfs->xOpen(pSubVfs, zName, pFile, flags, pOutFlags);
4146
+ /* The appendvfs is not to be used for transient or temporary databases.
4147
+ ** Just use the base VFS open to initialize the given file object and
4148
+ ** open the underlying file. (Appendvfs is then unused for this file.)
4149
+ */
4150
+ return pBaseVfs->xOpen(pBaseVfs, zName, pFile, flags, pOutFlags);
41434151
}
4144
- p = (ApndFile*)pFile;
4145
- memset(p, 0, sizeof(*p));
4146
- pSubFile = ORIGFILE(pFile);
4152
+ memset(pApndFile, 0, sizeof(ApndFile));
41474153
pFile->pMethods = &apnd_io_methods;
4148
- rc = pSubVfs->xOpen(pSubVfs, zName, pSubFile, flags, pOutFlags);
4149
- if( rc ) goto apnd_open_done;
4150
- rc = pSubFile->pMethods->xFileSize(pSubFile, &sz);
4154
+ pApndFile->iMark = -1; /* Append mark not yet written */
4155
+
4156
+ rc = pBaseVfs->xOpen(pBaseVfs, zName, pBaseFile, flags, pOutFlags);
4157
+ if( rc==SQLITE_OK ){
4158
+ rc = pBaseFile->pMethods->xFileSize(pBaseFile, &sz);
4159
+ }
41514160
if( rc ){
4152
- pSubFile->pMethods->xClose(pSubFile);
4153
- goto apnd_open_done;
4161
+ pBaseFile->pMethods->xClose(pBaseFile);
4162
+ pFile->pMethods = 0;
4163
+ return rc;
41544164
}
4155
- if( apndIsOrdinaryDatabaseFile(sz, pSubFile) ){
4156
- memmove(pFile, pSubFile, pSubVfs->szOsFile);
4165
+ if( apndIsOrdinaryDatabaseFile(sz, pBaseFile) ){
4166
+ /* The file being opened appears to be just an ordinary DB. Copy
4167
+ ** the base dispatch-table so this instance mimics the base VFS.
4168
+ */
4169
+ memmove(pApndFile, pBaseFile, pBaseVfs->szOsFile);
41574170
return SQLITE_OK;
41584171
}
4159
- /* Record that append mark has not been written until seen otherwise. */
4160
- p->iMark = -1;
4161
- p->iPgOne = apndReadMark(sz, pFile);
4162
- if( p->iPgOne>=0 ){
4163
- /* Append mark was found, infer its offset */
4164
- p->iMark = sz - p->iPgOne - APND_MARK_SIZE;
4172
+ pApndFile->iPgOne = apndReadMark(sz, pFile);
4173
+ if( pApndFile->iPgOne>=0 ){
4174
+ pApndFile->iMark = sz - APND_MARK_SIZE; /* Append mark found */
41654175
return SQLITE_OK;
41664176
}
41674177
if( (flags & SQLITE_OPEN_CREATE)==0 ){
4168
- pSubFile->pMethods->xClose(pSubFile);
4178
+ pBaseFile->pMethods->xClose(pBaseFile);
41694179
rc = SQLITE_CANTOPEN;
4170
- }
4171
- /* Round newly added appendvfs location to #define'd page boundary.
4172
- * Note that nothing has yet been written to the underlying file.
4173
- * The append mark will be written along with first content write.
4174
- * Until then, the p->iMark value indicates it is not yet written.
4175
- */
4176
- p->iPgOne = APND_START_ROUNDUP(sz, APND_ROUNDUP_BITS);
4177
-apnd_open_done:
4178
- if( rc ) pFile->pMethods = 0;
4180
+ pFile->pMethods = 0;
4181
+ }else{
4182
+ /* Round newly added appendvfs location to #define'd page boundary.
4183
+ ** Note that nothing has yet been written to the underlying file.
4184
+ ** The append mark will be written along with first content write.
4185
+ ** Until then, paf->iMark value indicates it is not yet written.
4186
+ */
4187
+ pApndFile->iPgOne = APND_START_ROUNDUP(sz);
4188
+ }
41794189
return rc;
41804190
}
41814191
41824192
/*
41834193
** Delete an apnd file.
@@ -13207,10 +13217,11 @@
1320713217
1320813218
rc = sqlite3_step(pStmt);
1320913219
if( rc!=SQLITE_ROW ) return;
1321013220
nColumn = sqlite3_column_count(pStmt);
1321113221
nAlloc = nColumn*4;
13222
+ if( nAlloc<=0 ) nAlloc = 1;
1321213223
azData = sqlite3_malloc64( nAlloc*sizeof(char*) );
1321313224
if( azData==0 ) shell_out_of_memory();
1321413225
for(i=0; i<nColumn; i++){
1321513226
azData[i] = strdup(sqlite3_column_name(pStmt,i));
1321613227
}
@@ -13246,10 +13257,11 @@
1324613257
n = strlenChar(z);
1324713258
j = i%nColumn;
1324813259
if( n>p->actualWidth[j] ) p->actualWidth[j] = n;
1324913260
}
1325013261
if( seenInterrupt ) goto columnar_end;
13262
+ if( nColumn==0 ) goto columnar_end;
1325113263
switch( p->cMode ){
1325213264
case MODE_Column: {
1325313265
colSep = " ";
1325413266
rowSep = "\n";
1325513267
if( p->showHeader ){
@@ -14035,17 +14047,17 @@
1403514047
".check GLOB Fail if output since .testcase does not match",
1403614048
".clone NEWDB Clone data into NEWDB from the existing database",
1403714049
".databases List names and files of attached databases",
1403814050
".dbconfig ?op? ?val? List or change sqlite3_db_config() options",
1403914051
".dbinfo ?DB? Show status information about the database",
14040
- ".dump ?TABLE? Render database content as SQL",
14052
+ ".dump ?OBJECTS? Render database content as SQL",
1404114053
" Options:",
1404214054
" --data-only Output only INSERT statements",
1404314055
" --newlines Allow unescaped newline characters in output",
1404414056
" --nosys Omit system tables (ex: \"sqlite_stat1\")",
1404514057
" --preserve-rowids Include ROWID values in the output",
14046
- " TABLE is a LIKE pattern for the tables to dump",
14058
+ " OBJECTS is a LIKE pattern for tables, indexes, triggers or views to dump",
1404714059
" Additional LIKE patterns can be given in subsequent arguments",
1404814060
".echo on|off Turn command echo on or off",
1404914061
".eqp on|off|full|... Enable or disable automatic EXPLAIN QUERY PLAN",
1405014062
" Other Modes:",
1405114063
#ifdef SQLITE_DEBUG
1405214064
--- src/shell.c
+++ src/shell.c
@@ -3662,12 +3662,12 @@
3662 ** set, then a new database is appended to the already existing file.
3663 **
3664 ** (5) Otherwise, SQLITE_CANTOPEN is returned.
3665 **
3666 ** To avoid unnecessary complications with the PENDING_BYTE, the size of
3667 ** the file containing the database is limited to 1GB. (1000013824 bytes)
3668 ** This VFS will not read or write past the 1GB mark. This restriction
3669 ** might be lifted in future versions. For now, if you need a larger
3670 ** database, then keep it in a separate file.
3671 **
3672 ** If the file being opened is a plain database (not an appended one), then
3673 ** this shim is a pass-through into the default underlying VFS. (rule 3)
@@ -3692,18 +3692,20 @@
3692
3693 /*
3694 ** Maximum size of the combined prefix + database + append-mark. This
3695 ** must be less than 0x40000000 to avoid locking issues on Windows.
3696 */
3697 #define APND_MAX_SIZE (65536*15259)
3698
3699 /*
3700 ** Size of storage page upon which to align appendvfs portion.
3701 */
3702 #ifndef APND_ROUNDUP_BITS
3703 #define APND_ROUNDUP_BITS 12
3704 #endif
 
 
3705
3706 /*
3707 ** Forward declaration of objects used by this utility
3708 */
3709 typedef struct sqlite3_vfs ApndVfs;
@@ -3713,43 +3715,49 @@
3713 ** access to randomness, etc.
3714 */
3715 #define ORIGVFS(p) ((sqlite3_vfs*)((p)->pAppData))
3716 #define ORIGFILE(p) ((sqlite3_file*)(((ApndFile*)(p))+1))
3717
3718 /* Invariants for an open appendvfs file:
3719 * Once an appendvfs file is opened, it will be in one of three states:
3720 * State 0: Never written. Underlying file (if any) is unaltered.
3721 * State 1: Append mark is persisted, content write is in progress.
3722 * State 2: Append mark is persisted, content writes are complete.
3723 *
3724 * State 0 is persistent in the sense that nothing will have been done
3725 * to the underlying file, including any attempt to convert it to an
3726 * appendvfs file.
3727 *
3728 * State 1 is normally transitory. However, if a write operation ends
3729 * abnormally (disk full, power loss, process kill, etc.), then State 1
3730 * may be persistent on disk with an incomplete content write-out. This
3731 * is logically equivalent to an interrupted write to an ordinary file,
3732 * where some unknown portion of to-be-written data is persisted while
3733 * the remainder is not. Database integrity in such cases is maintained
3734 * (or not) by the same measures available for ordinary file access.
3735 *
3736 * State 2 is persistent under normal circumstances (when there is no
3737 * abnormal termination of a write operation such that data provided
3738 * to the underlying VFS write method has not yet reached storage.)
3739 *
3740 * In order to maintain the state invariant, the append mark is written
3741 * in advance of content writes where any part of such content would
3742 * overwrite an existing (or yet to be written) append mark.
3743 */
 
 
 
 
 
 
 
 
3744 struct ApndFile {
3745 /* Access to IO methods of the underlying file */
3746 sqlite3_file base;
3747 /* File offset to beginning of appended content (unchanging) */
3748 sqlite3_int64 iPgOne;
3749 /* File offset of written append-mark, or -1 if unwritten */
3750 sqlite3_int64 iMark;
3751 };
3752
3753 /*
3754 ** Methods for ApndFile
3755 */
@@ -3875,11 +3883,11 @@
3875 unsigned char a[APND_MARK_SIZE];
3876 int i = APND_MARK_FOS_SZ;
3877 int rc;
3878 assert(pFile == ORIGFILE(paf));
3879 memcpy(a, APND_MARK_PREFIX, APND_MARK_PREFIX_SZ);
3880 while (--i >= 0) {
3881 a[APND_MARK_PREFIX_SZ+i] = (unsigned char)(iPgOne & 0xff);
3882 iPgOne >>= 8;
3883 }
3884 iWriteEnd += paf->iPgOne;
3885 if( SQLITE_OK==(rc = pFile->pMethods->xWrite
@@ -3903,12 +3911,11 @@
3903 if( iWriteEnd>=APND_MAX_SIZE ) return SQLITE_FULL;
3904 pFile = ORIGFILE(pFile);
3905 /* If append-mark is absent or will be overwritten, write it. */
3906 if( paf->iMark < 0 || paf->iPgOne + iWriteEnd > paf->iMark ){
3907 int rc = apndWriteMark(paf, pFile, iWriteEnd);
3908 if( SQLITE_OK!=rc )
3909 return rc;
3910 }
3911 return pFile->pMethods->xWrite(pFile, zBuf, iAmt, paf->iPgOne+iOfst);
3912 }
3913
3914 /*
@@ -3916,12 +3923,11 @@
3916 */
3917 static int apndTruncate(sqlite3_file *pFile, sqlite_int64 size){
3918 ApndFile *paf = (ApndFile *)pFile;
3919 pFile = ORIGFILE(pFile);
3920 /* The append mark goes out first so truncate failure does not lose it. */
3921 if( SQLITE_OK!=apndWriteMark(paf, pFile, size) )
3922 return SQLITE_IOERR;
3923 /* Truncate underlying file just past append mark */
3924 return pFile->pMethods->xTruncate(pFile, paf->iMark+APND_MARK_SIZE);
3925 }
3926
3927 /*
@@ -4033,12 +4039,13 @@
4033 sqlite3_int64 iOfst,
4034 int iAmt,
4035 void **pp
4036 ){
4037 ApndFile *p = (ApndFile *)pFile;
4038 if( p->iMark < 0 || iOfst+iAmt > p->iMark)
4039 return SQLITE_IOERR; /* Cannot read what is not yet there. */
 
4040 pFile = ORIGFILE(pFile);
4041 return pFile->pMethods->xFetch(pFile, iOfst+p->iPgOne, iAmt, pp);
4042 }
4043
4044 /* Release a memory-mapped page */
@@ -4085,18 +4092,22 @@
4085 static int apndIsAppendvfsDatabase(sqlite3_int64 sz, sqlite3_file *pFile){
4086 int rc;
4087 char zHdr[16];
4088 sqlite3_int64 iMark = apndReadMark(sz, pFile);
4089 if( iMark>=0 ){
4090 /* If file has right end-marker, the expected odd size, and the
4091 * SQLite DB type marker where the end-marker puts it, then it
4092 * is an appendvfs database (to be treated as such.)
4093 */
4094 rc = pFile->pMethods->xRead(pFile, zHdr, sizeof(zHdr), iMark);
4095 if( SQLITE_OK==rc && memcmp(zHdr, apvfsSqliteHdr, sizeof(zHdr))==0
4096 && (sz & 0x1ff)== APND_MARK_SIZE && sz>=512+APND_MARK_SIZE )
 
 
 
4097 return 1; /* It's an appendvfs database */
 
4098 }
4099 return 0;
4100 }
4101
4102 /*
@@ -4114,70 +4125,69 @@
4114 }else{
4115 return 1;
4116 }
4117 }
4118
4119 /* Round-up used to get appendvfs portion to begin at a page boundary. */
4120 #define APND_ALIGN_MASK(nbits) ((1<<nbits)-1)
4121 #define APND_START_ROUNDUP(fsz, nbits) \
4122 ( ((fsz)+APND_ALIGN_MASK(nbits)) & ~(sqlite3_int64)APND_ALIGN_MASK(nbits) )
4123
4124 /*
4125 ** Open an apnd file handle.
4126 */
4127 static int apndOpen(
4128 sqlite3_vfs *pVfs,
4129 const char *zName,
4130 sqlite3_file *pFile,
4131 int flags,
4132 int *pOutFlags
4133 ){
4134 ApndFile *p;
4135 sqlite3_file *pSubFile;
4136 sqlite3_vfs *pSubVfs;
4137 int rc;
4138 sqlite3_int64 sz;
4139 pSubVfs = ORIGVFS(pVfs);
4140 if( (flags & SQLITE_OPEN_MAIN_DB)==0 ){
4141 /* The appendvfs is not to be used for transient or temporary databases. */
4142 return pSubVfs->xOpen(pSubVfs, zName, pFile, flags, pOutFlags);
 
 
 
4143 }
4144 p = (ApndFile*)pFile;
4145 memset(p, 0, sizeof(*p));
4146 pSubFile = ORIGFILE(pFile);
4147 pFile->pMethods = &apnd_io_methods;
4148 rc = pSubVfs->xOpen(pSubVfs, zName, pSubFile, flags, pOutFlags);
4149 if( rc ) goto apnd_open_done;
4150 rc = pSubFile->pMethods->xFileSize(pSubFile, &sz);
 
 
 
4151 if( rc ){
4152 pSubFile->pMethods->xClose(pSubFile);
4153 goto apnd_open_done;
 
4154 }
4155 if( apndIsOrdinaryDatabaseFile(sz, pSubFile) ){
4156 memmove(pFile, pSubFile, pSubVfs->szOsFile);
 
 
 
4157 return SQLITE_OK;
4158 }
4159 /* Record that append mark has not been written until seen otherwise. */
4160 p->iMark = -1;
4161 p->iPgOne = apndReadMark(sz, pFile);
4162 if( p->iPgOne>=0 ){
4163 /* Append mark was found, infer its offset */
4164 p->iMark = sz - p->iPgOne - APND_MARK_SIZE;
4165 return SQLITE_OK;
4166 }
4167 if( (flags & SQLITE_OPEN_CREATE)==0 ){
4168 pSubFile->pMethods->xClose(pSubFile);
4169 rc = SQLITE_CANTOPEN;
4170 }
4171 /* Round newly added appendvfs location to #define'd page boundary.
4172 * Note that nothing has yet been written to the underlying file.
4173 * The append mark will be written along with first content write.
4174 * Until then, the p->iMark value indicates it is not yet written.
4175 */
4176 p->iPgOne = APND_START_ROUNDUP(sz, APND_ROUNDUP_BITS);
4177 apnd_open_done:
4178 if( rc ) pFile->pMethods = 0;
4179 return rc;
4180 }
4181
4182 /*
4183 ** Delete an apnd file.
@@ -13207,10 +13217,11 @@
13207
13208 rc = sqlite3_step(pStmt);
13209 if( rc!=SQLITE_ROW ) return;
13210 nColumn = sqlite3_column_count(pStmt);
13211 nAlloc = nColumn*4;
 
13212 azData = sqlite3_malloc64( nAlloc*sizeof(char*) );
13213 if( azData==0 ) shell_out_of_memory();
13214 for(i=0; i<nColumn; i++){
13215 azData[i] = strdup(sqlite3_column_name(pStmt,i));
13216 }
@@ -13246,10 +13257,11 @@
13246 n = strlenChar(z);
13247 j = i%nColumn;
13248 if( n>p->actualWidth[j] ) p->actualWidth[j] = n;
13249 }
13250 if( seenInterrupt ) goto columnar_end;
 
13251 switch( p->cMode ){
13252 case MODE_Column: {
13253 colSep = " ";
13254 rowSep = "\n";
13255 if( p->showHeader ){
@@ -14035,17 +14047,17 @@
14035 ".check GLOB Fail if output since .testcase does not match",
14036 ".clone NEWDB Clone data into NEWDB from the existing database",
14037 ".databases List names and files of attached databases",
14038 ".dbconfig ?op? ?val? List or change sqlite3_db_config() options",
14039 ".dbinfo ?DB? Show status information about the database",
14040 ".dump ?TABLE? Render database content as SQL",
14041 " Options:",
14042 " --data-only Output only INSERT statements",
14043 " --newlines Allow unescaped newline characters in output",
14044 " --nosys Omit system tables (ex: \"sqlite_stat1\")",
14045 " --preserve-rowids Include ROWID values in the output",
14046 " TABLE is a LIKE pattern for the tables to dump",
14047 " Additional LIKE patterns can be given in subsequent arguments",
14048 ".echo on|off Turn command echo on or off",
14049 ".eqp on|off|full|... Enable or disable automatic EXPLAIN QUERY PLAN",
14050 " Other Modes:",
14051 #ifdef SQLITE_DEBUG
14052
--- src/shell.c
+++ src/shell.c
@@ -3662,12 +3662,12 @@
3662 ** set, then a new database is appended to the already existing file.
3663 **
3664 ** (5) Otherwise, SQLITE_CANTOPEN is returned.
3665 **
3666 ** To avoid unnecessary complications with the PENDING_BYTE, the size of
3667 ** the file containing the database is limited to 1GiB. (1073741824 bytes)
3668 ** This VFS will not read or write past the 1GiB mark. This restriction
3669 ** might be lifted in future versions. For now, if you need a larger
3670 ** database, then keep it in a separate file.
3671 **
3672 ** If the file being opened is a plain database (not an appended one), then
3673 ** this shim is a pass-through into the default underlying VFS. (rule 3)
@@ -3692,18 +3692,20 @@
3692
3693 /*
3694 ** Maximum size of the combined prefix + database + append-mark. This
3695 ** must be less than 0x40000000 to avoid locking issues on Windows.
3696 */
3697 #define APND_MAX_SIZE (0x40000000)
3698
3699 /*
3700 ** Try to align the database to an even multiple of APND_ROUNDUP bytes.
3701 */
3702 #ifndef APND_ROUNDUP
3703 #define APND_ROUNDUP 4096
3704 #endif
3705 #define APND_ALIGN_MASK ((sqlite3_int64)(APND_ROUNDUP-1))
3706 #define APND_START_ROUNDUP(fsz) (((fsz)+APND_ALIGN_MASK) & ~APND_ALIGN_MASK)
3707
3708 /*
3709 ** Forward declaration of objects used by this utility
3710 */
3711 typedef struct sqlite3_vfs ApndVfs;
@@ -3713,43 +3715,49 @@
3715 ** access to randomness, etc.
3716 */
3717 #define ORIGVFS(p) ((sqlite3_vfs*)((p)->pAppData))
3718 #define ORIGFILE(p) ((sqlite3_file*)(((ApndFile*)(p))+1))
3719
3720 /* An open appendvfs file
3721 **
3722 ** An instance of this structure describes the appended database file.
3723 ** A separate sqlite3_file object is always appended. The appended
3724 ** sqlite3_file object (which can be accessed using ORIGFILE()) describes
3725 ** the entire file, including the prefix, the database, and the
3726 ** append-mark.
3727 **
3728 ** The structure of an AppendVFS database is like this:
3729 **
3730 ** +-------------+---------+----------+-------------+
3731 ** | prefix-file | padding | database | append-mark |
3732 ** +-------------+---------+----------+-------------+
3733 ** ^ ^
3734 ** | |
3735 ** iPgOne iMark
3736 **
3737 **
3738 ** "prefix file" - file onto which the database has been appended.
3739 ** "padding" - zero or more bytes inserted so that "database"
3740 ** starts on an APND_ROUNDUP boundary
3741 ** "database" - The SQLite database file
3742 ** "append-mark" - The 25-byte "Start-Of-SQLite3-NNNNNNNN" that indicates
3743 ** the offset from the start of prefix-file to the start
3744 ** of "database".
3745 **
3746 ** The size of the database is iMark - iPgOne.
3747 **
3748 ** The NNNNNNNN in the "Start-Of-SQLite3-NNNNNNNN" suffix is the value
3749 ** of iPgOne stored as a big-ending 64-bit integer.
3750 **
3751 ** iMark will be the size of the underlying file minus 25 (APND_MARKSIZE).
3752 ** Or, iMark is -1 to indicate that it has not yet been written.
3753 */
3754 struct ApndFile {
3755 sqlite3_file base; /* Subclass. MUST BE FIRST! */
3756 sqlite3_int64 iPgOne; /* Offset to the start of the database */
3757 sqlite3_int64 iMark; /* Offset of the append mark. -1 if unwritten */
3758 /* Always followed by another sqlite3_file that describes the whole file */
 
 
3759 };
3760
3761 /*
3762 ** Methods for ApndFile
3763 */
@@ -3875,11 +3883,11 @@
3883 unsigned char a[APND_MARK_SIZE];
3884 int i = APND_MARK_FOS_SZ;
3885 int rc;
3886 assert(pFile == ORIGFILE(paf));
3887 memcpy(a, APND_MARK_PREFIX, APND_MARK_PREFIX_SZ);
3888 while( --i >= 0 ){
3889 a[APND_MARK_PREFIX_SZ+i] = (unsigned char)(iPgOne & 0xff);
3890 iPgOne >>= 8;
3891 }
3892 iWriteEnd += paf->iPgOne;
3893 if( SQLITE_OK==(rc = pFile->pMethods->xWrite
@@ -3903,12 +3911,11 @@
3911 if( iWriteEnd>=APND_MAX_SIZE ) return SQLITE_FULL;
3912 pFile = ORIGFILE(pFile);
3913 /* If append-mark is absent or will be overwritten, write it. */
3914 if( paf->iMark < 0 || paf->iPgOne + iWriteEnd > paf->iMark ){
3915 int rc = apndWriteMark(paf, pFile, iWriteEnd);
3916 if( SQLITE_OK!=rc ) return rc;
 
3917 }
3918 return pFile->pMethods->xWrite(pFile, zBuf, iAmt, paf->iPgOne+iOfst);
3919 }
3920
3921 /*
@@ -3916,12 +3923,11 @@
3923 */
3924 static int apndTruncate(sqlite3_file *pFile, sqlite_int64 size){
3925 ApndFile *paf = (ApndFile *)pFile;
3926 pFile = ORIGFILE(pFile);
3927 /* The append mark goes out first so truncate failure does not lose it. */
3928 if( SQLITE_OK!=apndWriteMark(paf, pFile, size) ) return SQLITE_IOERR;
 
3929 /* Truncate underlying file just past append mark */
3930 return pFile->pMethods->xTruncate(pFile, paf->iMark+APND_MARK_SIZE);
3931 }
3932
3933 /*
@@ -4033,12 +4039,13 @@
4039 sqlite3_int64 iOfst,
4040 int iAmt,
4041 void **pp
4042 ){
4043 ApndFile *p = (ApndFile *)pFile;
4044 if( p->iMark < 0 || iOfst+iAmt > p->iMark ){
4045 return SQLITE_IOERR; /* Cannot read what is not yet there. */
4046 }
4047 pFile = ORIGFILE(pFile);
4048 return pFile->pMethods->xFetch(pFile, iOfst+p->iPgOne, iAmt, pp);
4049 }
4050
4051 /* Release a memory-mapped page */
@@ -4085,18 +4092,22 @@
4092 static int apndIsAppendvfsDatabase(sqlite3_int64 sz, sqlite3_file *pFile){
4093 int rc;
4094 char zHdr[16];
4095 sqlite3_int64 iMark = apndReadMark(sz, pFile);
4096 if( iMark>=0 ){
4097 /* If file has the correct end-marker, the expected odd size, and the
4098 ** SQLite DB type marker where the end-marker puts it, then it
4099 ** is an appendvfs database.
4100 */
4101 rc = pFile->pMethods->xRead(pFile, zHdr, sizeof(zHdr), iMark);
4102 if( SQLITE_OK==rc
4103 && memcmp(zHdr, apvfsSqliteHdr, sizeof(zHdr))==0
4104 && (sz & 0x1ff) == APND_MARK_SIZE
4105 && sz>=512+APND_MARK_SIZE
4106 ){
4107 return 1; /* It's an appendvfs database */
4108 }
4109 }
4110 return 0;
4111 }
4112
4113 /*
@@ -4114,70 +4125,69 @@
4125 }else{
4126 return 1;
4127 }
4128 }
4129
 
 
 
 
 
4130 /*
4131 ** Open an apnd file handle.
4132 */
4133 static int apndOpen(
4134 sqlite3_vfs *pApndVfs,
4135 const char *zName,
4136 sqlite3_file *pFile,
4137 int flags,
4138 int *pOutFlags
4139 ){
4140 ApndFile *pApndFile = (ApndFile*)pFile;
4141 sqlite3_file *pBaseFile = ORIGFILE(pFile);
4142 sqlite3_vfs *pBaseVfs = ORIGVFS(pApndVfs);
4143 int rc;
4144 sqlite3_int64 sz = 0;
 
4145 if( (flags & SQLITE_OPEN_MAIN_DB)==0 ){
4146 /* The appendvfs is not to be used for transient or temporary databases.
4147 ** Just use the base VFS open to initialize the given file object and
4148 ** open the underlying file. (Appendvfs is then unused for this file.)
4149 */
4150 return pBaseVfs->xOpen(pBaseVfs, zName, pFile, flags, pOutFlags);
4151 }
4152 memset(pApndFile, 0, sizeof(ApndFile));
 
 
4153 pFile->pMethods = &apnd_io_methods;
4154 pApndFile->iMark = -1; /* Append mark not yet written */
4155
4156 rc = pBaseVfs->xOpen(pBaseVfs, zName, pBaseFile, flags, pOutFlags);
4157 if( rc==SQLITE_OK ){
4158 rc = pBaseFile->pMethods->xFileSize(pBaseFile, &sz);
4159 }
4160 if( rc ){
4161 pBaseFile->pMethods->xClose(pBaseFile);
4162 pFile->pMethods = 0;
4163 return rc;
4164 }
4165 if( apndIsOrdinaryDatabaseFile(sz, pBaseFile) ){
4166 /* The file being opened appears to be just an ordinary DB. Copy
4167 ** the base dispatch-table so this instance mimics the base VFS.
4168 */
4169 memmove(pApndFile, pBaseFile, pBaseVfs->szOsFile);
4170 return SQLITE_OK;
4171 }
4172 pApndFile->iPgOne = apndReadMark(sz, pFile);
4173 if( pApndFile->iPgOne>=0 ){
4174 pApndFile->iMark = sz - APND_MARK_SIZE; /* Append mark found */
 
 
 
4175 return SQLITE_OK;
4176 }
4177 if( (flags & SQLITE_OPEN_CREATE)==0 ){
4178 pBaseFile->pMethods->xClose(pBaseFile);
4179 rc = SQLITE_CANTOPEN;
4180 pFile->pMethods = 0;
4181 }else{
4182 /* Round newly added appendvfs location to #define'd page boundary.
4183 ** Note that nothing has yet been written to the underlying file.
4184 ** The append mark will be written along with first content write.
4185 ** Until then, paf->iMark value indicates it is not yet written.
4186 */
4187 pApndFile->iPgOne = APND_START_ROUNDUP(sz);
4188 }
4189 return rc;
4190 }
4191
4192 /*
4193 ** Delete an apnd file.
@@ -13207,10 +13217,11 @@
13217
13218 rc = sqlite3_step(pStmt);
13219 if( rc!=SQLITE_ROW ) return;
13220 nColumn = sqlite3_column_count(pStmt);
13221 nAlloc = nColumn*4;
13222 if( nAlloc<=0 ) nAlloc = 1;
13223 azData = sqlite3_malloc64( nAlloc*sizeof(char*) );
13224 if( azData==0 ) shell_out_of_memory();
13225 for(i=0; i<nColumn; i++){
13226 azData[i] = strdup(sqlite3_column_name(pStmt,i));
13227 }
@@ -13246,10 +13257,11 @@
13257 n = strlenChar(z);
13258 j = i%nColumn;
13259 if( n>p->actualWidth[j] ) p->actualWidth[j] = n;
13260 }
13261 if( seenInterrupt ) goto columnar_end;
13262 if( nColumn==0 ) goto columnar_end;
13263 switch( p->cMode ){
13264 case MODE_Column: {
13265 colSep = " ";
13266 rowSep = "\n";
13267 if( p->showHeader ){
@@ -14035,17 +14047,17 @@
14047 ".check GLOB Fail if output since .testcase does not match",
14048 ".clone NEWDB Clone data into NEWDB from the existing database",
14049 ".databases List names and files of attached databases",
14050 ".dbconfig ?op? ?val? List or change sqlite3_db_config() options",
14051 ".dbinfo ?DB? Show status information about the database",
14052 ".dump ?OBJECTS? Render database content as SQL",
14053 " Options:",
14054 " --data-only Output only INSERT statements",
14055 " --newlines Allow unescaped newline characters in output",
14056 " --nosys Omit system tables (ex: \"sqlite_stat1\")",
14057 " --preserve-rowids Include ROWID values in the output",
14058 " OBJECTS is a LIKE pattern for tables, indexes, triggers or views to dump",
14059 " Additional LIKE patterns can be given in subsequent arguments",
14060 ".echo on|off Turn command echo on or off",
14061 ".eqp on|off|full|... Enable or disable automatic EXPLAIN QUERY PLAN",
14062 " Other Modes:",
14063 #ifdef SQLITE_DEBUG
14064
-1038
--- src/smtp.c
+++ src/smtp.c
@@ -646,1043 +646,5 @@
646646
fossil_fatal("ERROR: %s\n", p->zErr);
647647
}
648648
smtp_session_free(p);
649649
blob_reset(&body);
650650
}
651
-
652
-/*****************************************************************************
653
-** Server implementation
654
-*****************************************************************************/
655
-
656
-/*
657
-** Schema used by the email processing system.
658
-*/
659
-static const char zEmailSchema[] =
660
-@ -- bulk storage is in this table. This table can store either
661
-@ -- the body of email messages or transcripts of an smtp session.
662
-@ CREATE TABLE IF NOT EXISTS repository.emailblob(
663
-@ emailid INTEGER PRIMARY KEY AUTOINCREMENT, -- numeric idea for the entry
664
-@ enref INT, -- Number of references to this blob
665
-@ ets INT, -- Corresponding transcript, or NULL
666
-@ etime INT, -- insertion time, secs since 1970
667
-@ esz INT, -- uncompressed content size
668
-@ etxt TEXT -- content of this entry
669
-@ );
670
-@
671
-@ -- One row for each mailbox entry. All users emails are stored in
672
-@ -- this same table.
673
-@ CREATE TABLE IF NOT EXISTS repository.emailbox(
674
-@ ebid INTEGER PRIMARY KEY, -- Unique id for each mailbox entry
675
-@ euser TEXT, -- User who received this email
676
-@ edate INT, -- Date received. Seconds since 1970
677
-@ efrom TEXT, -- Who is the email from
678
-@ emsgid INT, -- Raw email text
679
-@ estate INT, -- 0: Unread, 1: read, 2: trash 3: sent
680
-@ esubject TEXT, -- Subject line for display
681
-@ etags TEXT -- zero or more tags
682
-@ );
683
-@
684
-@ -- Information on how to deliver incoming email.
685
-@ CREATE TABLE IF NOT EXISTS repository.emailroute(
686
-@ eaddr TEXT PRIMARY KEY, -- Email address
687
-@ epolicy TEXT -- How to handle email sent to this address
688
-@ ) WITHOUT ROWID;
689
-@
690
-@ -- Outgoing email queue
691
-@ CREATE TABLE IF NOT EXISTS repository.emailoutq(
692
-@ edomain TEXT, -- Destination domain. (ex: "fossil-scm.org")
693
-@ efrom TEXT, -- Sender email address (envelope "from")
694
-@ eto TEXT, -- Recipient email address (envelope "to")
695
-@ emsgid INT, -- Message body in the emailblob table
696
-@ ectime INT, -- Time enqueued. Seconds since 1970
697
-@ emtime INT, -- Time of last send attempt. Sec since 1970
698
-@ ensend INT, -- Number of send attempts
699
-@ ets INT -- Transcript of last failed attempt
700
-@ );
701
-@
702
-@ -- Triggers to automatically keep the emailblob.enref field up to date
703
-@ -- as entries in the emailblob, emailbox, and emailoutq tables are
704
-@ -- deleted.
705
-@ CREATE TRIGGER IF NOT EXISTS repository.emailblob_d1
706
-@ AFTER DELETE ON emailblob BEGIN
707
-@ UPDATE emailblob SET enref=enref-1 WHERE emailid=old.ets;
708
-@ END;
709
-@ CREATE TRIGGER IF NOT EXISTS repository.emailbox_d1
710
-@ AFTER DELETE ON emailbox BEGIN
711
-@ UPDATE emailblob SET enref=enref-1 WHERE emailid=old.emsgid;
712
-@ END;
713
-@ CREATE TRIGGER IF NOT EXISTS repository.emailoutq_d1
714
-@ AFTER DELETE ON emailoutq BEGIN
715
-@ UPDATE emailblob SET enref=enref-1 WHERE emailid IN (old.ets,old.emsgid);
716
-@ END;
717
-@
718
-@ -- An index on the emailblob entries which are unreferenced.
719
-@ CREATE INDEX IF NOT EXISTS repository.emailblob_nref ON emailblob(enref)
720
-@ WHERE enref<=0;
721
-;
722
-
723
-/*
724
-** Code used to delete the email tables.
725
-*/
726
-static const char zEmailDrop[] =
727
-@ DROP TABLE IF EXISTS emailblob;
728
-@ DROP TABLE IF EXISTS emailbox;
729
-@ DROP TABLE IF EXISTS emailroute;
730
-@ DROP TABLE IF EXISTS emailqueue;
731
-;
732
-
733
-#if INTERFACE
734
-/*
735
-** Mailbox message states
736
-*/
737
-#define MSG_UNREAD 0
738
-#define MSG_READ 1
739
-#define MSG_TRASH 2
740
-#endif /* INTERFACE */
741
-
742
-
743
-/*
744
-** Populate the schema of a database.
745
-**
746
-** eForce==0 Fast
747
-** eForce==1 Run CREATE TABLE statements every time
748
-** eForce==2 DROP then rerun CREATE TABLE
749
-*/
750
-void smtp_server_schema(int eForce){
751
- if( eForce==2 ){
752
- db_multi_exec(zEmailDrop/*works-like:""*/);
753
- }
754
- if( eForce==1 || !db_table_exists("repository","emailblob") ){
755
- db_multi_exec(zEmailSchema/*works-like:""*/);
756
- }
757
-}
758
-
759
-/*
760
-** WEBPAGE: setup_smtp
761
-**
762
-** Administrative page for configuring and controlling inbound email and
763
-** output email queuing. This page is available to administrators
764
-** only via the /Admin/EmailServer menu.
765
-*/
766
-void setup_smtp(void){
767
- Stmt q;
768
- login_check_credentials();
769
- if( !g.perm.Setup ){
770
- login_needed(0);
771
- return;
772
- }
773
- db_begin_transaction();
774
- style_set_current_feature("smtp");
775
- style_header("Email Server Setup");
776
- if( db_table_exists("repository","emailroute") ){
777
- style_submenu_element("emailblob table", "%R/emailblob");
778
- style_submenu_element("emailoutq table", "%R/emailoutq");
779
- db_prepare(&q, "SELECT eaddr, epolicy FROM emailroute ORDER BY 1");
780
- }else{
781
- db_prepare(&q, "SELECT null, null WHERE false");
782
- }
783
- @ <h1>Email Routing Table</h1>
784
- @ <table class="emailroutetab" cellpadding="5" border="1" cellspacing="0">
785
- @ <thead>
786
- @ <tr>
787
- @ <th>Email Address
788
- @ <th>Routing
789
- @ <th>
790
- @ </tr>
791
- @ </thead><tbody>
792
- while( db_step(&q)==SQLITE_ROW ){
793
- const char *zEAddr = db_column_text(&q, 0);
794
- const char *zEPolicy = db_column_text(&q, 1);
795
- @ <tr>
796
- @ <td valign="top">%h(zEAddr)</td>
797
- @ <td valign="top"><span style="white-space:pre;">%h(zEPolicy)</span></td>
798
- @ <td valign="top"><form method="POST" action="%R/setup_smtp_route">
799
- @ <input type="hidden" name="oaddr" value="%h(zEAddr)">
800
- @ <input type="submit" value="Edit">
801
- @ </form>
802
- }
803
- db_finalize(&q);
804
- @ <tr>
805
- @ <td colspan="3">
806
- @ <form method="POST" action="%R/setup_smtp_route">
807
- @ <input type="submit" value="New">
808
- @ &larr; Add a new email address
809
- @ </form>
810
- @ </table>
811
- style_finish_page();
812
- db_end_transaction(0);
813
-}
814
-
815
-/*
816
-** WEBPAGE: setup_smtp_route
817
-**
818
-** Edit a single entry in the emailroute table.
819
-** Query parameters:
820
-**
821
-** eaddr=ADDR ADDR is the email address as edited.
822
-**
823
-** oaddr=ADDR The original email address prior to editing.
824
-** Omit to add a new address.
825
-**
826
-** epolicy=TXT The routing policy.
827
-*/
828
-void setup_smtp_route(void){
829
- char *zEAddr = PT("eaddr"); /* new email address */
830
- char *zEPolicy = PT("epolicy"); /* new routing policy */
831
- char *zOAddr = PT("oaddr"); /* original email address */
832
- char *zErr = 0;
833
- int iErr = 0;
834
- login_check_credentials();
835
- if( !g.perm.Setup ){
836
- login_needed(0);
837
- return;
838
- }
839
- style_set_current_feature("smtp");
840
- style_header("Email Route Editor");
841
-
842
- if( P("edit") && cgi_csrf_safe(1) && zEAddr!=0 && zEPolicy!=0 ){
843
- smtp_server_schema(0);
844
- if( (zOAddr==0 || fossil_strcmp(zEAddr,zOAddr)!=0) ){
845
- /* New or changed email address */
846
- if( db_exists("SELECT 1 FROM emailroute WHERE eaddr=%Q",zEAddr) ){
847
- iErr = 1;
848
- zErr = mprintf("email address \"%h(zEAddr)\" already exists",zEAddr);
849
- goto smtp_route_edit;
850
- }
851
- if( zEPolicy[0]==0 ){
852
- iErr = 2;
853
- zErr = mprintf("empty route");
854
- goto smtp_route_edit;
855
- }
856
- }
857
- /* If the email address has changed, or if the new policy is blank,
858
- ** delete the old address and route information
859
- */
860
- db_begin_transaction();
861
- if( (zOAddr && fossil_strcmp(zEAddr,zOAddr)!=0) || zEPolicy[0]==0 ){
862
- db_multi_exec("DELETE FROM emailroute WHERE eaddr=%Q", zOAddr);
863
- }
864
- if( zEPolicy[0] ){
865
- /* Insert the new address and route */
866
- db_multi_exec(
867
- "REPLACE INTO emailroute(eaddr,epolicy) VALUES(%Q,%Q)",
868
- zEAddr, zEPolicy
869
- );
870
- }
871
- db_end_transaction(0);
872
- cgi_redirectf("%R/setup_smtp");
873
- }
874
- if( P("cancel")!=0 ){
875
- cgi_redirectf("%R/setup_smtp");
876
- }
877
-
878
-smtp_route_edit:
879
- if( zEAddr==0 ) zEAddr = zOAddr;
880
- if( zEPolicy==0 && db_table_exists("repository","emailroute") ){
881
- zEPolicy = db_text(0, "SELECT epolicy FROM emailroute WHERE eaddr=%Q",
882
- zEAddr);
883
- }
884
- if( zEPolicy==0 ) zEPolicy = "";
885
- @ <form method="POST" action="%R/setup_smtp_route">
886
- if( zOAddr ){
887
- @ <input type="hidden" name="oaddr" value="%h(zOAddr)">
888
- }
889
- @ <table class="label-value">
890
- @ <tr>
891
- @ <th>Email Address:</th>
892
- @ <td><input type="text" size=30 name="eaddr" value="%h(zEAddr)">
893
- if( iErr==1 ){
894
- @ <td><span class="generalError">&larr; %z(zErr)</span>
895
- }
896
- @ </tr>
897
- if( zOAddr && fossil_strcmp(zOAddr,zEAddr)!=0 ){
898
- @ <tr>
899
- @ <th>Original Address:</th>
900
- @ <td>%h(zOAddr)
901
- @ </tr>
902
- }
903
- @ <tr>
904
- @ <th>Routing:</th>
905
- @ <td><textarea name="epolicy" rows="3" cols="40">%h(zEPolicy)</textarea>
906
- if( iErr==2 ){
907
- @ <td valign="top"><span class="generalError">&larr; %z(zErr)</span>
908
- }
909
- @ </tr>
910
- @ <tr>
911
- @ <td>&nbsp;
912
- @ <td><input type="submit" name="edit" value="Apply">
913
- @ <input type="submit" name="cancel" value="Cancel">
914
- @ </tr>
915
- @ </table>
916
- @ <hr>
917
- @ <h1>Instructions</h1>
918
- @
919
- @ <p>The "Routing" field consists of zero or more lines where each
920
- @ line is an "action" followed by an "argument". Available actions:
921
- @ <ul>
922
- @ <li><p><b>forward</b> <i>email-address</i>
923
- @ <p>Forward the message to <i>email-address</i>.
924
- @ <li><p><b>mbox</b> <i>login-name</i>
925
- @ <p>Store the message in the local mailbox for the user
926
- @ with USER.LOGIN=<i>login-name</i>.
927
- @ </ul>
928
- @
929
- @ <p>To delete a route &rarr; erase all text from the "Routing" field then
930
- @ press the "Apply" button.
931
- style_finish_page();
932
-}
933
-
934
-#if LOCAL_INTERFACE
935
-/*
936
-** State information for the server
937
-*/
938
-struct SmtpServer {
939
- sqlite3_int64 idTranscript; /* Transcript ID number */
940
- sqlite3_int64 idMsg; /* Message ID number */
941
- const char *zIpAddr; /* Remote IP address */
942
- char *zEhlo; /* Client domain on the EHLO line */
943
- char *zFrom; /* MAIL FROM: argument */
944
- int nTo; /* Number of RCPT TO: lines seen */
945
- struct SmtpTo {
946
- char *z; /* Address in each RCPT TO line */
947
- int okRemote; /* zTo can be in another domain */
948
- } *aTo;
949
- u32 srvrFlags; /* Control flags */
950
- int nEts; /* Number of references to the transcript */
951
- int nRef; /* Number of references to idMsg */
952
- Blob msg; /* Content following DATA */
953
- Blob transcript; /* Session transcript */
954
-};
955
-
956
-#define SMTPSRV_CLEAR_MSG 1 /* smtp_server_clear() last message only */
957
-#define SMTPSRV_CLEAR_ALL 2 /* smtp_server_clear() everything */
958
-#define SMTPSRV_LOG 0x001 /* Record a transcript of the interaction */
959
-#define SMTPSRV_STDERR 0x002 /* Transcription written to stderr */
960
-#define SMTPSRV_DRYRUN 0x004 /* Do not record anything in database */
961
-
962
-#endif /* LOCAL_INTERFACE */
963
-
964
-/*
965
-** Clear the SmtpServer object. Deallocate resources.
966
-** How much to clear depends on eHowMuch
967
-*/
968
-static void smtp_server_clear(SmtpServer *p, int eHowMuch){
969
- int i;
970
- if( eHowMuch>=SMTPSRV_CLEAR_MSG ){
971
- fossil_free(p->zFrom);
972
- p->zFrom = 0;
973
- for(i=0; i<p->nTo; i++) fossil_free(p->aTo[i].z);
974
- fossil_free(p->aTo);
975
- p->aTo = 0;
976
- p->nTo = 0;
977
- blob_reset(&p->msg);
978
- p->idMsg = 0;
979
- }
980
- if( eHowMuch>=SMTPSRV_CLEAR_ALL ){
981
- blob_reset(&p->transcript);
982
- p->idTranscript = 0;
983
- fossil_free(p->zEhlo);
984
- p->zEhlo = 0;
985
- }
986
-}
987
-
988
-/*
989
-** Turn raw memory into an SmtpServer object.
990
-*/
991
-static void smtp_server_init(SmtpServer *p){
992
- memset(p, 0, sizeof(*p));
993
- blob_init(&p->msg, 0, 0);
994
- blob_init(&p->transcript, 0, 0);
995
-}
996
-
997
-/*
998
-** Append a new TO entry to the SmtpServer object. Do not do the
999
-** append if the same entry is already on the list.
1000
-**
1001
-** The zAddr argument is obtained from fossil_malloc(). This
1002
-** routine assumes ownership of the allocation.
1003
-*/
1004
-static void smtp_append_to(SmtpServer *p, char *zAddr, int okRemote){
1005
- int i;
1006
- for(i=0; zAddr[i]; i++){ zAddr[i] = fossil_tolower(zAddr[i]); }
1007
- for(i=0; i<p->nTo; i++){
1008
- if( strcmp(zAddr, p->aTo[i].z)==0 ){
1009
- fossil_free(zAddr);
1010
- if( p->aTo[i].okRemote==0 ) p->aTo[i].okRemote = okRemote;
1011
- return;
1012
- }
1013
- }
1014
- p->aTo = fossil_realloc(p->aTo, (p->nTo+1)*sizeof(p->aTo[0]));
1015
- p->aTo[p->nTo].z = zAddr;
1016
- p->aTo[p->nTo].okRemote = okRemote;
1017
- p->nTo++;
1018
-}
1019
-
1020
-/*
1021
-** Send a single line of output from the server to the client.
1022
-*/
1023
-static void smtp_server_send(SmtpServer *p, const char *zFormat, ...){
1024
- Blob b = empty_blob;
1025
- va_list ap;
1026
- char *z;
1027
- int n;
1028
- va_start(ap, zFormat);
1029
- blob_vappendf(&b, zFormat, ap);
1030
- va_end(ap);
1031
- z = blob_buffer(&b);
1032
- n = blob_size(&b);
1033
- assert( n>=2 );
1034
- assert( z[n-1]=='\n' );
1035
- assert( z[n-2]=='\r' );
1036
- if( p->srvrFlags & SMTPSRV_LOG ){
1037
- blob_appendf(&p->transcript, "S: %.*s\n", n-2, z);
1038
- }
1039
- if( p->srvrFlags & SMTPSRV_STDERR ){
1040
- fprintf(stderr, "S: %.*s\n", n-2, z);
1041
- }
1042
- fwrite(z, n, 1, stdout);
1043
- fflush(stdout);
1044
- blob_reset(&b);
1045
-}
1046
-
1047
-/*
1048
-** Read a single line from the client.
1049
-*/
1050
-static int smtp_server_gets(SmtpServer *p, char *aBuf, int nBuf){
1051
- int rc = fgets(aBuf, nBuf, stdin)!=0;
1052
- if( rc ){
1053
- if( (p->srvrFlags & SMTPSRV_LOG)!=0 ){
1054
- blob_appendf(&p->transcript, "C: %s", aBuf);
1055
- }
1056
- if( (p->srvrFlags & SMTPSRV_STDERR)!=0 ){
1057
- fprintf(stderr, "C: %s", aBuf);
1058
- }
1059
- }
1060
- return rc;
1061
-}
1062
-
1063
-/*
1064
-** RFC-5321 requires certain content be prepended to an email header
1065
-** as that email is received.
1066
-*/
1067
-static void smtp_server_prepend_header_lines(SmtpServer *p){
1068
- blob_appendf(&p->msg, "Received: from %s by Fossil-smtp\r\n", p->zIpAddr);
1069
-}
1070
-
1071
-/*
1072
-** Capture the incoming email data into the p->msg blob. Dequote
1073
-** lines of "..\r\n" into just ".\r\n".
1074
-*/
1075
-static void smtp_server_capture_data(SmtpServer *p, char *z, int n){
1076
- int nLine = 0;
1077
- while( fgets(z, n, stdin) ){
1078
- if( strncmp(z, ".\r\n", 3)==0 || strncmp(z, ".\n",2)==0 ) break;
1079
- nLine++;
1080
- if( strncmp(z, "..\r\n", 4)==0 || strncmp(z, "..\n",3)==0 ){
1081
- memmove(z, z+1, 4);
1082
- }
1083
- blob_append(&p->msg, z, -1);
1084
- }
1085
- if( p->srvrFlags & SMTPSRV_LOG ){
1086
- blob_appendf(&p->transcript, "C: # %d lines, %d bytes of content\n",
1087
- nLine, blob_size(&p->msg));
1088
- }
1089
- if( p->srvrFlags & SMTPSRV_STDERR ){
1090
- fprintf(stderr, "C: # %d lines, %d bytes of content\n",
1091
- nLine, blob_size(&p->msg));
1092
- }
1093
-}
1094
-
1095
-/*
1096
-** Send an email to a single email addess that is registered with
1097
-** this system, according to the instructions in emailroute. If
1098
-** zAddr is not in the emailroute table, then this routine is a
1099
-** no-op. Or if zAddr has already been processed, then this
1100
-** routine is a no-op.
1101
-*/
1102
-static void smtp_server_send_one_user(
1103
- SmtpServer *p, /* The current inbound email */
1104
- const char *zAddr, /* Who to forward this to */
1105
- int okRemote /* True if ok to foward to another domain */
1106
-){
1107
- char *zPolicy;
1108
- Blob policy, line, token, tail;
1109
-
1110
- zPolicy = db_text(0,
1111
- "SELECT epolicy FROM emailroute WHERE eaddr=%Q", zAddr);
1112
- if( zPolicy==0 ){
1113
- if( okRemote ){
1114
- int i;
1115
- for(i=0; zAddr[i] && zAddr[i]!='@'; i++){}
1116
- if( zAddr[i]=='@' && zAddr[i+1]!=0 ){
1117
- db_multi_exec(
1118
- "INSERT INTO emailoutq(edomain,efrom,eto,emsgid,ectime,"
1119
- "emtime,ensend)"
1120
- "VALUES(%Q,%Q,%Q,%lld,now(),0,0)",
1121
- zAddr+i+1, p->zFrom, zAddr, p->idMsg
1122
- );
1123
- p->nRef++;
1124
- }
1125
- }
1126
- return;
1127
- }
1128
- blob_init(&policy, zPolicy, -1);
1129
- while( blob_line(&policy, &line) ){
1130
- blob_trim(&line);
1131
- blob_token(&line, &token);
1132
- blob_tail(&line, &tail);
1133
- if( blob_size(&tail)==0 ) continue;
1134
- if( blob_eq_str(&token, "mbox", 4) ){
1135
- Blob subj;
1136
- email_header_value(&p->msg, "subject", &subj);
1137
- db_multi_exec(
1138
- "INSERT INTO emailbox(euser,edate,efrom,emsgid,estate,esubject)"
1139
- " VALUES(%Q,now(),%Q,%lld,0,%Q)",
1140
- blob_str(&tail), p->zFrom, p->idMsg,
1141
- blob_str(&subj)
1142
- );
1143
- blob_reset(&subj);
1144
- p->nRef++;
1145
- }
1146
- if( blob_eq_str(&token, "forward", 7) ){
1147
- smtp_append_to(p, fossil_strdup(blob_str(&tail)), 1);
1148
- }
1149
- blob_reset(&tail);
1150
- }
1151
-}
1152
-
1153
-/*
1154
-** The SmtpServer object contains a complete incoming email.
1155
-** Add this email to the database.
1156
-*/
1157
-static void smtp_server_route_incoming(SmtpServer *p, int bFinish){
1158
- Stmt s;
1159
- int i;
1160
- int nEtsStart = p->nEts;
1161
- if( p->zFrom
1162
- && p->nTo
1163
- && blob_size(&p->msg)
1164
- && (p->srvrFlags & SMTPSRV_DRYRUN)==0
1165
- ){
1166
- db_begin_write();
1167
- if( p->idTranscript==0 ) smtp_server_schema(0);
1168
- p->nRef = 0;
1169
- db_prepare(&s,
1170
- "INSERT INTO emailblob(ets,etime,etxt,enref,esz)"
1171
- " VALUES(:ets,now(),compress(:etxt),0,:esz)"
1172
- );
1173
- p->nEts++;
1174
- if( !bFinish && p->idTranscript==0 ){
1175
- db_bind_null(&s, ":ets");
1176
- db_bind_null(&s, ":etxt");
1177
- db_bind_null(&s, ":esz");
1178
- db_step(&s);
1179
- db_reset(&s);
1180
- p->idTranscript = db_last_insert_rowid();
1181
- }else if( bFinish ){
1182
- if( p->idTranscript ){
1183
- db_multi_exec(
1184
- "UPDATE emailblob SET etxt=compress(%Q), enref=%d, esz=%d"
1185
- " WHERE emailid=%lld",
1186
- blob_str(&p->transcript), p->nEts, blob_size(&p->transcript),
1187
- p->idTranscript);
1188
- }else{
1189
- db_bind_null(&s, ":ets");
1190
- db_bind_str(&s, ":etxt", &p->transcript);
1191
- db_bind_int(&s, ":esz", blob_size(&p->transcript));
1192
- db_step(&s);
1193
- db_reset(&s);
1194
- p->idTranscript = db_last_insert_rowid();
1195
- db_multi_exec(
1196
- "UPDATE emailblob SET enref=%d WHERE emailid=%lld",
1197
- p->nEts, p->idTranscript);
1198
- }
1199
- /* smtp_server_send(p, "221-Transcript id %lld nref %d\r\n",
1200
- ** p->idTranscript, p->nEts); */
1201
- }
1202
- db_bind_int64(&s, ":ets", p->idTranscript);
1203
- db_bind_str(&s, ":etxt", &p->msg);
1204
- db_bind_int(&s, ":esz", blob_size(&p->msg));
1205
- db_step(&s);
1206
- db_finalize(&s);
1207
- p->idMsg = db_last_insert_rowid();
1208
-
1209
- /* make entries in emailbox and emailoutq */
1210
- for(i=0; i<p->nTo; i++){
1211
- int okRemote = p->aTo[i].okRemote;
1212
- p->aTo[i].okRemote = 1;
1213
- smtp_server_send_one_user(p, p->aTo[i].z, okRemote);
1214
- }
1215
-
1216
- /* Fix up the emailblob.enref field of the email message body */
1217
- if( p->nRef ){
1218
- db_multi_exec(
1219
- "UPDATE emailblob SET enref=%d WHERE emailid=%lld",
1220
- p->nRef, p->idMsg
1221
- );
1222
- }else{
1223
- db_multi_exec(
1224
- "DELETE FROM emailblob WHERE emailid=%lld", p->idMsg
1225
- );
1226
- p->nEts = nEtsStart;
1227
- }
1228
-
1229
- /* Clean out legacy entries */
1230
- if( bFinish ){
1231
- db_multi_exec("DELETE FROM emailblob WHERE enref<=0");
1232
- }
1233
-
1234
- /* Finish the transaction after all changes are implemented */
1235
- db_commit_transaction();
1236
- }
1237
- smtp_server_clear(p, SMTPSRV_CLEAR_MSG);
1238
-}
1239
-
1240
-/*
1241
-** Remove stale content from the emailblob table.
1242
-*/
1243
-int smtp_cleanup(void){
1244
- int nAction = 0;
1245
- if( db_table_exists("repository","emailblob") ){
1246
- db_begin_transaction();
1247
- db_multi_exec(
1248
- "UPDATE emailblob SET ets=NULL WHERE enref<=0;"
1249
- "DELETE FROM emailblob WHERE enref<=0;"
1250
- );
1251
- nAction = db_changes();
1252
- db_end_transaction(0);
1253
- }
1254
- return nAction;
1255
-}
1256
-
1257
-/*
1258
-** COMMAND: test-emailblob-refcheck
1259
-**
1260
-** Usage: %fossil test-emailblob-refcheck [--repair] [--full] [--clean]
1261
-**
1262
-** Verify that the emailblob.enref field is correct. Report any errors.
1263
-** Use the --repair command to fix up the enref field. The --full option
1264
-** gives a full report showing the enref value on all entries in the
1265
-** emailblob table. If the --clean flags is used together with --repair,
1266
-** then emailblob table entires with enref==0 are removed.
1267
-*/
1268
-void test_refcheck_emailblob(void){
1269
- int doRepair;
1270
- int fullReport;
1271
- int doClean;
1272
- Blob sql;
1273
- Stmt q;
1274
- int nErr = 0;
1275
- db_find_and_open_repository(0, 0);
1276
- fullReport = find_option("full",0,0)!=0;
1277
- doRepair = find_option("repair",0,0)!=0;
1278
- doClean = find_option("clean",0,0)!=0;
1279
- verify_all_options();
1280
- if( !db_table_exists("repository","emailblob") ){
1281
- fossil_print("emailblob table is not configured - nothing to check\n");
1282
- return;
1283
- }
1284
- db_multi_exec(
1285
- "CREATE TEMP TABLE refcnt(id INTEGER PRIMARY KEY, n);"
1286
- "INSERT INTO refcnt SELECT ets, count(*) FROM ("
1287
- " SELECT ets FROM emailblob"
1288
- " UNION ALL"
1289
- " SELECT emsgid FROM emailbox"
1290
- " UNION ALL"
1291
- " SELECT emsgid FROM emailoutq"
1292
- ") WHERE ets IS NOT NULL GROUP BY 1;"
1293
- "INSERT OR IGNORE INTO refcnt(id,n) SELECT emailid, 0 FROM emailblob;"
1294
- );
1295
- if( doRepair ){
1296
- db_multi_exec(
1297
- "UPDATE emailblob SET enref=(SELECT n FROM refcnt WHERE id=emailid)"
1298
- );
1299
- if( doClean ){
1300
- smtp_cleanup();
1301
- }
1302
- }
1303
- blob_init(&sql, 0, 0);
1304
- blob_append_sql(&sql,
1305
- "SELECT a.emailid, a.enref, b.n"
1306
- " FROM emailblob AS a JOIN refcnt AS b ON a.emailid=b.id"
1307
- );
1308
- if( !fullReport ){
1309
- blob_append_sql(&sql, " WHERE a.enref!=b.n");
1310
- }
1311
- db_prepare_blob(&q, &sql);
1312
- blob_reset(&sql);
1313
- while( db_step(&q)==SQLITE_ROW ){
1314
- sqlite3_int64 id = db_column_int64(&q,0);
1315
- int n1 = db_column_int(&q, 1);
1316
- int n2 = db_column_int(&q, 2);
1317
- if( n1!=n2 ) nErr++;
1318
- fossil_print("%12lld %4d %4d%s\n", id, n1, n2, n1!=n2 ? " ERROR" : "");
1319
- }
1320
- db_finalize(&q);
1321
- if( nErr ){
1322
- fossil_print("Number of incorrect emailblob.enref values: %d\n",nErr);
1323
- }
1324
-}
1325
-
1326
-
1327
-/*
1328
-** COMMAND: smtpd*
1329
-**
1330
-** Usage: %fossil smtpd [OPTIONS] REPOSITORY
1331
-**
1332
-** Begin a SMTP conversation with a client using stdin/stdout. The
1333
-** received email is stored in REPOSITORY.
1334
-**
1335
-** Options:
1336
-**
1337
-** --dryrun Do not record any emails in the database
1338
-**
1339
-** --trace Print a transcript of the conversation on stderr
1340
-** for debugging and analysis
1341
-**
1342
-** --ipaddr ADDR The SMTP connection originates at ADDR. Or if ADDR
1343
-** is the name of an environment variable, the address
1344
-** is taken from that environment variable.
1345
-*/
1346
-void smtp_server(void){
1347
- char *zDbName;
1348
- const char *zDomain;
1349
- SmtpServer x;
1350
- char z[5000];
1351
-
1352
- smtp_server_init(&x);
1353
- zDomain = find_option("domain",0,1);
1354
- if( zDomain==0 ) zDomain = "";
1355
- x.srvrFlags = SMTPSRV_LOG;
1356
- if( find_option("trace",0,0)!=0 ) x.srvrFlags |= SMTPSRV_STDERR;
1357
- if( find_option("dryrun",0,0)!=0 ) x.srvrFlags |= SMTPSRV_DRYRUN;
1358
- x.zIpAddr = find_option("ipaddr",0,1);
1359
- if( x.zIpAddr ){
1360
- const char *zNew = fossil_getenv(x.zIpAddr);
1361
- if( zNew && zNew[0] ) x.zIpAddr = zNew;
1362
- }
1363
- if( x.zIpAddr==0 ){
1364
- x.zIpAddr = cgi_remote_ip(0);
1365
- if( x.zIpAddr==0 ) x.zIpAddr = "?.?.?.?";
1366
- }
1367
- verify_all_options();
1368
- if( g.argc!=3 ) usage("DBNAME");
1369
- zDbName = g.argv[2];
1370
- zDbName = enter_chroot_jail(zDbName, 0);
1371
- db_open_repository(zDbName);
1372
- add_content_sql_commands(g.db);
1373
- smtp_server_send(&x, "220 %s ESMTP https://fossil-scm.org/ %s\r\n",
1374
- zDomain, MANIFEST_VERSION);
1375
- while( smtp_server_gets(&x, z, sizeof(z)) ){
1376
- if( strncmp(z, "EHLO", 4)==0 && fossil_isspace(z[4]) ){
1377
- smtp_server_send(&x, "250 ok\r\n");
1378
- }else
1379
- if( strncmp(z, "HELO", 4)==0 && fossil_isspace(z[4]) ){
1380
- smtp_server_send(&x, "250 ok\r\n");
1381
- }else
1382
- if( strncmp(z, "MAIL FROM:<", 11)==0 ){
1383
- smtp_server_route_incoming(&x, 0);
1384
- smtp_server_clear(&x, SMTPSRV_CLEAR_MSG);
1385
- x.zFrom = email_copy_addr(z+11,'>');
1386
- if( x.zFrom==0 ){
1387
- smtp_server_send(&x, "500 unacceptable email address\r\n");
1388
- }else{
1389
- smtp_server_send(&x, "250 ok\r\n");
1390
- }
1391
- }else
1392
- if( strncmp(z, "RCPT TO:<", 9)==0 ){
1393
- char *zAddr;
1394
- if( x.zFrom==0 ){
1395
- smtp_server_send(&x, "500 missing MAIL FROM\r\n");
1396
- continue;
1397
- }
1398
- zAddr = email_copy_addr(z+9, '>');
1399
- if( zAddr==0 ){
1400
- smtp_server_send(&x, "505 no such user\r\n");
1401
- continue;
1402
- }
1403
- smtp_append_to(&x, zAddr, 0);
1404
- if( x.nTo>=100 ){
1405
- smtp_server_send(&x, "452 too many recipients\r\n");
1406
- continue;
1407
- }
1408
- smtp_server_send(&x, "250 ok\r\n");
1409
- }else
1410
- if( strncmp(z, "DATA", 4)==0 && fossil_isspace(z[4]) ){
1411
- if( x.zFrom==0 || x.nTo==0 ){
1412
- smtp_server_send(&x, "500 missing RCPT TO\r\n");
1413
- continue;
1414
- }
1415
- smtp_server_send(&x, "354 ready\r\n");
1416
- smtp_server_prepend_header_lines(&x);
1417
- smtp_server_capture_data(&x, z, sizeof(z));
1418
- smtp_server_send(&x, "250 ok\r\n");
1419
- }else
1420
- if( strncmp(z, "QUIT", 4)==0 && fossil_isspace(z[4]) ){
1421
- smtp_server_route_incoming(&x, 1);
1422
- smtp_server_send(&x, "221 closing connection\r\n");
1423
- break;
1424
- }else
1425
- {
1426
- smtp_server_send(&x, "500 unknown command\r\n");
1427
- }
1428
- }
1429
- smtp_server_clear(&x, SMTPSRV_CLEAR_ALL);
1430
-}
1431
-
1432
-/*
1433
-** Zero-terminate the argument. Return a pointer the start of the
1434
-** next argument, or to NULL if there are no more arguments.
1435
-*/
1436
-static char *pop3d_arg(char *z){
1437
- if( z[0]==0 || fossil_isspace(z[0]) ){
1438
- return 0;
1439
- }
1440
- z++;
1441
- while( z[0] && !fossil_isspace(z[0]) ){ z++; }
1442
- if( z[0]==0 ) return 0;
1443
- z[0] = 0;
1444
- z++;
1445
- if( z[0]==0 || fossil_isspace(z[0]) ) return 0;
1446
- return z;
1447
-}
1448
-
1449
-/*
1450
-** Write formatted output back to the pop3 client, and also to the
1451
-** log file, if there is a log file.
1452
-*/
1453
-static void pop3_print(FILE *pLog, const char *zFormat, ...){
1454
- va_list ap;
1455
- char zLine[500];
1456
- va_start(ap, zFormat);
1457
- sqlite3_vsnprintf(sizeof(zLine),zLine,zFormat,ap);
1458
- va_end(ap);
1459
- printf("%s\r\n", zLine);
1460
- fflush(stdout);
1461
- if( pLog ) fprintf(pLog, "S: %s\n", zLine);
1462
-}
1463
-
1464
-/*
1465
-** Try to log in for zUser and zPass.
1466
-**
1467
-** zUser can either point to a Fossil user name or to an email address
1468
-** found in the user table's info field, in angle brackets.
1469
-*/
1470
-static int pop3_login(const char *zUser, char *zPass){
1471
- return login_search_uid(&zUser, zPass) != 0;
1472
-}
1473
-
1474
-/*
1475
-** COMMAND: pop3d*
1476
-**
1477
-** Usage: %fossil pop3d [OPTIONS] REPOSITORY
1478
-**
1479
-** Begin a POP3 conversation with a client using stdin/stdout using
1480
-** the mailboxes stored in REPOSITORY.
1481
-**
1482
-** If launched as root, the process first enters a chroot jail using
1483
-** the directory of REPOSITORY as root, then drops all privileges and
1484
-** assumes the user and group of REPOSITORY before reading any content
1485
-** off of the wire.
1486
-**
1487
-** --logdir DIR Each pop3d session creates a new logfile
1488
-** in the directory DIR and records a transcript
1489
-** of the session there. The logfile is opened
1490
-** before entering the chroot jail.
1491
-*/
1492
-void pop3d_command(void){
1493
- char *zDbName;
1494
- char *zA1, *zA2, *zCmd, *z;
1495
- int inAuth = 1;
1496
- int i;
1497
- FILE *pLog = 0;
1498
- const char *zDir;
1499
- Stmt q;
1500
- char zIn[1000];
1501
- char zUser[100];
1502
- zDir = find_option("logdir",0,1);
1503
- if( zDir ){
1504
- char *zFile = file_time_tempname(zDir, ".txt");
1505
- pLog = fossil_fopen(zFile, "w");
1506
- fossil_free(zFile);
1507
- }
1508
- verify_all_options();
1509
- if( g.argc!=3 ) usage("DBNAME");
1510
- zDbName = g.argv[2];
1511
- zDbName = enter_chroot_jail(zDbName, 0);
1512
- db_open_repository(zDbName);
1513
- add_content_sql_commands(g.db);
1514
- pop3_print(pLog, "+OK POP3 server ready");
1515
- while( fgets(zIn, sizeof(zIn), stdin) ){
1516
- if( pLog ) fprintf(pLog, "C: %s", zIn);
1517
- zCmd = zIn;
1518
- zA1 = pop3d_arg(zCmd);
1519
- zA2 = zA1 ? pop3d_arg(zA1) : 0;
1520
- for(i=0; zCmd[i]; i++){ zCmd[i] = fossil_tolower(zCmd[i]); }
1521
- if( strcmp(zCmd,"quit")==0 ){
1522
- if( !inAuth ){
1523
- db_multi_exec(
1524
- "UPDATE emailbox SET estate=2"
1525
- " WHERE estate<2 AND ebid IN (SELECT ebid FROM pop3 WHERE isDel);"
1526
- );
1527
- }
1528
- pop3_print(pLog, "+OK");
1529
- break;
1530
- }
1531
- if( strcmp(zCmd,"capa")==0 ){
1532
- static const char *const azCap[] = {
1533
- "TOP", "USER", "UIDL",
1534
- };
1535
- int i;
1536
- pop3_print(pLog, "+OK");
1537
- for(i=0; i<sizeof(azCap)/sizeof(azCap[0]); i++){
1538
- pop3_print(pLog, "%s", azCap[i]);
1539
- }
1540
- pop3_print(pLog, ".");
1541
- continue;
1542
- }
1543
- if( inAuth ){
1544
- if( strcmp(zCmd,"user")==0 ){
1545
- if( zA1==0 || zA2!=0 ) goto cmd_error;
1546
- sqlite3_snprintf(sizeof(zUser),zUser,"%s",zA1);
1547
- goto cmd_ok;
1548
- }
1549
- if( strcmp(zCmd,"pass")==0 ){
1550
- if( zA1==0 || zA2!=0 ) goto cmd_error;
1551
- if( pop3_login(zUser,zA1)==0 ){
1552
- goto cmd_error;
1553
- }else{
1554
- inAuth = 0;
1555
- db_multi_exec(
1556
- "CREATE TEMP TABLE pop3("
1557
- " id INTEGER PRIMARY KEY,"
1558
- " emailid INT,"
1559
- " ebid INT,"
1560
- " isDel INT,"
1561
- " esz INT"
1562
- ");"
1563
- "INSERT INTO pop3(id,emailid,ebid,isDel,esz)"
1564
- " SELECT NULL, emailid, ebid, 0, esz FROM emailblob, emailbox"
1565
- " WHERE emailid=emsgid AND euser=%Q AND estate<=1"
1566
- " ORDER BY edate;",
1567
- zUser
1568
- );
1569
- goto cmd_ok;
1570
- }
1571
- }
1572
- /* Fossil cannot process APOP since the users clear-text password is
1573
- ** unknown. */
1574
- goto cmd_error;
1575
- }else{
1576
- if( strcmp(zCmd,"stat")==0 ){
1577
- db_prepare(&q, "SELECT count(*), sum(esz) FROM pop3 WHERE NOT isDel");
1578
- if( db_step(&q)==SQLITE_ROW ){
1579
- pop3_print(pLog, "+OK %d %d",
1580
- db_column_int(&q,0), db_column_int(&q,1));
1581
- }else{
1582
- pop3_print(pLog,"-ERR");
1583
- }
1584
- db_finalize(&q);
1585
- continue;
1586
- }
1587
- if( strcmp(zCmd,"list")==0 ){
1588
- if( zA1 ){
1589
- db_prepare(&q, "SELECT id, esz FROM pop3"
1590
- " WHERE id=%d AND NOT isDel", atoi(zA1));
1591
- if( db_step(&q)==SQLITE_ROW ){
1592
- pop3_print(pLog, "+OK %d %d",
1593
- db_column_int(&q,0), db_column_int(&q,1));
1594
- }else{
1595
- pop3_print(pLog, "-ERR");
1596
- }
1597
- }else{
1598
- pop3_print(pLog, "+OK");
1599
- db_prepare(&q, "SELECT id, esz FROM pop3 WHERE NOT isDel");
1600
- while( db_step(&q)==SQLITE_ROW ){
1601
- pop3_print(pLog, "%d %d",
1602
- db_column_int(&q,0), db_column_int(&q,1));
1603
- }
1604
- pop3_print(pLog, ".");
1605
- }
1606
- db_finalize(&q);
1607
- continue;
1608
- }
1609
- if( strcmp(zCmd,"retr")==0 || strcmp(zCmd,"top")==0 ){
1610
- Blob all, line;
1611
- int nLine = 0;
1612
- int iLimit;
1613
- int hdrPending = 1;
1614
- if( zA1==0 ) goto cmd_error;
1615
- iLimit = zA2 ? atoi(zA2) : 2147483647;
1616
- if( iLimit<0 ) goto cmd_error;
1617
- z = db_text(0, "SELECT decompress(emailblob.etxt) "
1618
- " FROM emailblob, pop3"
1619
- " WHERE emailblob.emailid=pop3.emailid"
1620
- " AND pop3.id=%d AND NOT pop3.isDel",
1621
- atoi(zA1));
1622
- if( z==0 ) goto cmd_error;
1623
- pop3_print(pLog, "+OK");
1624
- blob_init(&all, z, -1);
1625
- while( (hdrPending || iLimit>0) && blob_line(&all, &line) ){
1626
- char c = blob_buffer(&line)[0];
1627
- if( c=='.' ){
1628
- fputc('.', stdout);
1629
- }else if( c=='\r' || c=='\n' ){
1630
- hdrPending = 0;
1631
- }
1632
- fwrite(blob_buffer(&line), 1, blob_size(&line), stdout);
1633
- nLine++;
1634
- if( !hdrPending ) iLimit--;
1635
- }
1636
- if( pLog ) fprintf(pLog, "S: # %d lines of content\n", nLine);
1637
- pop3_print(pLog, ".");
1638
- fossil_free(z);
1639
- blob_reset(&all);
1640
- blob_reset(&line);
1641
- fflush(stdout);
1642
- continue;
1643
- }
1644
- if( strcmp(zCmd,"dele")==0 ){
1645
- if( zA1==0 ) goto cmd_error;
1646
- db_multi_exec("UPDATE pop3 SET isDel=1 WHERE id=%d",atoi(zA1));
1647
- goto cmd_ok;
1648
- }
1649
- if( strcmp(zCmd,"rset")==0 ){
1650
- db_multi_exec("UPDATE pop3 SET isDel=0");
1651
- goto cmd_ok;
1652
- }
1653
- if( strcmp(zCmd,"uidl")==0 ){
1654
- if( zA1 ){
1655
- db_prepare(&q, "SELECT id, emailid FROM pop3"
1656
- " WHERE id=%d AND NOT isDel", atoi(zA1));
1657
- if( db_step(&q)==SQLITE_ROW ){
1658
- pop3_print(pLog, "+OK %d %d",
1659
- db_column_int(&q,0), db_column_int(&q,1));
1660
- }else{
1661
- pop3_print(pLog,"-ERR");
1662
- }
1663
- }else{
1664
- pop3_print(pLog, "+OK");
1665
- db_prepare(&q, "SELECT id, emailid FROM pop3 WHERE NOT isDel");
1666
- while( db_step(&q)==SQLITE_ROW ){
1667
- pop3_print(pLog, "%d %d",
1668
- db_column_int(&q,0), db_column_int(&q,1));
1669
- }
1670
- pop3_print(pLog, ".");
1671
- }
1672
- db_finalize(&q);
1673
- continue;
1674
- }
1675
- if( strcmp(zCmd,"noop")==0 ){
1676
- goto cmd_ok;
1677
- }
1678
- /* Else, fall through into cmd_error */
1679
- }
1680
- cmd_error:
1681
- pop3_print(pLog, "-ERR");
1682
- continue;
1683
- cmd_ok:
1684
- pop3_print(pLog, "+OK");
1685
- continue;
1686
- }
1687
- if( pLog ) fclose(pLog);
1688
-}
1689651
--- src/smtp.c
+++ src/smtp.c
@@ -646,1043 +646,5 @@
646 fossil_fatal("ERROR: %s\n", p->zErr);
647 }
648 smtp_session_free(p);
649 blob_reset(&body);
650 }
651
652 /*****************************************************************************
653 ** Server implementation
654 *****************************************************************************/
655
656 /*
657 ** Schema used by the email processing system.
658 */
659 static const char zEmailSchema[] =
660 @ -- bulk storage is in this table. This table can store either
661 @ -- the body of email messages or transcripts of an smtp session.
662 @ CREATE TABLE IF NOT EXISTS repository.emailblob(
663 @ emailid INTEGER PRIMARY KEY AUTOINCREMENT, -- numeric idea for the entry
664 @ enref INT, -- Number of references to this blob
665 @ ets INT, -- Corresponding transcript, or NULL
666 @ etime INT, -- insertion time, secs since 1970
667 @ esz INT, -- uncompressed content size
668 @ etxt TEXT -- content of this entry
669 @ );
670 @
671 @ -- One row for each mailbox entry. All users emails are stored in
672 @ -- this same table.
673 @ CREATE TABLE IF NOT EXISTS repository.emailbox(
674 @ ebid INTEGER PRIMARY KEY, -- Unique id for each mailbox entry
675 @ euser TEXT, -- User who received this email
676 @ edate INT, -- Date received. Seconds since 1970
677 @ efrom TEXT, -- Who is the email from
678 @ emsgid INT, -- Raw email text
679 @ estate INT, -- 0: Unread, 1: read, 2: trash 3: sent
680 @ esubject TEXT, -- Subject line for display
681 @ etags TEXT -- zero or more tags
682 @ );
683 @
684 @ -- Information on how to deliver incoming email.
685 @ CREATE TABLE IF NOT EXISTS repository.emailroute(
686 @ eaddr TEXT PRIMARY KEY, -- Email address
687 @ epolicy TEXT -- How to handle email sent to this address
688 @ ) WITHOUT ROWID;
689 @
690 @ -- Outgoing email queue
691 @ CREATE TABLE IF NOT EXISTS repository.emailoutq(
692 @ edomain TEXT, -- Destination domain. (ex: "fossil-scm.org")
693 @ efrom TEXT, -- Sender email address (envelope "from")
694 @ eto TEXT, -- Recipient email address (envelope "to")
695 @ emsgid INT, -- Message body in the emailblob table
696 @ ectime INT, -- Time enqueued. Seconds since 1970
697 @ emtime INT, -- Time of last send attempt. Sec since 1970
698 @ ensend INT, -- Number of send attempts
699 @ ets INT -- Transcript of last failed attempt
700 @ );
701 @
702 @ -- Triggers to automatically keep the emailblob.enref field up to date
703 @ -- as entries in the emailblob, emailbox, and emailoutq tables are
704 @ -- deleted.
705 @ CREATE TRIGGER IF NOT EXISTS repository.emailblob_d1
706 @ AFTER DELETE ON emailblob BEGIN
707 @ UPDATE emailblob SET enref=enref-1 WHERE emailid=old.ets;
708 @ END;
709 @ CREATE TRIGGER IF NOT EXISTS repository.emailbox_d1
710 @ AFTER DELETE ON emailbox BEGIN
711 @ UPDATE emailblob SET enref=enref-1 WHERE emailid=old.emsgid;
712 @ END;
713 @ CREATE TRIGGER IF NOT EXISTS repository.emailoutq_d1
714 @ AFTER DELETE ON emailoutq BEGIN
715 @ UPDATE emailblob SET enref=enref-1 WHERE emailid IN (old.ets,old.emsgid);
716 @ END;
717 @
718 @ -- An index on the emailblob entries which are unreferenced.
719 @ CREATE INDEX IF NOT EXISTS repository.emailblob_nref ON emailblob(enref)
720 @ WHERE enref<=0;
721 ;
722
723 /*
724 ** Code used to delete the email tables.
725 */
726 static const char zEmailDrop[] =
727 @ DROP TABLE IF EXISTS emailblob;
728 @ DROP TABLE IF EXISTS emailbox;
729 @ DROP TABLE IF EXISTS emailroute;
730 @ DROP TABLE IF EXISTS emailqueue;
731 ;
732
733 #if INTERFACE
734 /*
735 ** Mailbox message states
736 */
737 #define MSG_UNREAD 0
738 #define MSG_READ 1
739 #define MSG_TRASH 2
740 #endif /* INTERFACE */
741
742
743 /*
744 ** Populate the schema of a database.
745 **
746 ** eForce==0 Fast
747 ** eForce==1 Run CREATE TABLE statements every time
748 ** eForce==2 DROP then rerun CREATE TABLE
749 */
750 void smtp_server_schema(int eForce){
751 if( eForce==2 ){
752 db_multi_exec(zEmailDrop/*works-like:""*/);
753 }
754 if( eForce==1 || !db_table_exists("repository","emailblob") ){
755 db_multi_exec(zEmailSchema/*works-like:""*/);
756 }
757 }
758
759 /*
760 ** WEBPAGE: setup_smtp
761 **
762 ** Administrative page for configuring and controlling inbound email and
763 ** output email queuing. This page is available to administrators
764 ** only via the /Admin/EmailServer menu.
765 */
766 void setup_smtp(void){
767 Stmt q;
768 login_check_credentials();
769 if( !g.perm.Setup ){
770 login_needed(0);
771 return;
772 }
773 db_begin_transaction();
774 style_set_current_feature("smtp");
775 style_header("Email Server Setup");
776 if( db_table_exists("repository","emailroute") ){
777 style_submenu_element("emailblob table", "%R/emailblob");
778 style_submenu_element("emailoutq table", "%R/emailoutq");
779 db_prepare(&q, "SELECT eaddr, epolicy FROM emailroute ORDER BY 1");
780 }else{
781 db_prepare(&q, "SELECT null, null WHERE false");
782 }
783 @ <h1>Email Routing Table</h1>
784 @ <table class="emailroutetab" cellpadding="5" border="1" cellspacing="0">
785 @ <thead>
786 @ <tr>
787 @ <th>Email Address
788 @ <th>Routing
789 @ <th>
790 @ </tr>
791 @ </thead><tbody>
792 while( db_step(&q)==SQLITE_ROW ){
793 const char *zEAddr = db_column_text(&q, 0);
794 const char *zEPolicy = db_column_text(&q, 1);
795 @ <tr>
796 @ <td valign="top">%h(zEAddr)</td>
797 @ <td valign="top"><span style="white-space:pre;">%h(zEPolicy)</span></td>
798 @ <td valign="top"><form method="POST" action="%R/setup_smtp_route">
799 @ <input type="hidden" name="oaddr" value="%h(zEAddr)">
800 @ <input type="submit" value="Edit">
801 @ </form>
802 }
803 db_finalize(&q);
804 @ <tr>
805 @ <td colspan="3">
806 @ <form method="POST" action="%R/setup_smtp_route">
807 @ <input type="submit" value="New">
808 @ &larr; Add a new email address
809 @ </form>
810 @ </table>
811 style_finish_page();
812 db_end_transaction(0);
813 }
814
815 /*
816 ** WEBPAGE: setup_smtp_route
817 **
818 ** Edit a single entry in the emailroute table.
819 ** Query parameters:
820 **
821 ** eaddr=ADDR ADDR is the email address as edited.
822 **
823 ** oaddr=ADDR The original email address prior to editing.
824 ** Omit to add a new address.
825 **
826 ** epolicy=TXT The routing policy.
827 */
828 void setup_smtp_route(void){
829 char *zEAddr = PT("eaddr"); /* new email address */
830 char *zEPolicy = PT("epolicy"); /* new routing policy */
831 char *zOAddr = PT("oaddr"); /* original email address */
832 char *zErr = 0;
833 int iErr = 0;
834 login_check_credentials();
835 if( !g.perm.Setup ){
836 login_needed(0);
837 return;
838 }
839 style_set_current_feature("smtp");
840 style_header("Email Route Editor");
841
842 if( P("edit") && cgi_csrf_safe(1) && zEAddr!=0 && zEPolicy!=0 ){
843 smtp_server_schema(0);
844 if( (zOAddr==0 || fossil_strcmp(zEAddr,zOAddr)!=0) ){
845 /* New or changed email address */
846 if( db_exists("SELECT 1 FROM emailroute WHERE eaddr=%Q",zEAddr) ){
847 iErr = 1;
848 zErr = mprintf("email address \"%h(zEAddr)\" already exists",zEAddr);
849 goto smtp_route_edit;
850 }
851 if( zEPolicy[0]==0 ){
852 iErr = 2;
853 zErr = mprintf("empty route");
854 goto smtp_route_edit;
855 }
856 }
857 /* If the email address has changed, or if the new policy is blank,
858 ** delete the old address and route information
859 */
860 db_begin_transaction();
861 if( (zOAddr && fossil_strcmp(zEAddr,zOAddr)!=0) || zEPolicy[0]==0 ){
862 db_multi_exec("DELETE FROM emailroute WHERE eaddr=%Q", zOAddr);
863 }
864 if( zEPolicy[0] ){
865 /* Insert the new address and route */
866 db_multi_exec(
867 "REPLACE INTO emailroute(eaddr,epolicy) VALUES(%Q,%Q)",
868 zEAddr, zEPolicy
869 );
870 }
871 db_end_transaction(0);
872 cgi_redirectf("%R/setup_smtp");
873 }
874 if( P("cancel")!=0 ){
875 cgi_redirectf("%R/setup_smtp");
876 }
877
878 smtp_route_edit:
879 if( zEAddr==0 ) zEAddr = zOAddr;
880 if( zEPolicy==0 && db_table_exists("repository","emailroute") ){
881 zEPolicy = db_text(0, "SELECT epolicy FROM emailroute WHERE eaddr=%Q",
882 zEAddr);
883 }
884 if( zEPolicy==0 ) zEPolicy = "";
885 @ <form method="POST" action="%R/setup_smtp_route">
886 if( zOAddr ){
887 @ <input type="hidden" name="oaddr" value="%h(zOAddr)">
888 }
889 @ <table class="label-value">
890 @ <tr>
891 @ <th>Email Address:</th>
892 @ <td><input type="text" size=30 name="eaddr" value="%h(zEAddr)">
893 if( iErr==1 ){
894 @ <td><span class="generalError">&larr; %z(zErr)</span>
895 }
896 @ </tr>
897 if( zOAddr && fossil_strcmp(zOAddr,zEAddr)!=0 ){
898 @ <tr>
899 @ <th>Original Address:</th>
900 @ <td>%h(zOAddr)
901 @ </tr>
902 }
903 @ <tr>
904 @ <th>Routing:</th>
905 @ <td><textarea name="epolicy" rows="3" cols="40">%h(zEPolicy)</textarea>
906 if( iErr==2 ){
907 @ <td valign="top"><span class="generalError">&larr; %z(zErr)</span>
908 }
909 @ </tr>
910 @ <tr>
911 @ <td>&nbsp;
912 @ <td><input type="submit" name="edit" value="Apply">
913 @ <input type="submit" name="cancel" value="Cancel">
914 @ </tr>
915 @ </table>
916 @ <hr>
917 @ <h1>Instructions</h1>
918 @
919 @ <p>The "Routing" field consists of zero or more lines where each
920 @ line is an "action" followed by an "argument". Available actions:
921 @ <ul>
922 @ <li><p><b>forward</b> <i>email-address</i>
923 @ <p>Forward the message to <i>email-address</i>.
924 @ <li><p><b>mbox</b> <i>login-name</i>
925 @ <p>Store the message in the local mailbox for the user
926 @ with USER.LOGIN=<i>login-name</i>.
927 @ </ul>
928 @
929 @ <p>To delete a route &rarr; erase all text from the "Routing" field then
930 @ press the "Apply" button.
931 style_finish_page();
932 }
933
934 #if LOCAL_INTERFACE
935 /*
936 ** State information for the server
937 */
938 struct SmtpServer {
939 sqlite3_int64 idTranscript; /* Transcript ID number */
940 sqlite3_int64 idMsg; /* Message ID number */
941 const char *zIpAddr; /* Remote IP address */
942 char *zEhlo; /* Client domain on the EHLO line */
943 char *zFrom; /* MAIL FROM: argument */
944 int nTo; /* Number of RCPT TO: lines seen */
945 struct SmtpTo {
946 char *z; /* Address in each RCPT TO line */
947 int okRemote; /* zTo can be in another domain */
948 } *aTo;
949 u32 srvrFlags; /* Control flags */
950 int nEts; /* Number of references to the transcript */
951 int nRef; /* Number of references to idMsg */
952 Blob msg; /* Content following DATA */
953 Blob transcript; /* Session transcript */
954 };
955
956 #define SMTPSRV_CLEAR_MSG 1 /* smtp_server_clear() last message only */
957 #define SMTPSRV_CLEAR_ALL 2 /* smtp_server_clear() everything */
958 #define SMTPSRV_LOG 0x001 /* Record a transcript of the interaction */
959 #define SMTPSRV_STDERR 0x002 /* Transcription written to stderr */
960 #define SMTPSRV_DRYRUN 0x004 /* Do not record anything in database */
961
962 #endif /* LOCAL_INTERFACE */
963
964 /*
965 ** Clear the SmtpServer object. Deallocate resources.
966 ** How much to clear depends on eHowMuch
967 */
968 static void smtp_server_clear(SmtpServer *p, int eHowMuch){
969 int i;
970 if( eHowMuch>=SMTPSRV_CLEAR_MSG ){
971 fossil_free(p->zFrom);
972 p->zFrom = 0;
973 for(i=0; i<p->nTo; i++) fossil_free(p->aTo[i].z);
974 fossil_free(p->aTo);
975 p->aTo = 0;
976 p->nTo = 0;
977 blob_reset(&p->msg);
978 p->idMsg = 0;
979 }
980 if( eHowMuch>=SMTPSRV_CLEAR_ALL ){
981 blob_reset(&p->transcript);
982 p->idTranscript = 0;
983 fossil_free(p->zEhlo);
984 p->zEhlo = 0;
985 }
986 }
987
988 /*
989 ** Turn raw memory into an SmtpServer object.
990 */
991 static void smtp_server_init(SmtpServer *p){
992 memset(p, 0, sizeof(*p));
993 blob_init(&p->msg, 0, 0);
994 blob_init(&p->transcript, 0, 0);
995 }
996
997 /*
998 ** Append a new TO entry to the SmtpServer object. Do not do the
999 ** append if the same entry is already on the list.
1000 **
1001 ** The zAddr argument is obtained from fossil_malloc(). This
1002 ** routine assumes ownership of the allocation.
1003 */
1004 static void smtp_append_to(SmtpServer *p, char *zAddr, int okRemote){
1005 int i;
1006 for(i=0; zAddr[i]; i++){ zAddr[i] = fossil_tolower(zAddr[i]); }
1007 for(i=0; i<p->nTo; i++){
1008 if( strcmp(zAddr, p->aTo[i].z)==0 ){
1009 fossil_free(zAddr);
1010 if( p->aTo[i].okRemote==0 ) p->aTo[i].okRemote = okRemote;
1011 return;
1012 }
1013 }
1014 p->aTo = fossil_realloc(p->aTo, (p->nTo+1)*sizeof(p->aTo[0]));
1015 p->aTo[p->nTo].z = zAddr;
1016 p->aTo[p->nTo].okRemote = okRemote;
1017 p->nTo++;
1018 }
1019
1020 /*
1021 ** Send a single line of output from the server to the client.
1022 */
1023 static void smtp_server_send(SmtpServer *p, const char *zFormat, ...){
1024 Blob b = empty_blob;
1025 va_list ap;
1026 char *z;
1027 int n;
1028 va_start(ap, zFormat);
1029 blob_vappendf(&b, zFormat, ap);
1030 va_end(ap);
1031 z = blob_buffer(&b);
1032 n = blob_size(&b);
1033 assert( n>=2 );
1034 assert( z[n-1]=='\n' );
1035 assert( z[n-2]=='\r' );
1036 if( p->srvrFlags & SMTPSRV_LOG ){
1037 blob_appendf(&p->transcript, "S: %.*s\n", n-2, z);
1038 }
1039 if( p->srvrFlags & SMTPSRV_STDERR ){
1040 fprintf(stderr, "S: %.*s\n", n-2, z);
1041 }
1042 fwrite(z, n, 1, stdout);
1043 fflush(stdout);
1044 blob_reset(&b);
1045 }
1046
1047 /*
1048 ** Read a single line from the client.
1049 */
1050 static int smtp_server_gets(SmtpServer *p, char *aBuf, int nBuf){
1051 int rc = fgets(aBuf, nBuf, stdin)!=0;
1052 if( rc ){
1053 if( (p->srvrFlags & SMTPSRV_LOG)!=0 ){
1054 blob_appendf(&p->transcript, "C: %s", aBuf);
1055 }
1056 if( (p->srvrFlags & SMTPSRV_STDERR)!=0 ){
1057 fprintf(stderr, "C: %s", aBuf);
1058 }
1059 }
1060 return rc;
1061 }
1062
1063 /*
1064 ** RFC-5321 requires certain content be prepended to an email header
1065 ** as that email is received.
1066 */
1067 static void smtp_server_prepend_header_lines(SmtpServer *p){
1068 blob_appendf(&p->msg, "Received: from %s by Fossil-smtp\r\n", p->zIpAddr);
1069 }
1070
1071 /*
1072 ** Capture the incoming email data into the p->msg blob. Dequote
1073 ** lines of "..\r\n" into just ".\r\n".
1074 */
1075 static void smtp_server_capture_data(SmtpServer *p, char *z, int n){
1076 int nLine = 0;
1077 while( fgets(z, n, stdin) ){
1078 if( strncmp(z, ".\r\n", 3)==0 || strncmp(z, ".\n",2)==0 ) break;
1079 nLine++;
1080 if( strncmp(z, "..\r\n", 4)==0 || strncmp(z, "..\n",3)==0 ){
1081 memmove(z, z+1, 4);
1082 }
1083 blob_append(&p->msg, z, -1);
1084 }
1085 if( p->srvrFlags & SMTPSRV_LOG ){
1086 blob_appendf(&p->transcript, "C: # %d lines, %d bytes of content\n",
1087 nLine, blob_size(&p->msg));
1088 }
1089 if( p->srvrFlags & SMTPSRV_STDERR ){
1090 fprintf(stderr, "C: # %d lines, %d bytes of content\n",
1091 nLine, blob_size(&p->msg));
1092 }
1093 }
1094
1095 /*
1096 ** Send an email to a single email addess that is registered with
1097 ** this system, according to the instructions in emailroute. If
1098 ** zAddr is not in the emailroute table, then this routine is a
1099 ** no-op. Or if zAddr has already been processed, then this
1100 ** routine is a no-op.
1101 */
1102 static void smtp_server_send_one_user(
1103 SmtpServer *p, /* The current inbound email */
1104 const char *zAddr, /* Who to forward this to */
1105 int okRemote /* True if ok to foward to another domain */
1106 ){
1107 char *zPolicy;
1108 Blob policy, line, token, tail;
1109
1110 zPolicy = db_text(0,
1111 "SELECT epolicy FROM emailroute WHERE eaddr=%Q", zAddr);
1112 if( zPolicy==0 ){
1113 if( okRemote ){
1114 int i;
1115 for(i=0; zAddr[i] && zAddr[i]!='@'; i++){}
1116 if( zAddr[i]=='@' && zAddr[i+1]!=0 ){
1117 db_multi_exec(
1118 "INSERT INTO emailoutq(edomain,efrom,eto,emsgid,ectime,"
1119 "emtime,ensend)"
1120 "VALUES(%Q,%Q,%Q,%lld,now(),0,0)",
1121 zAddr+i+1, p->zFrom, zAddr, p->idMsg
1122 );
1123 p->nRef++;
1124 }
1125 }
1126 return;
1127 }
1128 blob_init(&policy, zPolicy, -1);
1129 while( blob_line(&policy, &line) ){
1130 blob_trim(&line);
1131 blob_token(&line, &token);
1132 blob_tail(&line, &tail);
1133 if( blob_size(&tail)==0 ) continue;
1134 if( blob_eq_str(&token, "mbox", 4) ){
1135 Blob subj;
1136 email_header_value(&p->msg, "subject", &subj);
1137 db_multi_exec(
1138 "INSERT INTO emailbox(euser,edate,efrom,emsgid,estate,esubject)"
1139 " VALUES(%Q,now(),%Q,%lld,0,%Q)",
1140 blob_str(&tail), p->zFrom, p->idMsg,
1141 blob_str(&subj)
1142 );
1143 blob_reset(&subj);
1144 p->nRef++;
1145 }
1146 if( blob_eq_str(&token, "forward", 7) ){
1147 smtp_append_to(p, fossil_strdup(blob_str(&tail)), 1);
1148 }
1149 blob_reset(&tail);
1150 }
1151 }
1152
1153 /*
1154 ** The SmtpServer object contains a complete incoming email.
1155 ** Add this email to the database.
1156 */
1157 static void smtp_server_route_incoming(SmtpServer *p, int bFinish){
1158 Stmt s;
1159 int i;
1160 int nEtsStart = p->nEts;
1161 if( p->zFrom
1162 && p->nTo
1163 && blob_size(&p->msg)
1164 && (p->srvrFlags & SMTPSRV_DRYRUN)==0
1165 ){
1166 db_begin_write();
1167 if( p->idTranscript==0 ) smtp_server_schema(0);
1168 p->nRef = 0;
1169 db_prepare(&s,
1170 "INSERT INTO emailblob(ets,etime,etxt,enref,esz)"
1171 " VALUES(:ets,now(),compress(:etxt),0,:esz)"
1172 );
1173 p->nEts++;
1174 if( !bFinish && p->idTranscript==0 ){
1175 db_bind_null(&s, ":ets");
1176 db_bind_null(&s, ":etxt");
1177 db_bind_null(&s, ":esz");
1178 db_step(&s);
1179 db_reset(&s);
1180 p->idTranscript = db_last_insert_rowid();
1181 }else if( bFinish ){
1182 if( p->idTranscript ){
1183 db_multi_exec(
1184 "UPDATE emailblob SET etxt=compress(%Q), enref=%d, esz=%d"
1185 " WHERE emailid=%lld",
1186 blob_str(&p->transcript), p->nEts, blob_size(&p->transcript),
1187 p->idTranscript);
1188 }else{
1189 db_bind_null(&s, ":ets");
1190 db_bind_str(&s, ":etxt", &p->transcript);
1191 db_bind_int(&s, ":esz", blob_size(&p->transcript));
1192 db_step(&s);
1193 db_reset(&s);
1194 p->idTranscript = db_last_insert_rowid();
1195 db_multi_exec(
1196 "UPDATE emailblob SET enref=%d WHERE emailid=%lld",
1197 p->nEts, p->idTranscript);
1198 }
1199 /* smtp_server_send(p, "221-Transcript id %lld nref %d\r\n",
1200 ** p->idTranscript, p->nEts); */
1201 }
1202 db_bind_int64(&s, ":ets", p->idTranscript);
1203 db_bind_str(&s, ":etxt", &p->msg);
1204 db_bind_int(&s, ":esz", blob_size(&p->msg));
1205 db_step(&s);
1206 db_finalize(&s);
1207 p->idMsg = db_last_insert_rowid();
1208
1209 /* make entries in emailbox and emailoutq */
1210 for(i=0; i<p->nTo; i++){
1211 int okRemote = p->aTo[i].okRemote;
1212 p->aTo[i].okRemote = 1;
1213 smtp_server_send_one_user(p, p->aTo[i].z, okRemote);
1214 }
1215
1216 /* Fix up the emailblob.enref field of the email message body */
1217 if( p->nRef ){
1218 db_multi_exec(
1219 "UPDATE emailblob SET enref=%d WHERE emailid=%lld",
1220 p->nRef, p->idMsg
1221 );
1222 }else{
1223 db_multi_exec(
1224 "DELETE FROM emailblob WHERE emailid=%lld", p->idMsg
1225 );
1226 p->nEts = nEtsStart;
1227 }
1228
1229 /* Clean out legacy entries */
1230 if( bFinish ){
1231 db_multi_exec("DELETE FROM emailblob WHERE enref<=0");
1232 }
1233
1234 /* Finish the transaction after all changes are implemented */
1235 db_commit_transaction();
1236 }
1237 smtp_server_clear(p, SMTPSRV_CLEAR_MSG);
1238 }
1239
1240 /*
1241 ** Remove stale content from the emailblob table.
1242 */
1243 int smtp_cleanup(void){
1244 int nAction = 0;
1245 if( db_table_exists("repository","emailblob") ){
1246 db_begin_transaction();
1247 db_multi_exec(
1248 "UPDATE emailblob SET ets=NULL WHERE enref<=0;"
1249 "DELETE FROM emailblob WHERE enref<=0;"
1250 );
1251 nAction = db_changes();
1252 db_end_transaction(0);
1253 }
1254 return nAction;
1255 }
1256
1257 /*
1258 ** COMMAND: test-emailblob-refcheck
1259 **
1260 ** Usage: %fossil test-emailblob-refcheck [--repair] [--full] [--clean]
1261 **
1262 ** Verify that the emailblob.enref field is correct. Report any errors.
1263 ** Use the --repair command to fix up the enref field. The --full option
1264 ** gives a full report showing the enref value on all entries in the
1265 ** emailblob table. If the --clean flags is used together with --repair,
1266 ** then emailblob table entires with enref==0 are removed.
1267 */
1268 void test_refcheck_emailblob(void){
1269 int doRepair;
1270 int fullReport;
1271 int doClean;
1272 Blob sql;
1273 Stmt q;
1274 int nErr = 0;
1275 db_find_and_open_repository(0, 0);
1276 fullReport = find_option("full",0,0)!=0;
1277 doRepair = find_option("repair",0,0)!=0;
1278 doClean = find_option("clean",0,0)!=0;
1279 verify_all_options();
1280 if( !db_table_exists("repository","emailblob") ){
1281 fossil_print("emailblob table is not configured - nothing to check\n");
1282 return;
1283 }
1284 db_multi_exec(
1285 "CREATE TEMP TABLE refcnt(id INTEGER PRIMARY KEY, n);"
1286 "INSERT INTO refcnt SELECT ets, count(*) FROM ("
1287 " SELECT ets FROM emailblob"
1288 " UNION ALL"
1289 " SELECT emsgid FROM emailbox"
1290 " UNION ALL"
1291 " SELECT emsgid FROM emailoutq"
1292 ") WHERE ets IS NOT NULL GROUP BY 1;"
1293 "INSERT OR IGNORE INTO refcnt(id,n) SELECT emailid, 0 FROM emailblob;"
1294 );
1295 if( doRepair ){
1296 db_multi_exec(
1297 "UPDATE emailblob SET enref=(SELECT n FROM refcnt WHERE id=emailid)"
1298 );
1299 if( doClean ){
1300 smtp_cleanup();
1301 }
1302 }
1303 blob_init(&sql, 0, 0);
1304 blob_append_sql(&sql,
1305 "SELECT a.emailid, a.enref, b.n"
1306 " FROM emailblob AS a JOIN refcnt AS b ON a.emailid=b.id"
1307 );
1308 if( !fullReport ){
1309 blob_append_sql(&sql, " WHERE a.enref!=b.n");
1310 }
1311 db_prepare_blob(&q, &sql);
1312 blob_reset(&sql);
1313 while( db_step(&q)==SQLITE_ROW ){
1314 sqlite3_int64 id = db_column_int64(&q,0);
1315 int n1 = db_column_int(&q, 1);
1316 int n2 = db_column_int(&q, 2);
1317 if( n1!=n2 ) nErr++;
1318 fossil_print("%12lld %4d %4d%s\n", id, n1, n2, n1!=n2 ? " ERROR" : "");
1319 }
1320 db_finalize(&q);
1321 if( nErr ){
1322 fossil_print("Number of incorrect emailblob.enref values: %d\n",nErr);
1323 }
1324 }
1325
1326
1327 /*
1328 ** COMMAND: smtpd*
1329 **
1330 ** Usage: %fossil smtpd [OPTIONS] REPOSITORY
1331 **
1332 ** Begin a SMTP conversation with a client using stdin/stdout. The
1333 ** received email is stored in REPOSITORY.
1334 **
1335 ** Options:
1336 **
1337 ** --dryrun Do not record any emails in the database
1338 **
1339 ** --trace Print a transcript of the conversation on stderr
1340 ** for debugging and analysis
1341 **
1342 ** --ipaddr ADDR The SMTP connection originates at ADDR. Or if ADDR
1343 ** is the name of an environment variable, the address
1344 ** is taken from that environment variable.
1345 */
1346 void smtp_server(void){
1347 char *zDbName;
1348 const char *zDomain;
1349 SmtpServer x;
1350 char z[5000];
1351
1352 smtp_server_init(&x);
1353 zDomain = find_option("domain",0,1);
1354 if( zDomain==0 ) zDomain = "";
1355 x.srvrFlags = SMTPSRV_LOG;
1356 if( find_option("trace",0,0)!=0 ) x.srvrFlags |= SMTPSRV_STDERR;
1357 if( find_option("dryrun",0,0)!=0 ) x.srvrFlags |= SMTPSRV_DRYRUN;
1358 x.zIpAddr = find_option("ipaddr",0,1);
1359 if( x.zIpAddr ){
1360 const char *zNew = fossil_getenv(x.zIpAddr);
1361 if( zNew && zNew[0] ) x.zIpAddr = zNew;
1362 }
1363 if( x.zIpAddr==0 ){
1364 x.zIpAddr = cgi_remote_ip(0);
1365 if( x.zIpAddr==0 ) x.zIpAddr = "?.?.?.?";
1366 }
1367 verify_all_options();
1368 if( g.argc!=3 ) usage("DBNAME");
1369 zDbName = g.argv[2];
1370 zDbName = enter_chroot_jail(zDbName, 0);
1371 db_open_repository(zDbName);
1372 add_content_sql_commands(g.db);
1373 smtp_server_send(&x, "220 %s ESMTP https://fossil-scm.org/ %s\r\n",
1374 zDomain, MANIFEST_VERSION);
1375 while( smtp_server_gets(&x, z, sizeof(z)) ){
1376 if( strncmp(z, "EHLO", 4)==0 && fossil_isspace(z[4]) ){
1377 smtp_server_send(&x, "250 ok\r\n");
1378 }else
1379 if( strncmp(z, "HELO", 4)==0 && fossil_isspace(z[4]) ){
1380 smtp_server_send(&x, "250 ok\r\n");
1381 }else
1382 if( strncmp(z, "MAIL FROM:<", 11)==0 ){
1383 smtp_server_route_incoming(&x, 0);
1384 smtp_server_clear(&x, SMTPSRV_CLEAR_MSG);
1385 x.zFrom = email_copy_addr(z+11,'>');
1386 if( x.zFrom==0 ){
1387 smtp_server_send(&x, "500 unacceptable email address\r\n");
1388 }else{
1389 smtp_server_send(&x, "250 ok\r\n");
1390 }
1391 }else
1392 if( strncmp(z, "RCPT TO:<", 9)==0 ){
1393 char *zAddr;
1394 if( x.zFrom==0 ){
1395 smtp_server_send(&x, "500 missing MAIL FROM\r\n");
1396 continue;
1397 }
1398 zAddr = email_copy_addr(z+9, '>');
1399 if( zAddr==0 ){
1400 smtp_server_send(&x, "505 no such user\r\n");
1401 continue;
1402 }
1403 smtp_append_to(&x, zAddr, 0);
1404 if( x.nTo>=100 ){
1405 smtp_server_send(&x, "452 too many recipients\r\n");
1406 continue;
1407 }
1408 smtp_server_send(&x, "250 ok\r\n");
1409 }else
1410 if( strncmp(z, "DATA", 4)==0 && fossil_isspace(z[4]) ){
1411 if( x.zFrom==0 || x.nTo==0 ){
1412 smtp_server_send(&x, "500 missing RCPT TO\r\n");
1413 continue;
1414 }
1415 smtp_server_send(&x, "354 ready\r\n");
1416 smtp_server_prepend_header_lines(&x);
1417 smtp_server_capture_data(&x, z, sizeof(z));
1418 smtp_server_send(&x, "250 ok\r\n");
1419 }else
1420 if( strncmp(z, "QUIT", 4)==0 && fossil_isspace(z[4]) ){
1421 smtp_server_route_incoming(&x, 1);
1422 smtp_server_send(&x, "221 closing connection\r\n");
1423 break;
1424 }else
1425 {
1426 smtp_server_send(&x, "500 unknown command\r\n");
1427 }
1428 }
1429 smtp_server_clear(&x, SMTPSRV_CLEAR_ALL);
1430 }
1431
1432 /*
1433 ** Zero-terminate the argument. Return a pointer the start of the
1434 ** next argument, or to NULL if there are no more arguments.
1435 */
1436 static char *pop3d_arg(char *z){
1437 if( z[0]==0 || fossil_isspace(z[0]) ){
1438 return 0;
1439 }
1440 z++;
1441 while( z[0] && !fossil_isspace(z[0]) ){ z++; }
1442 if( z[0]==0 ) return 0;
1443 z[0] = 0;
1444 z++;
1445 if( z[0]==0 || fossil_isspace(z[0]) ) return 0;
1446 return z;
1447 }
1448
1449 /*
1450 ** Write formatted output back to the pop3 client, and also to the
1451 ** log file, if there is a log file.
1452 */
1453 static void pop3_print(FILE *pLog, const char *zFormat, ...){
1454 va_list ap;
1455 char zLine[500];
1456 va_start(ap, zFormat);
1457 sqlite3_vsnprintf(sizeof(zLine),zLine,zFormat,ap);
1458 va_end(ap);
1459 printf("%s\r\n", zLine);
1460 fflush(stdout);
1461 if( pLog ) fprintf(pLog, "S: %s\n", zLine);
1462 }
1463
1464 /*
1465 ** Try to log in for zUser and zPass.
1466 **
1467 ** zUser can either point to a Fossil user name or to an email address
1468 ** found in the user table's info field, in angle brackets.
1469 */
1470 static int pop3_login(const char *zUser, char *zPass){
1471 return login_search_uid(&zUser, zPass) != 0;
1472 }
1473
1474 /*
1475 ** COMMAND: pop3d*
1476 **
1477 ** Usage: %fossil pop3d [OPTIONS] REPOSITORY
1478 **
1479 ** Begin a POP3 conversation with a client using stdin/stdout using
1480 ** the mailboxes stored in REPOSITORY.
1481 **
1482 ** If launched as root, the process first enters a chroot jail using
1483 ** the directory of REPOSITORY as root, then drops all privileges and
1484 ** assumes the user and group of REPOSITORY before reading any content
1485 ** off of the wire.
1486 **
1487 ** --logdir DIR Each pop3d session creates a new logfile
1488 ** in the directory DIR and records a transcript
1489 ** of the session there. The logfile is opened
1490 ** before entering the chroot jail.
1491 */
1492 void pop3d_command(void){
1493 char *zDbName;
1494 char *zA1, *zA2, *zCmd, *z;
1495 int inAuth = 1;
1496 int i;
1497 FILE *pLog = 0;
1498 const char *zDir;
1499 Stmt q;
1500 char zIn[1000];
1501 char zUser[100];
1502 zDir = find_option("logdir",0,1);
1503 if( zDir ){
1504 char *zFile = file_time_tempname(zDir, ".txt");
1505 pLog = fossil_fopen(zFile, "w");
1506 fossil_free(zFile);
1507 }
1508 verify_all_options();
1509 if( g.argc!=3 ) usage("DBNAME");
1510 zDbName = g.argv[2];
1511 zDbName = enter_chroot_jail(zDbName, 0);
1512 db_open_repository(zDbName);
1513 add_content_sql_commands(g.db);
1514 pop3_print(pLog, "+OK POP3 server ready");
1515 while( fgets(zIn, sizeof(zIn), stdin) ){
1516 if( pLog ) fprintf(pLog, "C: %s", zIn);
1517 zCmd = zIn;
1518 zA1 = pop3d_arg(zCmd);
1519 zA2 = zA1 ? pop3d_arg(zA1) : 0;
1520 for(i=0; zCmd[i]; i++){ zCmd[i] = fossil_tolower(zCmd[i]); }
1521 if( strcmp(zCmd,"quit")==0 ){
1522 if( !inAuth ){
1523 db_multi_exec(
1524 "UPDATE emailbox SET estate=2"
1525 " WHERE estate<2 AND ebid IN (SELECT ebid FROM pop3 WHERE isDel);"
1526 );
1527 }
1528 pop3_print(pLog, "+OK");
1529 break;
1530 }
1531 if( strcmp(zCmd,"capa")==0 ){
1532 static const char *const azCap[] = {
1533 "TOP", "USER", "UIDL",
1534 };
1535 int i;
1536 pop3_print(pLog, "+OK");
1537 for(i=0; i<sizeof(azCap)/sizeof(azCap[0]); i++){
1538 pop3_print(pLog, "%s", azCap[i]);
1539 }
1540 pop3_print(pLog, ".");
1541 continue;
1542 }
1543 if( inAuth ){
1544 if( strcmp(zCmd,"user")==0 ){
1545 if( zA1==0 || zA2!=0 ) goto cmd_error;
1546 sqlite3_snprintf(sizeof(zUser),zUser,"%s",zA1);
1547 goto cmd_ok;
1548 }
1549 if( strcmp(zCmd,"pass")==0 ){
1550 if( zA1==0 || zA2!=0 ) goto cmd_error;
1551 if( pop3_login(zUser,zA1)==0 ){
1552 goto cmd_error;
1553 }else{
1554 inAuth = 0;
1555 db_multi_exec(
1556 "CREATE TEMP TABLE pop3("
1557 " id INTEGER PRIMARY KEY,"
1558 " emailid INT,"
1559 " ebid INT,"
1560 " isDel INT,"
1561 " esz INT"
1562 ");"
1563 "INSERT INTO pop3(id,emailid,ebid,isDel,esz)"
1564 " SELECT NULL, emailid, ebid, 0, esz FROM emailblob, emailbox"
1565 " WHERE emailid=emsgid AND euser=%Q AND estate<=1"
1566 " ORDER BY edate;",
1567 zUser
1568 );
1569 goto cmd_ok;
1570 }
1571 }
1572 /* Fossil cannot process APOP since the users clear-text password is
1573 ** unknown. */
1574 goto cmd_error;
1575 }else{
1576 if( strcmp(zCmd,"stat")==0 ){
1577 db_prepare(&q, "SELECT count(*), sum(esz) FROM pop3 WHERE NOT isDel");
1578 if( db_step(&q)==SQLITE_ROW ){
1579 pop3_print(pLog, "+OK %d %d",
1580 db_column_int(&q,0), db_column_int(&q,1));
1581 }else{
1582 pop3_print(pLog,"-ERR");
1583 }
1584 db_finalize(&q);
1585 continue;
1586 }
1587 if( strcmp(zCmd,"list")==0 ){
1588 if( zA1 ){
1589 db_prepare(&q, "SELECT id, esz FROM pop3"
1590 " WHERE id=%d AND NOT isDel", atoi(zA1));
1591 if( db_step(&q)==SQLITE_ROW ){
1592 pop3_print(pLog, "+OK %d %d",
1593 db_column_int(&q,0), db_column_int(&q,1));
1594 }else{
1595 pop3_print(pLog, "-ERR");
1596 }
1597 }else{
1598 pop3_print(pLog, "+OK");
1599 db_prepare(&q, "SELECT id, esz FROM pop3 WHERE NOT isDel");
1600 while( db_step(&q)==SQLITE_ROW ){
1601 pop3_print(pLog, "%d %d",
1602 db_column_int(&q,0), db_column_int(&q,1));
1603 }
1604 pop3_print(pLog, ".");
1605 }
1606 db_finalize(&q);
1607 continue;
1608 }
1609 if( strcmp(zCmd,"retr")==0 || strcmp(zCmd,"top")==0 ){
1610 Blob all, line;
1611 int nLine = 0;
1612 int iLimit;
1613 int hdrPending = 1;
1614 if( zA1==0 ) goto cmd_error;
1615 iLimit = zA2 ? atoi(zA2) : 2147483647;
1616 if( iLimit<0 ) goto cmd_error;
1617 z = db_text(0, "SELECT decompress(emailblob.etxt) "
1618 " FROM emailblob, pop3"
1619 " WHERE emailblob.emailid=pop3.emailid"
1620 " AND pop3.id=%d AND NOT pop3.isDel",
1621 atoi(zA1));
1622 if( z==0 ) goto cmd_error;
1623 pop3_print(pLog, "+OK");
1624 blob_init(&all, z, -1);
1625 while( (hdrPending || iLimit>0) && blob_line(&all, &line) ){
1626 char c = blob_buffer(&line)[0];
1627 if( c=='.' ){
1628 fputc('.', stdout);
1629 }else if( c=='\r' || c=='\n' ){
1630 hdrPending = 0;
1631 }
1632 fwrite(blob_buffer(&line), 1, blob_size(&line), stdout);
1633 nLine++;
1634 if( !hdrPending ) iLimit--;
1635 }
1636 if( pLog ) fprintf(pLog, "S: # %d lines of content\n", nLine);
1637 pop3_print(pLog, ".");
1638 fossil_free(z);
1639 blob_reset(&all);
1640 blob_reset(&line);
1641 fflush(stdout);
1642 continue;
1643 }
1644 if( strcmp(zCmd,"dele")==0 ){
1645 if( zA1==0 ) goto cmd_error;
1646 db_multi_exec("UPDATE pop3 SET isDel=1 WHERE id=%d",atoi(zA1));
1647 goto cmd_ok;
1648 }
1649 if( strcmp(zCmd,"rset")==0 ){
1650 db_multi_exec("UPDATE pop3 SET isDel=0");
1651 goto cmd_ok;
1652 }
1653 if( strcmp(zCmd,"uidl")==0 ){
1654 if( zA1 ){
1655 db_prepare(&q, "SELECT id, emailid FROM pop3"
1656 " WHERE id=%d AND NOT isDel", atoi(zA1));
1657 if( db_step(&q)==SQLITE_ROW ){
1658 pop3_print(pLog, "+OK %d %d",
1659 db_column_int(&q,0), db_column_int(&q,1));
1660 }else{
1661 pop3_print(pLog,"-ERR");
1662 }
1663 }else{
1664 pop3_print(pLog, "+OK");
1665 db_prepare(&q, "SELECT id, emailid FROM pop3 WHERE NOT isDel");
1666 while( db_step(&q)==SQLITE_ROW ){
1667 pop3_print(pLog, "%d %d",
1668 db_column_int(&q,0), db_column_int(&q,1));
1669 }
1670 pop3_print(pLog, ".");
1671 }
1672 db_finalize(&q);
1673 continue;
1674 }
1675 if( strcmp(zCmd,"noop")==0 ){
1676 goto cmd_ok;
1677 }
1678 /* Else, fall through into cmd_error */
1679 }
1680 cmd_error:
1681 pop3_print(pLog, "-ERR");
1682 continue;
1683 cmd_ok:
1684 pop3_print(pLog, "+OK");
1685 continue;
1686 }
1687 if( pLog ) fclose(pLog);
1688 }
1689
--- src/smtp.c
+++ src/smtp.c
@@ -646,1043 +646,5 @@
646 fossil_fatal("ERROR: %s\n", p->zErr);
647 }
648 smtp_session_free(p);
649 blob_reset(&body);
650 }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
651
+159 -98
--- src/sqlite3.c
+++ src/sqlite3.c
@@ -1,8 +1,8 @@
11
/******************************************************************************
22
** This file is an amalgamation of many separate C source files from SQLite
3
-** version 3.35.0. By combining all the individual C code files into this
3
+** version 3.35.3. By combining all the individual C code files into this
44
** single large file, the entire code can be compiled as a single translation
55
** unit. This allows many compilers to do optimizations that would not be
66
** possible if the files were compiled separately. Performance improvements
77
** of 5% or more are commonly seen when SQLite is compiled as a single
88
** translation unit.
@@ -1184,13 +1184,13 @@
11841184
**
11851185
** See also: [sqlite3_libversion()],
11861186
** [sqlite3_libversion_number()], [sqlite3_sourceid()],
11871187
** [sqlite_version()] and [sqlite_source_id()].
11881188
*/
1189
-#define SQLITE_VERSION "3.35.0"
1190
-#define SQLITE_VERSION_NUMBER 3035000
1191
-#define SQLITE_SOURCE_ID "2021-03-12 15:10:09 acd63062eb06748bfe9e4886639e4f2b54ea6a496a83f10716abbaba4115500b"
1189
+#define SQLITE_VERSION "3.35.3"
1190
+#define SQLITE_VERSION_NUMBER 3035003
1191
+#define SQLITE_SOURCE_ID "2021-03-21 18:23:48 6bb2134027a12801de8e0c73482d94682f902024800a7e426614f65a2fe4f64c"
11921192
11931193
/*
11941194
** CAPI3REF: Run-Time Library Version Numbers
11951195
** KEYWORDS: sqlite3_version sqlite3_sourceid
11961196
**
@@ -17005,11 +17005,14 @@
1700517005
u8 iDb; /* Which db file is being initialized */
1700617006
u8 busy; /* TRUE if currently initializing */
1700717007
unsigned orphanTrigger : 1; /* Last statement is orphaned TEMP trigger */
1700817008
unsigned imposterTable : 1; /* Building an imposter table */
1700917009
unsigned reopenMemdb : 1; /* ATTACH is really a reopen using MemDB */
17010
+ unsigned bDropColumn : 1; /* Doing schema check after DROP COLUMN */
1701017011
char **azInit; /* "type", "name", and "tbl_name" columns */
17012
+ /* or if bDropColumn, then azInit[0] is the */
17013
+ /* name of the column being dropped */
1701117014
} init;
1701217015
int nVdbeActive; /* Number of VDBEs currently running */
1701317016
int nVdbeRead; /* Number of active VDBEs that read or write */
1701417017
int nVdbeWrite; /* Number of active VDBEs that read and write */
1701517018
int nVdbeExec; /* Number of nested calls to VdbeExec() */
@@ -21066,10 +21069,11 @@
2106621069
u8 wrFlag; /* The wrFlag argument to sqlite3BtreeCursor() */
2106721070
#endif
2106821071
Bool isEphemeral:1; /* True for an ephemeral table */
2106921072
Bool useRandomRowid:1; /* Generate new record numbers semi-randomly */
2107021073
Bool isOrdered:1; /* True if the table is not BTREE_UNORDERED */
21074
+ Bool hasBeenDuped:1; /* This cursor was source or target of OP_OpenDup */
2107121075
u16 seekHit; /* See the OP_SeekHit and OP_IfNoHope opcodes */
2107221076
Btree *pBtx; /* Separate file holding temporary table */
2107321077
i64 seqCount; /* Sequence counter */
2107421078
u32 *aAltMap; /* Mapping from table to index column numbers */
2107521079
@@ -22866,10 +22870,11 @@
2286622870
int i, n;
2286722871
const unsigned char *z;
2286822872
int eType;
2286922873
memset(p, 0, sizeof(*p));
2287022874
if( argc==0 ){
22875
+ if( !sqlite3NotPureFunc(context) ) return 1;
2287122876
return setDateTimeToCurrent(context, p);
2287222877
}
2287322878
if( (eType = sqlite3_value_type(argv[0]))==SQLITE_FLOAT
2287422879
|| eType==SQLITE_INTEGER ){
2287522880
setRawDateNumber(p, sqlite3_value_double(argv[0]));
@@ -40214,11 +40219,12 @@
4021440219
*/
4021540220
static int unixBackupDir(const char *z, int *pJ){
4021640221
int j = *pJ;
4021740222
int i;
4021840223
if( j<=0 ) return 0;
40219
- for(i=j-1; ALWAYS(i>0) && z[i-1]!='/'; i--){}
40224
+ for(i=j-1; i>0 && z[i-1]!='/'; i--){}
40225
+ if( i==0 ) return 0;
4022040226
if( z[i]=='.' && i==j-2 && z[i+1]=='.' ) return 0;
4022140227
*pJ = i-1;
4022240228
return 1;
4022340229
}
4022440230
@@ -64271,11 +64277,11 @@
6427164277
u8 sharable; /* True if we can share pBt with another db */
6427264278
u8 locked; /* True if db currently has pBt locked */
6427364279
u8 hasIncrblobCur; /* True if there are one or more Incrblob cursors */
6427464280
int wantToLock; /* Number of nested calls to sqlite3BtreeEnter() */
6427564281
int nBackup; /* Number of backup operations reading this btree */
64276
- u32 iDataVersion; /* Combines with pBt->pPager->iDataVersion */
64282
+ u32 iBDataVersion; /* Combines with pBt->pPager->iDataVersion */
6427764283
Btree *pNext; /* List of other sharable Btrees from the same db */
6427864284
Btree *pPrev; /* Back pointer of the same list */
6427964285
#ifdef SQLITE_DEBUG
6428064286
u64 nSeek; /* Calls to sqlite3BtreeMovetoUnpacked() */
6428164287
#endif
@@ -67684,23 +67690,27 @@
6768467690
/*
6768567691
** Close an open database and invalidate all cursors.
6768667692
*/
6768767693
SQLITE_PRIVATE int sqlite3BtreeClose(Btree *p){
6768867694
BtShared *pBt = p->pBt;
67689
- BtCursor *pCur;
6769067695
6769167696
/* Close all cursors opened via this handle. */
6769267697
assert( sqlite3_mutex_held(p->db->mutex) );
6769367698
sqlite3BtreeEnter(p);
67694
- pCur = pBt->pCursor;
67695
- while( pCur ){
67696
- BtCursor *pTmp = pCur;
67697
- pCur = pCur->pNext;
67698
- if( pTmp->pBtree==p ){
67699
- sqlite3BtreeCloseCursor(pTmp);
67699
+
67700
+ /* Verify that no other cursors have this Btree open */
67701
+#ifdef SQLITE_DEBUG
67702
+ {
67703
+ BtCursor *pCur = pBt->pCursor;
67704
+ while( pCur ){
67705
+ BtCursor *pTmp = pCur;
67706
+ pCur = pCur->pNext;
67707
+ assert( pTmp->pBtree!=p );
67708
+
6770067709
}
6770167710
}
67711
+#endif
6770267712
6770367713
/* Rollback any active transaction and free the handle structure.
6770467714
** The call to sqlite3BtreeRollback() drops any table-locks held by
6770567715
** this handle.
6770667716
*/
@@ -69078,11 +69088,11 @@
6907869088
rc = sqlite3PagerCommitPhaseTwo(pBt->pPager);
6907969089
if( rc!=SQLITE_OK && bCleanup==0 ){
6908069090
sqlite3BtreeLeave(p);
6908169091
return rc;
6908269092
}
69083
- p->iDataVersion--; /* Compensate for pPager->iDataVersion++; */
69093
+ p->iBDataVersion--; /* Compensate for pPager->iDataVersion++; */
6908469094
pBt->inTransaction = TRANS_READ;
6908569095
btreeClearHasContent(pBt);
6908669096
}
6908769097
6908869098
btreeEndTransaction(p);
@@ -69488,11 +69498,18 @@
6948869498
}
6948969499
btreeReleaseAllCursorPages(pCur);
6949069500
unlockBtreeIfUnused(pBt);
6949169501
sqlite3_free(pCur->aOverflow);
6949269502
sqlite3_free(pCur->pKey);
69493
- sqlite3BtreeLeave(pBtree);
69503
+ if( (pBt->openFlags & BTREE_SINGLE) && pBt->pCursor==0 ){
69504
+ /* Since the BtShared is not sharable, there is no need to
69505
+ ** worry about the missing sqlite3BtreeLeave() call here. */
69506
+ assert( pBtree->sharable==0 );
69507
+ sqlite3BtreeClose(pBtree);
69508
+ }else{
69509
+ sqlite3BtreeLeave(pBtree);
69510
+ }
6949469511
pCur->pBtree = 0;
6949569512
}
6949669513
return SQLITE_OK;
6949769514
}
6949869515
@@ -74601,11 +74618,11 @@
7460174618
assert( SQLITE_OK==querySharedCacheTableLock(p, SCHEMA_ROOT, READ_LOCK) );
7460274619
assert( pBt->pPage1 );
7460374620
assert( idx>=0 && idx<=15 );
7460474621
7460574622
if( idx==BTREE_DATA_VERSION ){
74606
- *pMeta = sqlite3PagerDataVersion(pBt->pPager) + p->iDataVersion;
74623
+ *pMeta = sqlite3PagerDataVersion(pBt->pPager) + p->iBDataVersion;
7460774624
}else{
7460874625
*pMeta = get4byte(&pBt->pPage1->aData[36 + idx*4]);
7460974626
}
7461074627
7461174628
/* If auto-vacuum is disabled in this build and this is an auto-vacuum
@@ -80829,24 +80846,19 @@
8082980846
SQLITE_PRIVATE void sqlite3VdbeFreeCursor(Vdbe *p, VdbeCursor *pCx){
8083080847
if( pCx==0 ){
8083180848
return;
8083280849
}
8083380850
assert( pCx->pBtx==0 || pCx->eCurType==CURTYPE_BTREE );
80851
+ assert( pCx->pBtx==0 || pCx->isEphemeral );
8083480852
switch( pCx->eCurType ){
8083580853
case CURTYPE_SORTER: {
8083680854
sqlite3VdbeSorterClose(p->db, pCx);
8083780855
break;
8083880856
}
8083980857
case CURTYPE_BTREE: {
80840
- if( pCx->isEphemeral ){
80841
- if( pCx->pBtx ) sqlite3BtreeClose(pCx->pBtx);
80842
- /* The pCx->pCursor will be close automatically, if it exists, by
80843
- ** the call above. */
80844
- }else{
80845
- assert( pCx->uc.pCursor!=0 );
80846
- sqlite3BtreeCloseCursor(pCx->uc.pCursor);
80847
- }
80858
+ assert( pCx->uc.pCursor!=0 );
80859
+ sqlite3BtreeCloseCursor(pCx->uc.pCursor);
8084880860
break;
8084980861
}
8085080862
#ifndef SQLITE_OMIT_VIRTUALTABLE
8085180863
case CURTYPE_VTAB: {
8085280864
sqlite3_vtab_cursor *pVCur = pCx->uc.pVCur;
@@ -81926,10 +81938,11 @@
8192681938
SQLITE_PRIVATE int sqlite3VdbeCursorMoveto(VdbeCursor **pp, u32 *piCol){
8192781939
VdbeCursor *p = *pp;
8192881940
assert( p->eCurType==CURTYPE_BTREE || p->eCurType==CURTYPE_PSEUDO );
8192981941
if( p->deferredMoveto ){
8193081942
u32 iMap;
81943
+ assert( !p->isEphemeral );
8193181944
if( p->aAltMap && (iMap = p->aAltMap[1+*piCol])>0 && !p->nullRow ){
8193281945
*pp = p->pAltCursor;
8193381946
*piCol = iMap - 1;
8193481947
return SQLITE_OK;
8193581948
}
@@ -86134,15 +86147,10 @@
8613486147
ROUND8(sizeof(VdbeCursor)) + 2*sizeof(u32)*nField +
8613586148
(eCurType==CURTYPE_BTREE?sqlite3BtreeCursorSize():0);
8613686149
8613786150
assert( iCur>=0 && iCur<p->nCursor );
8613886151
if( p->apCsr[iCur] ){ /*OPTIMIZATION-IF-FALSE*/
86139
- /* Before calling sqlite3VdbeFreeCursor(), ensure the isEphemeral flag
86140
- ** is clear. Otherwise, if this is an ephemeral cursor created by
86141
- ** OP_OpenDup, the cursor will not be closed and will still be part
86142
- ** of a BtShared.pCursor list. */
86143
- if( p->apCsr[iCur]->pBtx==0 ) p->apCsr[iCur]->isEphemeral = 0;
8614486152
sqlite3VdbeFreeCursor(p, p->apCsr[iCur]);
8614586153
p->apCsr[iCur] = 0;
8614686154
}
8614786155
if( SQLITE_OK==sqlite3VdbeMemClearAndResize(pMem, nByte) ){
8614886156
p->apCsr[iCur] = pCx = (VdbeCursor*)pMem->z;
@@ -89824,21 +89832,24 @@
8982489832
VdbeCursor *pOrig; /* The original cursor to be duplicated */
8982589833
VdbeCursor *pCx; /* The new cursor */
8982689834
8982789835
pOrig = p->apCsr[pOp->p2];
8982889836
assert( pOrig );
89829
- assert( pOrig->pBtx!=0 ); /* Only ephemeral cursors can be duplicated */
89837
+ assert( pOrig->isEphemeral ); /* Only ephemeral cursors can be duplicated */
8983089838
8983189839
pCx = allocateCursor(p, pOp->p1, pOrig->nField, -1, CURTYPE_BTREE);
8983289840
if( pCx==0 ) goto no_mem;
8983389841
pCx->nullRow = 1;
8983489842
pCx->isEphemeral = 1;
8983589843
pCx->pKeyInfo = pOrig->pKeyInfo;
8983689844
pCx->isTable = pOrig->isTable;
8983789845
pCx->pgnoRoot = pOrig->pgnoRoot;
8983889846
pCx->isOrdered = pOrig->isOrdered;
89839
- rc = sqlite3BtreeCursor(pOrig->pBtx, pCx->pgnoRoot, BTREE_WRCSR,
89847
+ pCx->pBtx = pOrig->pBtx;
89848
+ pCx->hasBeenDuped = 1;
89849
+ pOrig->hasBeenDuped = 1;
89850
+ rc = sqlite3BtreeCursor(pCx->pBtx, pCx->pgnoRoot, BTREE_WRCSR,
8984089851
pCx->pKeyInfo, pCx->uc.pCursor);
8984189852
/* The sqlite3BtreeCursor() routine can only fail for the first cursor
8984289853
** opened for a database. Since there is already an open cursor when this
8984389854
** opcode is run, the sqlite3BtreeCursor() cannot fail */
8984489855
assert( rc==SQLITE_OK );
@@ -89900,13 +89911,14 @@
8990089911
assert( aMem[pOp->p3].flags & MEM_Null );
8990189912
aMem[pOp->p3].n = 0;
8990289913
aMem[pOp->p3].z = "";
8990389914
}
8990489915
pCx = p->apCsr[pOp->p1];
89905
- if( pCx && ALWAYS(pCx->pBtx) ){
89906
- /* If the ephermeral table is already open, erase all existing content
89907
- ** so that the table is empty again, rather than creating a new table. */
89916
+ if( pCx && !pCx->hasBeenDuped ){
89917
+ /* If the ephermeral table is already open and has no duplicates from
89918
+ ** OP_OpenDup, then erase all existing content so that the table is
89919
+ ** empty again, rather than creating a new table. */
8990889920
assert( pCx->isEphemeral );
8990989921
pCx->seqCount = 0;
8991089922
pCx->cacheStatus = CACHE_STALE;
8991189923
rc = sqlite3BtreeClearTable(pCx->pBtx, pCx->pgnoRoot, 0);
8991289924
}else{
@@ -89916,37 +89928,40 @@
8991689928
rc = sqlite3BtreeOpen(db->pVfs, 0, db, &pCx->pBtx,
8991789929
BTREE_OMIT_JOURNAL | BTREE_SINGLE | pOp->p5,
8991889930
vfsFlags);
8991989931
if( rc==SQLITE_OK ){
8992089932
rc = sqlite3BtreeBeginTrans(pCx->pBtx, 1, 0);
89921
- }
89922
- if( rc==SQLITE_OK ){
89923
- /* If a transient index is required, create it by calling
89924
- ** sqlite3BtreeCreateTable() with the BTREE_BLOBKEY flag before
89925
- ** opening it. If a transient table is required, just use the
89926
- ** automatically created table with root-page 1 (an BLOB_INTKEY table).
89927
- */
89928
- if( (pCx->pKeyInfo = pKeyInfo = pOp->p4.pKeyInfo)!=0 ){
89929
- assert( pOp->p4type==P4_KEYINFO );
89930
- rc = sqlite3BtreeCreateTable(pCx->pBtx, &pCx->pgnoRoot,
89931
- BTREE_BLOBKEY | pOp->p5);
89932
- if( rc==SQLITE_OK ){
89933
- assert( pCx->pgnoRoot==SCHEMA_ROOT+1 );
89934
- assert( pKeyInfo->db==db );
89935
- assert( pKeyInfo->enc==ENC(db) );
89936
- rc = sqlite3BtreeCursor(pCx->pBtx, pCx->pgnoRoot, BTREE_WRCSR,
89937
- pKeyInfo, pCx->uc.pCursor);
89938
- }
89939
- pCx->isTable = 0;
89940
- }else{
89941
- pCx->pgnoRoot = SCHEMA_ROOT;
89942
- rc = sqlite3BtreeCursor(pCx->pBtx, SCHEMA_ROOT, BTREE_WRCSR,
89943
- 0, pCx->uc.pCursor);
89944
- pCx->isTable = 1;
89945
- }
89946
- }
89947
- pCx->isOrdered = (pOp->p5!=BTREE_UNORDERED);
89933
+ if( rc==SQLITE_OK ){
89934
+ /* If a transient index is required, create it by calling
89935
+ ** sqlite3BtreeCreateTable() with the BTREE_BLOBKEY flag before
89936
+ ** opening it. If a transient table is required, just use the
89937
+ ** automatically created table with root-page 1 (an BLOB_INTKEY table).
89938
+ */
89939
+ if( (pCx->pKeyInfo = pKeyInfo = pOp->p4.pKeyInfo)!=0 ){
89940
+ assert( pOp->p4type==P4_KEYINFO );
89941
+ rc = sqlite3BtreeCreateTable(pCx->pBtx, &pCx->pgnoRoot,
89942
+ BTREE_BLOBKEY | pOp->p5);
89943
+ if( rc==SQLITE_OK ){
89944
+ assert( pCx->pgnoRoot==SCHEMA_ROOT+1 );
89945
+ assert( pKeyInfo->db==db );
89946
+ assert( pKeyInfo->enc==ENC(db) );
89947
+ rc = sqlite3BtreeCursor(pCx->pBtx, pCx->pgnoRoot, BTREE_WRCSR,
89948
+ pKeyInfo, pCx->uc.pCursor);
89949
+ }
89950
+ pCx->isTable = 0;
89951
+ }else{
89952
+ pCx->pgnoRoot = SCHEMA_ROOT;
89953
+ rc = sqlite3BtreeCursor(pCx->pBtx, SCHEMA_ROOT, BTREE_WRCSR,
89954
+ 0, pCx->uc.pCursor);
89955
+ pCx->isTable = 1;
89956
+ }
89957
+ }
89958
+ pCx->isOrdered = (pOp->p5!=BTREE_UNORDERED);
89959
+ if( rc ){
89960
+ sqlite3BtreeClose(pCx->pBtx);
89961
+ }
89962
+ }
8994889963
}
8994989964
if( rc ) goto abort_due_to_error;
8995089965
pCx->nullRow = 1;
8995189966
break;
8995289967
}
@@ -90376,17 +90391,17 @@
9037690391
** row by, perhaps by invoking sqlite3BtreeStep() on the cursor
9037790392
** between 0 and This.P1 times.
9037890393
**
9037990394
** There are three possible outcomes from this opcode:<ol>
9038090395
**
90381
-** <li> If after This.P1 steps, the cursor is still point to a place that
90382
-** is earlier in the btree than the target row,
90383
-** then fall through into the subsquence OP_SeekGE opcode.
90396
+** <li> If after This.P1 steps, the cursor is still pointing to a place that
90397
+** is earlier in the btree than the target row, then fall through
90398
+** into the subsquence OP_SeekGE opcode.
9038490399
**
9038590400
** <li> If the cursor is successfully moved to the target row by 0 or more
9038690401
** sqlite3BtreeNext() calls, then jump to This.P2, which will land just
90387
-** past the OP_IdxGT opcode that follows the OP_SeekGE.
90402
+** past the OP_IdxGT or OP_IdxGE opcode that follows the OP_SeekGE.
9038890403
**
9038990404
** <li> If the cursor ends up past the target row (indicating the the target
9039090405
** row does not exist in the btree) then jump to SeekOP.P2.
9039190406
** </ol>
9039290407
*/
@@ -90399,11 +90414,12 @@
9039990414
assert( pOp[1].opcode==OP_SeekGE );
9040090415
9040190416
/* pOp->p2 points to the first instruction past the OP_IdxGT that
9040290417
** follows the OP_SeekGE. */
9040390418
assert( pOp->p2>=(int)(pOp-aOp)+2 );
90404
- assert( aOp[pOp->p2-1].opcode==OP_IdxGT );
90419
+ assert( aOp[pOp->p2-1].opcode==OP_IdxGT || aOp[pOp->p2-1].opcode==OP_IdxGE );
90420
+ testcase( aOp[pOp->p2-1].opcode==OP_IdxGE );
9040590421
assert( pOp[1].p1==aOp[pOp->p2-1].p1 );
9040690422
assert( pOp[1].p2==aOp[pOp->p2-1].p2 );
9040790423
assert( pOp[1].p3==aOp[pOp->p2-1].p3 );
9040890424
9040990425
assert( pOp->p1>0 );
@@ -91934,10 +91950,12 @@
9193491950
pTabCur->nullRow = 0;
9193591951
pTabCur->movetoTarget = rowid;
9193691952
pTabCur->deferredMoveto = 1;
9193791953
assert( pOp->p4type==P4_INTARRAY || pOp->p4.ai==0 );
9193891954
pTabCur->aAltMap = pOp->p4.ai;
91955
+ assert( !pC->isEphemeral );
91956
+ assert( !pTabCur->isEphemeral );
9193991957
pTabCur->pAltCursor = pC;
9194091958
}else{
9194191959
pOut = out2Prerelease(p, pOp);
9194291960
pOut->u.i = rowid;
9194391961
}
@@ -99165,10 +99183,11 @@
9916599183
*/
9916699184
if( cnt==0 && zTab==0 ){
9916799185
assert( pExpr->op==TK_ID );
9916899186
if( ExprHasProperty(pExpr,EP_DblQuoted)
9916999187
&& areDoubleQuotedStringsEnabled(db, pTopNC)
99188
+ && (db->init.bDropColumn==0 || sqlite3StrICmp(zCol, db->init.azInit[0])!=0)
9917099189
){
9917199190
/* If a double-quoted identifier does not match any known column name,
9917299191
** then treat it as a string.
9917399192
**
9917499193
** This hack was added in the early days of SQLite in a misguided attempt
@@ -99179,10 +99198,15 @@
9917999198
** programmers. To all those frustrated programmers, my apologies.
9918099199
**
9918199200
** Someday, I hope to get rid of this hack. Unfortunately there is
9918299201
** a huge amount of legacy SQL that uses it. So for now, we just
9918399202
** issue a warning.
99203
+ **
99204
+ ** 2021-03-15: ticket 1c24a659e6d7f3a1
99205
+ ** Do not do the ID-to-STRING conversion when doing the schema
99206
+ ** sanity check following a DROP COLUMN if the identifer name matches
99207
+ ** the name of the column being dropped.
9918499208
*/
9918599209
sqlite3_log(SQLITE_WARNING,
9918699210
"double-quoted string literal: \"%w\"", zCol);
9918799211
#ifdef SQLITE_ENABLE_NORMALIZE
9918899212
sqlite3VdbeAddDblquoteStr(db, pParse->pVdbe, zCol);
@@ -106778,31 +106802,32 @@
106778106802
*/
106779106803
static void renameTestSchema(
106780106804
Parse *pParse, /* Parse context */
106781106805
const char *zDb, /* Name of db to verify schema of */
106782106806
int bTemp, /* True if this is the temp db */
106783
- const char *zWhen /* "when" part of error message */
106807
+ const char *zWhen, /* "when" part of error message */
106808
+ const char *zDropColumn /* Name of column being dropped */
106784106809
){
106785106810
pParse->colNamesSet = 1;
106786106811
sqlite3NestedParse(pParse,
106787106812
"SELECT 1 "
106788106813
"FROM \"%w\"." DFLT_SCHEMA_TABLE " "
106789106814
"WHERE name NOT LIKE 'sqliteX_%%' ESCAPE 'X'"
106790106815
" AND sql NOT LIKE 'create virtual%%'"
106791
- " AND sqlite_rename_test(%Q, sql, type, name, %d, %Q)=NULL ",
106816
+ " AND sqlite_rename_test(%Q, sql, type, name, %d, %Q, %Q)=NULL ",
106792106817
zDb,
106793
- zDb, bTemp, zWhen
106818
+ zDb, bTemp, zWhen, zDropColumn
106794106819
);
106795106820
106796106821
if( bTemp==0 ){
106797106822
sqlite3NestedParse(pParse,
106798106823
"SELECT 1 "
106799106824
"FROM temp." DFLT_SCHEMA_TABLE " "
106800106825
"WHERE name NOT LIKE 'sqliteX_%%' ESCAPE 'X'"
106801106826
" AND sql NOT LIKE 'create virtual%%'"
106802
- " AND sqlite_rename_test(%Q, sql, type, name, 1, %Q)=NULL ",
106803
- zDb, zWhen
106827
+ " AND sqlite_rename_test(%Q, sql, type, name, 1, %Q, %Q)=NULL ",
106828
+ zDb, zWhen, zDropColumn
106804106829
);
106805106830
}
106806106831
}
106807106832
106808106833
/*
@@ -106961,11 +106986,11 @@
106961106986
sqlite3NestedParse(pParse,
106962106987
"UPDATE sqlite_temp_schema SET "
106963106988
"sql = sqlite_rename_table(%Q, type, name, sql, %Q, %Q, 1), "
106964106989
"tbl_name = "
106965106990
"CASE WHEN tbl_name=%Q COLLATE nocase AND "
106966
- " sqlite_rename_test(%Q, sql, type, name, 1, 'after rename') "
106991
+ " sqlite_rename_test(%Q, sql, type, name, 1, 'after rename',0) "
106967106992
"THEN %Q ELSE tbl_name END "
106968106993
"WHERE type IN ('view', 'trigger')"
106969106994
, zDb, zTabName, zName, zTabName, zDb, zName);
106970106995
}
106971106996
@@ -106981,11 +107006,11 @@
106981107006
sqlite3VdbeAddOp4(v, OP_VRename, i, 0, 0,(const char*)pVTab, P4_VTAB);
106982107007
}
106983107008
#endif
106984107009
106985107010
renameReloadSchema(pParse, iDb, INITFLAG_AlterRename);
106986
- renameTestSchema(pParse, zDb, iDb==1, "after rename");
107011
+ renameTestSchema(pParse, zDb, iDb==1, "after rename", 0);
106987107012
106988107013
exit_rename_table:
106989107014
sqlite3SrcListDelete(db, pSrc);
106990107015
sqlite3DbFree(db, zName);
106991107016
db->mDbFlags = savedDbFlags;
@@ -107349,11 +107374,11 @@
107349107374
zDb, pTab->zName, iCol, zNew, bQuote
107350107375
);
107351107376
107352107377
/* Drop and reload the database schema. */
107353107378
renameReloadSchema(pParse, iSchema, INITFLAG_AlterRename);
107354
- renameTestSchema(pParse, zDb, iSchema==1, "after rename");
107379
+ renameTestSchema(pParse, zDb, iSchema==1, "after rename", 0);
107355107380
107356107381
exit_rename_column:
107357107382
sqlite3SrcListDelete(db, pSrc);
107358107383
sqlite3DbFree(db, zOld);
107359107384
sqlite3DbFree(db, zNew);
@@ -107773,16 +107798,21 @@
107773107798
static int renameParseSql(
107774107799
Parse *p, /* Memory to use for Parse object */
107775107800
const char *zDb, /* Name of schema SQL belongs to */
107776107801
sqlite3 *db, /* Database handle */
107777107802
const char *zSql, /* SQL to parse */
107778
- int bTemp /* True if SQL is from temp schema */
107803
+ int bTemp, /* True if SQL is from temp schema */
107804
+ const char *zDropColumn /* Name of column being dropped */
107779107805
){
107780107806
int rc;
107781107807
char *zErr = 0;
107782107808
107783107809
db->init.iDb = bTemp ? 1 : sqlite3FindDbName(db, zDb);
107810
+ if( zDropColumn ){
107811
+ db->init.bDropColumn = 1;
107812
+ db->init.azInit = (char**)&zDropColumn;
107813
+ }
107784107814
107785107815
/* Parse the SQL statement passed as the first argument. If no error
107786107816
** occurs and the parse does not result in a new table, index or
107787107817
** trigger object, the database must be corrupt. */
107788107818
memset(p, 0, sizeof(Parse));
@@ -107811,10 +107841,11 @@
107811107841
}
107812107842
}
107813107843
#endif
107814107844
107815107845
db->init.iDb = 0;
107846
+ db->init.bDropColumn = 0;
107816107847
return rc;
107817107848
}
107818107849
107819107850
/*
107820107851
** This function edits SQL statement zSql, replacing each token identified
@@ -108112,11 +108143,11 @@
108112108143
sCtx.iCol = ((iCol==pTab->iPKey) ? -1 : iCol);
108113108144
108114108145
#ifndef SQLITE_OMIT_AUTHORIZATION
108115108146
db->xAuth = 0;
108116108147
#endif
108117
- rc = renameParseSql(&sParse, zDb, db, zSql, bTemp);
108148
+ rc = renameParseSql(&sParse, zDb, db, zSql, bTemp, 0);
108118108149
108119108150
/* Find tokens that need to be replaced. */
108120108151
memset(&sWalker, 0, sizeof(Walker));
108121108152
sWalker.pParse = &sParse;
108122108153
sWalker.xExprCallback = renameColumnExprCb;
@@ -108154,16 +108185,16 @@
108154108185
sqlite3WalkExprList(&sWalker, pIdx->aColExpr);
108155108186
}
108156108187
for(pIdx=sParse.pNewIndex; pIdx; pIdx=pIdx->pNext){
108157108188
sqlite3WalkExprList(&sWalker, pIdx->aColExpr);
108158108189
}
108159
- }
108160108190
#ifndef SQLITE_OMIT_GENERATED_COLUMNS
108161
- for(i=0; i<sParse.pNewTable->nCol; i++){
108162
- sqlite3WalkExpr(&sWalker, sParse.pNewTable->aCol[i].pDflt);
108191
+ for(i=0; i<sParse.pNewTable->nCol; i++){
108192
+ sqlite3WalkExpr(&sWalker, sParse.pNewTable->aCol[i].pDflt);
108193
+ }
108194
+#endif
108163108195
}
108164
-#endif
108165108196
108166108197
for(pFKey=sParse.pNewTable->pFKey; pFKey; pFKey=pFKey->pNextFrom){
108167108198
for(i=0; i<pFKey->nCol; i++){
108168108199
if( bFKOnly==0 && pFKey->aCol[i].iFrom==iCol ){
108169108200
renameTokenFind(&sParse, &sCtx, (void*)&pFKey->aCol[i]);
@@ -108316,11 +108347,11 @@
108316108347
sWalker.pParse = &sParse;
108317108348
sWalker.xExprCallback = renameTableExprCb;
108318108349
sWalker.xSelectCallback = renameTableSelectCb;
108319108350
sWalker.u.pRename = &sCtx;
108320108351
108321
- rc = renameParseSql(&sParse, zDb, db, zInput, bTemp);
108352
+ rc = renameParseSql(&sParse, zDb, db, zInput, bTemp, 0);
108322108353
108323108354
if( rc==SQLITE_OK ){
108324108355
int isLegacy = (db->flags & SQLITE_LegacyAlter);
108325108356
if( sParse.pNewTable ){
108326108357
Table *pTab = sParse.pNewTable;
@@ -108432,10 +108463,11 @@
108432108463
** 1: SQL statement.
108433108464
** 2: Object type ("view", "table", "trigger" or "index").
108434108465
** 3: Object name.
108435108466
** 4: True if object is from temp schema.
108436108467
** 5: "when" part of error message.
108468
+** 6: Name of column being dropped, or NULL.
108437108469
**
108438108470
** Unless it finds an error, this function normally returns NULL. However, it
108439108471
** returns integer value 1 if:
108440108472
**
108441108473
** * the SQL argument creates a trigger, and
@@ -108450,10 +108482,11 @@
108450108482
char const *zDb = (const char*)sqlite3_value_text(argv[0]);
108451108483
char const *zInput = (const char*)sqlite3_value_text(argv[1]);
108452108484
int bTemp = sqlite3_value_int(argv[4]);
108453108485
int isLegacy = (db->flags & SQLITE_LegacyAlter);
108454108486
char const *zWhen = (const char*)sqlite3_value_text(argv[5]);
108487
+ char const *zDropColumn = (const char*)sqlite3_value_text(argv[6]);
108455108488
108456108489
#ifndef SQLITE_OMIT_AUTHORIZATION
108457108490
sqlite3_xauth xAuth = db->xAuth;
108458108491
db->xAuth = 0;
108459108492
#endif
@@ -108460,11 +108493,11 @@
108460108493
108461108494
UNUSED_PARAMETER(NotUsed);
108462108495
if( zDb && zInput ){
108463108496
int rc;
108464108497
Parse sParse;
108465
- rc = renameParseSql(&sParse, zDb, db, zInput, bTemp);
108498
+ rc = renameParseSql(&sParse, zDb, db, zInput, bTemp, zDropColumn);
108466108499
if( rc==SQLITE_OK ){
108467108500
if( isLegacy==0 && sParse.pNewTable && sParse.pNewTable->pSelect ){
108468108501
NameContext sNC;
108469108502
memset(&sNC, 0, sizeof(sNC));
108470108503
sNC.pParse = &sParse;
@@ -108528,11 +108561,11 @@
108528108561
sqlite3_xauth xAuth = db->xAuth;
108529108562
db->xAuth = 0;
108530108563
#endif
108531108564
108532108565
UNUSED_PARAMETER(NotUsed);
108533
- rc = renameParseSql(&sParse, zDb, db, zSql, iSchema==1);
108566
+ rc = renameParseSql(&sParse, zDb, db, zSql, iSchema==1, 0);
108534108567
if( rc!=SQLITE_OK ) goto drop_column_done;
108535108568
pTab = sParse.pNewTable;
108536108569
if( pTab==0 || pTab->nCol==1 || iCol>=pTab->nCol ){
108537108570
/* This can happen if the sqlite_schema table is corrupt */
108538108571
rc = SQLITE_CORRUPT_BKPT;
@@ -108621,21 +108654,21 @@
108621108654
108622108655
/* Edit the sqlite_schema table */
108623108656
iDb = sqlite3SchemaToIndex(db, pTab->pSchema);
108624108657
assert( iDb>=0 );
108625108658
zDb = db->aDb[iDb].zDbSName;
108626
- renameTestSchema(pParse, zDb, iDb==1, "");
108659
+ renameTestSchema(pParse, zDb, iDb==1, "", 0);
108627108660
sqlite3NestedParse(pParse,
108628108661
"UPDATE \"%w\"." DFLT_SCHEMA_TABLE " SET "
108629108662
"sql = sqlite_drop_column(%d, sql, %d) "
108630108663
"WHERE (type=='table' AND tbl_name=%Q COLLATE nocase)"
108631108664
, zDb, iDb, iCol, pTab->zName
108632108665
);
108633108666
108634108667
/* Drop and reload the database schema. */
108635108668
renameReloadSchema(pParse, iDb, INITFLAG_AlterDrop);
108636
- renameTestSchema(pParse, zDb, iDb==1, "after drop column");
108669
+ renameTestSchema(pParse, zDb, iDb==1, "after drop column", zCol);
108637108670
108638108671
/* Edit rows of table on disk */
108639108672
if( pParse->nErr==0 && (pTab->aCol[iCol].colFlags & COLFLAG_VIRTUAL)==0 ){
108640108673
int i;
108641108674
int addr;
@@ -108691,11 +108724,11 @@
108691108724
*/
108692108725
SQLITE_PRIVATE void sqlite3AlterFunctions(void){
108693108726
static FuncDef aAlterTableFuncs[] = {
108694108727
INTERNAL_FUNCTION(sqlite_rename_column, 9, renameColumnFunc),
108695108728
INTERNAL_FUNCTION(sqlite_rename_table, 7, renameTableFunc),
108696
- INTERNAL_FUNCTION(sqlite_rename_test, 6, renameTableTest),
108729
+ INTERNAL_FUNCTION(sqlite_rename_test, 7, renameTableTest),
108697108730
INTERNAL_FUNCTION(sqlite_drop_column, 3, dropColumnFunc),
108698108731
};
108699108732
sqlite3InsertBuiltinFuncs(aAlterTableFuncs, ArraySize(aAlterTableFuncs));
108700108733
}
108701108734
#endif /* SQLITE_ALTER_TABLE */
@@ -115663,11 +115696,15 @@
115663115696
}
115664115697
115665115698
/* Clean up before exiting */
115666115699
exit_create_index:
115667115700
if( pIndex ) sqlite3FreeIndex(db, pIndex);
115668
- if( pTab ){ /* Ensure all REPLACE indexes are at the end of the list */
115701
+ if( pTab ){
115702
+ /* Ensure all REPLACE indexes on pTab are at the end of the pIndex list.
115703
+ ** The list was already ordered when this routine was entered, so at this
115704
+ ** point at most a single index (the newly added index) will be out of
115705
+ ** order. So we have to reorder at most one index. */
115669115706
Index **ppFrom = &pTab->pIndex;
115670115707
Index *pThis;
115671115708
for(ppFrom=&pTab->pIndex; (pThis = *ppFrom)!=0; ppFrom=&pThis->pNext){
115672115709
Index *pNext;
115673115710
if( pThis->onError!=OE_Replace ) continue;
@@ -115677,10 +115714,20 @@
115677115714
pNext->pNext = pThis;
115678115715
ppFrom = &pNext->pNext;
115679115716
}
115680115717
break;
115681115718
}
115719
+#ifdef SQLITE_DEBUG
115720
+ /* Verify that all REPLACE indexes really are now at the end
115721
+ ** of the index list. In other words, no other index type ever
115722
+ ** comes after a REPLACE index on the list. */
115723
+ for(pThis = pTab->pIndex; pThis; pThis=pThis->pNext){
115724
+ assert( pThis->onError!=OE_Replace
115725
+ || pThis->pNext==0
115726
+ || pThis->pNext->onError==OE_Replace );
115727
+ }
115728
+#endif
115682115729
}
115683115730
sqlite3ExprDelete(db, pPIWhere);
115684115731
sqlite3ExprListDelete(db, pList);
115685115732
sqlite3SrcListDelete(db, pTblName);
115686115733
sqlite3DbFree(db, zName);
@@ -123093,11 +123140,13 @@
123093123140
pNx->pUpsertSrc = pTabList;
123094123141
pNx->regData = regData;
123095123142
pNx->iDataCur = iDataCur;
123096123143
pNx->iIdxCur = iIdxCur;
123097123144
if( pNx->pUpsertTarget ){
123098
- sqlite3UpsertAnalyzeTarget(pParse, pTabList, pNx);
123145
+ if( sqlite3UpsertAnalyzeTarget(pParse, pTabList, pNx) ){
123146
+ goto insert_cleanup;
123147
+ }
123099123148
}
123100123149
pNx = pNx->pNextUpsert;
123101123150
}while( pNx!=0 );
123102123151
}
123103123152
#endif
@@ -124515,11 +124564,11 @@
124515124564
int regData /* Data containing new record */
124516124565
){
124517124566
Vdbe *v = pParse->pVdbe;
124518124567
int r = sqlite3GetTempReg(pParse);
124519124568
assert( !HasRowid(pTab) );
124520
- assert( 0==(pParse->db->mDbFlags & DBFLAG_Vacuum) );
124569
+ assert( 0==(pParse->db->mDbFlags & DBFLAG_Vacuum) || CORRUPT_DB );
124521124570
sqlite3VdbeAddOp2(v, OP_Integer, 0, r);
124522124571
sqlite3VdbeAddOp4(v, OP_Insert, iCur, regData, r, (char*)pTab, P4_TABLE);
124523124572
sqlite3VdbeChangeP5(v, OPFLAG_ISNOOP);
124524124573
sqlite3ReleaseTempReg(pParse, r);
124525124574
}
@@ -133063,11 +133112,11 @@
133063133112
sqlite3HashInit(&ht);
133064133113
if( pEList ){
133065133114
nCol = pEList->nExpr;
133066133115
aCol = sqlite3DbMallocZero(db, sizeof(aCol[0])*nCol);
133067133116
testcase( aCol==0 );
133068
- if( nCol>32767 ) nCol = 32767;
133117
+ if( NEVER(nCol>32767) ) nCol = 32767;
133069133118
}else{
133070133119
nCol = 0;
133071133120
aCol = 0;
133072133121
}
133073133122
assert( nCol==(i16)nCol );
@@ -135116,10 +135165,11 @@
135116135165
135117135166
/* Restriction (23) */
135118135167
if( (p->selFlags & SF_Recursive) ) return 0;
135119135168
135120135169
if( pSrc->nSrc>1 ){
135170
+ if( pParse->nSelect>500 ) return 0;
135121135171
aCsrMap = sqlite3DbMallocZero(db, pParse->nTab*sizeof(int));
135122135172
}
135123135173
}
135124135174
135125135175
/***** If we reach this point, flattening is permitted. *****/
@@ -135192,10 +135242,11 @@
135192135242
p->op = TK_ALL;
135193135243
pSubitem->pTab = pItemTab;
135194135244
if( pNew==0 ){
135195135245
p->pPrior = pPrior;
135196135246
}else{
135247
+ pNew->selId = ++pParse->nSelect;
135197135248
if( aCsrMap && db->mallocFailed==0 ){
135198135249
renumberCursors(pParse, pNew, iFrom, aCsrMap);
135199135250
}
135200135251
pNew->pPrior = pPrior;
135201135252
if( pPrior ) pPrior->pNext = pNew;
@@ -136135,20 +136186,28 @@
136135136186
136136136187
pCte->zCteErr = "circular reference: %s";
136137136188
pSavedWith = pParse->pWith;
136138136189
pParse->pWith = pWith;
136139136190
if( pSel->selFlags & SF_Recursive ){
136191
+ int rc;
136140136192
assert( pRecTerm!=0 );
136141136193
assert( (pRecTerm->selFlags & SF_Recursive)==0 );
136142136194
assert( pRecTerm->pNext!=0 );
136143136195
assert( (pRecTerm->pNext->selFlags & SF_Recursive)!=0 );
136144136196
assert( pRecTerm->pWith==0 );
136145136197
pRecTerm->pWith = pSel->pWith;
136146
- sqlite3WalkSelect(pWalker, pRecTerm);
136198
+ rc = sqlite3WalkSelect(pWalker, pRecTerm);
136147136199
pRecTerm->pWith = 0;
136200
+ if( rc ){
136201
+ pParse->pWith = pSavedWith;
136202
+ return 2;
136203
+ }
136148136204
}else{
136149
- sqlite3WalkSelect(pWalker, pSel);
136205
+ if( sqlite3WalkSelect(pWalker, pSel) ){
136206
+ pParse->pWith = pSavedWith;
136207
+ return 2;
136208
+ }
136150136209
}
136151136210
pParse->pWith = pWith;
136152136211
136153136212
for(pLeft=pSel; pLeft->pPrior; pLeft=pLeft->pPrior);
136154136213
pEList = pLeft->pEList;
@@ -137453,11 +137512,13 @@
137453137512
sqlite3VdbeAddOp2(v, OP_Gosub, pPrior->regReturn, pPrior->addrFillSub);
137454137513
}
137455137514
sqlite3VdbeAddOp2(v, OP_OpenDup, pItem->iCursor, pPrior->iCursor);
137456137515
pSub->nSelectRow = pPrior->pSelect->nSelectRow;
137457137516
}else{
137458
- /* Generate a subroutine that will materialize the view. */
137517
+ /* Materalize the view. If the view is not correlated, generate a
137518
+ ** subroutine to do the materialization so that subsequent uses of
137519
+ ** the same view can reuse the materialization. */
137459137520
int topAddr;
137460137521
int onceAddr = 0;
137461137522
int retAddr;
137462137523
137463137524
testcase( pItem->addrFillSub==0 ); /* Ticket c52b09c7f38903b1311 */
@@ -137480,11 +137541,11 @@
137480137541
if( onceAddr ) sqlite3VdbeJumpHere(v, onceAddr);
137481137542
retAddr = sqlite3VdbeAddOp1(v, OP_Return, pItem->regReturn);
137482137543
VdbeComment((v, "end %s", pItem->pTab->zName));
137483137544
sqlite3VdbeChangeP1(v, topAddr, retAddr);
137484137545
sqlite3ClearTempRegCache(pParse);
137485
- if( pItem->fg.isCte ){
137546
+ if( pItem->fg.isCte && pItem->fg.isCorrelated==0 ){
137486137547
CteUse *pCteUse = pItem->u2.pCteUse;
137487137548
pCteUse->addrM9e = pItem->addrFillSub;
137488137549
pCteUse->regRtn = pItem->regReturn;
137489137550
pCteUse->iCur = pItem->iCursor;
137490137551
pCteUse->nRowEst = pSub->nSelectRow;
@@ -162285,11 +162346,11 @@
162285162346
/* Fx */ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2
162286162347
#endif
162287162348
#ifdef SQLITE_EBCDIC
162288162349
/* x0 x1 x2 x3 x4 x5 x6 x7 x8 x9 xa xb xc xd xe xf */
162289162350
/* 0x */ 29, 28, 28, 28, 28, 7, 28, 28, 28, 28, 28, 28, 7, 7, 28, 28,
162290
-/* 1x */ 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28,
162351
+/* 1x */ 28, 28, 28, 28, 28, 7, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28,
162291162352
/* 2x */ 28, 28, 28, 28, 28, 7, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28,
162292162353
/* 3x */ 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28,
162293162354
/* 4x */ 7, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 26, 12, 17, 20, 10,
162294162355
/* 5x */ 24, 28, 28, 28, 28, 28, 28, 28, 28, 28, 15, 4, 21, 18, 19, 28,
162295162356
/* 6x */ 11, 16, 28, 28, 28, 28, 28, 28, 28, 28, 28, 23, 22, 2, 13, 6,
@@ -229190,11 +229251,11 @@
229190229251
int nArg, /* Number of args */
229191229252
sqlite3_value **apUnused /* Function arguments */
229192229253
){
229193229254
assert( nArg==0 );
229194229255
UNUSED_PARAM2(nArg, apUnused);
229195
- sqlite3_result_text(pCtx, "fts5: 2021-03-12 15:10:09 acd63062eb06748bfe9e4886639e4f2b54ea6a496a83f10716abbaba4115500b", -1, SQLITE_TRANSIENT);
229256
+ sqlite3_result_text(pCtx, "fts5: 2021-03-21 18:23:48 6bb2134027a12801de8e0c73482d94682f902024800a7e426614f65a2fe4f64c", -1, SQLITE_TRANSIENT);
229196229257
}
229197229258
229198229259
/*
229199229260
** Return true if zName is the extension on one of the shadow tables used
229200229261
** by this module.
@@ -234116,12 +234177,12 @@
234116234177
}
234117234178
#endif /* SQLITE_CORE */
234118234179
#endif /* !defined(SQLITE_CORE) || defined(SQLITE_ENABLE_STMTVTAB) */
234119234180
234120234181
/************** End of stmt.c ************************************************/
234121
-#if __LINE__!=234121
234182
+#if __LINE__!=234182
234122234183
#undef SQLITE_SOURCE_ID
234123
-#define SQLITE_SOURCE_ID "2021-03-12 15:10:09 acd63062eb06748bfe9e4886639e4f2b54ea6a496a83f10716abbaba4115alt2"
234184
+#define SQLITE_SOURCE_ID "2021-03-21 18:23:48 6bb2134027a12801de8e0c73482d94682f902024800a7e426614f65a2fe4alt2"
234124234185
#endif
234125234186
/* Return the source-id for this library */
234126234187
SQLITE_API const char *sqlite3_sourceid(void){ return SQLITE_SOURCE_ID; }
234127234188
/************************** End of sqlite3.c ******************************/
234128234189
--- src/sqlite3.c
+++ src/sqlite3.c
@@ -1,8 +1,8 @@
1 /******************************************************************************
2 ** This file is an amalgamation of many separate C source files from SQLite
3 ** version 3.35.0. By combining all the individual C code files into this
4 ** single large file, the entire code can be compiled as a single translation
5 ** unit. This allows many compilers to do optimizations that would not be
6 ** possible if the files were compiled separately. Performance improvements
7 ** of 5% or more are commonly seen when SQLite is compiled as a single
8 ** translation unit.
@@ -1184,13 +1184,13 @@
1184 **
1185 ** See also: [sqlite3_libversion()],
1186 ** [sqlite3_libversion_number()], [sqlite3_sourceid()],
1187 ** [sqlite_version()] and [sqlite_source_id()].
1188 */
1189 #define SQLITE_VERSION "3.35.0"
1190 #define SQLITE_VERSION_NUMBER 3035000
1191 #define SQLITE_SOURCE_ID "2021-03-12 15:10:09 acd63062eb06748bfe9e4886639e4f2b54ea6a496a83f10716abbaba4115500b"
1192
1193 /*
1194 ** CAPI3REF: Run-Time Library Version Numbers
1195 ** KEYWORDS: sqlite3_version sqlite3_sourceid
1196 **
@@ -17005,11 +17005,14 @@
17005 u8 iDb; /* Which db file is being initialized */
17006 u8 busy; /* TRUE if currently initializing */
17007 unsigned orphanTrigger : 1; /* Last statement is orphaned TEMP trigger */
17008 unsigned imposterTable : 1; /* Building an imposter table */
17009 unsigned reopenMemdb : 1; /* ATTACH is really a reopen using MemDB */
 
17010 char **azInit; /* "type", "name", and "tbl_name" columns */
 
 
17011 } init;
17012 int nVdbeActive; /* Number of VDBEs currently running */
17013 int nVdbeRead; /* Number of active VDBEs that read or write */
17014 int nVdbeWrite; /* Number of active VDBEs that read and write */
17015 int nVdbeExec; /* Number of nested calls to VdbeExec() */
@@ -21066,10 +21069,11 @@
21066 u8 wrFlag; /* The wrFlag argument to sqlite3BtreeCursor() */
21067 #endif
21068 Bool isEphemeral:1; /* True for an ephemeral table */
21069 Bool useRandomRowid:1; /* Generate new record numbers semi-randomly */
21070 Bool isOrdered:1; /* True if the table is not BTREE_UNORDERED */
 
21071 u16 seekHit; /* See the OP_SeekHit and OP_IfNoHope opcodes */
21072 Btree *pBtx; /* Separate file holding temporary table */
21073 i64 seqCount; /* Sequence counter */
21074 u32 *aAltMap; /* Mapping from table to index column numbers */
21075
@@ -22866,10 +22870,11 @@
22866 int i, n;
22867 const unsigned char *z;
22868 int eType;
22869 memset(p, 0, sizeof(*p));
22870 if( argc==0 ){
 
22871 return setDateTimeToCurrent(context, p);
22872 }
22873 if( (eType = sqlite3_value_type(argv[0]))==SQLITE_FLOAT
22874 || eType==SQLITE_INTEGER ){
22875 setRawDateNumber(p, sqlite3_value_double(argv[0]));
@@ -40214,11 +40219,12 @@
40214 */
40215 static int unixBackupDir(const char *z, int *pJ){
40216 int j = *pJ;
40217 int i;
40218 if( j<=0 ) return 0;
40219 for(i=j-1; ALWAYS(i>0) && z[i-1]!='/'; i--){}
 
40220 if( z[i]=='.' && i==j-2 && z[i+1]=='.' ) return 0;
40221 *pJ = i-1;
40222 return 1;
40223 }
40224
@@ -64271,11 +64277,11 @@
64271 u8 sharable; /* True if we can share pBt with another db */
64272 u8 locked; /* True if db currently has pBt locked */
64273 u8 hasIncrblobCur; /* True if there are one or more Incrblob cursors */
64274 int wantToLock; /* Number of nested calls to sqlite3BtreeEnter() */
64275 int nBackup; /* Number of backup operations reading this btree */
64276 u32 iDataVersion; /* Combines with pBt->pPager->iDataVersion */
64277 Btree *pNext; /* List of other sharable Btrees from the same db */
64278 Btree *pPrev; /* Back pointer of the same list */
64279 #ifdef SQLITE_DEBUG
64280 u64 nSeek; /* Calls to sqlite3BtreeMovetoUnpacked() */
64281 #endif
@@ -67684,23 +67690,27 @@
67684 /*
67685 ** Close an open database and invalidate all cursors.
67686 */
67687 SQLITE_PRIVATE int sqlite3BtreeClose(Btree *p){
67688 BtShared *pBt = p->pBt;
67689 BtCursor *pCur;
67690
67691 /* Close all cursors opened via this handle. */
67692 assert( sqlite3_mutex_held(p->db->mutex) );
67693 sqlite3BtreeEnter(p);
67694 pCur = pBt->pCursor;
67695 while( pCur ){
67696 BtCursor *pTmp = pCur;
67697 pCur = pCur->pNext;
67698 if( pTmp->pBtree==p ){
67699 sqlite3BtreeCloseCursor(pTmp);
 
 
 
 
67700 }
67701 }
 
67702
67703 /* Rollback any active transaction and free the handle structure.
67704 ** The call to sqlite3BtreeRollback() drops any table-locks held by
67705 ** this handle.
67706 */
@@ -69078,11 +69088,11 @@
69078 rc = sqlite3PagerCommitPhaseTwo(pBt->pPager);
69079 if( rc!=SQLITE_OK && bCleanup==0 ){
69080 sqlite3BtreeLeave(p);
69081 return rc;
69082 }
69083 p->iDataVersion--; /* Compensate for pPager->iDataVersion++; */
69084 pBt->inTransaction = TRANS_READ;
69085 btreeClearHasContent(pBt);
69086 }
69087
69088 btreeEndTransaction(p);
@@ -69488,11 +69498,18 @@
69488 }
69489 btreeReleaseAllCursorPages(pCur);
69490 unlockBtreeIfUnused(pBt);
69491 sqlite3_free(pCur->aOverflow);
69492 sqlite3_free(pCur->pKey);
69493 sqlite3BtreeLeave(pBtree);
 
 
 
 
 
 
 
69494 pCur->pBtree = 0;
69495 }
69496 return SQLITE_OK;
69497 }
69498
@@ -74601,11 +74618,11 @@
74601 assert( SQLITE_OK==querySharedCacheTableLock(p, SCHEMA_ROOT, READ_LOCK) );
74602 assert( pBt->pPage1 );
74603 assert( idx>=0 && idx<=15 );
74604
74605 if( idx==BTREE_DATA_VERSION ){
74606 *pMeta = sqlite3PagerDataVersion(pBt->pPager) + p->iDataVersion;
74607 }else{
74608 *pMeta = get4byte(&pBt->pPage1->aData[36 + idx*4]);
74609 }
74610
74611 /* If auto-vacuum is disabled in this build and this is an auto-vacuum
@@ -80829,24 +80846,19 @@
80829 SQLITE_PRIVATE void sqlite3VdbeFreeCursor(Vdbe *p, VdbeCursor *pCx){
80830 if( pCx==0 ){
80831 return;
80832 }
80833 assert( pCx->pBtx==0 || pCx->eCurType==CURTYPE_BTREE );
 
80834 switch( pCx->eCurType ){
80835 case CURTYPE_SORTER: {
80836 sqlite3VdbeSorterClose(p->db, pCx);
80837 break;
80838 }
80839 case CURTYPE_BTREE: {
80840 if( pCx->isEphemeral ){
80841 if( pCx->pBtx ) sqlite3BtreeClose(pCx->pBtx);
80842 /* The pCx->pCursor will be close automatically, if it exists, by
80843 ** the call above. */
80844 }else{
80845 assert( pCx->uc.pCursor!=0 );
80846 sqlite3BtreeCloseCursor(pCx->uc.pCursor);
80847 }
80848 break;
80849 }
80850 #ifndef SQLITE_OMIT_VIRTUALTABLE
80851 case CURTYPE_VTAB: {
80852 sqlite3_vtab_cursor *pVCur = pCx->uc.pVCur;
@@ -81926,10 +81938,11 @@
81926 SQLITE_PRIVATE int sqlite3VdbeCursorMoveto(VdbeCursor **pp, u32 *piCol){
81927 VdbeCursor *p = *pp;
81928 assert( p->eCurType==CURTYPE_BTREE || p->eCurType==CURTYPE_PSEUDO );
81929 if( p->deferredMoveto ){
81930 u32 iMap;
 
81931 if( p->aAltMap && (iMap = p->aAltMap[1+*piCol])>0 && !p->nullRow ){
81932 *pp = p->pAltCursor;
81933 *piCol = iMap - 1;
81934 return SQLITE_OK;
81935 }
@@ -86134,15 +86147,10 @@
86134 ROUND8(sizeof(VdbeCursor)) + 2*sizeof(u32)*nField +
86135 (eCurType==CURTYPE_BTREE?sqlite3BtreeCursorSize():0);
86136
86137 assert( iCur>=0 && iCur<p->nCursor );
86138 if( p->apCsr[iCur] ){ /*OPTIMIZATION-IF-FALSE*/
86139 /* Before calling sqlite3VdbeFreeCursor(), ensure the isEphemeral flag
86140 ** is clear. Otherwise, if this is an ephemeral cursor created by
86141 ** OP_OpenDup, the cursor will not be closed and will still be part
86142 ** of a BtShared.pCursor list. */
86143 if( p->apCsr[iCur]->pBtx==0 ) p->apCsr[iCur]->isEphemeral = 0;
86144 sqlite3VdbeFreeCursor(p, p->apCsr[iCur]);
86145 p->apCsr[iCur] = 0;
86146 }
86147 if( SQLITE_OK==sqlite3VdbeMemClearAndResize(pMem, nByte) ){
86148 p->apCsr[iCur] = pCx = (VdbeCursor*)pMem->z;
@@ -89824,21 +89832,24 @@
89824 VdbeCursor *pOrig; /* The original cursor to be duplicated */
89825 VdbeCursor *pCx; /* The new cursor */
89826
89827 pOrig = p->apCsr[pOp->p2];
89828 assert( pOrig );
89829 assert( pOrig->pBtx!=0 ); /* Only ephemeral cursors can be duplicated */
89830
89831 pCx = allocateCursor(p, pOp->p1, pOrig->nField, -1, CURTYPE_BTREE);
89832 if( pCx==0 ) goto no_mem;
89833 pCx->nullRow = 1;
89834 pCx->isEphemeral = 1;
89835 pCx->pKeyInfo = pOrig->pKeyInfo;
89836 pCx->isTable = pOrig->isTable;
89837 pCx->pgnoRoot = pOrig->pgnoRoot;
89838 pCx->isOrdered = pOrig->isOrdered;
89839 rc = sqlite3BtreeCursor(pOrig->pBtx, pCx->pgnoRoot, BTREE_WRCSR,
 
 
 
89840 pCx->pKeyInfo, pCx->uc.pCursor);
89841 /* The sqlite3BtreeCursor() routine can only fail for the first cursor
89842 ** opened for a database. Since there is already an open cursor when this
89843 ** opcode is run, the sqlite3BtreeCursor() cannot fail */
89844 assert( rc==SQLITE_OK );
@@ -89900,13 +89911,14 @@
89900 assert( aMem[pOp->p3].flags & MEM_Null );
89901 aMem[pOp->p3].n = 0;
89902 aMem[pOp->p3].z = "";
89903 }
89904 pCx = p->apCsr[pOp->p1];
89905 if( pCx && ALWAYS(pCx->pBtx) ){
89906 /* If the ephermeral table is already open, erase all existing content
89907 ** so that the table is empty again, rather than creating a new table. */
 
89908 assert( pCx->isEphemeral );
89909 pCx->seqCount = 0;
89910 pCx->cacheStatus = CACHE_STALE;
89911 rc = sqlite3BtreeClearTable(pCx->pBtx, pCx->pgnoRoot, 0);
89912 }else{
@@ -89916,37 +89928,40 @@
89916 rc = sqlite3BtreeOpen(db->pVfs, 0, db, &pCx->pBtx,
89917 BTREE_OMIT_JOURNAL | BTREE_SINGLE | pOp->p5,
89918 vfsFlags);
89919 if( rc==SQLITE_OK ){
89920 rc = sqlite3BtreeBeginTrans(pCx->pBtx, 1, 0);
89921 }
89922 if( rc==SQLITE_OK ){
89923 /* If a transient index is required, create it by calling
89924 ** sqlite3BtreeCreateTable() with the BTREE_BLOBKEY flag before
89925 ** opening it. If a transient table is required, just use the
89926 ** automatically created table with root-page 1 (an BLOB_INTKEY table).
89927 */
89928 if( (pCx->pKeyInfo = pKeyInfo = pOp->p4.pKeyInfo)!=0 ){
89929 assert( pOp->p4type==P4_KEYINFO );
89930 rc = sqlite3BtreeCreateTable(pCx->pBtx, &pCx->pgnoRoot,
89931 BTREE_BLOBKEY | pOp->p5);
89932 if( rc==SQLITE_OK ){
89933 assert( pCx->pgnoRoot==SCHEMA_ROOT+1 );
89934 assert( pKeyInfo->db==db );
89935 assert( pKeyInfo->enc==ENC(db) );
89936 rc = sqlite3BtreeCursor(pCx->pBtx, pCx->pgnoRoot, BTREE_WRCSR,
89937 pKeyInfo, pCx->uc.pCursor);
89938 }
89939 pCx->isTable = 0;
89940 }else{
89941 pCx->pgnoRoot = SCHEMA_ROOT;
89942 rc = sqlite3BtreeCursor(pCx->pBtx, SCHEMA_ROOT, BTREE_WRCSR,
89943 0, pCx->uc.pCursor);
89944 pCx->isTable = 1;
89945 }
89946 }
89947 pCx->isOrdered = (pOp->p5!=BTREE_UNORDERED);
 
 
 
89948 }
89949 if( rc ) goto abort_due_to_error;
89950 pCx->nullRow = 1;
89951 break;
89952 }
@@ -90376,17 +90391,17 @@
90376 ** row by, perhaps by invoking sqlite3BtreeStep() on the cursor
90377 ** between 0 and This.P1 times.
90378 **
90379 ** There are three possible outcomes from this opcode:<ol>
90380 **
90381 ** <li> If after This.P1 steps, the cursor is still point to a place that
90382 ** is earlier in the btree than the target row,
90383 ** then fall through into the subsquence OP_SeekGE opcode.
90384 **
90385 ** <li> If the cursor is successfully moved to the target row by 0 or more
90386 ** sqlite3BtreeNext() calls, then jump to This.P2, which will land just
90387 ** past the OP_IdxGT opcode that follows the OP_SeekGE.
90388 **
90389 ** <li> If the cursor ends up past the target row (indicating the the target
90390 ** row does not exist in the btree) then jump to SeekOP.P2.
90391 ** </ol>
90392 */
@@ -90399,11 +90414,12 @@
90399 assert( pOp[1].opcode==OP_SeekGE );
90400
90401 /* pOp->p2 points to the first instruction past the OP_IdxGT that
90402 ** follows the OP_SeekGE. */
90403 assert( pOp->p2>=(int)(pOp-aOp)+2 );
90404 assert( aOp[pOp->p2-1].opcode==OP_IdxGT );
 
90405 assert( pOp[1].p1==aOp[pOp->p2-1].p1 );
90406 assert( pOp[1].p2==aOp[pOp->p2-1].p2 );
90407 assert( pOp[1].p3==aOp[pOp->p2-1].p3 );
90408
90409 assert( pOp->p1>0 );
@@ -91934,10 +91950,12 @@
91934 pTabCur->nullRow = 0;
91935 pTabCur->movetoTarget = rowid;
91936 pTabCur->deferredMoveto = 1;
91937 assert( pOp->p4type==P4_INTARRAY || pOp->p4.ai==0 );
91938 pTabCur->aAltMap = pOp->p4.ai;
 
 
91939 pTabCur->pAltCursor = pC;
91940 }else{
91941 pOut = out2Prerelease(p, pOp);
91942 pOut->u.i = rowid;
91943 }
@@ -99165,10 +99183,11 @@
99165 */
99166 if( cnt==0 && zTab==0 ){
99167 assert( pExpr->op==TK_ID );
99168 if( ExprHasProperty(pExpr,EP_DblQuoted)
99169 && areDoubleQuotedStringsEnabled(db, pTopNC)
 
99170 ){
99171 /* If a double-quoted identifier does not match any known column name,
99172 ** then treat it as a string.
99173 **
99174 ** This hack was added in the early days of SQLite in a misguided attempt
@@ -99179,10 +99198,15 @@
99179 ** programmers. To all those frustrated programmers, my apologies.
99180 **
99181 ** Someday, I hope to get rid of this hack. Unfortunately there is
99182 ** a huge amount of legacy SQL that uses it. So for now, we just
99183 ** issue a warning.
 
 
 
 
 
99184 */
99185 sqlite3_log(SQLITE_WARNING,
99186 "double-quoted string literal: \"%w\"", zCol);
99187 #ifdef SQLITE_ENABLE_NORMALIZE
99188 sqlite3VdbeAddDblquoteStr(db, pParse->pVdbe, zCol);
@@ -106778,31 +106802,32 @@
106778 */
106779 static void renameTestSchema(
106780 Parse *pParse, /* Parse context */
106781 const char *zDb, /* Name of db to verify schema of */
106782 int bTemp, /* True if this is the temp db */
106783 const char *zWhen /* "when" part of error message */
 
106784 ){
106785 pParse->colNamesSet = 1;
106786 sqlite3NestedParse(pParse,
106787 "SELECT 1 "
106788 "FROM \"%w\"." DFLT_SCHEMA_TABLE " "
106789 "WHERE name NOT LIKE 'sqliteX_%%' ESCAPE 'X'"
106790 " AND sql NOT LIKE 'create virtual%%'"
106791 " AND sqlite_rename_test(%Q, sql, type, name, %d, %Q)=NULL ",
106792 zDb,
106793 zDb, bTemp, zWhen
106794 );
106795
106796 if( bTemp==0 ){
106797 sqlite3NestedParse(pParse,
106798 "SELECT 1 "
106799 "FROM temp." DFLT_SCHEMA_TABLE " "
106800 "WHERE name NOT LIKE 'sqliteX_%%' ESCAPE 'X'"
106801 " AND sql NOT LIKE 'create virtual%%'"
106802 " AND sqlite_rename_test(%Q, sql, type, name, 1, %Q)=NULL ",
106803 zDb, zWhen
106804 );
106805 }
106806 }
106807
106808 /*
@@ -106961,11 +106986,11 @@
106961 sqlite3NestedParse(pParse,
106962 "UPDATE sqlite_temp_schema SET "
106963 "sql = sqlite_rename_table(%Q, type, name, sql, %Q, %Q, 1), "
106964 "tbl_name = "
106965 "CASE WHEN tbl_name=%Q COLLATE nocase AND "
106966 " sqlite_rename_test(%Q, sql, type, name, 1, 'after rename') "
106967 "THEN %Q ELSE tbl_name END "
106968 "WHERE type IN ('view', 'trigger')"
106969 , zDb, zTabName, zName, zTabName, zDb, zName);
106970 }
106971
@@ -106981,11 +107006,11 @@
106981 sqlite3VdbeAddOp4(v, OP_VRename, i, 0, 0,(const char*)pVTab, P4_VTAB);
106982 }
106983 #endif
106984
106985 renameReloadSchema(pParse, iDb, INITFLAG_AlterRename);
106986 renameTestSchema(pParse, zDb, iDb==1, "after rename");
106987
106988 exit_rename_table:
106989 sqlite3SrcListDelete(db, pSrc);
106990 sqlite3DbFree(db, zName);
106991 db->mDbFlags = savedDbFlags;
@@ -107349,11 +107374,11 @@
107349 zDb, pTab->zName, iCol, zNew, bQuote
107350 );
107351
107352 /* Drop and reload the database schema. */
107353 renameReloadSchema(pParse, iSchema, INITFLAG_AlterRename);
107354 renameTestSchema(pParse, zDb, iSchema==1, "after rename");
107355
107356 exit_rename_column:
107357 sqlite3SrcListDelete(db, pSrc);
107358 sqlite3DbFree(db, zOld);
107359 sqlite3DbFree(db, zNew);
@@ -107773,16 +107798,21 @@
107773 static int renameParseSql(
107774 Parse *p, /* Memory to use for Parse object */
107775 const char *zDb, /* Name of schema SQL belongs to */
107776 sqlite3 *db, /* Database handle */
107777 const char *zSql, /* SQL to parse */
107778 int bTemp /* True if SQL is from temp schema */
 
107779 ){
107780 int rc;
107781 char *zErr = 0;
107782
107783 db->init.iDb = bTemp ? 1 : sqlite3FindDbName(db, zDb);
 
 
 
 
107784
107785 /* Parse the SQL statement passed as the first argument. If no error
107786 ** occurs and the parse does not result in a new table, index or
107787 ** trigger object, the database must be corrupt. */
107788 memset(p, 0, sizeof(Parse));
@@ -107811,10 +107841,11 @@
107811 }
107812 }
107813 #endif
107814
107815 db->init.iDb = 0;
 
107816 return rc;
107817 }
107818
107819 /*
107820 ** This function edits SQL statement zSql, replacing each token identified
@@ -108112,11 +108143,11 @@
108112 sCtx.iCol = ((iCol==pTab->iPKey) ? -1 : iCol);
108113
108114 #ifndef SQLITE_OMIT_AUTHORIZATION
108115 db->xAuth = 0;
108116 #endif
108117 rc = renameParseSql(&sParse, zDb, db, zSql, bTemp);
108118
108119 /* Find tokens that need to be replaced. */
108120 memset(&sWalker, 0, sizeof(Walker));
108121 sWalker.pParse = &sParse;
108122 sWalker.xExprCallback = renameColumnExprCb;
@@ -108154,16 +108185,16 @@
108154 sqlite3WalkExprList(&sWalker, pIdx->aColExpr);
108155 }
108156 for(pIdx=sParse.pNewIndex; pIdx; pIdx=pIdx->pNext){
108157 sqlite3WalkExprList(&sWalker, pIdx->aColExpr);
108158 }
108159 }
108160 #ifndef SQLITE_OMIT_GENERATED_COLUMNS
108161 for(i=0; i<sParse.pNewTable->nCol; i++){
108162 sqlite3WalkExpr(&sWalker, sParse.pNewTable->aCol[i].pDflt);
 
 
108163 }
108164 #endif
108165
108166 for(pFKey=sParse.pNewTable->pFKey; pFKey; pFKey=pFKey->pNextFrom){
108167 for(i=0; i<pFKey->nCol; i++){
108168 if( bFKOnly==0 && pFKey->aCol[i].iFrom==iCol ){
108169 renameTokenFind(&sParse, &sCtx, (void*)&pFKey->aCol[i]);
@@ -108316,11 +108347,11 @@
108316 sWalker.pParse = &sParse;
108317 sWalker.xExprCallback = renameTableExprCb;
108318 sWalker.xSelectCallback = renameTableSelectCb;
108319 sWalker.u.pRename = &sCtx;
108320
108321 rc = renameParseSql(&sParse, zDb, db, zInput, bTemp);
108322
108323 if( rc==SQLITE_OK ){
108324 int isLegacy = (db->flags & SQLITE_LegacyAlter);
108325 if( sParse.pNewTable ){
108326 Table *pTab = sParse.pNewTable;
@@ -108432,10 +108463,11 @@
108432 ** 1: SQL statement.
108433 ** 2: Object type ("view", "table", "trigger" or "index").
108434 ** 3: Object name.
108435 ** 4: True if object is from temp schema.
108436 ** 5: "when" part of error message.
 
108437 **
108438 ** Unless it finds an error, this function normally returns NULL. However, it
108439 ** returns integer value 1 if:
108440 **
108441 ** * the SQL argument creates a trigger, and
@@ -108450,10 +108482,11 @@
108450 char const *zDb = (const char*)sqlite3_value_text(argv[0]);
108451 char const *zInput = (const char*)sqlite3_value_text(argv[1]);
108452 int bTemp = sqlite3_value_int(argv[4]);
108453 int isLegacy = (db->flags & SQLITE_LegacyAlter);
108454 char const *zWhen = (const char*)sqlite3_value_text(argv[5]);
 
108455
108456 #ifndef SQLITE_OMIT_AUTHORIZATION
108457 sqlite3_xauth xAuth = db->xAuth;
108458 db->xAuth = 0;
108459 #endif
@@ -108460,11 +108493,11 @@
108460
108461 UNUSED_PARAMETER(NotUsed);
108462 if( zDb && zInput ){
108463 int rc;
108464 Parse sParse;
108465 rc = renameParseSql(&sParse, zDb, db, zInput, bTemp);
108466 if( rc==SQLITE_OK ){
108467 if( isLegacy==0 && sParse.pNewTable && sParse.pNewTable->pSelect ){
108468 NameContext sNC;
108469 memset(&sNC, 0, sizeof(sNC));
108470 sNC.pParse = &sParse;
@@ -108528,11 +108561,11 @@
108528 sqlite3_xauth xAuth = db->xAuth;
108529 db->xAuth = 0;
108530 #endif
108531
108532 UNUSED_PARAMETER(NotUsed);
108533 rc = renameParseSql(&sParse, zDb, db, zSql, iSchema==1);
108534 if( rc!=SQLITE_OK ) goto drop_column_done;
108535 pTab = sParse.pNewTable;
108536 if( pTab==0 || pTab->nCol==1 || iCol>=pTab->nCol ){
108537 /* This can happen if the sqlite_schema table is corrupt */
108538 rc = SQLITE_CORRUPT_BKPT;
@@ -108621,21 +108654,21 @@
108621
108622 /* Edit the sqlite_schema table */
108623 iDb = sqlite3SchemaToIndex(db, pTab->pSchema);
108624 assert( iDb>=0 );
108625 zDb = db->aDb[iDb].zDbSName;
108626 renameTestSchema(pParse, zDb, iDb==1, "");
108627 sqlite3NestedParse(pParse,
108628 "UPDATE \"%w\"." DFLT_SCHEMA_TABLE " SET "
108629 "sql = sqlite_drop_column(%d, sql, %d) "
108630 "WHERE (type=='table' AND tbl_name=%Q COLLATE nocase)"
108631 , zDb, iDb, iCol, pTab->zName
108632 );
108633
108634 /* Drop and reload the database schema. */
108635 renameReloadSchema(pParse, iDb, INITFLAG_AlterDrop);
108636 renameTestSchema(pParse, zDb, iDb==1, "after drop column");
108637
108638 /* Edit rows of table on disk */
108639 if( pParse->nErr==0 && (pTab->aCol[iCol].colFlags & COLFLAG_VIRTUAL)==0 ){
108640 int i;
108641 int addr;
@@ -108691,11 +108724,11 @@
108691 */
108692 SQLITE_PRIVATE void sqlite3AlterFunctions(void){
108693 static FuncDef aAlterTableFuncs[] = {
108694 INTERNAL_FUNCTION(sqlite_rename_column, 9, renameColumnFunc),
108695 INTERNAL_FUNCTION(sqlite_rename_table, 7, renameTableFunc),
108696 INTERNAL_FUNCTION(sqlite_rename_test, 6, renameTableTest),
108697 INTERNAL_FUNCTION(sqlite_drop_column, 3, dropColumnFunc),
108698 };
108699 sqlite3InsertBuiltinFuncs(aAlterTableFuncs, ArraySize(aAlterTableFuncs));
108700 }
108701 #endif /* SQLITE_ALTER_TABLE */
@@ -115663,11 +115696,15 @@
115663 }
115664
115665 /* Clean up before exiting */
115666 exit_create_index:
115667 if( pIndex ) sqlite3FreeIndex(db, pIndex);
115668 if( pTab ){ /* Ensure all REPLACE indexes are at the end of the list */
 
 
 
 
115669 Index **ppFrom = &pTab->pIndex;
115670 Index *pThis;
115671 for(ppFrom=&pTab->pIndex; (pThis = *ppFrom)!=0; ppFrom=&pThis->pNext){
115672 Index *pNext;
115673 if( pThis->onError!=OE_Replace ) continue;
@@ -115677,10 +115714,20 @@
115677 pNext->pNext = pThis;
115678 ppFrom = &pNext->pNext;
115679 }
115680 break;
115681 }
 
 
 
 
 
 
 
 
 
 
115682 }
115683 sqlite3ExprDelete(db, pPIWhere);
115684 sqlite3ExprListDelete(db, pList);
115685 sqlite3SrcListDelete(db, pTblName);
115686 sqlite3DbFree(db, zName);
@@ -123093,11 +123140,13 @@
123093 pNx->pUpsertSrc = pTabList;
123094 pNx->regData = regData;
123095 pNx->iDataCur = iDataCur;
123096 pNx->iIdxCur = iIdxCur;
123097 if( pNx->pUpsertTarget ){
123098 sqlite3UpsertAnalyzeTarget(pParse, pTabList, pNx);
 
 
123099 }
123100 pNx = pNx->pNextUpsert;
123101 }while( pNx!=0 );
123102 }
123103 #endif
@@ -124515,11 +124564,11 @@
124515 int regData /* Data containing new record */
124516 ){
124517 Vdbe *v = pParse->pVdbe;
124518 int r = sqlite3GetTempReg(pParse);
124519 assert( !HasRowid(pTab) );
124520 assert( 0==(pParse->db->mDbFlags & DBFLAG_Vacuum) );
124521 sqlite3VdbeAddOp2(v, OP_Integer, 0, r);
124522 sqlite3VdbeAddOp4(v, OP_Insert, iCur, regData, r, (char*)pTab, P4_TABLE);
124523 sqlite3VdbeChangeP5(v, OPFLAG_ISNOOP);
124524 sqlite3ReleaseTempReg(pParse, r);
124525 }
@@ -133063,11 +133112,11 @@
133063 sqlite3HashInit(&ht);
133064 if( pEList ){
133065 nCol = pEList->nExpr;
133066 aCol = sqlite3DbMallocZero(db, sizeof(aCol[0])*nCol);
133067 testcase( aCol==0 );
133068 if( nCol>32767 ) nCol = 32767;
133069 }else{
133070 nCol = 0;
133071 aCol = 0;
133072 }
133073 assert( nCol==(i16)nCol );
@@ -135116,10 +135165,11 @@
135116
135117 /* Restriction (23) */
135118 if( (p->selFlags & SF_Recursive) ) return 0;
135119
135120 if( pSrc->nSrc>1 ){
 
135121 aCsrMap = sqlite3DbMallocZero(db, pParse->nTab*sizeof(int));
135122 }
135123 }
135124
135125 /***** If we reach this point, flattening is permitted. *****/
@@ -135192,10 +135242,11 @@
135192 p->op = TK_ALL;
135193 pSubitem->pTab = pItemTab;
135194 if( pNew==0 ){
135195 p->pPrior = pPrior;
135196 }else{
 
135197 if( aCsrMap && db->mallocFailed==0 ){
135198 renumberCursors(pParse, pNew, iFrom, aCsrMap);
135199 }
135200 pNew->pPrior = pPrior;
135201 if( pPrior ) pPrior->pNext = pNew;
@@ -136135,20 +136186,28 @@
136135
136136 pCte->zCteErr = "circular reference: %s";
136137 pSavedWith = pParse->pWith;
136138 pParse->pWith = pWith;
136139 if( pSel->selFlags & SF_Recursive ){
 
136140 assert( pRecTerm!=0 );
136141 assert( (pRecTerm->selFlags & SF_Recursive)==0 );
136142 assert( pRecTerm->pNext!=0 );
136143 assert( (pRecTerm->pNext->selFlags & SF_Recursive)!=0 );
136144 assert( pRecTerm->pWith==0 );
136145 pRecTerm->pWith = pSel->pWith;
136146 sqlite3WalkSelect(pWalker, pRecTerm);
136147 pRecTerm->pWith = 0;
 
 
 
 
136148 }else{
136149 sqlite3WalkSelect(pWalker, pSel);
 
 
 
136150 }
136151 pParse->pWith = pWith;
136152
136153 for(pLeft=pSel; pLeft->pPrior; pLeft=pLeft->pPrior);
136154 pEList = pLeft->pEList;
@@ -137453,11 +137512,13 @@
137453 sqlite3VdbeAddOp2(v, OP_Gosub, pPrior->regReturn, pPrior->addrFillSub);
137454 }
137455 sqlite3VdbeAddOp2(v, OP_OpenDup, pItem->iCursor, pPrior->iCursor);
137456 pSub->nSelectRow = pPrior->pSelect->nSelectRow;
137457 }else{
137458 /* Generate a subroutine that will materialize the view. */
 
 
137459 int topAddr;
137460 int onceAddr = 0;
137461 int retAddr;
137462
137463 testcase( pItem->addrFillSub==0 ); /* Ticket c52b09c7f38903b1311 */
@@ -137480,11 +137541,11 @@
137480 if( onceAddr ) sqlite3VdbeJumpHere(v, onceAddr);
137481 retAddr = sqlite3VdbeAddOp1(v, OP_Return, pItem->regReturn);
137482 VdbeComment((v, "end %s", pItem->pTab->zName));
137483 sqlite3VdbeChangeP1(v, topAddr, retAddr);
137484 sqlite3ClearTempRegCache(pParse);
137485 if( pItem->fg.isCte ){
137486 CteUse *pCteUse = pItem->u2.pCteUse;
137487 pCteUse->addrM9e = pItem->addrFillSub;
137488 pCteUse->regRtn = pItem->regReturn;
137489 pCteUse->iCur = pItem->iCursor;
137490 pCteUse->nRowEst = pSub->nSelectRow;
@@ -162285,11 +162346,11 @@
162285 /* Fx */ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2
162286 #endif
162287 #ifdef SQLITE_EBCDIC
162288 /* x0 x1 x2 x3 x4 x5 x6 x7 x8 x9 xa xb xc xd xe xf */
162289 /* 0x */ 29, 28, 28, 28, 28, 7, 28, 28, 28, 28, 28, 28, 7, 7, 28, 28,
162290 /* 1x */ 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28,
162291 /* 2x */ 28, 28, 28, 28, 28, 7, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28,
162292 /* 3x */ 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28,
162293 /* 4x */ 7, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 26, 12, 17, 20, 10,
162294 /* 5x */ 24, 28, 28, 28, 28, 28, 28, 28, 28, 28, 15, 4, 21, 18, 19, 28,
162295 /* 6x */ 11, 16, 28, 28, 28, 28, 28, 28, 28, 28, 28, 23, 22, 2, 13, 6,
@@ -229190,11 +229251,11 @@
229190 int nArg, /* Number of args */
229191 sqlite3_value **apUnused /* Function arguments */
229192 ){
229193 assert( nArg==0 );
229194 UNUSED_PARAM2(nArg, apUnused);
229195 sqlite3_result_text(pCtx, "fts5: 2021-03-12 15:10:09 acd63062eb06748bfe9e4886639e4f2b54ea6a496a83f10716abbaba4115500b", -1, SQLITE_TRANSIENT);
229196 }
229197
229198 /*
229199 ** Return true if zName is the extension on one of the shadow tables used
229200 ** by this module.
@@ -234116,12 +234177,12 @@
234116 }
234117 #endif /* SQLITE_CORE */
234118 #endif /* !defined(SQLITE_CORE) || defined(SQLITE_ENABLE_STMTVTAB) */
234119
234120 /************** End of stmt.c ************************************************/
234121 #if __LINE__!=234121
234122 #undef SQLITE_SOURCE_ID
234123 #define SQLITE_SOURCE_ID "2021-03-12 15:10:09 acd63062eb06748bfe9e4886639e4f2b54ea6a496a83f10716abbaba4115alt2"
234124 #endif
234125 /* Return the source-id for this library */
234126 SQLITE_API const char *sqlite3_sourceid(void){ return SQLITE_SOURCE_ID; }
234127 /************************** End of sqlite3.c ******************************/
234128
--- src/sqlite3.c
+++ src/sqlite3.c
@@ -1,8 +1,8 @@
1 /******************************************************************************
2 ** This file is an amalgamation of many separate C source files from SQLite
3 ** version 3.35.3. By combining all the individual C code files into this
4 ** single large file, the entire code can be compiled as a single translation
5 ** unit. This allows many compilers to do optimizations that would not be
6 ** possible if the files were compiled separately. Performance improvements
7 ** of 5% or more are commonly seen when SQLite is compiled as a single
8 ** translation unit.
@@ -1184,13 +1184,13 @@
1184 **
1185 ** See also: [sqlite3_libversion()],
1186 ** [sqlite3_libversion_number()], [sqlite3_sourceid()],
1187 ** [sqlite_version()] and [sqlite_source_id()].
1188 */
1189 #define SQLITE_VERSION "3.35.3"
1190 #define SQLITE_VERSION_NUMBER 3035003
1191 #define SQLITE_SOURCE_ID "2021-03-21 18:23:48 6bb2134027a12801de8e0c73482d94682f902024800a7e426614f65a2fe4f64c"
1192
1193 /*
1194 ** CAPI3REF: Run-Time Library Version Numbers
1195 ** KEYWORDS: sqlite3_version sqlite3_sourceid
1196 **
@@ -17005,11 +17005,14 @@
17005 u8 iDb; /* Which db file is being initialized */
17006 u8 busy; /* TRUE if currently initializing */
17007 unsigned orphanTrigger : 1; /* Last statement is orphaned TEMP trigger */
17008 unsigned imposterTable : 1; /* Building an imposter table */
17009 unsigned reopenMemdb : 1; /* ATTACH is really a reopen using MemDB */
17010 unsigned bDropColumn : 1; /* Doing schema check after DROP COLUMN */
17011 char **azInit; /* "type", "name", and "tbl_name" columns */
17012 /* or if bDropColumn, then azInit[0] is the */
17013 /* name of the column being dropped */
17014 } init;
17015 int nVdbeActive; /* Number of VDBEs currently running */
17016 int nVdbeRead; /* Number of active VDBEs that read or write */
17017 int nVdbeWrite; /* Number of active VDBEs that read and write */
17018 int nVdbeExec; /* Number of nested calls to VdbeExec() */
@@ -21066,10 +21069,11 @@
21069 u8 wrFlag; /* The wrFlag argument to sqlite3BtreeCursor() */
21070 #endif
21071 Bool isEphemeral:1; /* True for an ephemeral table */
21072 Bool useRandomRowid:1; /* Generate new record numbers semi-randomly */
21073 Bool isOrdered:1; /* True if the table is not BTREE_UNORDERED */
21074 Bool hasBeenDuped:1; /* This cursor was source or target of OP_OpenDup */
21075 u16 seekHit; /* See the OP_SeekHit and OP_IfNoHope opcodes */
21076 Btree *pBtx; /* Separate file holding temporary table */
21077 i64 seqCount; /* Sequence counter */
21078 u32 *aAltMap; /* Mapping from table to index column numbers */
21079
@@ -22866,10 +22870,11 @@
22870 int i, n;
22871 const unsigned char *z;
22872 int eType;
22873 memset(p, 0, sizeof(*p));
22874 if( argc==0 ){
22875 if( !sqlite3NotPureFunc(context) ) return 1;
22876 return setDateTimeToCurrent(context, p);
22877 }
22878 if( (eType = sqlite3_value_type(argv[0]))==SQLITE_FLOAT
22879 || eType==SQLITE_INTEGER ){
22880 setRawDateNumber(p, sqlite3_value_double(argv[0]));
@@ -40214,11 +40219,12 @@
40219 */
40220 static int unixBackupDir(const char *z, int *pJ){
40221 int j = *pJ;
40222 int i;
40223 if( j<=0 ) return 0;
40224 for(i=j-1; i>0 && z[i-1]!='/'; i--){}
40225 if( i==0 ) return 0;
40226 if( z[i]=='.' && i==j-2 && z[i+1]=='.' ) return 0;
40227 *pJ = i-1;
40228 return 1;
40229 }
40230
@@ -64271,11 +64277,11 @@
64277 u8 sharable; /* True if we can share pBt with another db */
64278 u8 locked; /* True if db currently has pBt locked */
64279 u8 hasIncrblobCur; /* True if there are one or more Incrblob cursors */
64280 int wantToLock; /* Number of nested calls to sqlite3BtreeEnter() */
64281 int nBackup; /* Number of backup operations reading this btree */
64282 u32 iBDataVersion; /* Combines with pBt->pPager->iDataVersion */
64283 Btree *pNext; /* List of other sharable Btrees from the same db */
64284 Btree *pPrev; /* Back pointer of the same list */
64285 #ifdef SQLITE_DEBUG
64286 u64 nSeek; /* Calls to sqlite3BtreeMovetoUnpacked() */
64287 #endif
@@ -67684,23 +67690,27 @@
67690 /*
67691 ** Close an open database and invalidate all cursors.
67692 */
67693 SQLITE_PRIVATE int sqlite3BtreeClose(Btree *p){
67694 BtShared *pBt = p->pBt;
 
67695
67696 /* Close all cursors opened via this handle. */
67697 assert( sqlite3_mutex_held(p->db->mutex) );
67698 sqlite3BtreeEnter(p);
67699
67700 /* Verify that no other cursors have this Btree open */
67701 #ifdef SQLITE_DEBUG
67702 {
67703 BtCursor *pCur = pBt->pCursor;
67704 while( pCur ){
67705 BtCursor *pTmp = pCur;
67706 pCur = pCur->pNext;
67707 assert( pTmp->pBtree!=p );
67708
67709 }
67710 }
67711 #endif
67712
67713 /* Rollback any active transaction and free the handle structure.
67714 ** The call to sqlite3BtreeRollback() drops any table-locks held by
67715 ** this handle.
67716 */
@@ -69078,11 +69088,11 @@
69088 rc = sqlite3PagerCommitPhaseTwo(pBt->pPager);
69089 if( rc!=SQLITE_OK && bCleanup==0 ){
69090 sqlite3BtreeLeave(p);
69091 return rc;
69092 }
69093 p->iBDataVersion--; /* Compensate for pPager->iDataVersion++; */
69094 pBt->inTransaction = TRANS_READ;
69095 btreeClearHasContent(pBt);
69096 }
69097
69098 btreeEndTransaction(p);
@@ -69488,11 +69498,18 @@
69498 }
69499 btreeReleaseAllCursorPages(pCur);
69500 unlockBtreeIfUnused(pBt);
69501 sqlite3_free(pCur->aOverflow);
69502 sqlite3_free(pCur->pKey);
69503 if( (pBt->openFlags & BTREE_SINGLE) && pBt->pCursor==0 ){
69504 /* Since the BtShared is not sharable, there is no need to
69505 ** worry about the missing sqlite3BtreeLeave() call here. */
69506 assert( pBtree->sharable==0 );
69507 sqlite3BtreeClose(pBtree);
69508 }else{
69509 sqlite3BtreeLeave(pBtree);
69510 }
69511 pCur->pBtree = 0;
69512 }
69513 return SQLITE_OK;
69514 }
69515
@@ -74601,11 +74618,11 @@
74618 assert( SQLITE_OK==querySharedCacheTableLock(p, SCHEMA_ROOT, READ_LOCK) );
74619 assert( pBt->pPage1 );
74620 assert( idx>=0 && idx<=15 );
74621
74622 if( idx==BTREE_DATA_VERSION ){
74623 *pMeta = sqlite3PagerDataVersion(pBt->pPager) + p->iBDataVersion;
74624 }else{
74625 *pMeta = get4byte(&pBt->pPage1->aData[36 + idx*4]);
74626 }
74627
74628 /* If auto-vacuum is disabled in this build and this is an auto-vacuum
@@ -80829,24 +80846,19 @@
80846 SQLITE_PRIVATE void sqlite3VdbeFreeCursor(Vdbe *p, VdbeCursor *pCx){
80847 if( pCx==0 ){
80848 return;
80849 }
80850 assert( pCx->pBtx==0 || pCx->eCurType==CURTYPE_BTREE );
80851 assert( pCx->pBtx==0 || pCx->isEphemeral );
80852 switch( pCx->eCurType ){
80853 case CURTYPE_SORTER: {
80854 sqlite3VdbeSorterClose(p->db, pCx);
80855 break;
80856 }
80857 case CURTYPE_BTREE: {
80858 assert( pCx->uc.pCursor!=0 );
80859 sqlite3BtreeCloseCursor(pCx->uc.pCursor);
 
 
 
 
 
 
80860 break;
80861 }
80862 #ifndef SQLITE_OMIT_VIRTUALTABLE
80863 case CURTYPE_VTAB: {
80864 sqlite3_vtab_cursor *pVCur = pCx->uc.pVCur;
@@ -81926,10 +81938,11 @@
81938 SQLITE_PRIVATE int sqlite3VdbeCursorMoveto(VdbeCursor **pp, u32 *piCol){
81939 VdbeCursor *p = *pp;
81940 assert( p->eCurType==CURTYPE_BTREE || p->eCurType==CURTYPE_PSEUDO );
81941 if( p->deferredMoveto ){
81942 u32 iMap;
81943 assert( !p->isEphemeral );
81944 if( p->aAltMap && (iMap = p->aAltMap[1+*piCol])>0 && !p->nullRow ){
81945 *pp = p->pAltCursor;
81946 *piCol = iMap - 1;
81947 return SQLITE_OK;
81948 }
@@ -86134,15 +86147,10 @@
86147 ROUND8(sizeof(VdbeCursor)) + 2*sizeof(u32)*nField +
86148 (eCurType==CURTYPE_BTREE?sqlite3BtreeCursorSize():0);
86149
86150 assert( iCur>=0 && iCur<p->nCursor );
86151 if( p->apCsr[iCur] ){ /*OPTIMIZATION-IF-FALSE*/
 
 
 
 
 
86152 sqlite3VdbeFreeCursor(p, p->apCsr[iCur]);
86153 p->apCsr[iCur] = 0;
86154 }
86155 if( SQLITE_OK==sqlite3VdbeMemClearAndResize(pMem, nByte) ){
86156 p->apCsr[iCur] = pCx = (VdbeCursor*)pMem->z;
@@ -89824,21 +89832,24 @@
89832 VdbeCursor *pOrig; /* The original cursor to be duplicated */
89833 VdbeCursor *pCx; /* The new cursor */
89834
89835 pOrig = p->apCsr[pOp->p2];
89836 assert( pOrig );
89837 assert( pOrig->isEphemeral ); /* Only ephemeral cursors can be duplicated */
89838
89839 pCx = allocateCursor(p, pOp->p1, pOrig->nField, -1, CURTYPE_BTREE);
89840 if( pCx==0 ) goto no_mem;
89841 pCx->nullRow = 1;
89842 pCx->isEphemeral = 1;
89843 pCx->pKeyInfo = pOrig->pKeyInfo;
89844 pCx->isTable = pOrig->isTable;
89845 pCx->pgnoRoot = pOrig->pgnoRoot;
89846 pCx->isOrdered = pOrig->isOrdered;
89847 pCx->pBtx = pOrig->pBtx;
89848 pCx->hasBeenDuped = 1;
89849 pOrig->hasBeenDuped = 1;
89850 rc = sqlite3BtreeCursor(pCx->pBtx, pCx->pgnoRoot, BTREE_WRCSR,
89851 pCx->pKeyInfo, pCx->uc.pCursor);
89852 /* The sqlite3BtreeCursor() routine can only fail for the first cursor
89853 ** opened for a database. Since there is already an open cursor when this
89854 ** opcode is run, the sqlite3BtreeCursor() cannot fail */
89855 assert( rc==SQLITE_OK );
@@ -89900,13 +89911,14 @@
89911 assert( aMem[pOp->p3].flags & MEM_Null );
89912 aMem[pOp->p3].n = 0;
89913 aMem[pOp->p3].z = "";
89914 }
89915 pCx = p->apCsr[pOp->p1];
89916 if( pCx && !pCx->hasBeenDuped ){
89917 /* If the ephermeral table is already open and has no duplicates from
89918 ** OP_OpenDup, then erase all existing content so that the table is
89919 ** empty again, rather than creating a new table. */
89920 assert( pCx->isEphemeral );
89921 pCx->seqCount = 0;
89922 pCx->cacheStatus = CACHE_STALE;
89923 rc = sqlite3BtreeClearTable(pCx->pBtx, pCx->pgnoRoot, 0);
89924 }else{
@@ -89916,37 +89928,40 @@
89928 rc = sqlite3BtreeOpen(db->pVfs, 0, db, &pCx->pBtx,
89929 BTREE_OMIT_JOURNAL | BTREE_SINGLE | pOp->p5,
89930 vfsFlags);
89931 if( rc==SQLITE_OK ){
89932 rc = sqlite3BtreeBeginTrans(pCx->pBtx, 1, 0);
89933 if( rc==SQLITE_OK ){
89934 /* If a transient index is required, create it by calling
89935 ** sqlite3BtreeCreateTable() with the BTREE_BLOBKEY flag before
89936 ** opening it. If a transient table is required, just use the
89937 ** automatically created table with root-page 1 (an BLOB_INTKEY table).
89938 */
89939 if( (pCx->pKeyInfo = pKeyInfo = pOp->p4.pKeyInfo)!=0 ){
89940 assert( pOp->p4type==P4_KEYINFO );
89941 rc = sqlite3BtreeCreateTable(pCx->pBtx, &pCx->pgnoRoot,
89942 BTREE_BLOBKEY | pOp->p5);
89943 if( rc==SQLITE_OK ){
89944 assert( pCx->pgnoRoot==SCHEMA_ROOT+1 );
89945 assert( pKeyInfo->db==db );
89946 assert( pKeyInfo->enc==ENC(db) );
89947 rc = sqlite3BtreeCursor(pCx->pBtx, pCx->pgnoRoot, BTREE_WRCSR,
89948 pKeyInfo, pCx->uc.pCursor);
89949 }
89950 pCx->isTable = 0;
89951 }else{
89952 pCx->pgnoRoot = SCHEMA_ROOT;
89953 rc = sqlite3BtreeCursor(pCx->pBtx, SCHEMA_ROOT, BTREE_WRCSR,
89954 0, pCx->uc.pCursor);
89955 pCx->isTable = 1;
89956 }
89957 }
89958 pCx->isOrdered = (pOp->p5!=BTREE_UNORDERED);
89959 if( rc ){
89960 sqlite3BtreeClose(pCx->pBtx);
89961 }
89962 }
89963 }
89964 if( rc ) goto abort_due_to_error;
89965 pCx->nullRow = 1;
89966 break;
89967 }
@@ -90376,17 +90391,17 @@
90391 ** row by, perhaps by invoking sqlite3BtreeStep() on the cursor
90392 ** between 0 and This.P1 times.
90393 **
90394 ** There are three possible outcomes from this opcode:<ol>
90395 **
90396 ** <li> If after This.P1 steps, the cursor is still pointing to a place that
90397 ** is earlier in the btree than the target row, then fall through
90398 ** into the subsquence OP_SeekGE opcode.
90399 **
90400 ** <li> If the cursor is successfully moved to the target row by 0 or more
90401 ** sqlite3BtreeNext() calls, then jump to This.P2, which will land just
90402 ** past the OP_IdxGT or OP_IdxGE opcode that follows the OP_SeekGE.
90403 **
90404 ** <li> If the cursor ends up past the target row (indicating the the target
90405 ** row does not exist in the btree) then jump to SeekOP.P2.
90406 ** </ol>
90407 */
@@ -90399,11 +90414,12 @@
90414 assert( pOp[1].opcode==OP_SeekGE );
90415
90416 /* pOp->p2 points to the first instruction past the OP_IdxGT that
90417 ** follows the OP_SeekGE. */
90418 assert( pOp->p2>=(int)(pOp-aOp)+2 );
90419 assert( aOp[pOp->p2-1].opcode==OP_IdxGT || aOp[pOp->p2-1].opcode==OP_IdxGE );
90420 testcase( aOp[pOp->p2-1].opcode==OP_IdxGE );
90421 assert( pOp[1].p1==aOp[pOp->p2-1].p1 );
90422 assert( pOp[1].p2==aOp[pOp->p2-1].p2 );
90423 assert( pOp[1].p3==aOp[pOp->p2-1].p3 );
90424
90425 assert( pOp->p1>0 );
@@ -91934,10 +91950,12 @@
91950 pTabCur->nullRow = 0;
91951 pTabCur->movetoTarget = rowid;
91952 pTabCur->deferredMoveto = 1;
91953 assert( pOp->p4type==P4_INTARRAY || pOp->p4.ai==0 );
91954 pTabCur->aAltMap = pOp->p4.ai;
91955 assert( !pC->isEphemeral );
91956 assert( !pTabCur->isEphemeral );
91957 pTabCur->pAltCursor = pC;
91958 }else{
91959 pOut = out2Prerelease(p, pOp);
91960 pOut->u.i = rowid;
91961 }
@@ -99165,10 +99183,11 @@
99183 */
99184 if( cnt==0 && zTab==0 ){
99185 assert( pExpr->op==TK_ID );
99186 if( ExprHasProperty(pExpr,EP_DblQuoted)
99187 && areDoubleQuotedStringsEnabled(db, pTopNC)
99188 && (db->init.bDropColumn==0 || sqlite3StrICmp(zCol, db->init.azInit[0])!=0)
99189 ){
99190 /* If a double-quoted identifier does not match any known column name,
99191 ** then treat it as a string.
99192 **
99193 ** This hack was added in the early days of SQLite in a misguided attempt
@@ -99179,10 +99198,15 @@
99198 ** programmers. To all those frustrated programmers, my apologies.
99199 **
99200 ** Someday, I hope to get rid of this hack. Unfortunately there is
99201 ** a huge amount of legacy SQL that uses it. So for now, we just
99202 ** issue a warning.
99203 **
99204 ** 2021-03-15: ticket 1c24a659e6d7f3a1
99205 ** Do not do the ID-to-STRING conversion when doing the schema
99206 ** sanity check following a DROP COLUMN if the identifer name matches
99207 ** the name of the column being dropped.
99208 */
99209 sqlite3_log(SQLITE_WARNING,
99210 "double-quoted string literal: \"%w\"", zCol);
99211 #ifdef SQLITE_ENABLE_NORMALIZE
99212 sqlite3VdbeAddDblquoteStr(db, pParse->pVdbe, zCol);
@@ -106778,31 +106802,32 @@
106802 */
106803 static void renameTestSchema(
106804 Parse *pParse, /* Parse context */
106805 const char *zDb, /* Name of db to verify schema of */
106806 int bTemp, /* True if this is the temp db */
106807 const char *zWhen, /* "when" part of error message */
106808 const char *zDropColumn /* Name of column being dropped */
106809 ){
106810 pParse->colNamesSet = 1;
106811 sqlite3NestedParse(pParse,
106812 "SELECT 1 "
106813 "FROM \"%w\"." DFLT_SCHEMA_TABLE " "
106814 "WHERE name NOT LIKE 'sqliteX_%%' ESCAPE 'X'"
106815 " AND sql NOT LIKE 'create virtual%%'"
106816 " AND sqlite_rename_test(%Q, sql, type, name, %d, %Q, %Q)=NULL ",
106817 zDb,
106818 zDb, bTemp, zWhen, zDropColumn
106819 );
106820
106821 if( bTemp==0 ){
106822 sqlite3NestedParse(pParse,
106823 "SELECT 1 "
106824 "FROM temp." DFLT_SCHEMA_TABLE " "
106825 "WHERE name NOT LIKE 'sqliteX_%%' ESCAPE 'X'"
106826 " AND sql NOT LIKE 'create virtual%%'"
106827 " AND sqlite_rename_test(%Q, sql, type, name, 1, %Q, %Q)=NULL ",
106828 zDb, zWhen, zDropColumn
106829 );
106830 }
106831 }
106832
106833 /*
@@ -106961,11 +106986,11 @@
106986 sqlite3NestedParse(pParse,
106987 "UPDATE sqlite_temp_schema SET "
106988 "sql = sqlite_rename_table(%Q, type, name, sql, %Q, %Q, 1), "
106989 "tbl_name = "
106990 "CASE WHEN tbl_name=%Q COLLATE nocase AND "
106991 " sqlite_rename_test(%Q, sql, type, name, 1, 'after rename',0) "
106992 "THEN %Q ELSE tbl_name END "
106993 "WHERE type IN ('view', 'trigger')"
106994 , zDb, zTabName, zName, zTabName, zDb, zName);
106995 }
106996
@@ -106981,11 +107006,11 @@
107006 sqlite3VdbeAddOp4(v, OP_VRename, i, 0, 0,(const char*)pVTab, P4_VTAB);
107007 }
107008 #endif
107009
107010 renameReloadSchema(pParse, iDb, INITFLAG_AlterRename);
107011 renameTestSchema(pParse, zDb, iDb==1, "after rename", 0);
107012
107013 exit_rename_table:
107014 sqlite3SrcListDelete(db, pSrc);
107015 sqlite3DbFree(db, zName);
107016 db->mDbFlags = savedDbFlags;
@@ -107349,11 +107374,11 @@
107374 zDb, pTab->zName, iCol, zNew, bQuote
107375 );
107376
107377 /* Drop and reload the database schema. */
107378 renameReloadSchema(pParse, iSchema, INITFLAG_AlterRename);
107379 renameTestSchema(pParse, zDb, iSchema==1, "after rename", 0);
107380
107381 exit_rename_column:
107382 sqlite3SrcListDelete(db, pSrc);
107383 sqlite3DbFree(db, zOld);
107384 sqlite3DbFree(db, zNew);
@@ -107773,16 +107798,21 @@
107798 static int renameParseSql(
107799 Parse *p, /* Memory to use for Parse object */
107800 const char *zDb, /* Name of schema SQL belongs to */
107801 sqlite3 *db, /* Database handle */
107802 const char *zSql, /* SQL to parse */
107803 int bTemp, /* True if SQL is from temp schema */
107804 const char *zDropColumn /* Name of column being dropped */
107805 ){
107806 int rc;
107807 char *zErr = 0;
107808
107809 db->init.iDb = bTemp ? 1 : sqlite3FindDbName(db, zDb);
107810 if( zDropColumn ){
107811 db->init.bDropColumn = 1;
107812 db->init.azInit = (char**)&zDropColumn;
107813 }
107814
107815 /* Parse the SQL statement passed as the first argument. If no error
107816 ** occurs and the parse does not result in a new table, index or
107817 ** trigger object, the database must be corrupt. */
107818 memset(p, 0, sizeof(Parse));
@@ -107811,10 +107841,11 @@
107841 }
107842 }
107843 #endif
107844
107845 db->init.iDb = 0;
107846 db->init.bDropColumn = 0;
107847 return rc;
107848 }
107849
107850 /*
107851 ** This function edits SQL statement zSql, replacing each token identified
@@ -108112,11 +108143,11 @@
108143 sCtx.iCol = ((iCol==pTab->iPKey) ? -1 : iCol);
108144
108145 #ifndef SQLITE_OMIT_AUTHORIZATION
108146 db->xAuth = 0;
108147 #endif
108148 rc = renameParseSql(&sParse, zDb, db, zSql, bTemp, 0);
108149
108150 /* Find tokens that need to be replaced. */
108151 memset(&sWalker, 0, sizeof(Walker));
108152 sWalker.pParse = &sParse;
108153 sWalker.xExprCallback = renameColumnExprCb;
@@ -108154,16 +108185,16 @@
108185 sqlite3WalkExprList(&sWalker, pIdx->aColExpr);
108186 }
108187 for(pIdx=sParse.pNewIndex; pIdx; pIdx=pIdx->pNext){
108188 sqlite3WalkExprList(&sWalker, pIdx->aColExpr);
108189 }
 
108190 #ifndef SQLITE_OMIT_GENERATED_COLUMNS
108191 for(i=0; i<sParse.pNewTable->nCol; i++){
108192 sqlite3WalkExpr(&sWalker, sParse.pNewTable->aCol[i].pDflt);
108193 }
108194 #endif
108195 }
 
108196
108197 for(pFKey=sParse.pNewTable->pFKey; pFKey; pFKey=pFKey->pNextFrom){
108198 for(i=0; i<pFKey->nCol; i++){
108199 if( bFKOnly==0 && pFKey->aCol[i].iFrom==iCol ){
108200 renameTokenFind(&sParse, &sCtx, (void*)&pFKey->aCol[i]);
@@ -108316,11 +108347,11 @@
108347 sWalker.pParse = &sParse;
108348 sWalker.xExprCallback = renameTableExprCb;
108349 sWalker.xSelectCallback = renameTableSelectCb;
108350 sWalker.u.pRename = &sCtx;
108351
108352 rc = renameParseSql(&sParse, zDb, db, zInput, bTemp, 0);
108353
108354 if( rc==SQLITE_OK ){
108355 int isLegacy = (db->flags & SQLITE_LegacyAlter);
108356 if( sParse.pNewTable ){
108357 Table *pTab = sParse.pNewTable;
@@ -108432,10 +108463,11 @@
108463 ** 1: SQL statement.
108464 ** 2: Object type ("view", "table", "trigger" or "index").
108465 ** 3: Object name.
108466 ** 4: True if object is from temp schema.
108467 ** 5: "when" part of error message.
108468 ** 6: Name of column being dropped, or NULL.
108469 **
108470 ** Unless it finds an error, this function normally returns NULL. However, it
108471 ** returns integer value 1 if:
108472 **
108473 ** * the SQL argument creates a trigger, and
@@ -108450,10 +108482,11 @@
108482 char const *zDb = (const char*)sqlite3_value_text(argv[0]);
108483 char const *zInput = (const char*)sqlite3_value_text(argv[1]);
108484 int bTemp = sqlite3_value_int(argv[4]);
108485 int isLegacy = (db->flags & SQLITE_LegacyAlter);
108486 char const *zWhen = (const char*)sqlite3_value_text(argv[5]);
108487 char const *zDropColumn = (const char*)sqlite3_value_text(argv[6]);
108488
108489 #ifndef SQLITE_OMIT_AUTHORIZATION
108490 sqlite3_xauth xAuth = db->xAuth;
108491 db->xAuth = 0;
108492 #endif
@@ -108460,11 +108493,11 @@
108493
108494 UNUSED_PARAMETER(NotUsed);
108495 if( zDb && zInput ){
108496 int rc;
108497 Parse sParse;
108498 rc = renameParseSql(&sParse, zDb, db, zInput, bTemp, zDropColumn);
108499 if( rc==SQLITE_OK ){
108500 if( isLegacy==0 && sParse.pNewTable && sParse.pNewTable->pSelect ){
108501 NameContext sNC;
108502 memset(&sNC, 0, sizeof(sNC));
108503 sNC.pParse = &sParse;
@@ -108528,11 +108561,11 @@
108561 sqlite3_xauth xAuth = db->xAuth;
108562 db->xAuth = 0;
108563 #endif
108564
108565 UNUSED_PARAMETER(NotUsed);
108566 rc = renameParseSql(&sParse, zDb, db, zSql, iSchema==1, 0);
108567 if( rc!=SQLITE_OK ) goto drop_column_done;
108568 pTab = sParse.pNewTable;
108569 if( pTab==0 || pTab->nCol==1 || iCol>=pTab->nCol ){
108570 /* This can happen if the sqlite_schema table is corrupt */
108571 rc = SQLITE_CORRUPT_BKPT;
@@ -108621,21 +108654,21 @@
108654
108655 /* Edit the sqlite_schema table */
108656 iDb = sqlite3SchemaToIndex(db, pTab->pSchema);
108657 assert( iDb>=0 );
108658 zDb = db->aDb[iDb].zDbSName;
108659 renameTestSchema(pParse, zDb, iDb==1, "", 0);
108660 sqlite3NestedParse(pParse,
108661 "UPDATE \"%w\"." DFLT_SCHEMA_TABLE " SET "
108662 "sql = sqlite_drop_column(%d, sql, %d) "
108663 "WHERE (type=='table' AND tbl_name=%Q COLLATE nocase)"
108664 , zDb, iDb, iCol, pTab->zName
108665 );
108666
108667 /* Drop and reload the database schema. */
108668 renameReloadSchema(pParse, iDb, INITFLAG_AlterDrop);
108669 renameTestSchema(pParse, zDb, iDb==1, "after drop column", zCol);
108670
108671 /* Edit rows of table on disk */
108672 if( pParse->nErr==0 && (pTab->aCol[iCol].colFlags & COLFLAG_VIRTUAL)==0 ){
108673 int i;
108674 int addr;
@@ -108691,11 +108724,11 @@
108724 */
108725 SQLITE_PRIVATE void sqlite3AlterFunctions(void){
108726 static FuncDef aAlterTableFuncs[] = {
108727 INTERNAL_FUNCTION(sqlite_rename_column, 9, renameColumnFunc),
108728 INTERNAL_FUNCTION(sqlite_rename_table, 7, renameTableFunc),
108729 INTERNAL_FUNCTION(sqlite_rename_test, 7, renameTableTest),
108730 INTERNAL_FUNCTION(sqlite_drop_column, 3, dropColumnFunc),
108731 };
108732 sqlite3InsertBuiltinFuncs(aAlterTableFuncs, ArraySize(aAlterTableFuncs));
108733 }
108734 #endif /* SQLITE_ALTER_TABLE */
@@ -115663,11 +115696,15 @@
115696 }
115697
115698 /* Clean up before exiting */
115699 exit_create_index:
115700 if( pIndex ) sqlite3FreeIndex(db, pIndex);
115701 if( pTab ){
115702 /* Ensure all REPLACE indexes on pTab are at the end of the pIndex list.
115703 ** The list was already ordered when this routine was entered, so at this
115704 ** point at most a single index (the newly added index) will be out of
115705 ** order. So we have to reorder at most one index. */
115706 Index **ppFrom = &pTab->pIndex;
115707 Index *pThis;
115708 for(ppFrom=&pTab->pIndex; (pThis = *ppFrom)!=0; ppFrom=&pThis->pNext){
115709 Index *pNext;
115710 if( pThis->onError!=OE_Replace ) continue;
@@ -115677,10 +115714,20 @@
115714 pNext->pNext = pThis;
115715 ppFrom = &pNext->pNext;
115716 }
115717 break;
115718 }
115719 #ifdef SQLITE_DEBUG
115720 /* Verify that all REPLACE indexes really are now at the end
115721 ** of the index list. In other words, no other index type ever
115722 ** comes after a REPLACE index on the list. */
115723 for(pThis = pTab->pIndex; pThis; pThis=pThis->pNext){
115724 assert( pThis->onError!=OE_Replace
115725 || pThis->pNext==0
115726 || pThis->pNext->onError==OE_Replace );
115727 }
115728 #endif
115729 }
115730 sqlite3ExprDelete(db, pPIWhere);
115731 sqlite3ExprListDelete(db, pList);
115732 sqlite3SrcListDelete(db, pTblName);
115733 sqlite3DbFree(db, zName);
@@ -123093,11 +123140,13 @@
123140 pNx->pUpsertSrc = pTabList;
123141 pNx->regData = regData;
123142 pNx->iDataCur = iDataCur;
123143 pNx->iIdxCur = iIdxCur;
123144 if( pNx->pUpsertTarget ){
123145 if( sqlite3UpsertAnalyzeTarget(pParse, pTabList, pNx) ){
123146 goto insert_cleanup;
123147 }
123148 }
123149 pNx = pNx->pNextUpsert;
123150 }while( pNx!=0 );
123151 }
123152 #endif
@@ -124515,11 +124564,11 @@
124564 int regData /* Data containing new record */
124565 ){
124566 Vdbe *v = pParse->pVdbe;
124567 int r = sqlite3GetTempReg(pParse);
124568 assert( !HasRowid(pTab) );
124569 assert( 0==(pParse->db->mDbFlags & DBFLAG_Vacuum) || CORRUPT_DB );
124570 sqlite3VdbeAddOp2(v, OP_Integer, 0, r);
124571 sqlite3VdbeAddOp4(v, OP_Insert, iCur, regData, r, (char*)pTab, P4_TABLE);
124572 sqlite3VdbeChangeP5(v, OPFLAG_ISNOOP);
124573 sqlite3ReleaseTempReg(pParse, r);
124574 }
@@ -133063,11 +133112,11 @@
133112 sqlite3HashInit(&ht);
133113 if( pEList ){
133114 nCol = pEList->nExpr;
133115 aCol = sqlite3DbMallocZero(db, sizeof(aCol[0])*nCol);
133116 testcase( aCol==0 );
133117 if( NEVER(nCol>32767) ) nCol = 32767;
133118 }else{
133119 nCol = 0;
133120 aCol = 0;
133121 }
133122 assert( nCol==(i16)nCol );
@@ -135116,10 +135165,11 @@
135165
135166 /* Restriction (23) */
135167 if( (p->selFlags & SF_Recursive) ) return 0;
135168
135169 if( pSrc->nSrc>1 ){
135170 if( pParse->nSelect>500 ) return 0;
135171 aCsrMap = sqlite3DbMallocZero(db, pParse->nTab*sizeof(int));
135172 }
135173 }
135174
135175 /***** If we reach this point, flattening is permitted. *****/
@@ -135192,10 +135242,11 @@
135242 p->op = TK_ALL;
135243 pSubitem->pTab = pItemTab;
135244 if( pNew==0 ){
135245 p->pPrior = pPrior;
135246 }else{
135247 pNew->selId = ++pParse->nSelect;
135248 if( aCsrMap && db->mallocFailed==0 ){
135249 renumberCursors(pParse, pNew, iFrom, aCsrMap);
135250 }
135251 pNew->pPrior = pPrior;
135252 if( pPrior ) pPrior->pNext = pNew;
@@ -136135,20 +136186,28 @@
136186
136187 pCte->zCteErr = "circular reference: %s";
136188 pSavedWith = pParse->pWith;
136189 pParse->pWith = pWith;
136190 if( pSel->selFlags & SF_Recursive ){
136191 int rc;
136192 assert( pRecTerm!=0 );
136193 assert( (pRecTerm->selFlags & SF_Recursive)==0 );
136194 assert( pRecTerm->pNext!=0 );
136195 assert( (pRecTerm->pNext->selFlags & SF_Recursive)!=0 );
136196 assert( pRecTerm->pWith==0 );
136197 pRecTerm->pWith = pSel->pWith;
136198 rc = sqlite3WalkSelect(pWalker, pRecTerm);
136199 pRecTerm->pWith = 0;
136200 if( rc ){
136201 pParse->pWith = pSavedWith;
136202 return 2;
136203 }
136204 }else{
136205 if( sqlite3WalkSelect(pWalker, pSel) ){
136206 pParse->pWith = pSavedWith;
136207 return 2;
136208 }
136209 }
136210 pParse->pWith = pWith;
136211
136212 for(pLeft=pSel; pLeft->pPrior; pLeft=pLeft->pPrior);
136213 pEList = pLeft->pEList;
@@ -137453,11 +137512,13 @@
137512 sqlite3VdbeAddOp2(v, OP_Gosub, pPrior->regReturn, pPrior->addrFillSub);
137513 }
137514 sqlite3VdbeAddOp2(v, OP_OpenDup, pItem->iCursor, pPrior->iCursor);
137515 pSub->nSelectRow = pPrior->pSelect->nSelectRow;
137516 }else{
137517 /* Materalize the view. If the view is not correlated, generate a
137518 ** subroutine to do the materialization so that subsequent uses of
137519 ** the same view can reuse the materialization. */
137520 int topAddr;
137521 int onceAddr = 0;
137522 int retAddr;
137523
137524 testcase( pItem->addrFillSub==0 ); /* Ticket c52b09c7f38903b1311 */
@@ -137480,11 +137541,11 @@
137541 if( onceAddr ) sqlite3VdbeJumpHere(v, onceAddr);
137542 retAddr = sqlite3VdbeAddOp1(v, OP_Return, pItem->regReturn);
137543 VdbeComment((v, "end %s", pItem->pTab->zName));
137544 sqlite3VdbeChangeP1(v, topAddr, retAddr);
137545 sqlite3ClearTempRegCache(pParse);
137546 if( pItem->fg.isCte && pItem->fg.isCorrelated==0 ){
137547 CteUse *pCteUse = pItem->u2.pCteUse;
137548 pCteUse->addrM9e = pItem->addrFillSub;
137549 pCteUse->regRtn = pItem->regReturn;
137550 pCteUse->iCur = pItem->iCursor;
137551 pCteUse->nRowEst = pSub->nSelectRow;
@@ -162285,11 +162346,11 @@
162346 /* Fx */ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2
162347 #endif
162348 #ifdef SQLITE_EBCDIC
162349 /* x0 x1 x2 x3 x4 x5 x6 x7 x8 x9 xa xb xc xd xe xf */
162350 /* 0x */ 29, 28, 28, 28, 28, 7, 28, 28, 28, 28, 28, 28, 7, 7, 28, 28,
162351 /* 1x */ 28, 28, 28, 28, 28, 7, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28,
162352 /* 2x */ 28, 28, 28, 28, 28, 7, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28,
162353 /* 3x */ 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28,
162354 /* 4x */ 7, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 26, 12, 17, 20, 10,
162355 /* 5x */ 24, 28, 28, 28, 28, 28, 28, 28, 28, 28, 15, 4, 21, 18, 19, 28,
162356 /* 6x */ 11, 16, 28, 28, 28, 28, 28, 28, 28, 28, 28, 23, 22, 2, 13, 6,
@@ -229190,11 +229251,11 @@
229251 int nArg, /* Number of args */
229252 sqlite3_value **apUnused /* Function arguments */
229253 ){
229254 assert( nArg==0 );
229255 UNUSED_PARAM2(nArg, apUnused);
229256 sqlite3_result_text(pCtx, "fts5: 2021-03-21 18:23:48 6bb2134027a12801de8e0c73482d94682f902024800a7e426614f65a2fe4f64c", -1, SQLITE_TRANSIENT);
229257 }
229258
229259 /*
229260 ** Return true if zName is the extension on one of the shadow tables used
229261 ** by this module.
@@ -234116,12 +234177,12 @@
234177 }
234178 #endif /* SQLITE_CORE */
234179 #endif /* !defined(SQLITE_CORE) || defined(SQLITE_ENABLE_STMTVTAB) */
234180
234181 /************** End of stmt.c ************************************************/
234182 #if __LINE__!=234182
234183 #undef SQLITE_SOURCE_ID
234184 #define SQLITE_SOURCE_ID "2021-03-21 18:23:48 6bb2134027a12801de8e0c73482d94682f902024800a7e426614f65a2fe4alt2"
234185 #endif
234186 /* Return the source-id for this library */
234187 SQLITE_API const char *sqlite3_sourceid(void){ return SQLITE_SOURCE_ID; }
234188 /************************** End of sqlite3.c ******************************/
234189
+3 -3
--- src/sqlite3.h
+++ src/sqlite3.h
@@ -121,13 +121,13 @@
121121
**
122122
** See also: [sqlite3_libversion()],
123123
** [sqlite3_libversion_number()], [sqlite3_sourceid()],
124124
** [sqlite_version()] and [sqlite_source_id()].
125125
*/
126
-#define SQLITE_VERSION "3.35.0"
127
-#define SQLITE_VERSION_NUMBER 3035000
128
-#define SQLITE_SOURCE_ID "2021-03-12 15:10:09 acd63062eb06748bfe9e4886639e4f2b54ea6a496a83f10716abbaba4115500b"
126
+#define SQLITE_VERSION "3.35.3"
127
+#define SQLITE_VERSION_NUMBER 3035003
128
+#define SQLITE_SOURCE_ID "2021-03-21 18:23:48 6bb2134027a12801de8e0c73482d94682f902024800a7e426614f65a2fe4f64c"
129129
130130
/*
131131
** CAPI3REF: Run-Time Library Version Numbers
132132
** KEYWORDS: sqlite3_version sqlite3_sourceid
133133
**
134134
--- src/sqlite3.h
+++ src/sqlite3.h
@@ -121,13 +121,13 @@
121 **
122 ** See also: [sqlite3_libversion()],
123 ** [sqlite3_libversion_number()], [sqlite3_sourceid()],
124 ** [sqlite_version()] and [sqlite_source_id()].
125 */
126 #define SQLITE_VERSION "3.35.0"
127 #define SQLITE_VERSION_NUMBER 3035000
128 #define SQLITE_SOURCE_ID "2021-03-12 15:10:09 acd63062eb06748bfe9e4886639e4f2b54ea6a496a83f10716abbaba4115500b"
129
130 /*
131 ** CAPI3REF: Run-Time Library Version Numbers
132 ** KEYWORDS: sqlite3_version sqlite3_sourceid
133 **
134
--- src/sqlite3.h
+++ src/sqlite3.h
@@ -121,13 +121,13 @@
121 **
122 ** See also: [sqlite3_libversion()],
123 ** [sqlite3_libversion_number()], [sqlite3_sourceid()],
124 ** [sqlite_version()] and [sqlite_source_id()].
125 */
126 #define SQLITE_VERSION "3.35.3"
127 #define SQLITE_VERSION_NUMBER 3035003
128 #define SQLITE_SOURCE_ID "2021-03-21 18:23:48 6bb2134027a12801de8e0c73482d94682f902024800a7e426614f65a2fe4f64c"
129
130 /*
131 ** CAPI3REF: Run-Time Library Version Numbers
132 ** KEYWORDS: sqlite3_version sqlite3_sourceid
133 **
134
+47 -3
--- src/th.c
+++ src/th.c
@@ -222,11 +222,20 @@
222222
Buffer *pBuffer,
223223
const char *zAdd,
224224
int nAdd
225225
){
226226
int nNew = (pBuffer->nBuf+nAdd)*2+32;
227
+#if defined(TH_MEMDEBUG)
228
+ char *zNew = (char *)Th_Malloc(interp, nNew);
229
+ th_memcpy(zNew, pBuffer->zBuf, pBuffer->nBuf);
230
+ Th_Free(interp, pBuffer->zBuf);
231
+ pBuffer->zBuf = zNew;
232
+#else
233
+ int nOld = pBuffer->nBufAlloc;
227234
pBuffer->zBuf = Th_Realloc(interp, pBuffer->zBuf, nNew);
235
+ memset(pBuffer->zBuf+nOld, 0, nNew-nOld);
236
+#endif
228237
pBuffer->nBufAlloc = nNew;
229238
th_memcpy(&pBuffer->zBuf[pBuffer->nBuf], zAdd, nAdd);
230239
pBuffer->nBuf += nAdd;
231240
}
232241
static void thBufferWriteFast(
@@ -1535,10 +1544,37 @@
15351544
}else{
15361545
return (char *)Th_Malloc(pInterp, 1);
15371546
}
15381547
}
15391548
1549
+#if defined(TH_MEMDEBUG)
1550
+/*
1551
+** Wrappers around the supplied malloc() and free()
1552
+*/
1553
+void *Th_DbgMalloc(Th_Interp *pInterp, int nByte){
1554
+ void *p;
1555
+ Th_Vtab *pVtab = pInterp->pVtab;
1556
+ if( pVtab ){
1557
+ p = pVtab->xMalloc(nByte);
1558
+ if( p ) memset(p, 0, nByte);
1559
+ }else{
1560
+ p = Th_SysMalloc(pInterp, nByte);
1561
+ }
1562
+ return p;
1563
+}
1564
+void Th_DbgFree(Th_Interp *pInterp, void *z){
1565
+ if( z ){
1566
+ Th_Vtab *pVtab = pInterp->pVtab;
1567
+ if( pVtab ){
1568
+ pVtab->xFree(z);
1569
+ }else{
1570
+ Th_SysFree(pInterp, z);
1571
+ }
1572
+ }
1573
+}
1574
+#endif
1575
+
15401576
/*
15411577
** Install a new th1 command.
15421578
**
15431579
** If a command of the same name already exists, it is deleted automatically.
15441580
*/
@@ -1821,16 +1857,24 @@
18211857
}
18221858
18231859
/*
18241860
** Create a new interpreter.
18251861
*/
1826
-Th_Interp * Th_CreateInterp(void){
1862
+Th_Interp * Th_CreateInterp(Th_Vtab *pVtab){
1863
+ int nByte = sizeof(Th_Interp) + sizeof(Th_Frame);
18271864
Th_Interp *p;
18281865
18291866
/* Allocate and initialise the interpreter and the global frame */
1830
- p = Th_Malloc(0, sizeof(Th_Interp) + sizeof(Th_Frame));
1831
- memset(p, 0, sizeof(Th_Interp));
1867
+#if defined(TH_MEMDEBUG)
1868
+ if( pVtab ){
1869
+ p = pVtab->xMalloc(nByte);
1870
+ memset(p, 0, nByte);
1871
+ p->pVtab = pVtab;
1872
+ }else
1873
+#endif
1874
+ p = Th_SysMalloc(0, nByte);
1875
+
18321876
p->paCmd = Th_HashNew(p);
18331877
thPushFrame(p, (Th_Frame *)&p[1]);
18341878
thInitialize(p);
18351879
18361880
return p;
18371881
--- src/th.c
+++ src/th.c
@@ -222,11 +222,20 @@
222 Buffer *pBuffer,
223 const char *zAdd,
224 int nAdd
225 ){
226 int nNew = (pBuffer->nBuf+nAdd)*2+32;
 
 
 
 
 
 
 
227 pBuffer->zBuf = Th_Realloc(interp, pBuffer->zBuf, nNew);
 
 
228 pBuffer->nBufAlloc = nNew;
229 th_memcpy(&pBuffer->zBuf[pBuffer->nBuf], zAdd, nAdd);
230 pBuffer->nBuf += nAdd;
231 }
232 static void thBufferWriteFast(
@@ -1535,10 +1544,37 @@
1535 }else{
1536 return (char *)Th_Malloc(pInterp, 1);
1537 }
1538 }
1539
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1540 /*
1541 ** Install a new th1 command.
1542 **
1543 ** If a command of the same name already exists, it is deleted automatically.
1544 */
@@ -1821,16 +1857,24 @@
1821 }
1822
1823 /*
1824 ** Create a new interpreter.
1825 */
1826 Th_Interp * Th_CreateInterp(void){
 
1827 Th_Interp *p;
1828
1829 /* Allocate and initialise the interpreter and the global frame */
1830 p = Th_Malloc(0, sizeof(Th_Interp) + sizeof(Th_Frame));
1831 memset(p, 0, sizeof(Th_Interp));
 
 
 
 
 
 
 
1832 p->paCmd = Th_HashNew(p);
1833 thPushFrame(p, (Th_Frame *)&p[1]);
1834 thInitialize(p);
1835
1836 return p;
1837
--- src/th.c
+++ src/th.c
@@ -222,11 +222,20 @@
222 Buffer *pBuffer,
223 const char *zAdd,
224 int nAdd
225 ){
226 int nNew = (pBuffer->nBuf+nAdd)*2+32;
227 #if defined(TH_MEMDEBUG)
228 char *zNew = (char *)Th_Malloc(interp, nNew);
229 th_memcpy(zNew, pBuffer->zBuf, pBuffer->nBuf);
230 Th_Free(interp, pBuffer->zBuf);
231 pBuffer->zBuf = zNew;
232 #else
233 int nOld = pBuffer->nBufAlloc;
234 pBuffer->zBuf = Th_Realloc(interp, pBuffer->zBuf, nNew);
235 memset(pBuffer->zBuf+nOld, 0, nNew-nOld);
236 #endif
237 pBuffer->nBufAlloc = nNew;
238 th_memcpy(&pBuffer->zBuf[pBuffer->nBuf], zAdd, nAdd);
239 pBuffer->nBuf += nAdd;
240 }
241 static void thBufferWriteFast(
@@ -1535,10 +1544,37 @@
1544 }else{
1545 return (char *)Th_Malloc(pInterp, 1);
1546 }
1547 }
1548
1549 #if defined(TH_MEMDEBUG)
1550 /*
1551 ** Wrappers around the supplied malloc() and free()
1552 */
1553 void *Th_DbgMalloc(Th_Interp *pInterp, int nByte){
1554 void *p;
1555 Th_Vtab *pVtab = pInterp->pVtab;
1556 if( pVtab ){
1557 p = pVtab->xMalloc(nByte);
1558 if( p ) memset(p, 0, nByte);
1559 }else{
1560 p = Th_SysMalloc(pInterp, nByte);
1561 }
1562 return p;
1563 }
1564 void Th_DbgFree(Th_Interp *pInterp, void *z){
1565 if( z ){
1566 Th_Vtab *pVtab = pInterp->pVtab;
1567 if( pVtab ){
1568 pVtab->xFree(z);
1569 }else{
1570 Th_SysFree(pInterp, z);
1571 }
1572 }
1573 }
1574 #endif
1575
1576 /*
1577 ** Install a new th1 command.
1578 **
1579 ** If a command of the same name already exists, it is deleted automatically.
1580 */
@@ -1821,16 +1857,24 @@
1857 }
1858
1859 /*
1860 ** Create a new interpreter.
1861 */
1862 Th_Interp * Th_CreateInterp(Th_Vtab *pVtab){
1863 int nByte = sizeof(Th_Interp) + sizeof(Th_Frame);
1864 Th_Interp *p;
1865
1866 /* Allocate and initialise the interpreter and the global frame */
1867 #if defined(TH_MEMDEBUG)
1868 if( pVtab ){
1869 p = pVtab->xMalloc(nByte);
1870 memset(p, 0, nByte);
1871 p->pVtab = pVtab;
1872 }else
1873 #endif
1874 p = Th_SysMalloc(0, nByte);
1875
1876 p->paCmd = Th_HashNew(p);
1877 thPushFrame(p, (Th_Frame *)&p[1]);
1878 thInitialize(p);
1879
1880 return p;
1881
+21 -6
--- src/th.h
+++ src/th.h
@@ -21,11 +21,11 @@
2121
typedef struct Th_Interp Th_Interp;
2222
2323
/*
2424
** Create and delete interpreters.
2525
*/
26
-Th_Interp * Th_CreateInterp(void);
26
+Th_Interp * Th_CreateInterp(Th_Vtab *);
2727
void Th_DeleteInterp(Th_Interp *);
2828
2929
/*
3030
** Evaluate an TH program in the stack frame identified by parameter
3131
** iFrame, according to the following rules:
@@ -121,16 +121,31 @@
121121
122122
/*
123123
** Access the memory management functions associated with the specified
124124
** interpreter.
125125
*/
126
+#if defined(TH_MEMDEBUG)
127
+void *Th_DbgMalloc(Th_Interp *, int);
128
+void Th_DbgFree(Th_Interp *, void *);
129
+#endif
130
+
126131
void *fossil_malloc_zero(size_t);
127
-void *fossil_realloc(void*,size_t);
128
-void fossil_free(void*);
129
-#define Th_Malloc(I,N) fossil_malloc_zero(N)
130
-#define Th_Realloc(I,P,N) fossil_realloc(P,N)
131
-#define Th_Free(I,P) fossil_free(P)
132
+void *fossil_realloc(void *, size_t);
133
+void fossil_free(void *);
134
+
135
+#define Th_SysMalloc(I,N) fossil_malloc_zero((N))
136
+#define Th_SysRealloc(I,P,N) fossil_realloc((P),(N))
137
+#define Th_SysFree(I,P) fossil_free((P))
138
+
139
+#if defined(TH_MEMDEBUG)
140
+# define Th_Malloc(I,N) Th_DbgMalloc((I),(N))
141
+# define Th_Free(I,P) Th_DbgFree((I),(P))
142
+#else
143
+# define Th_Malloc(I,N) Th_SysMalloc((I),(N))
144
+# define Th_Realloc(I,P,N) Th_SysRealloc((I),(P),(N))
145
+# define Th_Free(I,P) Th_SysFree((I),(P))
146
+#endif
132147
133148
/*
134149
** Functions for handling TH lists.
135150
*/
136151
int Th_ListAppend(Th_Interp *, char **, int *, const char *, int);
137152
--- src/th.h
+++ src/th.h
@@ -21,11 +21,11 @@
21 typedef struct Th_Interp Th_Interp;
22
23 /*
24 ** Create and delete interpreters.
25 */
26 Th_Interp * Th_CreateInterp(void);
27 void Th_DeleteInterp(Th_Interp *);
28
29 /*
30 ** Evaluate an TH program in the stack frame identified by parameter
31 ** iFrame, according to the following rules:
@@ -121,16 +121,31 @@
121
122 /*
123 ** Access the memory management functions associated with the specified
124 ** interpreter.
125 */
 
 
 
 
 
126 void *fossil_malloc_zero(size_t);
127 void *fossil_realloc(void*,size_t);
128 void fossil_free(void*);
129 #define Th_Malloc(I,N) fossil_malloc_zero(N)
130 #define Th_Realloc(I,P,N) fossil_realloc(P,N)
131 #define Th_Free(I,P) fossil_free(P)
 
 
 
 
 
 
 
 
 
 
132
133 /*
134 ** Functions for handling TH lists.
135 */
136 int Th_ListAppend(Th_Interp *, char **, int *, const char *, int);
137
--- src/th.h
+++ src/th.h
@@ -21,11 +21,11 @@
21 typedef struct Th_Interp Th_Interp;
22
23 /*
24 ** Create and delete interpreters.
25 */
26 Th_Interp * Th_CreateInterp(Th_Vtab *);
27 void Th_DeleteInterp(Th_Interp *);
28
29 /*
30 ** Evaluate an TH program in the stack frame identified by parameter
31 ** iFrame, according to the following rules:
@@ -121,16 +121,31 @@
121
122 /*
123 ** Access the memory management functions associated with the specified
124 ** interpreter.
125 */
126 #if defined(TH_MEMDEBUG)
127 void *Th_DbgMalloc(Th_Interp *, int);
128 void Th_DbgFree(Th_Interp *, void *);
129 #endif
130
131 void *fossil_malloc_zero(size_t);
132 void *fossil_realloc(void *, size_t);
133 void fossil_free(void *);
134
135 #define Th_SysMalloc(I,N) fossil_malloc_zero((N))
136 #define Th_SysRealloc(I,P,N) fossil_realloc((P),(N))
137 #define Th_SysFree(I,P) fossil_free((P))
138
139 #if defined(TH_MEMDEBUG)
140 # define Th_Malloc(I,N) Th_DbgMalloc((I),(N))
141 # define Th_Free(I,P) Th_DbgFree((I),(P))
142 #else
143 # define Th_Malloc(I,N) Th_SysMalloc((I),(N))
144 # define Th_Realloc(I,P,N) Th_SysRealloc((I),(P),(N))
145 # define Th_Free(I,P) Th_SysFree((I),(P))
146 #endif
147
148 /*
149 ** Functions for handling TH lists.
150 */
151 int Th_ListAppend(Th_Interp *, char **, int *, const char *, int);
152
+50 -4
--- src/th_main.c
+++ src/th_main.c
@@ -68,10 +68,46 @@
6868
** configuration ("user") database are currently open.
6969
*/
7070
#define Th_IsRepositoryOpen() (g.repositoryOpen)
7171
#define Th_IsConfigOpen() (g.zConfigDbName!=0)
7272
73
+/*
74
+** When memory debugging is enabled, use our custom memory allocator.
75
+*/
76
+#if defined(TH_MEMDEBUG)
77
+/*
78
+** Global variable counting the number of outstanding calls to malloc()
79
+** made by the th1 implementation. This is used to catch memory leaks
80
+** in the interpreter. Obviously, it also means th1 is not threadsafe.
81
+*/
82
+static int nOutstandingMalloc = 0;
83
+
84
+/*
85
+** Implementations of malloc() and free() to pass to the interpreter.
86
+*/
87
+static void *xMalloc(unsigned int n){
88
+ void *p = fossil_malloc(n);
89
+ if( p ){
90
+ nOutstandingMalloc++;
91
+ }
92
+ return p;
93
+}
94
+static void xFree(void *p){
95
+ if( p ){
96
+ nOutstandingMalloc--;
97
+ }
98
+ free(p);
99
+}
100
+static Th_Vtab vtab = { xMalloc, xFree };
101
+
102
+/*
103
+** Returns the number of outstanding TH1 memory allocations.
104
+*/
105
+int Th_GetOutstandingMalloc(){
106
+ return nOutstandingMalloc;
107
+}
108
+#endif
73109
74110
/*
75111
** Generate a TH1 trace message if debugging is enabled.
76112
*/
77113
void Th_Trace(const char *zFormat, ...){
@@ -350,11 +386,11 @@
350386
if(0==pOut && pThOut!=0){
351387
pOut = pThOut;
352388
}
353389
if(TH_INIT_NO_ENCODE & g.th1Flags){
354390
encode = 0;
355
- }
391
+ }
356392
if( enableOutput && n ){
357393
if( n<0 ) n = strlen(z);
358394
if( encode ){
359395
z = htmlize(z, n);
360396
n = strlen(z);
@@ -627,10 +663,11 @@
627663
blob_zero(&title); blob_zero(&body);
628664
markdown_to_html(&src, &title, &body);
629665
Th_ListAppend(interp, &zValue, &nValue, blob_str(&title), blob_size(&title));
630666
Th_ListAppend(interp, &zValue, &nValue, blob_str(&body), blob_size(&body));
631667
Th_SetResult(interp, zValue, nValue);
668
+ Th_Free(interp, zValue);
632669
return TH_OK;
633670
}
634671
635672
/*
636673
** TH1 command: decorate STRING
@@ -810,15 +847,15 @@
810847
}else if( azCap[i][0]=='*' ){
811848
rc = 1;
812849
}else{
813850
rc = login_has_capability(azCap[i], anCap[i], 0);
814851
}
815
- break;
852
+ break;
816853
}
817854
Th_Free(interp, azCap);
818855
Th_SetResultInt(interp, rc);
819
- return TH_OK;
856
+ return TH_OK;
820857
}
821858
822859
823860
/*
824861
** TH1 command: searchable STRING...
@@ -2356,11 +2393,20 @@
23562393
}
23572394
if( forceReset || forceTcl || g.interp==0 ){
23582395
int created = 0;
23592396
int i;
23602397
if( g.interp==0 ){
2361
- g.interp = Th_CreateInterp();
2398
+ Th_Vtab *pVtab = 0;
2399
+#if defined(TH_MEMDEBUG)
2400
+ if( fossil_getenv("TH1_DELETE_INTERP")!=0 ){
2401
+ pVtab = &vtab;
2402
+ if( g.thTrace ){
2403
+ Th_Trace("th1-init MEMDEBUG ENABLED<br />\n");
2404
+ }
2405
+ }
2406
+#endif
2407
+ g.interp = Th_CreateInterp(pVtab);
23622408
created = 1;
23632409
}
23642410
if( forceReset || created ){
23652411
th_register_language(g.interp); /* Basic scripting commands. */
23662412
}
23672413
--- src/th_main.c
+++ src/th_main.c
@@ -68,10 +68,46 @@
68 ** configuration ("user") database are currently open.
69 */
70 #define Th_IsRepositoryOpen() (g.repositoryOpen)
71 #define Th_IsConfigOpen() (g.zConfigDbName!=0)
72
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
73
74 /*
75 ** Generate a TH1 trace message if debugging is enabled.
76 */
77 void Th_Trace(const char *zFormat, ...){
@@ -350,11 +386,11 @@
350 if(0==pOut && pThOut!=0){
351 pOut = pThOut;
352 }
353 if(TH_INIT_NO_ENCODE & g.th1Flags){
354 encode = 0;
355 }
356 if( enableOutput && n ){
357 if( n<0 ) n = strlen(z);
358 if( encode ){
359 z = htmlize(z, n);
360 n = strlen(z);
@@ -627,10 +663,11 @@
627 blob_zero(&title); blob_zero(&body);
628 markdown_to_html(&src, &title, &body);
629 Th_ListAppend(interp, &zValue, &nValue, blob_str(&title), blob_size(&title));
630 Th_ListAppend(interp, &zValue, &nValue, blob_str(&body), blob_size(&body));
631 Th_SetResult(interp, zValue, nValue);
 
632 return TH_OK;
633 }
634
635 /*
636 ** TH1 command: decorate STRING
@@ -810,15 +847,15 @@
810 }else if( azCap[i][0]=='*' ){
811 rc = 1;
812 }else{
813 rc = login_has_capability(azCap[i], anCap[i], 0);
814 }
815 break;
816 }
817 Th_Free(interp, azCap);
818 Th_SetResultInt(interp, rc);
819 return TH_OK;
820 }
821
822
823 /*
824 ** TH1 command: searchable STRING...
@@ -2356,11 +2393,20 @@
2356 }
2357 if( forceReset || forceTcl || g.interp==0 ){
2358 int created = 0;
2359 int i;
2360 if( g.interp==0 ){
2361 g.interp = Th_CreateInterp();
 
 
 
 
 
 
 
 
 
2362 created = 1;
2363 }
2364 if( forceReset || created ){
2365 th_register_language(g.interp); /* Basic scripting commands. */
2366 }
2367
--- src/th_main.c
+++ src/th_main.c
@@ -68,10 +68,46 @@
68 ** configuration ("user") database are currently open.
69 */
70 #define Th_IsRepositoryOpen() (g.repositoryOpen)
71 #define Th_IsConfigOpen() (g.zConfigDbName!=0)
72
73 /*
74 ** When memory debugging is enabled, use our custom memory allocator.
75 */
76 #if defined(TH_MEMDEBUG)
77 /*
78 ** Global variable counting the number of outstanding calls to malloc()
79 ** made by the th1 implementation. This is used to catch memory leaks
80 ** in the interpreter. Obviously, it also means th1 is not threadsafe.
81 */
82 static int nOutstandingMalloc = 0;
83
84 /*
85 ** Implementations of malloc() and free() to pass to the interpreter.
86 */
87 static void *xMalloc(unsigned int n){
88 void *p = fossil_malloc(n);
89 if( p ){
90 nOutstandingMalloc++;
91 }
92 return p;
93 }
94 static void xFree(void *p){
95 if( p ){
96 nOutstandingMalloc--;
97 }
98 free(p);
99 }
100 static Th_Vtab vtab = { xMalloc, xFree };
101
102 /*
103 ** Returns the number of outstanding TH1 memory allocations.
104 */
105 int Th_GetOutstandingMalloc(){
106 return nOutstandingMalloc;
107 }
108 #endif
109
110 /*
111 ** Generate a TH1 trace message if debugging is enabled.
112 */
113 void Th_Trace(const char *zFormat, ...){
@@ -350,11 +386,11 @@
386 if(0==pOut && pThOut!=0){
387 pOut = pThOut;
388 }
389 if(TH_INIT_NO_ENCODE & g.th1Flags){
390 encode = 0;
391 }
392 if( enableOutput && n ){
393 if( n<0 ) n = strlen(z);
394 if( encode ){
395 z = htmlize(z, n);
396 n = strlen(z);
@@ -627,10 +663,11 @@
663 blob_zero(&title); blob_zero(&body);
664 markdown_to_html(&src, &title, &body);
665 Th_ListAppend(interp, &zValue, &nValue, blob_str(&title), blob_size(&title));
666 Th_ListAppend(interp, &zValue, &nValue, blob_str(&body), blob_size(&body));
667 Th_SetResult(interp, zValue, nValue);
668 Th_Free(interp, zValue);
669 return TH_OK;
670 }
671
672 /*
673 ** TH1 command: decorate STRING
@@ -810,15 +847,15 @@
847 }else if( azCap[i][0]=='*' ){
848 rc = 1;
849 }else{
850 rc = login_has_capability(azCap[i], anCap[i], 0);
851 }
852 break;
853 }
854 Th_Free(interp, azCap);
855 Th_SetResultInt(interp, rc);
856 return TH_OK;
857 }
858
859
860 /*
861 ** TH1 command: searchable STRING...
@@ -2356,11 +2393,20 @@
2393 }
2394 if( forceReset || forceTcl || g.interp==0 ){
2395 int created = 0;
2396 int i;
2397 if( g.interp==0 ){
2398 Th_Vtab *pVtab = 0;
2399 #if defined(TH_MEMDEBUG)
2400 if( fossil_getenv("TH1_DELETE_INTERP")!=0 ){
2401 pVtab = &vtab;
2402 if( g.thTrace ){
2403 Th_Trace("th1-init MEMDEBUG ENABLED<br />\n");
2404 }
2405 }
2406 #endif
2407 g.interp = Th_CreateInterp(pVtab);
2408 created = 1;
2409 }
2410 if( forceReset || created ){
2411 th_register_language(g.interp); /* Basic scripting commands. */
2412 }
2413
+1 -1
--- src/user.c
+++ src/user.c
@@ -38,11 +38,11 @@
3838
if( z[i]>0 && z[i]<' ' ) z[i] = ' ';
3939
}
4040
blob_append(pBlob, z, -1);
4141
}
4242
43
-#if defined(_WIN32) || defined(__BIONIC__) && !defined(FOSSIL_HAVE_GETPASS)
43
+#if defined(_WIN32) || (defined(__BIONIC__) && !defined(FOSSIL_HAVE_GETPASS))
4444
#ifdef _WIN32
4545
#include <conio.h>
4646
#endif
4747
4848
/*
4949
5050
DELETED src/webmail.c
--- src/user.c
+++ src/user.c
@@ -38,11 +38,11 @@
38 if( z[i]>0 && z[i]<' ' ) z[i] = ' ';
39 }
40 blob_append(pBlob, z, -1);
41 }
42
43 #if defined(_WIN32) || defined(__BIONIC__) && !defined(FOSSIL_HAVE_GETPASS)
44 #ifdef _WIN32
45 #include <conio.h>
46 #endif
47
48 /*
49
50 ELETED src/webmail.c
--- src/user.c
+++ src/user.c
@@ -38,11 +38,11 @@
38 if( z[i]>0 && z[i]<' ' ) z[i] = ' ';
39 }
40 blob_append(pBlob, z, -1);
41 }
42
43 #if defined(_WIN32) || (defined(__BIONIC__) && !defined(FOSSIL_HAVE_GETPASS))
44 #ifdef _WIN32
45 #include <conio.h>
46 #endif
47
48 /*
49
50 ELETED src/webmail.c
D src/webmail.c
-921
--- a/src/webmail.c
+++ b/src/webmail.c
@@ -1,921 +0,0 @@
1
-/*
2
-** Copyright (c) 2018 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
-** Implementation of web pages for managing the email storage tables
19
-** (if they exist):
20
-**
21
-** emailbox
22
-** emailblob
23
-** emailroute
24
-*/
25
-#include "config.h"
26
-#include "webmail.h"
27
-#include <assert.h>
28
-
29
-
30
-#if INTERFACE
31
-
32
-/* Recognized content encodings */
33
-#define EMAILENC_NONE 0 /* No encoding */
34
-#define EMAILENC_B64 1 /* Base64 encoded */
35
-#define EMAILENC_QUOTED 2 /* Quoted printable */
36
-
37
-/* An instance of the following object records the location of important
38
-** attributes on a single element in a multipart email message body.
39
-*/
40
-struct EmailBody {
41
- char zMimetype[32]; /* Mimetype */
42
- u8 encoding; /* Type of encoding */
43
- char *zFilename; /* From content-disposition: */
44
- char *zContent; /* Content. \0 terminator inserted */
45
-};
46
-
47
-/*
48
-** An instance of the following object describes the struture of
49
-** an rfc-2822 email message.
50
-*/
51
-struct EmailToc {
52
- int nHdr; /* Number of header lines */
53
- int nHdrAlloc; /* Number of header lines allocated */
54
- char **azHdr; /* Pointer to header line. \0 terminator inserted */
55
- int nBody; /* Number of body segments */
56
- int nBodyAlloc; /* Number of body segments allocated */
57
- EmailBody *aBody; /* Location of body information */
58
-};
59
-#endif
60
-
61
-/*
62
-** Free An EmailToc object
63
-*/
64
-void emailtoc_free(EmailToc *p){
65
- int i;
66
- fossil_free(p->azHdr);
67
- for(i=0; i<p->nBody; i++){
68
- fossil_free(p->aBody[i].zFilename);
69
- }
70
- fossil_free(p->aBody);
71
- fossil_free(p);
72
-}
73
-
74
-/*
75
-** Allocate a new EmailToc object
76
-*/
77
-EmailToc *emailtoc_alloc(void){
78
- EmailToc *p = fossil_malloc( sizeof(*p) );
79
- memset(p, 0, sizeof(*p));
80
- return p;
81
-}
82
-
83
-/*
84
-** Add a new body element to an EmailToc.
85
-*/
86
-EmailBody *emailtoc_new_body(EmailToc *p){
87
- EmailBody *pNew;
88
- p->nBody++;
89
- if( p->nBody>p->nBodyAlloc ){
90
- p->nBodyAlloc = (p->nBodyAlloc+1)*2;
91
- p->aBody = fossil_realloc(p->aBody, sizeof(p->aBody[0])*p->nBodyAlloc);
92
- }
93
- pNew = &p->aBody[p->nBody-1];
94
- memset(pNew, 0, sizeof(*pNew));
95
- return pNew;
96
-}
97
-
98
-/*
99
-** Add a new header line to the EmailToc.
100
-*/
101
-void emailtoc_new_header_line(EmailToc *p, char *z){
102
- p->nHdr++;
103
- if( p->nHdr>p->nHdrAlloc ){
104
- p->nHdrAlloc = (p->nHdrAlloc+1)*2;
105
- p->azHdr = fossil_realloc(p->azHdr, sizeof(p->azHdr[0])*p->nHdrAlloc);
106
- }
107
- p->azHdr[p->nHdr-1] = z;
108
-}
109
-
110
-/*
111
-** Return the length of a line in an email header. Continuation lines
112
-** are included. Hence, this routine returns the number of bytes up to
113
-** and including the first \n character that is followed by something
114
-** other than whitespace.
115
-*/
116
-static int email_line_length(const char *z){
117
- int i;
118
- for(i=0; z[i] && (z[i]!='\n' || z[i+1]==' ' || z[i+1]=='\t'); i++){}
119
- if( z[i]=='\n' ) i++;
120
- return i;
121
-}
122
-
123
-/*
124
-** Look for a parameter of the form NAME=VALUE in the given email
125
-** header line. Return a copy of VALUE in space obtained from
126
-** fossil_malloc(). Or return NULL if there is no such parameter.
127
-*/
128
-static char *email_hdr_value(const char *z, const char *zName){
129
- int nName = (int)strlen(zName);
130
- int i;
131
- const char *z2 = strstr(z, zName);
132
- if( z2==0 ) return 0;
133
- z2 += nName;
134
- if( z2[0]!='=' ) return 0;
135
- z2++;
136
- if( z2[0]=='"' ){
137
- z2++;
138
- for(i=0; z2[i] && z2[i]!='"'; i++){}
139
- if( z2[i]!='"' ) return 0;
140
- }else{
141
- for(i=0; z2[i] && !fossil_isspace(z2[i]); i++){}
142
- }
143
- return mprintf("%.*s", i, z2);
144
-}
145
-
146
-/*
147
-** Return a pointer to the first non-whitespace character in z
148
-*/
149
-static const char *firstToken(const char *z){
150
- while( fossil_isspace(*z) ){
151
- z++;
152
- }
153
- return z;
154
-}
155
-
156
-/*
157
-** The n-bytes of content in z is a single multipart mime segment
158
-** with its own header and body. Decode this one segment and add it to p;
159
-**
160
-** Rows of the header of the segment are added to p if bAddHeader is
161
-** true.
162
-*/
163
-LOCAL void emailtoc_add_multipart_segment(
164
- EmailToc *p, /* Append the segments here */
165
- char *z, /* The body component */
166
- int bAddHeader /* True to add header lines to p */
167
-){
168
- int i, j;
169
- int n;
170
- int multipartBody = 0;
171
- EmailBody *pBody = emailtoc_new_body(p);
172
- i = 0;
173
- while( z[i] ){
174
- n = email_line_length(&z[i]);
175
- if( (n==2 && z[i]=='\r' && z[i+1]=='\n') || z[i]=='\n' || n==0 ){
176
- /* This is the blank line at the end of the header */
177
- i += n;
178
- break;
179
- }
180
- for(j=i+n; j>i && fossil_isspace(z[j-1]); j--){}
181
- z[j] = 0;
182
- if( sqlite3_strnicmp(z+i, "Content-Type:", 13)==0 ){
183
- const char *z2 = firstToken(z+i+13);
184
- if( z2 && strncmp(z2, "multipart/", 10)==0 ){
185
- multipartBody = 1;
186
- }else{
187
- int j;
188
- for(j=0; z2[j]=='/' || fossil_isalnum(z2[j]); j++){}
189
- if( j>=sizeof(pBody->zMimetype) ) j = sizeof(pBody->zMimetype);
190
- memcpy(pBody->zMimetype, z2, j);
191
- pBody->zMimetype[j] = 0;
192
- }
193
- }
194
- /* 123456789 123456789 123456 */
195
- if( sqlite3_strnicmp(z+i, "Content-Transfer-Encoding:", 26)==0 ){
196
- const char *z2 = firstToken(z+(i+26));
197
- if( z2 && sqlite3_strnicmp(z2, "base64", 6)==0 ){
198
- pBody->encoding = EMAILENC_B64;
199
- /* 123456789 123456 */
200
- }else if( sqlite3_strnicmp(z2, "quoted-printable", 16)==0 ){
201
- pBody->encoding = EMAILENC_QUOTED;
202
- }else{
203
- pBody->encoding = EMAILENC_NONE;
204
- }
205
- }
206
- if( bAddHeader ){
207
- emailtoc_new_header_line(p, z+i);
208
- }else if( sqlite3_strnicmp(z+i, "Content-Disposition:", 20)==0 ){
209
- /* 123456789 123456789 */
210
- fossil_free(pBody->zFilename);
211
- pBody->zFilename = email_hdr_value(z+i, "filename");
212
- }
213
- i += n;
214
- }
215
- if( multipartBody ){
216
- p->nBody--;
217
- emailtoc_add_multipart(p, z+i);
218
- }else{
219
- pBody->zContent = z+i;
220
- }
221
-}
222
-
223
-/*
224
-** The n-bytes of content in z are a multipart/ body component for
225
-** an email message. Decode this into its individual segments.
226
-**
227
-** The component should start and end with a boundary line. There
228
-** may be additional boundary lines in the middle.
229
-*/
230
-LOCAL void emailtoc_add_multipart(
231
- EmailToc *p, /* Append the segments here */
232
- char *z /* The body component. zero-terminated */
233
-){
234
- int nB; /* Size of the boundary string */
235
- int iStart; /* Start of the coding region past boundary mark */
236
- int i; /* Loop index */
237
- char *zBoundary = 0; /* Boundary marker */
238
-
239
- /* Skip forward to the beginning of the boundary mark. The boundary
240
- ** mark always begins with "--" */
241
- while( z[0]!='-' || z[1]!='-' ){
242
- while( z[0] && z[0]!='\n' ) z++;
243
- if( z[0]==0 ) return;
244
- z++;
245
- }
246
-
247
- /* Find the length of the boundary mark. */
248
- zBoundary = z;
249
- for(nB=0; z[nB] && !fossil_isspace(z[nB]); nB++){}
250
- if( nB==0 ) return;
251
-
252
- z += nB;
253
- while( fossil_isspace(z[0]) ) z++;
254
- zBoundary[nB] = 0;
255
- for(i=iStart=0; z[i]; i++){
256
- if( z[i]=='\n' && strncmp(z+i+1, zBoundary, nB)==0 ){
257
- z[i+1] = 0;
258
- emailtoc_add_multipart_segment(p, z+iStart, 0);
259
- iStart = i+nB;
260
- if( z[iStart]=='-' && z[iStart+1]=='-' ) return;
261
- while( fossil_isspace(z[iStart]) ) iStart++;
262
- i = iStart;
263
- }
264
- }
265
-}
266
-
267
-/*
268
-** Compute a table-of-contents (EmailToc) for the email message
269
-** provided on the input.
270
-**
271
-** This routine will cause pEmail to become zero-terminated if it is
272
-** not already. It will also insert zero characters into parts of
273
-** the message, to delimit the various components.
274
-*/
275
-EmailToc *emailtoc_from_email(Blob *pEmail){
276
- char *z;
277
- EmailToc *p = emailtoc_alloc();
278
- blob_terminate(pEmail);
279
- z = blob_buffer(pEmail);
280
- emailtoc_add_multipart_segment(p, z, 1);
281
- return p;
282
-}
283
-
284
-/*
285
-** Inplace-unfolding of an email header line.
286
-**
287
-** Actually - this routine works by converting all contiguous sequences
288
-** of whitespace into a single space character.
289
-*/
290
-static void email_hdr_unfold(char *z){
291
- int i, j;
292
- char c;
293
- for(i=j=0; (c = z[i])!=0; i++){
294
- if( fossil_isspace(c) ){
295
- c = ' ';
296
- if( j && z[j-1]==' ' ) continue;
297
- }
298
- z[j++] = c;
299
- }
300
- z[j] = 0;
301
-}
302
-
303
-/*
304
-** COMMAND: test-decode-email
305
-**
306
-** Usage: %fossil test-decode-email FILE
307
-**
308
-** Read an rfc-2822 formatted email out of FILE, then write a decoding
309
-** to stdout. Use for testing and validating the email decoder.
310
-*/
311
-void test_email_decode_cmd(void){
312
- Blob email;
313
- EmailToc *p;
314
- int i;
315
- verify_all_options();
316
- if( g.argc!=3 ) usage("FILE");
317
- blob_read_from_file(&email, g.argv[2], ExtFILE);
318
- p = emailtoc_from_email(&email);
319
- fossil_print("%d header line and %d content segments\n",
320
- p->nHdr, p->nBody);
321
- for(i=0; i<p->nHdr; i++){
322
- email_hdr_unfold(p->azHdr[i]);
323
- fossil_print("%3d: %s\n", i, p->azHdr[i]);
324
- }
325
- for(i=0; i<p->nBody; i++){
326
- fossil_print("\nBODY %d mime \"%s\" encoding %d",
327
- i, p->aBody[i].zMimetype, p->aBody[i].encoding);
328
- if( p->aBody[i].zFilename ){
329
- fossil_print(" filename \"%s\"", p->aBody[i].zFilename);
330
- }
331
- fossil_print("\n");
332
- if( strncmp(p->aBody[i].zMimetype,"text/",5)!=0 ) continue;
333
- switch( p->aBody[i].encoding ){
334
- case EMAILENC_B64: {
335
- int n = 0;
336
- decodeBase64(p->aBody[i].zContent, &n, p->aBody[i].zContent);
337
- fossil_print("%s", p->aBody[i].zContent);
338
- if( n && p->aBody[i].zContent[n-1]!='\n' ) fossil_print("\n");
339
- break;
340
- }
341
- case EMAILENC_QUOTED: {
342
- int n = 0;
343
- decodeQuotedPrintable(p->aBody[i].zContent, &n);
344
- fossil_print("%s", p->aBody[i].zContent);
345
- if( n && p->aBody[i].zContent[n-1]!='\n' ) fossil_print("\n");
346
- break;
347
- }
348
- default: {
349
- fossil_print("%s\n", p->aBody[i].zContent);
350
- break;
351
- }
352
- }
353
- }
354
- emailtoc_free(p);
355
- blob_reset(&email);
356
-}
357
-
358
-/*
359
-** Add the select/option box to the timeline submenu that shows
360
-** the various email message formats.
361
-*/
362
-static void webmail_f_submenu(void){
363
- static const char *const az[] = {
364
- "0", "Normal",
365
- "1", "Decoded",
366
- "2", "Raw",
367
- };
368
- style_submenu_multichoice("f", sizeof(az)/(2*sizeof(az[0])), az, 0);
369
-}
370
-
371
-/*
372
-** If the first N characters of z[] are the name of a header field
373
-** that should be shown in "Normal" mode, then return 1.
374
-*/
375
-static int webmail_normal_header(const char *z, int N){
376
- static const char *const az[] = {
377
- "To", "Cc", "Bcc", "Date", "From", "Subject",
378
- };
379
- int i;
380
- for(i=0; i<sizeof(az)/sizeof(az[0]); i++){
381
- if( sqlite3_strnicmp(z, az[i], N)==0 ) return 1;
382
- }
383
- return 0;
384
-}
385
-
386
-/*
387
-** Paint a page showing a single email message
388
-*/
389
-static void webmail_show_one_message(
390
- HQuery *pUrl, /* Calling context */
391
- int emailid, /* emailbox.ebid to display */
392
- const char *zUser /* User who owns it, or NULL if does not matter */
393
-){
394
- Blob sql;
395
- Stmt q;
396
- int eState = -1;
397
- int eTranscript = 0;
398
- char zENum[30];
399
- style_submenu_element("Index", "%s", url_render(pUrl,"id",0,0,0));
400
- webmail_f_submenu();
401
- blob_init(&sql, 0, 0);
402
- db_begin_transaction();
403
- blob_append_sql(&sql,
404
- "SELECT decompress(etxt), estate, emailblob.ets"
405
- " FROM emailblob, emailbox"
406
- " WHERE emailid=emsgid AND ebid=%d",
407
- emailid
408
- );
409
- if( zUser ) blob_append_sql(&sql, " AND euser=%Q", zUser);
410
- db_prepare_blob(&q, &sql);
411
- blob_reset(&sql);
412
- style_set_current_feature("webmail");
413
- style_header("Message %d",emailid);
414
- if( db_step(&q)==SQLITE_ROW ){
415
- Blob msg = db_column_text_as_blob(&q, 0);
416
- int eFormat = atoi(PD("f","0"));
417
- eState = db_column_int(&q, 1);
418
- eTranscript = db_column_int(&q, 2);
419
- if( eFormat==2 ){
420
- @ <pre>%h(db_column_text(&q, 0))</pre>
421
- }else{
422
- EmailToc *p = emailtoc_from_email(&msg);
423
- int i, j;
424
- @ <p>
425
- for(i=0; i<p->nHdr; i++){
426
- char *z = p->azHdr[i];
427
- email_hdr_unfold(z);
428
- for(j=0; z[j] && z[j]!=':'; j++){}
429
- if( eFormat==0 && !webmail_normal_header(z, j) ) continue;
430
- if( z[j]!=':' ){
431
- @ %h(z)<br>
432
- }else{
433
- z[j] = 0;
434
- @ <b>%h(z):</b> %h(z+j+1)<br>
435
- }
436
- }
437
- for(i=0; i<p->nBody; i++){
438
- @ <hr><b>Messsage Body #%d(i): %h(p->aBody[i].zMimetype) \
439
- if( p->aBody[i].zFilename ){
440
- @ "%h(p->aBody[i].zFilename)"
441
- }
442
- @ </b>
443
- if( eFormat==0 ){
444
- if( strncmp(p->aBody[i].zMimetype, "text/plain", 10)!=0 ) continue;
445
- if( p->aBody[i].zFilename ) continue;
446
- }else{
447
- if( strncmp(p->aBody[i].zMimetype, "text/", 5)!=0 ) continue;
448
- }
449
- switch( p->aBody[i].encoding ){
450
- case EMAILENC_B64: {
451
- int n = 0;
452
- decodeBase64(p->aBody[i].zContent, &n, p->aBody[i].zContent);
453
- break;
454
- }
455
- case EMAILENC_QUOTED: {
456
- int n = 0;
457
- decodeQuotedPrintable(p->aBody[i].zContent, &n);
458
- break;
459
- }
460
- }
461
- @ <pre>%h(p->aBody[i].zContent)</pre>
462
- }
463
- }
464
- }
465
- db_finalize(&q);
466
-
467
- /* Optionally show the SMTP transcript */
468
- if( eTranscript>0
469
- && db_exists("SELECT 1 FROM emailblob WHERE emailid=%d", eTranscript)
470
- ){
471
- if( P("ts")==0 ){
472
- sqlite3_snprintf(sizeof(zENum), zENum, "%d", emailid);
473
- style_submenu_element("SMTP Transcript","%s",
474
- url_render(pUrl, "ts", "1", "id", zENum));
475
- }else{
476
- db_prepare(&q,
477
- "SELECT decompress(etxt) FROM emailblob WHERE emailid=%d", eTranscript
478
- );
479
- if( db_step(&q)==SQLITE_ROW ){
480
- const char *zTranscript = db_column_text(&q, 0);
481
- @ <hr>
482
- @ <pre>%h(zTranscript)</pre>
483
- }
484
- db_finalize(&q);
485
- }
486
- }
487
-
488
- if( eState==0 ){
489
- /* If is message is currently Unread, change it to Read */
490
- blob_append_sql(&sql,
491
- "UPDATE emailbox SET estate=1 "
492
- " WHERE estate=0 AND ebid=%d",
493
- emailid
494
- );
495
- if( zUser ) blob_append_sql(&sql, " AND euser=%Q", zUser);
496
- db_multi_exec("%s", blob_sql_text(&sql));
497
- blob_reset(&sql);
498
- eState = 1;
499
- }
500
-
501
- url_add_parameter(pUrl, "id", 0);
502
- sqlite3_snprintf(sizeof(zENum), zENum, "e%d", emailid);
503
- if( eState==2 ){
504
- style_submenu_element("Undelete","%s",
505
- url_render(pUrl,"read","1",zENum,"1"));
506
- }
507
- if( eState==1 ){
508
- style_submenu_element("Delete", "%s",
509
- url_render(pUrl,"trash","1",zENum,"1"));
510
- style_submenu_element("Mark As Unread", "%s",
511
- url_render(pUrl,"unread","1",zENum,"1"));
512
- }
513
- if( eState==3 ){
514
- style_submenu_element("Delete", "%s",
515
- url_render(pUrl,"trash","1",zENum,"1"));
516
- }
517
-
518
- db_end_transaction(0);
519
- style_finish_page();
520
- return;
521
-}
522
-
523
-/*
524
-** Scan the query parameters looking for parameters with name of the
525
-** form "eN" where N is an integer. For all such integers, change
526
-** the state of every emailbox entry with ebid==N to eStateNew provided
527
-** that either zUser is NULL or matches.
528
-**
529
-** Or if eNewState==99, then delete the entries.
530
-*/
531
-static void webmail_change_state(int eNewState, const char *zUser){
532
- Blob sql;
533
- int sep = '(';
534
- int i;
535
- const char *zName;
536
- int n;
537
- if( !cgi_csrf_safe(0) ) return;
538
- blob_init(&sql, 0, 0);
539
- if( eNewState==99 ){
540
- blob_append_sql(&sql, "DELETE FROM emailbox WHERE estate==2 AND ebid IN ");
541
- }else{
542
- blob_append_sql(&sql, "UPDATE emailbox SET estate=%d WHERE ebid IN ",
543
- eNewState);
544
- }
545
- for(i=0; (zName = cgi_parameter_name(i))!=0; i++){
546
- if( zName[0]!='e' ) continue;
547
- if( !fossil_isdigit(zName[1]) ) continue;
548
- n = atoi(zName+1);
549
- blob_append_sql(&sql, "%c%d", sep, n);
550
- sep = ',';
551
- }
552
- if( zUser ){
553
- blob_append_sql(&sql, ") AND euser=%Q", zUser);
554
- }else{
555
- blob_append_sql(&sql, ")");
556
- }
557
- if( sep==',' ){
558
- db_multi_exec("%s", blob_sql_text(&sql));
559
- }
560
- blob_reset(&sql);
561
-}
562
-
563
-
564
-/*
565
-** Add the select/option box to the timeline submenu that shows
566
-** which messages to include in the index.
567
-*/
568
-static void webmail_d_submenu(void){
569
- static const char *const az[] = {
570
- "0", "InBox",
571
- "1", "Unread",
572
- "2", "Trash",
573
- "3", "Sent",
574
- "4", "Everything",
575
- };
576
- style_submenu_multichoice("d", sizeof(az)/(2*sizeof(az[0])), az, 0);
577
-}
578
-
579
-/*
580
-** WEBPAGE: webmail
581
-**
582
-** This page can be used to read content from the EMAILBOX table
583
-** that contains email received by the "fossil smtpd" command.
584
-**
585
-** Query parameters:
586
-**
587
-** id=N Show a single email entry emailbox.ebid==N
588
-** f=N Display format. 0: decoded 1: raw
589
-** user=USER Show mailbox for USER (admin only).
590
-** user=* Show mailbox for all users (admin only).
591
-** d=N 0: inbox+unread 1: unread-only 2: trash 3: all
592
-** eN Select email entry emailbox.ebid==N
593
-** trash Move selected entries to trash (estate=2)
594
-** read Mark selected entries as read (estate=1)
595
-** unread Mark selected entries as unread (estate=0)
596
-**
597
-*/
598
-void webmail_page(void){
599
- int emailid;
600
- Stmt q;
601
- Blob sql;
602
- int showAll = 0;
603
- const char *zUser = 0;
604
- int d = 0; /* Display mode. 0..3. d= query parameter */
605
- int pg = 0; /* Page number */
606
- int N = 50; /* Results per page */
607
- int got; /* Number of results on this page */
608
- char zPPg[30]; /* Previous page */
609
- char zNPg[30]; /* Next page */
610
- HQuery url;
611
- login_check_credentials();
612
- if( !login_is_individual() ){
613
- login_needed(0);
614
- return;
615
- }
616
- style_set_current_feature("webmail");
617
- if( !db_table_exists("repository","emailbox") ){
618
- style_header("Webmail Not Available");
619
- @ <p>This repository is not configured to provide webmail</p>
620
- style_finish_page();
621
- return;
622
- }
623
- add_content_sql_commands(g.db);
624
- emailid = atoi(PD("id","0"));
625
- url_initialize(&url, "webmail");
626
- if( g.perm.Admin ){
627
- zUser = PD("user",g.zLogin);
628
- if( zUser ){
629
- url_add_parameter(&url, "user", zUser);
630
- if( fossil_strcmp(zUser,"*")==0 ){
631
- showAll = 1;
632
- zUser = 0;
633
- }
634
- }
635
- }else{
636
- zUser = g.zLogin;
637
- }
638
- if( P("d") ) url_add_parameter(&url, "d", P("d"));
639
- if( emailid>0 ){
640
- webmail_show_one_message(&url, emailid, zUser);
641
- return;
642
- }
643
- style_header("Webmail");
644
- webmail_d_submenu();
645
- db_begin_transaction();
646
- if( P("trash")!=0 ) webmail_change_state(2,zUser);
647
- if( P("unread")!=0 ) webmail_change_state(0,zUser);
648
- if( P("read")!=0 ) webmail_change_state(1,zUser);
649
- if( P("purge")!=0 ) webmail_change_state(99,zUser);
650
- blob_init(&sql, 0, 0);
651
- blob_append_sql(&sql,
652
- "CREATE TEMP TABLE tmbox AS "
653
- "SELECT ebid," /* 0 */
654
- " efrom," /* 1 */
655
- " datetime(edate,'unixepoch')," /* 2 */
656
- " estate," /* 3 */
657
- " esubject," /* 4 */
658
- " euser" /* 5 */
659
- " FROM emailbox"
660
- );
661
- d = atoi(PD("d","0"));
662
- switch( d ){
663
- case 0: { /* Show unread and read */
664
- blob_append_sql(&sql, " WHERE estate<=1");
665
- break;
666
- }
667
- case 1: { /* Unread messages only */
668
- blob_append_sql(&sql, " WHERE estate=0");
669
- break;
670
- }
671
- case 2: { /* Trashcan only */
672
- blob_append_sql(&sql, " WHERE estate=2");
673
- break;
674
- }
675
- case 3: { /* Outgoing email only */
676
- blob_append_sql(&sql, " WHERE estate=3");
677
- break;
678
- }
679
- case 4: { /* Everything */
680
- blob_append_sql(&sql, " WHERE 1");
681
- break;
682
- }
683
- }
684
- if( showAll ){
685
- style_submenu_element("My Emails", "%s", url_render(&url,"user",0,0,0));
686
- }else if( zUser!=0 ){
687
- style_submenu_element("All Users", "%s", url_render(&url,"user","*",0,0));
688
- if( fossil_strcmp(zUser, g.zLogin)!=0 ){
689
- style_submenu_element("My Emails", "%s", url_render(&url,"user",0,0,0));
690
- }
691
- if( zUser ){
692
- blob_append_sql(&sql, " AND euser=%Q", zUser);
693
- }else{
694
- blob_append_sql(&sql, " AND euser=%Q", g.zLogin);
695
- }
696
- }else{
697
- if( g.perm.Admin ){
698
- style_submenu_element("All Users", "%s", url_render(&url,"user","*",0,0));
699
- }
700
- blob_append_sql(&sql, " AND euser=%Q", g.zLogin);
701
- }
702
- pg = atoi(PD("pg","0"));
703
- blob_append_sql(&sql, " ORDER BY edate DESC limit %d offset %d", N+1, pg*N);
704
- db_multi_exec("%s", blob_sql_text(&sql));
705
- got = db_int(0, "SELECT count(*) FROM tmbox");
706
- db_prepare(&q, "SELECT * FROM tmbox LIMIT %d", N);
707
- blob_reset(&sql);
708
- @ <form action="%R/webmail" method="POST">
709
- @ <input type="hidden" name="d" value="%d(d)">
710
- @ <input type="hidden" name="user" value="%h(zUser?zUser:"*")">
711
- @ <table border="0" width="100%%">
712
- @ <tr><td align="left">
713
- if( d==2 ){
714
- @ <input type="submit" name="read" value="Undelete">
715
- @ <input type="submit" name="purge" value="Delete Permanently">
716
- }else{
717
- @ <input type="submit" name="trash" value="Delete">
718
- if( d!=1 ){
719
- @ <input type="submit" name="unread" value="Mark as unread">
720
- }
721
- @ <input type="submit" name="read" value="Mark as read">
722
- }
723
- @ <button onclick="webmailSelectAll(); return false;">Select All</button>
724
- @ <a href="%h(url_render(&url,0,0,0,0))">refresh</a>
725
- @ </td><td align="right">
726
- if( pg>0 ){
727
- sqlite3_snprintf(sizeof(zPPg), zPPg, "%d", pg-1);
728
- @ <a href="%s(url_render(&url,"pg",zPPg,0,0))">&lt; Newer</a>&nbsp;&nbsp;
729
- }
730
- if( got>50 ){
731
- sqlite3_snprintf(sizeof(zNPg),zNPg,"%d",pg+1);
732
- @ <a href="%s(url_render(&url,"pg",zNPg,0,0))">Older &gt;</a></td>
733
- }
734
- @ </table>
735
- @ <table>
736
- while( db_step(&q)==SQLITE_ROW ){
737
- const char *zId = db_column_text(&q,0);
738
- const char *zFrom = db_column_text(&q, 1);
739
- const char *zDate = db_column_text(&q, 2);
740
- const char *zSubject = db_column_text(&q, 4);
741
- if( zSubject==0 || zSubject[0]==0 ) zSubject = "(no subject)";
742
- @ <tr>
743
- @ <td><input type="checkbox" class="webmailckbox" name="e%s(zId)"></td>
744
- @ <td>%h(zFrom)</td>
745
- @ <td><a href="%h(url_render(&url,"id",zId,0,0))">%h(zSubject)</a> \
746
- @ %s(zDate)</td>
747
- if( showAll ){
748
- const char *zTo = db_column_text(&q,5);
749
- @ <td><a href="%h(url_render(&url,"user",zTo,0,0))">%h(zTo)</a></td>
750
- }
751
- @ </tr>
752
- }
753
- db_finalize(&q);
754
- @ </table>
755
- @ </form>
756
- @ <script>
757
- @ function webmailSelectAll(){
758
- @ var x = document.getElementsByClassName("webmailckbox");
759
- @ for(i=0; i<x.length; i++){
760
- @ x[i].checked = true;
761
- @ }
762
- @ }
763
- @ </script>
764
- style_finish_page();
765
- db_end_transaction(0);
766
-}
767
-
768
-/*
769
-** WEBPAGE: emailblob
770
-**
771
-** This page, accessible only to administrators, allows easy viewing of
772
-** the emailblob table - the table that contains the text of email messages
773
-** both inbound and outbound, and transcripts of SMTP sessions.
774
-**
775
-** id=N Show the text of emailblob with emailid==N
776
-**
777
-*/
778
-void webmail_emailblob_page(void){
779
- int id = atoi(PD("id","0"));
780
- Stmt q;
781
- login_check_credentials();
782
- if( !g.perm.Setup ){
783
- login_needed(0);
784
- return;
785
- }
786
- add_content_sql_commands(g.db);
787
- style_set_current_feature("webmail");
788
- style_header("emailblob table");
789
- if( id>0 ){
790
- style_submenu_element("Index", "%R/emailblob");
791
- @ <ul>
792
- db_prepare(&q, "SELECT emailid FROM emailblob WHERE ets=%d", id);
793
- while( db_step(&q)==SQLITE_ROW ){
794
- int id = db_column_int(&q, 0);
795
- @ <li> <a href="%R/emailblob?id=%d(id)">emailblob entry %d(id)</a>
796
- }
797
- db_finalize(&q);
798
- db_prepare(&q, "SELECT euser, estate FROM emailbox WHERE emsgid=%d", id);
799
- while( db_step(&q)==SQLITE_ROW ){
800
- const char *zUser = db_column_text(&q, 0);
801
- int e = db_column_int(&q, 1);
802
- @ <li> emailbox for %h(zUser) state %d(e)
803
- }
804
- db_finalize(&q);
805
- db_prepare(&q, "SELECT efrom, eto FROM emailoutq WHERE emsgid=%d", id);
806
- while( db_step(&q)==SQLITE_ROW ){
807
- const char *zFrom = db_column_text(&q, 0);
808
- const char *zTo = db_column_text(&q, 1);
809
- @ <li> emailoutq message body from %h(zFrom) to %h(zTo)
810
- }
811
- db_finalize(&q);
812
- db_prepare(&q, "SELECT efrom, eto FROM emailoutq WHERE ets=%d", id);
813
- while( db_step(&q)==SQLITE_ROW ){
814
- const char *zFrom = db_column_text(&q, 0);
815
- const char *zTo = db_column_text(&q, 1);
816
- @ <li> emailoutq transcript from %h(zFrom) to %h(zTo)
817
- }
818
- db_finalize(&q);
819
- @ </ul>
820
- @ <hr>
821
- db_prepare(&q, "SELECT decompress(etxt) FROM emailblob WHERE emailid=%d",
822
- id);
823
- while( db_step(&q)==SQLITE_ROW ){
824
- const char *zContent = db_column_text(&q, 0);
825
- @ <pre>%h(zContent)</pre>
826
- }
827
- db_finalize(&q);
828
- }else{
829
- style_submenu_element("emailoutq table","%R/emailoutq");
830
- db_prepare(&q,
831
- "SELECT emailid, enref, ets, datetime(etime,'unixepoch'), esz,"
832
- " length(etxt)"
833
- " FROM emailblob ORDER BY etime DESC, emailid DESC");
834
- @ <table border="1" cellpadding="5" cellspacing="0" class="sortable" \
835
- @ data-column-types='nnntkk'>
836
- @ <thead><tr><th> emailid <th> enref <th> ets <th> etime \
837
- @ <th> uncompressed <th> compressed </tr></thead><tbody>
838
- while( db_step(&q)==SQLITE_ROW ){
839
- int id = db_column_int(&q, 0);
840
- int nref = db_column_int(&q, 1);
841
- int ets = db_column_int(&q, 2);
842
- const char *zDate = db_column_text(&q, 3);
843
- int sz = db_column_int(&q,4);
844
- int csz = db_column_int(&q,5);
845
- @ <tr>
846
- @ <td align="right"><a href="%R/emailblob?id=%d(id)">%d(id)</a>
847
- @ <td align="right">%d(nref)</td>
848
- if( ets>0 ){
849
- @ <td align="right">%d(ets)</td>
850
- }else{
851
- @ <td>&nbsp;</td>
852
- }
853
- @ <td>%h(zDate)</td>
854
- @ <td align="right" data-sortkey='%08x(sz)'>%,d(sz)</td>
855
- @ <td align="right" data-sortkey='%08x(csz)'>%,d(csz)</td>
856
- @ </tr>
857
- }
858
- @ </tbody></table>
859
- db_finalize(&q);
860
- style_table_sorter();
861
- }
862
- style_finish_page();
863
-}
864
-
865
-/*
866
-** WEBPAGE: emailoutq
867
-**
868
-** This page, accessible only to administrators, allows easy viewing of
869
-** the emailoutq table - the table that contains the email messages
870
-** that are queued for transmission via SMTP.
871
-*/
872
-void webmail_emailoutq_page(void){
873
- Stmt q;
874
- login_check_credentials();
875
- if( !g.perm.Setup ){
876
- login_needed(0);
877
- return;
878
- }
879
- add_content_sql_commands(g.db);
880
- style_set_current_feature("webmail");
881
- style_header("emailoutq table");
882
- style_submenu_element("emailblob table","%R/emailblob");
883
- db_prepare(&q,
884
- "SELECT edomain, efrom, eto, emsgid, "
885
- " datetime(ectime,'unixepoch'),"
886
- " datetime(nullif(emtime,0),'unixepoch'),"
887
- " ensend, ets"
888
- " FROM emailoutq"
889
- );
890
- @ <table border="1" cellpadding="5" cellspacing="0" class="sortable" \
891
- @ data-column-types='tttnttnn'>
892
- @ <thead><tr><th> edomain <th> efrom <th> eto <th> emsgid \
893
- @ <th> ectime <th> emtime <th> ensend <th> ets </tr></thead><tbody>
894
- while( db_step(&q)==SQLITE_ROW ){
895
- const char *zDomain = db_column_text(&q, 0);
896
- const char *zFrom = db_column_text(&q, 1);
897
- const char *zTo = db_column_text(&q, 2);
898
- int emsgid = db_column_int(&q, 3);
899
- const char *zCTime = db_column_text(&q, 4);
900
- const char *zMTime = db_column_text(&q, 5);
901
- int ensend = db_column_int(&q, 6);
902
- int ets = db_column_int(&q, 7);
903
- @ <tr>
904
- @ <td>%h(zDomain)
905
- @ <td>%h(zFrom)
906
- @ <td>%h(zTo)
907
- @ <td align="right"><a href="%R/emailblob?id=%d(emsgid)">%d(emsgid)</a>
908
- @ <td>%h(zCTime)
909
- @ <td>%h(zMTime)
910
- @ <td align="right">%d(ensend)
911
- if( ets>0 ){
912
- @ <td align="right"><a href="%R/emailblob?id=%d(ets)">%d(ets)</a></td>
913
- }else{
914
- @ <td>&nbsp;</td>
915
- }
916
- }
917
- @ </tbody></table>
918
- db_finalize(&q);
919
- style_table_sorter();
920
- style_finish_page();
921
-}
--- a/src/webmail.c
+++ b/src/webmail.c
@@ -1,921 +0,0 @@
1 /*
2 ** Copyright (c) 2018 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 ** Implementation of web pages for managing the email storage tables
19 ** (if they exist):
20 **
21 ** emailbox
22 ** emailblob
23 ** emailroute
24 */
25 #include "config.h"
26 #include "webmail.h"
27 #include <assert.h>
28
29
30 #if INTERFACE
31
32 /* Recognized content encodings */
33 #define EMAILENC_NONE 0 /* No encoding */
34 #define EMAILENC_B64 1 /* Base64 encoded */
35 #define EMAILENC_QUOTED 2 /* Quoted printable */
36
37 /* An instance of the following object records the location of important
38 ** attributes on a single element in a multipart email message body.
39 */
40 struct EmailBody {
41 char zMimetype[32]; /* Mimetype */
42 u8 encoding; /* Type of encoding */
43 char *zFilename; /* From content-disposition: */
44 char *zContent; /* Content. \0 terminator inserted */
45 };
46
47 /*
48 ** An instance of the following object describes the struture of
49 ** an rfc-2822 email message.
50 */
51 struct EmailToc {
52 int nHdr; /* Number of header lines */
53 int nHdrAlloc; /* Number of header lines allocated */
54 char **azHdr; /* Pointer to header line. \0 terminator inserted */
55 int nBody; /* Number of body segments */
56 int nBodyAlloc; /* Number of body segments allocated */
57 EmailBody *aBody; /* Location of body information */
58 };
59 #endif
60
61 /*
62 ** Free An EmailToc object
63 */
64 void emailtoc_free(EmailToc *p){
65 int i;
66 fossil_free(p->azHdr);
67 for(i=0; i<p->nBody; i++){
68 fossil_free(p->aBody[i].zFilename);
69 }
70 fossil_free(p->aBody);
71 fossil_free(p);
72 }
73
74 /*
75 ** Allocate a new EmailToc object
76 */
77 EmailToc *emailtoc_alloc(void){
78 EmailToc *p = fossil_malloc( sizeof(*p) );
79 memset(p, 0, sizeof(*p));
80 return p;
81 }
82
83 /*
84 ** Add a new body element to an EmailToc.
85 */
86 EmailBody *emailtoc_new_body(EmailToc *p){
87 EmailBody *pNew;
88 p->nBody++;
89 if( p->nBody>p->nBodyAlloc ){
90 p->nBodyAlloc = (p->nBodyAlloc+1)*2;
91 p->aBody = fossil_realloc(p->aBody, sizeof(p->aBody[0])*p->nBodyAlloc);
92 }
93 pNew = &p->aBody[p->nBody-1];
94 memset(pNew, 0, sizeof(*pNew));
95 return pNew;
96 }
97
98 /*
99 ** Add a new header line to the EmailToc.
100 */
101 void emailtoc_new_header_line(EmailToc *p, char *z){
102 p->nHdr++;
103 if( p->nHdr>p->nHdrAlloc ){
104 p->nHdrAlloc = (p->nHdrAlloc+1)*2;
105 p->azHdr = fossil_realloc(p->azHdr, sizeof(p->azHdr[0])*p->nHdrAlloc);
106 }
107 p->azHdr[p->nHdr-1] = z;
108 }
109
110 /*
111 ** Return the length of a line in an email header. Continuation lines
112 ** are included. Hence, this routine returns the number of bytes up to
113 ** and including the first \n character that is followed by something
114 ** other than whitespace.
115 */
116 static int email_line_length(const char *z){
117 int i;
118 for(i=0; z[i] && (z[i]!='\n' || z[i+1]==' ' || z[i+1]=='\t'); i++){}
119 if( z[i]=='\n' ) i++;
120 return i;
121 }
122
123 /*
124 ** Look for a parameter of the form NAME=VALUE in the given email
125 ** header line. Return a copy of VALUE in space obtained from
126 ** fossil_malloc(). Or return NULL if there is no such parameter.
127 */
128 static char *email_hdr_value(const char *z, const char *zName){
129 int nName = (int)strlen(zName);
130 int i;
131 const char *z2 = strstr(z, zName);
132 if( z2==0 ) return 0;
133 z2 += nName;
134 if( z2[0]!='=' ) return 0;
135 z2++;
136 if( z2[0]=='"' ){
137 z2++;
138 for(i=0; z2[i] && z2[i]!='"'; i++){}
139 if( z2[i]!='"' ) return 0;
140 }else{
141 for(i=0; z2[i] && !fossil_isspace(z2[i]); i++){}
142 }
143 return mprintf("%.*s", i, z2);
144 }
145
146 /*
147 ** Return a pointer to the first non-whitespace character in z
148 */
149 static const char *firstToken(const char *z){
150 while( fossil_isspace(*z) ){
151 z++;
152 }
153 return z;
154 }
155
156 /*
157 ** The n-bytes of content in z is a single multipart mime segment
158 ** with its own header and body. Decode this one segment and add it to p;
159 **
160 ** Rows of the header of the segment are added to p if bAddHeader is
161 ** true.
162 */
163 LOCAL void emailtoc_add_multipart_segment(
164 EmailToc *p, /* Append the segments here */
165 char *z, /* The body component */
166 int bAddHeader /* True to add header lines to p */
167 ){
168 int i, j;
169 int n;
170 int multipartBody = 0;
171 EmailBody *pBody = emailtoc_new_body(p);
172 i = 0;
173 while( z[i] ){
174 n = email_line_length(&z[i]);
175 if( (n==2 && z[i]=='\r' && z[i+1]=='\n') || z[i]=='\n' || n==0 ){
176 /* This is the blank line at the end of the header */
177 i += n;
178 break;
179 }
180 for(j=i+n; j>i && fossil_isspace(z[j-1]); j--){}
181 z[j] = 0;
182 if( sqlite3_strnicmp(z+i, "Content-Type:", 13)==0 ){
183 const char *z2 = firstToken(z+i+13);
184 if( z2 && strncmp(z2, "multipart/", 10)==0 ){
185 multipartBody = 1;
186 }else{
187 int j;
188 for(j=0; z2[j]=='/' || fossil_isalnum(z2[j]); j++){}
189 if( j>=sizeof(pBody->zMimetype) ) j = sizeof(pBody->zMimetype);
190 memcpy(pBody->zMimetype, z2, j);
191 pBody->zMimetype[j] = 0;
192 }
193 }
194 /* 123456789 123456789 123456 */
195 if( sqlite3_strnicmp(z+i, "Content-Transfer-Encoding:", 26)==0 ){
196 const char *z2 = firstToken(z+(i+26));
197 if( z2 && sqlite3_strnicmp(z2, "base64", 6)==0 ){
198 pBody->encoding = EMAILENC_B64;
199 /* 123456789 123456 */
200 }else if( sqlite3_strnicmp(z2, "quoted-printable", 16)==0 ){
201 pBody->encoding = EMAILENC_QUOTED;
202 }else{
203 pBody->encoding = EMAILENC_NONE;
204 }
205 }
206 if( bAddHeader ){
207 emailtoc_new_header_line(p, z+i);
208 }else if( sqlite3_strnicmp(z+i, "Content-Disposition:", 20)==0 ){
209 /* 123456789 123456789 */
210 fossil_free(pBody->zFilename);
211 pBody->zFilename = email_hdr_value(z+i, "filename");
212 }
213 i += n;
214 }
215 if( multipartBody ){
216 p->nBody--;
217 emailtoc_add_multipart(p, z+i);
218 }else{
219 pBody->zContent = z+i;
220 }
221 }
222
223 /*
224 ** The n-bytes of content in z are a multipart/ body component for
225 ** an email message. Decode this into its individual segments.
226 **
227 ** The component should start and end with a boundary line. There
228 ** may be additional boundary lines in the middle.
229 */
230 LOCAL void emailtoc_add_multipart(
231 EmailToc *p, /* Append the segments here */
232 char *z /* The body component. zero-terminated */
233 ){
234 int nB; /* Size of the boundary string */
235 int iStart; /* Start of the coding region past boundary mark */
236 int i; /* Loop index */
237 char *zBoundary = 0; /* Boundary marker */
238
239 /* Skip forward to the beginning of the boundary mark. The boundary
240 ** mark always begins with "--" */
241 while( z[0]!='-' || z[1]!='-' ){
242 while( z[0] && z[0]!='\n' ) z++;
243 if( z[0]==0 ) return;
244 z++;
245 }
246
247 /* Find the length of the boundary mark. */
248 zBoundary = z;
249 for(nB=0; z[nB] && !fossil_isspace(z[nB]); nB++){}
250 if( nB==0 ) return;
251
252 z += nB;
253 while( fossil_isspace(z[0]) ) z++;
254 zBoundary[nB] = 0;
255 for(i=iStart=0; z[i]; i++){
256 if( z[i]=='\n' && strncmp(z+i+1, zBoundary, nB)==0 ){
257 z[i+1] = 0;
258 emailtoc_add_multipart_segment(p, z+iStart, 0);
259 iStart = i+nB;
260 if( z[iStart]=='-' && z[iStart+1]=='-' ) return;
261 while( fossil_isspace(z[iStart]) ) iStart++;
262 i = iStart;
263 }
264 }
265 }
266
267 /*
268 ** Compute a table-of-contents (EmailToc) for the email message
269 ** provided on the input.
270 **
271 ** This routine will cause pEmail to become zero-terminated if it is
272 ** not already. It will also insert zero characters into parts of
273 ** the message, to delimit the various components.
274 */
275 EmailToc *emailtoc_from_email(Blob *pEmail){
276 char *z;
277 EmailToc *p = emailtoc_alloc();
278 blob_terminate(pEmail);
279 z = blob_buffer(pEmail);
280 emailtoc_add_multipart_segment(p, z, 1);
281 return p;
282 }
283
284 /*
285 ** Inplace-unfolding of an email header line.
286 **
287 ** Actually - this routine works by converting all contiguous sequences
288 ** of whitespace into a single space character.
289 */
290 static void email_hdr_unfold(char *z){
291 int i, j;
292 char c;
293 for(i=j=0; (c = z[i])!=0; i++){
294 if( fossil_isspace(c) ){
295 c = ' ';
296 if( j && z[j-1]==' ' ) continue;
297 }
298 z[j++] = c;
299 }
300 z[j] = 0;
301 }
302
303 /*
304 ** COMMAND: test-decode-email
305 **
306 ** Usage: %fossil test-decode-email FILE
307 **
308 ** Read an rfc-2822 formatted email out of FILE, then write a decoding
309 ** to stdout. Use for testing and validating the email decoder.
310 */
311 void test_email_decode_cmd(void){
312 Blob email;
313 EmailToc *p;
314 int i;
315 verify_all_options();
316 if( g.argc!=3 ) usage("FILE");
317 blob_read_from_file(&email, g.argv[2], ExtFILE);
318 p = emailtoc_from_email(&email);
319 fossil_print("%d header line and %d content segments\n",
320 p->nHdr, p->nBody);
321 for(i=0; i<p->nHdr; i++){
322 email_hdr_unfold(p->azHdr[i]);
323 fossil_print("%3d: %s\n", i, p->azHdr[i]);
324 }
325 for(i=0; i<p->nBody; i++){
326 fossil_print("\nBODY %d mime \"%s\" encoding %d",
327 i, p->aBody[i].zMimetype, p->aBody[i].encoding);
328 if( p->aBody[i].zFilename ){
329 fossil_print(" filename \"%s\"", p->aBody[i].zFilename);
330 }
331 fossil_print("\n");
332 if( strncmp(p->aBody[i].zMimetype,"text/",5)!=0 ) continue;
333 switch( p->aBody[i].encoding ){
334 case EMAILENC_B64: {
335 int n = 0;
336 decodeBase64(p->aBody[i].zContent, &n, p->aBody[i].zContent);
337 fossil_print("%s", p->aBody[i].zContent);
338 if( n && p->aBody[i].zContent[n-1]!='\n' ) fossil_print("\n");
339 break;
340 }
341 case EMAILENC_QUOTED: {
342 int n = 0;
343 decodeQuotedPrintable(p->aBody[i].zContent, &n);
344 fossil_print("%s", p->aBody[i].zContent);
345 if( n && p->aBody[i].zContent[n-1]!='\n' ) fossil_print("\n");
346 break;
347 }
348 default: {
349 fossil_print("%s\n", p->aBody[i].zContent);
350 break;
351 }
352 }
353 }
354 emailtoc_free(p);
355 blob_reset(&email);
356 }
357
358 /*
359 ** Add the select/option box to the timeline submenu that shows
360 ** the various email message formats.
361 */
362 static void webmail_f_submenu(void){
363 static const char *const az[] = {
364 "0", "Normal",
365 "1", "Decoded",
366 "2", "Raw",
367 };
368 style_submenu_multichoice("f", sizeof(az)/(2*sizeof(az[0])), az, 0);
369 }
370
371 /*
372 ** If the first N characters of z[] are the name of a header field
373 ** that should be shown in "Normal" mode, then return 1.
374 */
375 static int webmail_normal_header(const char *z, int N){
376 static const char *const az[] = {
377 "To", "Cc", "Bcc", "Date", "From", "Subject",
378 };
379 int i;
380 for(i=0; i<sizeof(az)/sizeof(az[0]); i++){
381 if( sqlite3_strnicmp(z, az[i], N)==0 ) return 1;
382 }
383 return 0;
384 }
385
386 /*
387 ** Paint a page showing a single email message
388 */
389 static void webmail_show_one_message(
390 HQuery *pUrl, /* Calling context */
391 int emailid, /* emailbox.ebid to display */
392 const char *zUser /* User who owns it, or NULL if does not matter */
393 ){
394 Blob sql;
395 Stmt q;
396 int eState = -1;
397 int eTranscript = 0;
398 char zENum[30];
399 style_submenu_element("Index", "%s", url_render(pUrl,"id",0,0,0));
400 webmail_f_submenu();
401 blob_init(&sql, 0, 0);
402 db_begin_transaction();
403 blob_append_sql(&sql,
404 "SELECT decompress(etxt), estate, emailblob.ets"
405 " FROM emailblob, emailbox"
406 " WHERE emailid=emsgid AND ebid=%d",
407 emailid
408 );
409 if( zUser ) blob_append_sql(&sql, " AND euser=%Q", zUser);
410 db_prepare_blob(&q, &sql);
411 blob_reset(&sql);
412 style_set_current_feature("webmail");
413 style_header("Message %d",emailid);
414 if( db_step(&q)==SQLITE_ROW ){
415 Blob msg = db_column_text_as_blob(&q, 0);
416 int eFormat = atoi(PD("f","0"));
417 eState = db_column_int(&q, 1);
418 eTranscript = db_column_int(&q, 2);
419 if( eFormat==2 ){
420 @ <pre>%h(db_column_text(&q, 0))</pre>
421 }else{
422 EmailToc *p = emailtoc_from_email(&msg);
423 int i, j;
424 @ <p>
425 for(i=0; i<p->nHdr; i++){
426 char *z = p->azHdr[i];
427 email_hdr_unfold(z);
428 for(j=0; z[j] && z[j]!=':'; j++){}
429 if( eFormat==0 && !webmail_normal_header(z, j) ) continue;
430 if( z[j]!=':' ){
431 @ %h(z)<br>
432 }else{
433 z[j] = 0;
434 @ <b>%h(z):</b> %h(z+j+1)<br>
435 }
436 }
437 for(i=0; i<p->nBody; i++){
438 @ <hr><b>Messsage Body #%d(i): %h(p->aBody[i].zMimetype) \
439 if( p->aBody[i].zFilename ){
440 @ "%h(p->aBody[i].zFilename)"
441 }
442 @ </b>
443 if( eFormat==0 ){
444 if( strncmp(p->aBody[i].zMimetype, "text/plain", 10)!=0 ) continue;
445 if( p->aBody[i].zFilename ) continue;
446 }else{
447 if( strncmp(p->aBody[i].zMimetype, "text/", 5)!=0 ) continue;
448 }
449 switch( p->aBody[i].encoding ){
450 case EMAILENC_B64: {
451 int n = 0;
452 decodeBase64(p->aBody[i].zContent, &n, p->aBody[i].zContent);
453 break;
454 }
455 case EMAILENC_QUOTED: {
456 int n = 0;
457 decodeQuotedPrintable(p->aBody[i].zContent, &n);
458 break;
459 }
460 }
461 @ <pre>%h(p->aBody[i].zContent)</pre>
462 }
463 }
464 }
465 db_finalize(&q);
466
467 /* Optionally show the SMTP transcript */
468 if( eTranscript>0
469 && db_exists("SELECT 1 FROM emailblob WHERE emailid=%d", eTranscript)
470 ){
471 if( P("ts")==0 ){
472 sqlite3_snprintf(sizeof(zENum), zENum, "%d", emailid);
473 style_submenu_element("SMTP Transcript","%s",
474 url_render(pUrl, "ts", "1", "id", zENum));
475 }else{
476 db_prepare(&q,
477 "SELECT decompress(etxt) FROM emailblob WHERE emailid=%d", eTranscript
478 );
479 if( db_step(&q)==SQLITE_ROW ){
480 const char *zTranscript = db_column_text(&q, 0);
481 @ <hr>
482 @ <pre>%h(zTranscript)</pre>
483 }
484 db_finalize(&q);
485 }
486 }
487
488 if( eState==0 ){
489 /* If is message is currently Unread, change it to Read */
490 blob_append_sql(&sql,
491 "UPDATE emailbox SET estate=1 "
492 " WHERE estate=0 AND ebid=%d",
493 emailid
494 );
495 if( zUser ) blob_append_sql(&sql, " AND euser=%Q", zUser);
496 db_multi_exec("%s", blob_sql_text(&sql));
497 blob_reset(&sql);
498 eState = 1;
499 }
500
501 url_add_parameter(pUrl, "id", 0);
502 sqlite3_snprintf(sizeof(zENum), zENum, "e%d", emailid);
503 if( eState==2 ){
504 style_submenu_element("Undelete","%s",
505 url_render(pUrl,"read","1",zENum,"1"));
506 }
507 if( eState==1 ){
508 style_submenu_element("Delete", "%s",
509 url_render(pUrl,"trash","1",zENum,"1"));
510 style_submenu_element("Mark As Unread", "%s",
511 url_render(pUrl,"unread","1",zENum,"1"));
512 }
513 if( eState==3 ){
514 style_submenu_element("Delete", "%s",
515 url_render(pUrl,"trash","1",zENum,"1"));
516 }
517
518 db_end_transaction(0);
519 style_finish_page();
520 return;
521 }
522
523 /*
524 ** Scan the query parameters looking for parameters with name of the
525 ** form "eN" where N is an integer. For all such integers, change
526 ** the state of every emailbox entry with ebid==N to eStateNew provided
527 ** that either zUser is NULL or matches.
528 **
529 ** Or if eNewState==99, then delete the entries.
530 */
531 static void webmail_change_state(int eNewState, const char *zUser){
532 Blob sql;
533 int sep = '(';
534 int i;
535 const char *zName;
536 int n;
537 if( !cgi_csrf_safe(0) ) return;
538 blob_init(&sql, 0, 0);
539 if( eNewState==99 ){
540 blob_append_sql(&sql, "DELETE FROM emailbox WHERE estate==2 AND ebid IN ");
541 }else{
542 blob_append_sql(&sql, "UPDATE emailbox SET estate=%d WHERE ebid IN ",
543 eNewState);
544 }
545 for(i=0; (zName = cgi_parameter_name(i))!=0; i++){
546 if( zName[0]!='e' ) continue;
547 if( !fossil_isdigit(zName[1]) ) continue;
548 n = atoi(zName+1);
549 blob_append_sql(&sql, "%c%d", sep, n);
550 sep = ',';
551 }
552 if( zUser ){
553 blob_append_sql(&sql, ") AND euser=%Q", zUser);
554 }else{
555 blob_append_sql(&sql, ")");
556 }
557 if( sep==',' ){
558 db_multi_exec("%s", blob_sql_text(&sql));
559 }
560 blob_reset(&sql);
561 }
562
563
564 /*
565 ** Add the select/option box to the timeline submenu that shows
566 ** which messages to include in the index.
567 */
568 static void webmail_d_submenu(void){
569 static const char *const az[] = {
570 "0", "InBox",
571 "1", "Unread",
572 "2", "Trash",
573 "3", "Sent",
574 "4", "Everything",
575 };
576 style_submenu_multichoice("d", sizeof(az)/(2*sizeof(az[0])), az, 0);
577 }
578
579 /*
580 ** WEBPAGE: webmail
581 **
582 ** This page can be used to read content from the EMAILBOX table
583 ** that contains email received by the "fossil smtpd" command.
584 **
585 ** Query parameters:
586 **
587 ** id=N Show a single email entry emailbox.ebid==N
588 ** f=N Display format. 0: decoded 1: raw
589 ** user=USER Show mailbox for USER (admin only).
590 ** user=* Show mailbox for all users (admin only).
591 ** d=N 0: inbox+unread 1: unread-only 2: trash 3: all
592 ** eN Select email entry emailbox.ebid==N
593 ** trash Move selected entries to trash (estate=2)
594 ** read Mark selected entries as read (estate=1)
595 ** unread Mark selected entries as unread (estate=0)
596 **
597 */
598 void webmail_page(void){
599 int emailid;
600 Stmt q;
601 Blob sql;
602 int showAll = 0;
603 const char *zUser = 0;
604 int d = 0; /* Display mode. 0..3. d= query parameter */
605 int pg = 0; /* Page number */
606 int N = 50; /* Results per page */
607 int got; /* Number of results on this page */
608 char zPPg[30]; /* Previous page */
609 char zNPg[30]; /* Next page */
610 HQuery url;
611 login_check_credentials();
612 if( !login_is_individual() ){
613 login_needed(0);
614 return;
615 }
616 style_set_current_feature("webmail");
617 if( !db_table_exists("repository","emailbox") ){
618 style_header("Webmail Not Available");
619 @ <p>This repository is not configured to provide webmail</p>
620 style_finish_page();
621 return;
622 }
623 add_content_sql_commands(g.db);
624 emailid = atoi(PD("id","0"));
625 url_initialize(&url, "webmail");
626 if( g.perm.Admin ){
627 zUser = PD("user",g.zLogin);
628 if( zUser ){
629 url_add_parameter(&url, "user", zUser);
630 if( fossil_strcmp(zUser,"*")==0 ){
631 showAll = 1;
632 zUser = 0;
633 }
634 }
635 }else{
636 zUser = g.zLogin;
637 }
638 if( P("d") ) url_add_parameter(&url, "d", P("d"));
639 if( emailid>0 ){
640 webmail_show_one_message(&url, emailid, zUser);
641 return;
642 }
643 style_header("Webmail");
644 webmail_d_submenu();
645 db_begin_transaction();
646 if( P("trash")!=0 ) webmail_change_state(2,zUser);
647 if( P("unread")!=0 ) webmail_change_state(0,zUser);
648 if( P("read")!=0 ) webmail_change_state(1,zUser);
649 if( P("purge")!=0 ) webmail_change_state(99,zUser);
650 blob_init(&sql, 0, 0);
651 blob_append_sql(&sql,
652 "CREATE TEMP TABLE tmbox AS "
653 "SELECT ebid," /* 0 */
654 " efrom," /* 1 */
655 " datetime(edate,'unixepoch')," /* 2 */
656 " estate," /* 3 */
657 " esubject," /* 4 */
658 " euser" /* 5 */
659 " FROM emailbox"
660 );
661 d = atoi(PD("d","0"));
662 switch( d ){
663 case 0: { /* Show unread and read */
664 blob_append_sql(&sql, " WHERE estate<=1");
665 break;
666 }
667 case 1: { /* Unread messages only */
668 blob_append_sql(&sql, " WHERE estate=0");
669 break;
670 }
671 case 2: { /* Trashcan only */
672 blob_append_sql(&sql, " WHERE estate=2");
673 break;
674 }
675 case 3: { /* Outgoing email only */
676 blob_append_sql(&sql, " WHERE estate=3");
677 break;
678 }
679 case 4: { /* Everything */
680 blob_append_sql(&sql, " WHERE 1");
681 break;
682 }
683 }
684 if( showAll ){
685 style_submenu_element("My Emails", "%s", url_render(&url,"user",0,0,0));
686 }else if( zUser!=0 ){
687 style_submenu_element("All Users", "%s", url_render(&url,"user","*",0,0));
688 if( fossil_strcmp(zUser, g.zLogin)!=0 ){
689 style_submenu_element("My Emails", "%s", url_render(&url,"user",0,0,0));
690 }
691 if( zUser ){
692 blob_append_sql(&sql, " AND euser=%Q", zUser);
693 }else{
694 blob_append_sql(&sql, " AND euser=%Q", g.zLogin);
695 }
696 }else{
697 if( g.perm.Admin ){
698 style_submenu_element("All Users", "%s", url_render(&url,"user","*",0,0));
699 }
700 blob_append_sql(&sql, " AND euser=%Q", g.zLogin);
701 }
702 pg = atoi(PD("pg","0"));
703 blob_append_sql(&sql, " ORDER BY edate DESC limit %d offset %d", N+1, pg*N);
704 db_multi_exec("%s", blob_sql_text(&sql));
705 got = db_int(0, "SELECT count(*) FROM tmbox");
706 db_prepare(&q, "SELECT * FROM tmbox LIMIT %d", N);
707 blob_reset(&sql);
708 @ <form action="%R/webmail" method="POST">
709 @ <input type="hidden" name="d" value="%d(d)">
710 @ <input type="hidden" name="user" value="%h(zUser?zUser:"*")">
711 @ <table border="0" width="100%%">
712 @ <tr><td align="left">
713 if( d==2 ){
714 @ <input type="submit" name="read" value="Undelete">
715 @ <input type="submit" name="purge" value="Delete Permanently">
716 }else{
717 @ <input type="submit" name="trash" value="Delete">
718 if( d!=1 ){
719 @ <input type="submit" name="unread" value="Mark as unread">
720 }
721 @ <input type="submit" name="read" value="Mark as read">
722 }
723 @ <button onclick="webmailSelectAll(); return false;">Select All</button>
724 @ <a href="%h(url_render(&url,0,0,0,0))">refresh</a>
725 @ </td><td align="right">
726 if( pg>0 ){
727 sqlite3_snprintf(sizeof(zPPg), zPPg, "%d", pg-1);
728 @ <a href="%s(url_render(&url,"pg",zPPg,0,0))">&lt; Newer</a>&nbsp;&nbsp;
729 }
730 if( got>50 ){
731 sqlite3_snprintf(sizeof(zNPg),zNPg,"%d",pg+1);
732 @ <a href="%s(url_render(&url,"pg",zNPg,0,0))">Older &gt;</a></td>
733 }
734 @ </table>
735 @ <table>
736 while( db_step(&q)==SQLITE_ROW ){
737 const char *zId = db_column_text(&q,0);
738 const char *zFrom = db_column_text(&q, 1);
739 const char *zDate = db_column_text(&q, 2);
740 const char *zSubject = db_column_text(&q, 4);
741 if( zSubject==0 || zSubject[0]==0 ) zSubject = "(no subject)";
742 @ <tr>
743 @ <td><input type="checkbox" class="webmailckbox" name="e%s(zId)"></td>
744 @ <td>%h(zFrom)</td>
745 @ <td><a href="%h(url_render(&url,"id",zId,0,0))">%h(zSubject)</a> \
746 @ %s(zDate)</td>
747 if( showAll ){
748 const char *zTo = db_column_text(&q,5);
749 @ <td><a href="%h(url_render(&url,"user",zTo,0,0))">%h(zTo)</a></td>
750 }
751 @ </tr>
752 }
753 db_finalize(&q);
754 @ </table>
755 @ </form>
756 @ <script>
757 @ function webmailSelectAll(){
758 @ var x = document.getElementsByClassName("webmailckbox");
759 @ for(i=0; i<x.length; i++){
760 @ x[i].checked = true;
761 @ }
762 @ }
763 @ </script>
764 style_finish_page();
765 db_end_transaction(0);
766 }
767
768 /*
769 ** WEBPAGE: emailblob
770 **
771 ** This page, accessible only to administrators, allows easy viewing of
772 ** the emailblob table - the table that contains the text of email messages
773 ** both inbound and outbound, and transcripts of SMTP sessions.
774 **
775 ** id=N Show the text of emailblob with emailid==N
776 **
777 */
778 void webmail_emailblob_page(void){
779 int id = atoi(PD("id","0"));
780 Stmt q;
781 login_check_credentials();
782 if( !g.perm.Setup ){
783 login_needed(0);
784 return;
785 }
786 add_content_sql_commands(g.db);
787 style_set_current_feature("webmail");
788 style_header("emailblob table");
789 if( id>0 ){
790 style_submenu_element("Index", "%R/emailblob");
791 @ <ul>
792 db_prepare(&q, "SELECT emailid FROM emailblob WHERE ets=%d", id);
793 while( db_step(&q)==SQLITE_ROW ){
794 int id = db_column_int(&q, 0);
795 @ <li> <a href="%R/emailblob?id=%d(id)">emailblob entry %d(id)</a>
796 }
797 db_finalize(&q);
798 db_prepare(&q, "SELECT euser, estate FROM emailbox WHERE emsgid=%d", id);
799 while( db_step(&q)==SQLITE_ROW ){
800 const char *zUser = db_column_text(&q, 0);
801 int e = db_column_int(&q, 1);
802 @ <li> emailbox for %h(zUser) state %d(e)
803 }
804 db_finalize(&q);
805 db_prepare(&q, "SELECT efrom, eto FROM emailoutq WHERE emsgid=%d", id);
806 while( db_step(&q)==SQLITE_ROW ){
807 const char *zFrom = db_column_text(&q, 0);
808 const char *zTo = db_column_text(&q, 1);
809 @ <li> emailoutq message body from %h(zFrom) to %h(zTo)
810 }
811 db_finalize(&q);
812 db_prepare(&q, "SELECT efrom, eto FROM emailoutq WHERE ets=%d", id);
813 while( db_step(&q)==SQLITE_ROW ){
814 const char *zFrom = db_column_text(&q, 0);
815 const char *zTo = db_column_text(&q, 1);
816 @ <li> emailoutq transcript from %h(zFrom) to %h(zTo)
817 }
818 db_finalize(&q);
819 @ </ul>
820 @ <hr>
821 db_prepare(&q, "SELECT decompress(etxt) FROM emailblob WHERE emailid=%d",
822 id);
823 while( db_step(&q)==SQLITE_ROW ){
824 const char *zContent = db_column_text(&q, 0);
825 @ <pre>%h(zContent)</pre>
826 }
827 db_finalize(&q);
828 }else{
829 style_submenu_element("emailoutq table","%R/emailoutq");
830 db_prepare(&q,
831 "SELECT emailid, enref, ets, datetime(etime,'unixepoch'), esz,"
832 " length(etxt)"
833 " FROM emailblob ORDER BY etime DESC, emailid DESC");
834 @ <table border="1" cellpadding="5" cellspacing="0" class="sortable" \
835 @ data-column-types='nnntkk'>
836 @ <thead><tr><th> emailid <th> enref <th> ets <th> etime \
837 @ <th> uncompressed <th> compressed </tr></thead><tbody>
838 while( db_step(&q)==SQLITE_ROW ){
839 int id = db_column_int(&q, 0);
840 int nref = db_column_int(&q, 1);
841 int ets = db_column_int(&q, 2);
842 const char *zDate = db_column_text(&q, 3);
843 int sz = db_column_int(&q,4);
844 int csz = db_column_int(&q,5);
845 @ <tr>
846 @ <td align="right"><a href="%R/emailblob?id=%d(id)">%d(id)</a>
847 @ <td align="right">%d(nref)</td>
848 if( ets>0 ){
849 @ <td align="right">%d(ets)</td>
850 }else{
851 @ <td>&nbsp;</td>
852 }
853 @ <td>%h(zDate)</td>
854 @ <td align="right" data-sortkey='%08x(sz)'>%,d(sz)</td>
855 @ <td align="right" data-sortkey='%08x(csz)'>%,d(csz)</td>
856 @ </tr>
857 }
858 @ </tbody></table>
859 db_finalize(&q);
860 style_table_sorter();
861 }
862 style_finish_page();
863 }
864
865 /*
866 ** WEBPAGE: emailoutq
867 **
868 ** This page, accessible only to administrators, allows easy viewing of
869 ** the emailoutq table - the table that contains the email messages
870 ** that are queued for transmission via SMTP.
871 */
872 void webmail_emailoutq_page(void){
873 Stmt q;
874 login_check_credentials();
875 if( !g.perm.Setup ){
876 login_needed(0);
877 return;
878 }
879 add_content_sql_commands(g.db);
880 style_set_current_feature("webmail");
881 style_header("emailoutq table");
882 style_submenu_element("emailblob table","%R/emailblob");
883 db_prepare(&q,
884 "SELECT edomain, efrom, eto, emsgid, "
885 " datetime(ectime,'unixepoch'),"
886 " datetime(nullif(emtime,0),'unixepoch'),"
887 " ensend, ets"
888 " FROM emailoutq"
889 );
890 @ <table border="1" cellpadding="5" cellspacing="0" class="sortable" \
891 @ data-column-types='tttnttnn'>
892 @ <thead><tr><th> edomain <th> efrom <th> eto <th> emsgid \
893 @ <th> ectime <th> emtime <th> ensend <th> ets </tr></thead><tbody>
894 while( db_step(&q)==SQLITE_ROW ){
895 const char *zDomain = db_column_text(&q, 0);
896 const char *zFrom = db_column_text(&q, 1);
897 const char *zTo = db_column_text(&q, 2);
898 int emsgid = db_column_int(&q, 3);
899 const char *zCTime = db_column_text(&q, 4);
900 const char *zMTime = db_column_text(&q, 5);
901 int ensend = db_column_int(&q, 6);
902 int ets = db_column_int(&q, 7);
903 @ <tr>
904 @ <td>%h(zDomain)
905 @ <td>%h(zFrom)
906 @ <td>%h(zTo)
907 @ <td align="right"><a href="%R/emailblob?id=%d(emsgid)">%d(emsgid)</a>
908 @ <td>%h(zCTime)
909 @ <td>%h(zMTime)
910 @ <td align="right">%d(ensend)
911 if( ets>0 ){
912 @ <td align="right"><a href="%R/emailblob?id=%d(ets)">%d(ets)</a></td>
913 }else{
914 @ <td>&nbsp;</td>
915 }
916 }
917 @ </tbody></table>
918 db_finalize(&q);
919 style_table_sorter();
920 style_finish_page();
921 }
--- a/src/webmail.c
+++ b/src/webmail.c
@@ -1,921 +0,0 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
+14 -8
--- src/wiki.c
+++ src/wiki.c
@@ -367,10 +367,12 @@
367367
@ <li> Create a %z(href("%R/technoteedit"))new tech-note</a>.</li>
368368
}
369369
}
370370
@ <li> %z(href("%R/wcontent"))List of All Wiki Pages</a>
371371
@ available on this server.</li>
372
+ @ <li> %z(href("%R/timeline?y=e"))List of All Tech-notes</a>
373
+ @ available on this server.</li>
372374
if( g.perm.ModWiki ){
373375
@ <li> %z(href("%R/modreq"))Tend to pending moderation requests</a></li>
374376
}
375377
if( search_restrict(SRCH_WIKI)!=0 ){
376378
@ <li> %z(href("%R/wikisrch"))Search</a> for wiki pages containing key
@@ -1978,16 +1980,16 @@
19781980
** <html><body>...</body></html>.
19791981
** -p|--pre If -h|-H is used and the page or technote has
19801982
** the text/plain mimetype, its HTML-escaped output
19811983
** will be wrapped in <pre>...</pre>.
19821984
**
1983
-** > fossil wiki (create|commit) PAGENAME ?FILE? ?OPTIONS?
1985
+** > fossil wiki (create|commit) (PAGENAME | TECHNOTE-COMMENT) ?FILE? ?OPTIONS?
19841986
**
19851987
** Create a new or commit changes to an existing wiki page or
19861988
** technote from FILE or from standard input. PAGENAME is the
1987
-** name of the wiki entry or the timeline comment of the
1988
-** technote.
1989
+** name of the wiki entry. TECHNOTE-COMMENT is the timeline comment of
1990
+** the technote.
19891991
**
19901992
** Options:
19911993
** -M|--mimetype TEXT-FORMAT The mime type of the update.
19921994
** Defaults to the type used by
19931995
** the previous version of the
@@ -1996,16 +1998,20 @@
19961998
** text/x-markdown and text/plain. fossil,
19971999
** markdown or plain can be specified as
19982000
** synonyms of these values.
19992001
** -t|--technote DATETIME Specifies the timestamp of
20002002
** the technote to be created or
2001
-** updated. When updating a tech note
2002
-** the most recently modified tech note
2003
-** with the specified timestamp will be
2004
-** updated.
2003
+** updated. The timestamp specifies when
2004
+** this technote appears in the timeline
2005
+** and is its permanent handle although
2006
+** it may not be unique. When updating
2007
+** a technote the most recently modified
2008
+** tech note with the specified timestamp
2009
+** will be updated.
20052010
** -t|--technote TECHNOTE-ID Specifies the technote to be
2006
-** updated by its technote id.
2011
+** updated by its technote id, which is
2012
+** its UUID.
20072013
** --technote-tags TAGS The set of tags for a technote.
20082014
** --technote-bgcolor COLOR The color used for the technote
20092015
** on the timeline.
20102016
**
20112017
** > fossil wiki list ?OPTIONS?
20122018
--- src/wiki.c
+++ src/wiki.c
@@ -367,10 +367,12 @@
367 @ <li> Create a %z(href("%R/technoteedit"))new tech-note</a>.</li>
368 }
369 }
370 @ <li> %z(href("%R/wcontent"))List of All Wiki Pages</a>
371 @ available on this server.</li>
 
 
372 if( g.perm.ModWiki ){
373 @ <li> %z(href("%R/modreq"))Tend to pending moderation requests</a></li>
374 }
375 if( search_restrict(SRCH_WIKI)!=0 ){
376 @ <li> %z(href("%R/wikisrch"))Search</a> for wiki pages containing key
@@ -1978,16 +1980,16 @@
1978 ** <html><body>...</body></html>.
1979 ** -p|--pre If -h|-H is used and the page or technote has
1980 ** the text/plain mimetype, its HTML-escaped output
1981 ** will be wrapped in <pre>...</pre>.
1982 **
1983 ** > fossil wiki (create|commit) PAGENAME ?FILE? ?OPTIONS?
1984 **
1985 ** Create a new or commit changes to an existing wiki page or
1986 ** technote from FILE or from standard input. PAGENAME is the
1987 ** name of the wiki entry or the timeline comment of the
1988 ** technote.
1989 **
1990 ** Options:
1991 ** -M|--mimetype TEXT-FORMAT The mime type of the update.
1992 ** Defaults to the type used by
1993 ** the previous version of the
@@ -1996,16 +1998,20 @@
1996 ** text/x-markdown and text/plain. fossil,
1997 ** markdown or plain can be specified as
1998 ** synonyms of these values.
1999 ** -t|--technote DATETIME Specifies the timestamp of
2000 ** the technote to be created or
2001 ** updated. When updating a tech note
2002 ** the most recently modified tech note
2003 ** with the specified timestamp will be
2004 ** updated.
 
 
 
2005 ** -t|--technote TECHNOTE-ID Specifies the technote to be
2006 ** updated by its technote id.
 
2007 ** --technote-tags TAGS The set of tags for a technote.
2008 ** --technote-bgcolor COLOR The color used for the technote
2009 ** on the timeline.
2010 **
2011 ** > fossil wiki list ?OPTIONS?
2012
--- src/wiki.c
+++ src/wiki.c
@@ -367,10 +367,12 @@
367 @ <li> Create a %z(href("%R/technoteedit"))new tech-note</a>.</li>
368 }
369 }
370 @ <li> %z(href("%R/wcontent"))List of All Wiki Pages</a>
371 @ available on this server.</li>
372 @ <li> %z(href("%R/timeline?y=e"))List of All Tech-notes</a>
373 @ available on this server.</li>
374 if( g.perm.ModWiki ){
375 @ <li> %z(href("%R/modreq"))Tend to pending moderation requests</a></li>
376 }
377 if( search_restrict(SRCH_WIKI)!=0 ){
378 @ <li> %z(href("%R/wikisrch"))Search</a> for wiki pages containing key
@@ -1978,16 +1980,16 @@
1980 ** <html><body>...</body></html>.
1981 ** -p|--pre If -h|-H is used and the page or technote has
1982 ** the text/plain mimetype, its HTML-escaped output
1983 ** will be wrapped in <pre>...</pre>.
1984 **
1985 ** > fossil wiki (create|commit) (PAGENAME | TECHNOTE-COMMENT) ?FILE? ?OPTIONS?
1986 **
1987 ** Create a new or commit changes to an existing wiki page or
1988 ** technote from FILE or from standard input. PAGENAME is the
1989 ** name of the wiki entry. TECHNOTE-COMMENT is the timeline comment of
1990 ** the technote.
1991 **
1992 ** Options:
1993 ** -M|--mimetype TEXT-FORMAT The mime type of the update.
1994 ** Defaults to the type used by
1995 ** the previous version of the
@@ -1996,16 +1998,20 @@
1998 ** text/x-markdown and text/plain. fossil,
1999 ** markdown or plain can be specified as
2000 ** synonyms of these values.
2001 ** -t|--technote DATETIME Specifies the timestamp of
2002 ** the technote to be created or
2003 ** updated. The timestamp specifies when
2004 ** this technote appears in the timeline
2005 ** and is its permanent handle although
2006 ** it may not be unique. When updating
2007 ** a technote the most recently modified
2008 ** tech note with the specified timestamp
2009 ** will be updated.
2010 ** -t|--technote TECHNOTE-ID Specifies the technote to be
2011 ** updated by its technote id, which is
2012 ** its UUID.
2013 ** --technote-tags TAGS The set of tags for a technote.
2014 ** --technote-bgcolor COLOR The color used for the technote
2015 ** on the timeline.
2016 **
2017 ** > fossil wiki list ?OPTIONS?
2018
+14 -8
--- src/wiki.c
+++ src/wiki.c
@@ -367,10 +367,12 @@
367367
@ <li> Create a %z(href("%R/technoteedit"))new tech-note</a>.</li>
368368
}
369369
}
370370
@ <li> %z(href("%R/wcontent"))List of All Wiki Pages</a>
371371
@ available on this server.</li>
372
+ @ <li> %z(href("%R/timeline?y=e"))List of All Tech-notes</a>
373
+ @ available on this server.</li>
372374
if( g.perm.ModWiki ){
373375
@ <li> %z(href("%R/modreq"))Tend to pending moderation requests</a></li>
374376
}
375377
if( search_restrict(SRCH_WIKI)!=0 ){
376378
@ <li> %z(href("%R/wikisrch"))Search</a> for wiki pages containing key
@@ -1978,16 +1980,16 @@
19781980
** <html><body>...</body></html>.
19791981
** -p|--pre If -h|-H is used and the page or technote has
19801982
** the text/plain mimetype, its HTML-escaped output
19811983
** will be wrapped in <pre>...</pre>.
19821984
**
1983
-** > fossil wiki (create|commit) PAGENAME ?FILE? ?OPTIONS?
1985
+** > fossil wiki (create|commit) (PAGENAME | TECHNOTE-COMMENT) ?FILE? ?OPTIONS?
19841986
**
19851987
** Create a new or commit changes to an existing wiki page or
19861988
** technote from FILE or from standard input. PAGENAME is the
1987
-** name of the wiki entry or the timeline comment of the
1988
-** technote.
1989
+** name of the wiki entry. TECHNOTE-COMMENT is the timeline comment of
1990
+** the technote.
19891991
**
19901992
** Options:
19911993
** -M|--mimetype TEXT-FORMAT The mime type of the update.
19921994
** Defaults to the type used by
19931995
** the previous version of the
@@ -1996,16 +1998,20 @@
19961998
** text/x-markdown and text/plain. fossil,
19971999
** markdown or plain can be specified as
19982000
** synonyms of these values.
19992001
** -t|--technote DATETIME Specifies the timestamp of
20002002
** the technote to be created or
2001
-** updated. When updating a tech note
2002
-** the most recently modified tech note
2003
-** with the specified timestamp will be
2004
-** updated.
2003
+** updated. The timestamp specifies when
2004
+** this technote appears in the timeline
2005
+** and is its permanent handle although
2006
+** it may not be unique. When updating
2007
+** a technote the most recently modified
2008
+** tech note with the specified timestamp
2009
+** will be updated.
20052010
** -t|--technote TECHNOTE-ID Specifies the technote to be
2006
-** updated by its technote id.
2011
+** updated by its technote id, which is
2012
+** its UUID.
20072013
** --technote-tags TAGS The set of tags for a technote.
20082014
** --technote-bgcolor COLOR The color used for the technote
20092015
** on the timeline.
20102016
**
20112017
** > fossil wiki list ?OPTIONS?
20122018
--- src/wiki.c
+++ src/wiki.c
@@ -367,10 +367,12 @@
367 @ <li> Create a %z(href("%R/technoteedit"))new tech-note</a>.</li>
368 }
369 }
370 @ <li> %z(href("%R/wcontent"))List of All Wiki Pages</a>
371 @ available on this server.</li>
 
 
372 if( g.perm.ModWiki ){
373 @ <li> %z(href("%R/modreq"))Tend to pending moderation requests</a></li>
374 }
375 if( search_restrict(SRCH_WIKI)!=0 ){
376 @ <li> %z(href("%R/wikisrch"))Search</a> for wiki pages containing key
@@ -1978,16 +1980,16 @@
1978 ** <html><body>...</body></html>.
1979 ** -p|--pre If -h|-H is used and the page or technote has
1980 ** the text/plain mimetype, its HTML-escaped output
1981 ** will be wrapped in <pre>...</pre>.
1982 **
1983 ** > fossil wiki (create|commit) PAGENAME ?FILE? ?OPTIONS?
1984 **
1985 ** Create a new or commit changes to an existing wiki page or
1986 ** technote from FILE or from standard input. PAGENAME is the
1987 ** name of the wiki entry or the timeline comment of the
1988 ** technote.
1989 **
1990 ** Options:
1991 ** -M|--mimetype TEXT-FORMAT The mime type of the update.
1992 ** Defaults to the type used by
1993 ** the previous version of the
@@ -1996,16 +1998,20 @@
1996 ** text/x-markdown and text/plain. fossil,
1997 ** markdown or plain can be specified as
1998 ** synonyms of these values.
1999 ** -t|--technote DATETIME Specifies the timestamp of
2000 ** the technote to be created or
2001 ** updated. When updating a tech note
2002 ** the most recently modified tech note
2003 ** with the specified timestamp will be
2004 ** updated.
 
 
 
2005 ** -t|--technote TECHNOTE-ID Specifies the technote to be
2006 ** updated by its technote id.
 
2007 ** --technote-tags TAGS The set of tags for a technote.
2008 ** --technote-bgcolor COLOR The color used for the technote
2009 ** on the timeline.
2010 **
2011 ** > fossil wiki list ?OPTIONS?
2012
--- src/wiki.c
+++ src/wiki.c
@@ -367,10 +367,12 @@
367 @ <li> Create a %z(href("%R/technoteedit"))new tech-note</a>.</li>
368 }
369 }
370 @ <li> %z(href("%R/wcontent"))List of All Wiki Pages</a>
371 @ available on this server.</li>
372 @ <li> %z(href("%R/timeline?y=e"))List of All Tech-notes</a>
373 @ available on this server.</li>
374 if( g.perm.ModWiki ){
375 @ <li> %z(href("%R/modreq"))Tend to pending moderation requests</a></li>
376 }
377 if( search_restrict(SRCH_WIKI)!=0 ){
378 @ <li> %z(href("%R/wikisrch"))Search</a> for wiki pages containing key
@@ -1978,16 +1980,16 @@
1980 ** <html><body>...</body></html>.
1981 ** -p|--pre If -h|-H is used and the page or technote has
1982 ** the text/plain mimetype, its HTML-escaped output
1983 ** will be wrapped in <pre>...</pre>.
1984 **
1985 ** > fossil wiki (create|commit) (PAGENAME | TECHNOTE-COMMENT) ?FILE? ?OPTIONS?
1986 **
1987 ** Create a new or commit changes to an existing wiki page or
1988 ** technote from FILE or from standard input. PAGENAME is the
1989 ** name of the wiki entry. TECHNOTE-COMMENT is the timeline comment of
1990 ** the technote.
1991 **
1992 ** Options:
1993 ** -M|--mimetype TEXT-FORMAT The mime type of the update.
1994 ** Defaults to the type used by
1995 ** the previous version of the
@@ -1996,16 +1998,20 @@
1998 ** text/x-markdown and text/plain. fossil,
1999 ** markdown or plain can be specified as
2000 ** synonyms of these values.
2001 ** -t|--technote DATETIME Specifies the timestamp of
2002 ** the technote to be created or
2003 ** updated. The timestamp specifies when
2004 ** this technote appears in the timeline
2005 ** and is its permanent handle although
2006 ** it may not be unique. When updating
2007 ** a technote the most recently modified
2008 ** tech note with the specified timestamp
2009 ** will be updated.
2010 ** -t|--technote TECHNOTE-ID Specifies the technote to be
2011 ** updated by its technote id, which is
2012 ** its UUID.
2013 ** --technote-tags TAGS The set of tags for a technote.
2014 ** --technote-bgcolor COLOR The color used for the technote
2015 ** on the timeline.
2016 **
2017 ** > fossil wiki list ?OPTIONS?
2018
+12 -2
--- test/tester.tcl
+++ test/tester.tcl
@@ -251,11 +251,10 @@
251251
#
252252
# TODO: If the list of supported versionable settings in "db.c" is modified,
253253
# this list (and procedure) most likely needs to be modified as well.
254254
#
255255
set result [list \
256
- allow-symlinks \
257256
binary-glob \
258257
clean-glob \
259258
crlf-glob \
260259
crnl-glob \
261260
dotfiles \
@@ -287,10 +286,16 @@
287286
backoffice-disable \
288287
backoffice-logfile \
289288
backoffice-nodelay \
290289
binary-glob \
291290
case-sensitive \
291
+ chat-alert-sound \
292
+ chat-initial-history \
293
+ chat-inline-images \
294
+ chat-keep-count \
295
+ chat-keep-days \
296
+ chat-poll-timeout \
292297
clean-glob \
293298
clearsign \
294299
comment-format \
295300
crlf-glob \
296301
crnl-glob \
@@ -316,36 +321,41 @@
316321
fileedit-glob \
317322
forbid-delta-manifests \
318323
gdiff-command \
319324
gmerge-command \
320325
hash-digits \
326
+ hooks \
321327
http-port \
322328
https-login \
323329
ignore-glob \
324330
keep-glob \
325331
localauth \
326332
lock-timeout \
327333
main-branch \
334
+ mainmenu \
328335
manifest \
329336
max-loadavg \
330337
max-upload \
331338
mimetypes \
332339
mtime-changes \
333340
pgp-command \
341
+ preferred-diff-type \
334342
proxy \
335343
redirect-to-https \
336344
relative-paths \
337345
repo-cksum \
338346
repolist-skin \
339347
safe-html \
340348
self-register \
349
+ sitemap-extra \
341350
ssh-command \
342351
ssl-ca-location \
343352
ssl-identity \
344353
tclsh \
345354
th1-setup \
346355
th1-uri-regexp \
356
+ user-color-map \
347357
uv-sync \
348358
web-browser]
349359
350360
fossil test-th-eval "hasfeature legacyMvRm"
351361
@@ -602,11 +612,11 @@
602612
proc test_block_in_checkout { name rscript {tscript ""} } {
603613
if {$::outside_fossil_repo || $::dirty_ckout} {
604614
set $::CODE 0
605615
set $::RESULT ""
606616
} else {
607
- run_in_checkout $rscript
617
+ uplevel 1 [list run_in_checkout $rscript]
608618
if {[string length $tscript] == 0} {
609619
return ""
610620
} else {
611621
set code [catch {
612622
uplevel 1 $tscript
613623
--- test/tester.tcl
+++ test/tester.tcl
@@ -251,11 +251,10 @@
251 #
252 # TODO: If the list of supported versionable settings in "db.c" is modified,
253 # this list (and procedure) most likely needs to be modified as well.
254 #
255 set result [list \
256 allow-symlinks \
257 binary-glob \
258 clean-glob \
259 crlf-glob \
260 crnl-glob \
261 dotfiles \
@@ -287,10 +286,16 @@
287 backoffice-disable \
288 backoffice-logfile \
289 backoffice-nodelay \
290 binary-glob \
291 case-sensitive \
 
 
 
 
 
 
292 clean-glob \
293 clearsign \
294 comment-format \
295 crlf-glob \
296 crnl-glob \
@@ -316,36 +321,41 @@
316 fileedit-glob \
317 forbid-delta-manifests \
318 gdiff-command \
319 gmerge-command \
320 hash-digits \
 
321 http-port \
322 https-login \
323 ignore-glob \
324 keep-glob \
325 localauth \
326 lock-timeout \
327 main-branch \
 
328 manifest \
329 max-loadavg \
330 max-upload \
331 mimetypes \
332 mtime-changes \
333 pgp-command \
 
334 proxy \
335 redirect-to-https \
336 relative-paths \
337 repo-cksum \
338 repolist-skin \
339 safe-html \
340 self-register \
 
341 ssh-command \
342 ssl-ca-location \
343 ssl-identity \
344 tclsh \
345 th1-setup \
346 th1-uri-regexp \
 
347 uv-sync \
348 web-browser]
349
350 fossil test-th-eval "hasfeature legacyMvRm"
351
@@ -602,11 +612,11 @@
602 proc test_block_in_checkout { name rscript {tscript ""} } {
603 if {$::outside_fossil_repo || $::dirty_ckout} {
604 set $::CODE 0
605 set $::RESULT ""
606 } else {
607 run_in_checkout $rscript
608 if {[string length $tscript] == 0} {
609 return ""
610 } else {
611 set code [catch {
612 uplevel 1 $tscript
613
--- test/tester.tcl
+++ test/tester.tcl
@@ -251,11 +251,10 @@
251 #
252 # TODO: If the list of supported versionable settings in "db.c" is modified,
253 # this list (and procedure) most likely needs to be modified as well.
254 #
255 set result [list \
 
256 binary-glob \
257 clean-glob \
258 crlf-glob \
259 crnl-glob \
260 dotfiles \
@@ -287,10 +286,16 @@
286 backoffice-disable \
287 backoffice-logfile \
288 backoffice-nodelay \
289 binary-glob \
290 case-sensitive \
291 chat-alert-sound \
292 chat-initial-history \
293 chat-inline-images \
294 chat-keep-count \
295 chat-keep-days \
296 chat-poll-timeout \
297 clean-glob \
298 clearsign \
299 comment-format \
300 crlf-glob \
301 crnl-glob \
@@ -316,36 +321,41 @@
321 fileedit-glob \
322 forbid-delta-manifests \
323 gdiff-command \
324 gmerge-command \
325 hash-digits \
326 hooks \
327 http-port \
328 https-login \
329 ignore-glob \
330 keep-glob \
331 localauth \
332 lock-timeout \
333 main-branch \
334 mainmenu \
335 manifest \
336 max-loadavg \
337 max-upload \
338 mimetypes \
339 mtime-changes \
340 pgp-command \
341 preferred-diff-type \
342 proxy \
343 redirect-to-https \
344 relative-paths \
345 repo-cksum \
346 repolist-skin \
347 safe-html \
348 self-register \
349 sitemap-extra \
350 ssh-command \
351 ssl-ca-location \
352 ssl-identity \
353 tclsh \
354 th1-setup \
355 th1-uri-regexp \
356 user-color-map \
357 uv-sync \
358 web-browser]
359
360 fossil test-th-eval "hasfeature legacyMvRm"
361
@@ -602,11 +612,11 @@
612 proc test_block_in_checkout { name rscript {tscript ""} } {
613 if {$::outside_fossil_repo || $::dirty_ckout} {
614 set $::CODE 0
615 set $::RESULT ""
616 } else {
617 uplevel 1 [list run_in_checkout $rscript]
618 if {[string length $tscript] == 0} {
619 return ""
620 } else {
621 set code [catch {
622 uplevel 1 $tscript
623
+12 -10
--- test/th1.test
+++ test/th1.test
@@ -778,11 +778,12 @@
778778
{TH_ERROR: wrong # args: should be "defHeader"}}
779779
780780
###############################################################################
781781
782782
fossil test-th-eval "defHeader"
783
-test th1-defHeader-2 {[string match *<body> [normalize_result]]}
783
+test th1-defHeader-2 {[string match *<body> [normalize_result]] || \
784
+ [string match "*<body class=\"\$current_feature\">" [normalize_result]]}
784785
785786
###############################################################################
786787
787788
fossil test-th-eval "styleHeader {Page Title Here}"
788789
test th1-header-1 {$RESULT eq {TH_ERROR: repository unavailable}}
@@ -1046,19 +1047,20 @@
10461047
# command lists eliminates a dependence on order.
10471048
#
10481049
fossil test-th-eval "info commands"
10491050
set sorted_result [lsort $RESULT]
10501051
protOut "Sorted: $sorted_result"
1051
-set base_commands {anoncap anycap array artifact break breakpoint catch\
1052
- cgiHeaderLine checkout combobox continue copybtn date decorate \
1053
- defHeader dir enable_output encode64 error expr for getParameter \
1054
- glob_match globalState hascap hasfeature html htmlize http httpize if \
1055
- info insertCsrf lindex linecount list llength lsearch markdown nonce \
1056
- proc puts query randhex redirect regexp reinitialize rename render \
1057
- repository return searchable set setParameter setting stime string \
1058
- styleFooter styleHeader styleScript tclReady trace unset unversioned \
1059
- uplevel upvar utime verifyCsrf verifyLogin wiki}
1052
+set base_commands {anoncap anycap array artifact break breakpoint \
1053
+ builtin_request_js capexpr captureTh1 catch cgiHeaderLine checkout \
1054
+ combobox continue copybtn date decorate defHeader dir enable_htmlify \
1055
+ enable_output encode64 error expr for foreach getParameter glob_match \
1056
+ globalState hascap hasfeature html htmlize http httpize if info \
1057
+ insertCsrf lappend lindex linecount list llength lsearch markdown \
1058
+ nonce proc puts query randhex redirect regexp reinitialize rename \
1059
+ render repository return searchable set setParameter setting stime \
1060
+ string styleFooter styleHeader styleScript tclReady trace unset \
1061
+ unversioned uplevel upvar utime verifyCsrf verifyLogin wiki}
10601062
set tcl_commands {tclEval tclExpr tclInvoke tclIsSafe tclMakeSafe}
10611063
if {$th1Tcl} {
10621064
test th1-info-commands-1 {$sorted_result eq [lsort "$base_commands $tcl_commands"]}
10631065
} else {
10641066
test th1-info-commands-1 {$sorted_result eq [lsort "$base_commands"]}
10651067
--- test/th1.test
+++ test/th1.test
@@ -778,11 +778,12 @@
778 {TH_ERROR: wrong # args: should be "defHeader"}}
779
780 ###############################################################################
781
782 fossil test-th-eval "defHeader"
783 test th1-defHeader-2 {[string match *<body> [normalize_result]]}
 
784
785 ###############################################################################
786
787 fossil test-th-eval "styleHeader {Page Title Here}"
788 test th1-header-1 {$RESULT eq {TH_ERROR: repository unavailable}}
@@ -1046,19 +1047,20 @@
1046 # command lists eliminates a dependence on order.
1047 #
1048 fossil test-th-eval "info commands"
1049 set sorted_result [lsort $RESULT]
1050 protOut "Sorted: $sorted_result"
1051 set base_commands {anoncap anycap array artifact break breakpoint catch\
1052 cgiHeaderLine checkout combobox continue copybtn date decorate \
1053 defHeader dir enable_output encode64 error expr for getParameter \
1054 glob_match globalState hascap hasfeature html htmlize http httpize if \
1055 info insertCsrf lindex linecount list llength lsearch markdown nonce \
1056 proc puts query randhex redirect regexp reinitialize rename render \
1057 repository return searchable set setParameter setting stime string \
1058 styleFooter styleHeader styleScript tclReady trace unset unversioned \
1059 uplevel upvar utime verifyCsrf verifyLogin wiki}
 
1060 set tcl_commands {tclEval tclExpr tclInvoke tclIsSafe tclMakeSafe}
1061 if {$th1Tcl} {
1062 test th1-info-commands-1 {$sorted_result eq [lsort "$base_commands $tcl_commands"]}
1063 } else {
1064 test th1-info-commands-1 {$sorted_result eq [lsort "$base_commands"]}
1065
--- test/th1.test
+++ test/th1.test
@@ -778,11 +778,12 @@
778 {TH_ERROR: wrong # args: should be "defHeader"}}
779
780 ###############################################################################
781
782 fossil test-th-eval "defHeader"
783 test th1-defHeader-2 {[string match *<body> [normalize_result]] || \
784 [string match "*<body class=\"\$current_feature\">" [normalize_result]]}
785
786 ###############################################################################
787
788 fossil test-th-eval "styleHeader {Page Title Here}"
789 test th1-header-1 {$RESULT eq {TH_ERROR: repository unavailable}}
@@ -1046,19 +1047,20 @@
1047 # command lists eliminates a dependence on order.
1048 #
1049 fossil test-th-eval "info commands"
1050 set sorted_result [lsort $RESULT]
1051 protOut "Sorted: $sorted_result"
1052 set base_commands {anoncap anycap array artifact break breakpoint \
1053 builtin_request_js capexpr captureTh1 catch cgiHeaderLine checkout \
1054 combobox continue copybtn date decorate defHeader dir enable_htmlify \
1055 enable_output encode64 error expr for foreach getParameter glob_match \
1056 globalState hascap hasfeature html htmlize http httpize if info \
1057 insertCsrf lappend lindex linecount list llength lsearch markdown \
1058 nonce proc puts query randhex redirect regexp reinitialize rename \
1059 render repository return searchable set setParameter setting stime \
1060 string styleFooter styleHeader styleScript tclReady trace unset \
1061 unversioned uplevel upvar utime verifyCsrf verifyLogin wiki}
1062 set tcl_commands {tclEval tclExpr tclInvoke tclIsSafe tclMakeSafe}
1063 if {$th1Tcl} {
1064 test th1-info-commands-1 {$sorted_result eq [lsort "$base_commands $tcl_commands"]}
1065 } else {
1066 test th1-info-commands-1 {$sorted_result eq [lsort "$base_commands"]}
1067
--- test/unversioned.test
+++ test/unversioned.test
@@ -328,11 +328,11 @@
328328
write_file unversioned-client1.txt "This is unversioned client file #1."
329329
330330
###############################################################################
331331
332332
fossil_maybe_answer y clone $remote uvrepo.fossil
333
-fossil open uvrepo.fossil
333
+fossil open -f uvrepo.fossil
334334
335335
###############################################################################
336336
337337
fossil unversioned list
338338
test unversioned-45 {[normalize_result] eq {}}
339339
--- test/unversioned.test
+++ test/unversioned.test
@@ -328,11 +328,11 @@
328 write_file unversioned-client1.txt "This is unversioned client file #1."
329
330 ###############################################################################
331
332 fossil_maybe_answer y clone $remote uvrepo.fossil
333 fossil open uvrepo.fossil
334
335 ###############################################################################
336
337 fossil unversioned list
338 test unversioned-45 {[normalize_result] eq {}}
339
--- test/unversioned.test
+++ test/unversioned.test
@@ -328,11 +328,11 @@
328 write_file unversioned-client1.txt "This is unversioned client file #1."
329
330 ###############################################################################
331
332 fossil_maybe_answer y clone $remote uvrepo.fossil
333 fossil open -f uvrepo.fossil
334
335 ###############################################################################
336
337 fossil unversioned list
338 test unversioned-45 {[normalize_result] eq {}}
339
+4 -10
--- win/Makefile.dmc
+++ win/Makefile.dmc
@@ -28,13 +28,13 @@
2828
2929
SQLITE_OPTIONS = -DNDEBUG=1 -DSQLITE_DQS=0 -DSQLITE_THREADSAFE=0 -DSQLITE_DEFAULT_MEMSTATUS=0 -DSQLITE_DEFAULT_WAL_SYNCHRONOUS=1 -DSQLITE_LIKE_DOESNT_MATCH_BLOBS -DSQLITE_OMIT_DECLTYPE -DSQLITE_OMIT_DEPRECATED -DSQLITE_OMIT_PROGRESS_CALLBACK -DSQLITE_OMIT_SHARED_CACHE -DSQLITE_OMIT_LOAD_EXTENSION -DSQLITE_MAX_EXPR_DEPTH=0 -DSQLITE_USE_ALLOCA -DSQLITE_ENABLE_LOCKING_STYLE=0 -DSQLITE_DEFAULT_FILE_FORMAT=4 -DSQLITE_ENABLE_EXPLAIN_COMMENTS -DSQLITE_ENABLE_FTS4 -DSQLITE_ENABLE_DBSTAT_VTAB -DSQLITE_ENABLE_JSON1 -DSQLITE_ENABLE_FTS5 -DSQLITE_ENABLE_STMTVTAB -DSQLITE_HAVE_ZLIB -DSQLITE_INTROSPECTION_PRAGMAS -DSQLITE_ENABLE_DBPAGE_VTAB -DSQLITE_TRUSTED_SCHEMA=0
3030
3131
SHELL_OPTIONS = -DNDEBUG=1 -DSQLITE_DQS=0 -DSQLITE_THREADSAFE=0 -DSQLITE_DEFAULT_MEMSTATUS=0 -DSQLITE_DEFAULT_WAL_SYNCHRONOUS=1 -DSQLITE_LIKE_DOESNT_MATCH_BLOBS -DSQLITE_OMIT_DECLTYPE -DSQLITE_OMIT_DEPRECATED -DSQLITE_OMIT_PROGRESS_CALLBACK -DSQLITE_OMIT_SHARED_CACHE -DSQLITE_OMIT_LOAD_EXTENSION -DSQLITE_MAX_EXPR_DEPTH=0 -DSQLITE_USE_ALLOCA -DSQLITE_ENABLE_LOCKING_STYLE=0 -DSQLITE_DEFAULT_FILE_FORMAT=4 -DSQLITE_ENABLE_EXPLAIN_COMMENTS -DSQLITE_ENABLE_FTS4 -DSQLITE_ENABLE_DBSTAT_VTAB -DSQLITE_ENABLE_JSON1 -DSQLITE_ENABLE_FTS5 -DSQLITE_ENABLE_STMTVTAB -DSQLITE_HAVE_ZLIB -DSQLITE_INTROSPECTION_PRAGMAS -DSQLITE_ENABLE_DBPAGE_VTAB -DSQLITE_TRUSTED_SCHEMA=0 -Dmain=sqlite3_shell -DSQLITE_SHELL_IS_UTF8=1 -DSQLITE_OMIT_LOAD_EXTENSION=1 -DUSE_SYSTEM_SQLITE=$(USE_SYSTEM_SQLITE) -DSQLITE_SHELL_DBNAME_PROC=sqlcmd_get_dbname -DSQLITE_SHELL_INIT_PROC=sqlcmd_init_proc -Daccess=file_access -Dsystem=fossil_system -Dgetenv=fossil_getenv -Dfopen=fossil_fopen
3232
33
-SRC = add_.c ajax_.c alerts_.c allrepo_.c attach_.c backlink_.c backoffice_.c bag_.c bisect_.c blob_.c branch_.c browse_.c builtin_.c bundle_.c cache_.c capabilities_.c captcha_.c cgi_.c chat_.c checkin_.c checkout_.c clearsign_.c clone_.c color_.c comformat_.c configure_.c content_.c cookies_.c db_.c delta_.c deltacmd_.c deltafunc_.c descendants_.c diff_.c diffcmd_.c dispatch_.c doc_.c encode_.c etag_.c event_.c export_.c extcgi_.c file_.c fileedit_.c finfo_.c foci_.c forum_.c fshell_.c fusefs_.c fuzz_.c glob_.c graph_.c gzip_.c hname_.c hook_.c http_.c http_socket_.c http_ssl_.c http_transport_.c import_.c info_.c interwiki_.c json_.c json_artifact_.c json_branch_.c json_config_.c json_diff_.c json_dir_.c json_finfo_.c json_login_.c json_query_.c json_report_.c json_status_.c json_tag_.c json_timeline_.c json_user_.c json_wiki_.c leaf_.c loadctrl_.c login_.c lookslike_.c main_.c manifest_.c markdown_.c markdown_html_.c md5_.c merge_.c merge3_.c moderate_.c name_.c path_.c piechart_.c pikchr_.c pikchrshow_.c pivot_.c popen_.c pqueue_.c printf_.c publish_.c purge_.c rebuild_.c regexp_.c repolist_.c report_.c rss_.c schema_.c search_.c security_audit_.c setup_.c setupuser_.c sha1_.c sha1hard_.c sha3_.c shun_.c sitemap_.c skins_.c smtp_.c sqlcmd_.c stash_.c stat_.c statrep_.c style_.c sync_.c tag_.c tar_.c terminal_.c th_main_.c timeline_.c tkt_.c tktsetup_.c undo_.c unicode_.c unversioned_.c update_.c url_.c user_.c utf8_.c util_.c verify_.c vfile_.c webmail_.c wiki_.c wikiformat_.c winfile_.c winhttp_.c xfer_.c xfersetup_.c zip_.c
33
+SRC = add_.c ajax_.c alerts_.c allrepo_.c attach_.c backlink_.c backoffice_.c bag_.c bisect_.c blob_.c branch_.c browse_.c builtin_.c bundle_.c cache_.c capabilities_.c captcha_.c cgi_.c chat_.c checkin_.c checkout_.c clearsign_.c clone_.c color_.c comformat_.c configure_.c content_.c cookies_.c db_.c delta_.c deltacmd_.c deltafunc_.c descendants_.c diff_.c diffcmd_.c dispatch_.c doc_.c encode_.c etag_.c event_.c export_.c extcgi_.c file_.c fileedit_.c finfo_.c foci_.c forum_.c fshell_.c fusefs_.c fuzz_.c glob_.c graph_.c gzip_.c hname_.c hook_.c http_.c http_socket_.c http_ssl_.c http_transport_.c import_.c info_.c interwiki_.c json_.c json_artifact_.c json_branch_.c json_config_.c json_diff_.c json_dir_.c json_finfo_.c json_login_.c json_query_.c json_report_.c json_status_.c json_tag_.c json_timeline_.c json_user_.c json_wiki_.c leaf_.c loadctrl_.c login_.c lookslike_.c main_.c manifest_.c markdown_.c markdown_html_.c md5_.c merge_.c merge3_.c moderate_.c name_.c path_.c piechart_.c pikchr_.c pikchrshow_.c pivot_.c popen_.c pqueue_.c printf_.c publish_.c purge_.c rebuild_.c regexp_.c repolist_.c report_.c rss_.c schema_.c search_.c security_audit_.c setup_.c setupuser_.c sha1_.c sha1hard_.c sha3_.c shun_.c sitemap_.c skins_.c smtp_.c sqlcmd_.c stash_.c stat_.c statrep_.c style_.c sync_.c tag_.c tar_.c terminal_.c th_main_.c timeline_.c tkt_.c tktsetup_.c undo_.c unicode_.c unversioned_.c update_.c url_.c user_.c utf8_.c util_.c verify_.c vfile_.c wiki_.c wikiformat_.c winfile_.c winhttp_.c xfer_.c xfersetup_.c zip_.c
3434
35
-OBJ = $(OBJDIR)\add$O $(OBJDIR)\ajax$O $(OBJDIR)\alerts$O $(OBJDIR)\allrepo$O $(OBJDIR)\attach$O $(OBJDIR)\backlink$O $(OBJDIR)\backoffice$O $(OBJDIR)\bag$O $(OBJDIR)\bisect$O $(OBJDIR)\blob$O $(OBJDIR)\branch$O $(OBJDIR)\browse$O $(OBJDIR)\builtin$O $(OBJDIR)\bundle$O $(OBJDIR)\cache$O $(OBJDIR)\capabilities$O $(OBJDIR)\captcha$O $(OBJDIR)\cgi$O $(OBJDIR)\chat$O $(OBJDIR)\checkin$O $(OBJDIR)\checkout$O $(OBJDIR)\clearsign$O $(OBJDIR)\clone$O $(OBJDIR)\color$O $(OBJDIR)\comformat$O $(OBJDIR)\configure$O $(OBJDIR)\content$O $(OBJDIR)\cookies$O $(OBJDIR)\db$O $(OBJDIR)\delta$O $(OBJDIR)\deltacmd$O $(OBJDIR)\deltafunc$O $(OBJDIR)\descendants$O $(OBJDIR)\diff$O $(OBJDIR)\diffcmd$O $(OBJDIR)\dispatch$O $(OBJDIR)\doc$O $(OBJDIR)\encode$O $(OBJDIR)\etag$O $(OBJDIR)\event$O $(OBJDIR)\export$O $(OBJDIR)\extcgi$O $(OBJDIR)\file$O $(OBJDIR)\fileedit$O $(OBJDIR)\finfo$O $(OBJDIR)\foci$O $(OBJDIR)\forum$O $(OBJDIR)\fshell$O $(OBJDIR)\fusefs$O $(OBJDIR)\fuzz$O $(OBJDIR)\glob$O $(OBJDIR)\graph$O $(OBJDIR)\gzip$O $(OBJDIR)\hname$O $(OBJDIR)\hook$O $(OBJDIR)\http$O $(OBJDIR)\http_socket$O $(OBJDIR)\http_ssl$O $(OBJDIR)\http_transport$O $(OBJDIR)\import$O $(OBJDIR)\info$O $(OBJDIR)\interwiki$O $(OBJDIR)\json$O $(OBJDIR)\json_artifact$O $(OBJDIR)\json_branch$O $(OBJDIR)\json_config$O $(OBJDIR)\json_diff$O $(OBJDIR)\json_dir$O $(OBJDIR)\json_finfo$O $(OBJDIR)\json_login$O $(OBJDIR)\json_query$O $(OBJDIR)\json_report$O $(OBJDIR)\json_status$O $(OBJDIR)\json_tag$O $(OBJDIR)\json_timeline$O $(OBJDIR)\json_user$O $(OBJDIR)\json_wiki$O $(OBJDIR)\leaf$O $(OBJDIR)\loadctrl$O $(OBJDIR)\login$O $(OBJDIR)\lookslike$O $(OBJDIR)\main$O $(OBJDIR)\manifest$O $(OBJDIR)\markdown$O $(OBJDIR)\markdown_html$O $(OBJDIR)\md5$O $(OBJDIR)\merge$O $(OBJDIR)\merge3$O $(OBJDIR)\moderate$O $(OBJDIR)\name$O $(OBJDIR)\path$O $(OBJDIR)\piechart$O $(OBJDIR)\pikchr$O $(OBJDIR)\pikchrshow$O $(OBJDIR)\pivot$O $(OBJDIR)\popen$O $(OBJDIR)\pqueue$O $(OBJDIR)\printf$O $(OBJDIR)\publish$O $(OBJDIR)\purge$O $(OBJDIR)\rebuild$O $(OBJDIR)\regexp$O $(OBJDIR)\repolist$O $(OBJDIR)\report$O $(OBJDIR)\rss$O $(OBJDIR)\schema$O $(OBJDIR)\search$O $(OBJDIR)\security_audit$O $(OBJDIR)\setup$O $(OBJDIR)\setupuser$O $(OBJDIR)\sha1$O $(OBJDIR)\sha1hard$O $(OBJDIR)\sha3$O $(OBJDIR)\shun$O $(OBJDIR)\sitemap$O $(OBJDIR)\skins$O $(OBJDIR)\smtp$O $(OBJDIR)\sqlcmd$O $(OBJDIR)\stash$O $(OBJDIR)\stat$O $(OBJDIR)\statrep$O $(OBJDIR)\style$O $(OBJDIR)\sync$O $(OBJDIR)\tag$O $(OBJDIR)\tar$O $(OBJDIR)\terminal$O $(OBJDIR)\th_main$O $(OBJDIR)\timeline$O $(OBJDIR)\tkt$O $(OBJDIR)\tktsetup$O $(OBJDIR)\undo$O $(OBJDIR)\unicode$O $(OBJDIR)\unversioned$O $(OBJDIR)\update$O $(OBJDIR)\url$O $(OBJDIR)\user$O $(OBJDIR)\utf8$O $(OBJDIR)\util$O $(OBJDIR)\verify$O $(OBJDIR)\vfile$O $(OBJDIR)\webmail$O $(OBJDIR)\wiki$O $(OBJDIR)\wikiformat$O $(OBJDIR)\winfile$O $(OBJDIR)\winhttp$O $(OBJDIR)\xfer$O $(OBJDIR)\xfersetup$O $(OBJDIR)\zip$O $(OBJDIR)\shell$O $(OBJDIR)\sqlite3$O $(OBJDIR)\th$O $(OBJDIR)\th_lang$O
35
+OBJ = $(OBJDIR)\add$O $(OBJDIR)\ajax$O $(OBJDIR)\alerts$O $(OBJDIR)\allrepo$O $(OBJDIR)\attach$O $(OBJDIR)\backlink$O $(OBJDIR)\backoffice$O $(OBJDIR)\bag$O $(OBJDIR)\bisect$O $(OBJDIR)\blob$O $(OBJDIR)\branch$O $(OBJDIR)\browse$O $(OBJDIR)\builtin$O $(OBJDIR)\bundle$O $(OBJDIR)\cache$O $(OBJDIR)\capabilities$O $(OBJDIR)\captcha$O $(OBJDIR)\cgi$O $(OBJDIR)\chat$O $(OBJDIR)\checkin$O $(OBJDIR)\checkout$O $(OBJDIR)\clearsign$O $(OBJDIR)\clone$O $(OBJDIR)\color$O $(OBJDIR)\comformat$O $(OBJDIR)\configure$O $(OBJDIR)\content$O $(OBJDIR)\cookies$O $(OBJDIR)\db$O $(OBJDIR)\delta$O $(OBJDIR)\deltacmd$O $(OBJDIR)\deltafunc$O $(OBJDIR)\descendants$O $(OBJDIR)\diff$O $(OBJDIR)\diffcmd$O $(OBJDIR)\dispatch$O $(OBJDIR)\doc$O $(OBJDIR)\encode$O $(OBJDIR)\etag$O $(OBJDIR)\event$O $(OBJDIR)\export$O $(OBJDIR)\extcgi$O $(OBJDIR)\file$O $(OBJDIR)\fileedit$O $(OBJDIR)\finfo$O $(OBJDIR)\foci$O $(OBJDIR)\forum$O $(OBJDIR)\fshell$O $(OBJDIR)\fusefs$O $(OBJDIR)\fuzz$O $(OBJDIR)\glob$O $(OBJDIR)\graph$O $(OBJDIR)\gzip$O $(OBJDIR)\hname$O $(OBJDIR)\hook$O $(OBJDIR)\http$O $(OBJDIR)\http_socket$O $(OBJDIR)\http_ssl$O $(OBJDIR)\http_transport$O $(OBJDIR)\import$O $(OBJDIR)\info$O $(OBJDIR)\interwiki$O $(OBJDIR)\json$O $(OBJDIR)\json_artifact$O $(OBJDIR)\json_branch$O $(OBJDIR)\json_config$O $(OBJDIR)\json_diff$O $(OBJDIR)\json_dir$O $(OBJDIR)\json_finfo$O $(OBJDIR)\json_login$O $(OBJDIR)\json_query$O $(OBJDIR)\json_report$O $(OBJDIR)\json_status$O $(OBJDIR)\json_tag$O $(OBJDIR)\json_timeline$O $(OBJDIR)\json_user$O $(OBJDIR)\json_wiki$O $(OBJDIR)\leaf$O $(OBJDIR)\loadctrl$O $(OBJDIR)\login$O $(OBJDIR)\lookslike$O $(OBJDIR)\main$O $(OBJDIR)\manifest$O $(OBJDIR)\markdown$O $(OBJDIR)\markdown_html$O $(OBJDIR)\md5$O $(OBJDIR)\merge$O $(OBJDIR)\merge3$O $(OBJDIR)\moderate$O $(OBJDIR)\name$O $(OBJDIR)\path$O $(OBJDIR)\piechart$O $(OBJDIR)\pikchr$O $(OBJDIR)\pikchrshow$O $(OBJDIR)\pivot$O $(OBJDIR)\popen$O $(OBJDIR)\pqueue$O $(OBJDIR)\printf$O $(OBJDIR)\publish$O $(OBJDIR)\purge$O $(OBJDIR)\rebuild$O $(OBJDIR)\regexp$O $(OBJDIR)\repolist$O $(OBJDIR)\report$O $(OBJDIR)\rss$O $(OBJDIR)\schema$O $(OBJDIR)\search$O $(OBJDIR)\security_audit$O $(OBJDIR)\setup$O $(OBJDIR)\setupuser$O $(OBJDIR)\sha1$O $(OBJDIR)\sha1hard$O $(OBJDIR)\sha3$O $(OBJDIR)\shun$O $(OBJDIR)\sitemap$O $(OBJDIR)\skins$O $(OBJDIR)\smtp$O $(OBJDIR)\sqlcmd$O $(OBJDIR)\stash$O $(OBJDIR)\stat$O $(OBJDIR)\statrep$O $(OBJDIR)\style$O $(OBJDIR)\sync$O $(OBJDIR)\tag$O $(OBJDIR)\tar$O $(OBJDIR)\terminal$O $(OBJDIR)\th_main$O $(OBJDIR)\timeline$O $(OBJDIR)\tkt$O $(OBJDIR)\tktsetup$O $(OBJDIR)\undo$O $(OBJDIR)\unicode$O $(OBJDIR)\unversioned$O $(OBJDIR)\update$O $(OBJDIR)\url$O $(OBJDIR)\user$O $(OBJDIR)\utf8$O $(OBJDIR)\util$O $(OBJDIR)\verify$O $(OBJDIR)\vfile$O $(OBJDIR)\wiki$O $(OBJDIR)\wikiformat$O $(OBJDIR)\winfile$O $(OBJDIR)\winhttp$O $(OBJDIR)\xfer$O $(OBJDIR)\xfersetup$O $(OBJDIR)\zip$O $(OBJDIR)\shell$O $(OBJDIR)\sqlite3$O $(OBJDIR)\th$O $(OBJDIR)\th_lang$O
3636
3737
3838
RC=$(DMDIR)\bin\rcc
3939
RCFLAGS=-32 -w1 -I$(SRCDIR) /D__DMC__
4040
@@ -49,11 +49,11 @@
4949
5050
$(OBJDIR)\fossil.res: $B\win\fossil.rc
5151
$(RC) $(RCFLAGS) -o$@ $**
5252
5353
$(OBJDIR)\link: $B\win\Makefile.dmc $(OBJDIR)\fossil.res
54
- +echo add ajax alerts allrepo attach backlink backoffice bag bisect blob branch browse builtin bundle cache capabilities captcha cgi chat checkin checkout clearsign clone color comformat configure content cookies db delta deltacmd deltafunc descendants diff diffcmd dispatch doc encode etag event export extcgi file fileedit finfo foci forum fshell fusefs fuzz glob graph gzip hname hook http http_socket http_ssl http_transport import info interwiki json json_artifact json_branch json_config json_diff json_dir json_finfo json_login json_query json_report json_status json_tag json_timeline json_user json_wiki leaf loadctrl login lookslike main manifest markdown markdown_html md5 merge merge3 moderate name path piechart pikchr pikchrshow pivot popen pqueue printf publish purge rebuild regexp repolist report rss schema search security_audit setup setupuser sha1 sha1hard sha3 shun sitemap skins smtp sqlcmd stash stat statrep style sync tag tar terminal th_main timeline tkt tktsetup undo unicode unversioned update url user utf8 util verify vfile webmail wiki wikiformat winfile winhttp xfer xfersetup zip shell sqlite3 th th_lang > $@
54
+ +echo add ajax alerts allrepo attach backlink backoffice bag bisect blob branch browse builtin bundle cache capabilities captcha cgi chat checkin checkout clearsign clone color comformat configure content cookies db delta deltacmd deltafunc descendants diff diffcmd dispatch doc encode etag event export extcgi file fileedit finfo foci forum fshell fusefs fuzz glob graph gzip hname hook http http_socket http_ssl http_transport import info interwiki json json_artifact json_branch json_config json_diff json_dir json_finfo json_login json_query json_report json_status json_tag json_timeline json_user json_wiki leaf loadctrl login lookslike main manifest markdown markdown_html md5 merge merge3 moderate name path piechart pikchr pikchrshow pivot popen pqueue printf publish purge rebuild regexp repolist report rss schema search security_audit setup setupuser sha1 sha1hard sha3 shun sitemap skins smtp sqlcmd stash stat statrep style sync tag tar terminal th_main timeline tkt tktsetup undo unicode unversioned update url user utf8 util verify vfile wiki wikiformat winfile winhttp xfer xfersetup zip shell sqlite3 th th_lang > $@
5555
+echo fossil >> $@
5656
+echo fossil >> $@
5757
+echo $(LIBS) >> $@
5858
+echo. >> $@
5959
+echo fossil >> $@
@@ -962,16 +962,10 @@
962962
$(TCC) -o$@ -c vfile_.c
963963
964964
vfile_.c : $(SRCDIR)\vfile.c
965965
+translate$E $** > $@
966966
967
-$(OBJDIR)\webmail$O : webmail_.c webmail.h
968
- $(TCC) -o$@ -c webmail_.c
969
-
970
-webmail_.c : $(SRCDIR)\webmail.c
971
- +translate$E $** > $@
972
-
973967
$(OBJDIR)\wiki$O : wiki_.c wiki.h
974968
$(TCC) -o$@ -c wiki_.c
975969
976970
wiki_.c : $(SRCDIR)\wiki.c
977971
+translate$E $** > $@
@@ -1011,7 +1005,7 @@
10111005
10121006
zip_.c : $(SRCDIR)\zip.c
10131007
+translate$E $** > $@
10141008
10151009
headers: makeheaders$E page_index.h builtin_data.h VERSION.h
1016
- +makeheaders$E add_.c:add.h ajax_.c:ajax.h alerts_.c:alerts.h allrepo_.c:allrepo.h attach_.c:attach.h backlink_.c:backlink.h backoffice_.c:backoffice.h bag_.c:bag.h bisect_.c:bisect.h blob_.c:blob.h branch_.c:branch.h browse_.c:browse.h builtin_.c:builtin.h bundle_.c:bundle.h cache_.c:cache.h capabilities_.c:capabilities.h captcha_.c:captcha.h cgi_.c:cgi.h chat_.c:chat.h checkin_.c:checkin.h checkout_.c:checkout.h clearsign_.c:clearsign.h clone_.c:clone.h color_.c:color.h comformat_.c:comformat.h configure_.c:configure.h content_.c:content.h cookies_.c:cookies.h db_.c:db.h delta_.c:delta.h deltacmd_.c:deltacmd.h deltafunc_.c:deltafunc.h descendants_.c:descendants.h diff_.c:diff.h diffcmd_.c:diffcmd.h dispatch_.c:dispatch.h doc_.c:doc.h encode_.c:encode.h etag_.c:etag.h event_.c:event.h export_.c:export.h extcgi_.c:extcgi.h file_.c:file.h fileedit_.c:fileedit.h finfo_.c:finfo.h foci_.c:foci.h forum_.c:forum.h fshell_.c:fshell.h fusefs_.c:fusefs.h fuzz_.c:fuzz.h glob_.c:glob.h graph_.c:graph.h gzip_.c:gzip.h hname_.c:hname.h hook_.c:hook.h http_.c:http.h http_socket_.c:http_socket.h http_ssl_.c:http_ssl.h http_transport_.c:http_transport.h import_.c:import.h info_.c:info.h interwiki_.c:interwiki.h json_.c:json.h json_artifact_.c:json_artifact.h json_branch_.c:json_branch.h json_config_.c:json_config.h json_diff_.c:json_diff.h json_dir_.c:json_dir.h json_finfo_.c:json_finfo.h json_login_.c:json_login.h json_query_.c:json_query.h json_report_.c:json_report.h json_status_.c:json_status.h json_tag_.c:json_tag.h json_timeline_.c:json_timeline.h json_user_.c:json_user.h json_wiki_.c:json_wiki.h leaf_.c:leaf.h loadctrl_.c:loadctrl.h login_.c:login.h lookslike_.c:lookslike.h main_.c:main.h manifest_.c:manifest.h markdown_.c:markdown.h markdown_html_.c:markdown_html.h md5_.c:md5.h merge_.c:merge.h merge3_.c:merge3.h moderate_.c:moderate.h name_.c:name.h path_.c:path.h piechart_.c:piechart.h pikchr_.c:pikchr.h pikchrshow_.c:pikchrshow.h pivot_.c:pivot.h popen_.c:popen.h pqueue_.c:pqueue.h printf_.c:printf.h publish_.c:publish.h purge_.c:purge.h rebuild_.c:rebuild.h regexp_.c:regexp.h repolist_.c:repolist.h report_.c:report.h rss_.c:rss.h schema_.c:schema.h search_.c:search.h security_audit_.c:security_audit.h setup_.c:setup.h setupuser_.c:setupuser.h sha1_.c:sha1.h sha1hard_.c:sha1hard.h sha3_.c:sha3.h shun_.c:shun.h sitemap_.c:sitemap.h skins_.c:skins.h smtp_.c:smtp.h sqlcmd_.c:sqlcmd.h stash_.c:stash.h stat_.c:stat.h statrep_.c:statrep.h style_.c:style.h sync_.c:sync.h tag_.c:tag.h tar_.c:tar.h terminal_.c:terminal.h th_main_.c:th_main.h timeline_.c:timeline.h tkt_.c:tkt.h tktsetup_.c:tktsetup.h undo_.c:undo.h unicode_.c:unicode.h unversioned_.c:unversioned.h update_.c:update.h url_.c:url.h user_.c:user.h utf8_.c:utf8.h util_.c:util.h verify_.c:verify.h vfile_.c:vfile.h webmail_.c:webmail.h wiki_.c:wiki.h wikiformat_.c:wikiformat.h winfile_.c:winfile.h winhttp_.c:winhttp.h xfer_.c:xfer.h xfersetup_.c:xfersetup.h zip_.c:zip.h $(SRCDIR)\sqlite3.h $(SRCDIR)\th.h VERSION.h $(SRCDIR)\cson_amalgamation.h
1010
+ +makeheaders$E add_.c:add.h ajax_.c:ajax.h alerts_.c:alerts.h allrepo_.c:allrepo.h attach_.c:attach.h backlink_.c:backlink.h backoffice_.c:backoffice.h bag_.c:bag.h bisect_.c:bisect.h blob_.c:blob.h branch_.c:branch.h browse_.c:browse.h builtin_.c:builtin.h bundle_.c:bundle.h cache_.c:cache.h capabilities_.c:capabilities.h captcha_.c:captcha.h cgi_.c:cgi.h chat_.c:chat.h checkin_.c:checkin.h checkout_.c:checkout.h clearsign_.c:clearsign.h clone_.c:clone.h color_.c:color.h comformat_.c:comformat.h configure_.c:configure.h content_.c:content.h cookies_.c:cookies.h db_.c:db.h delta_.c:delta.h deltacmd_.c:deltacmd.h deltafunc_.c:deltafunc.h descendants_.c:descendants.h diff_.c:diff.h diffcmd_.c:diffcmd.h dispatch_.c:dispatch.h doc_.c:doc.h encode_.c:encode.h etag_.c:etag.h event_.c:event.h export_.c:export.h extcgi_.c:extcgi.h file_.c:file.h fileedit_.c:fileedit.h finfo_.c:finfo.h foci_.c:foci.h forum_.c:forum.h fshell_.c:fshell.h fusefs_.c:fusefs.h fuzz_.c:fuzz.h glob_.c:glob.h graph_.c:graph.h gzip_.c:gzip.h hname_.c:hname.h hook_.c:hook.h http_.c:http.h http_socket_.c:http_socket.h http_ssl_.c:http_ssl.h http_transport_.c:http_transport.h import_.c:import.h info_.c:info.h interwiki_.c:interwiki.h json_.c:json.h json_artifact_.c:json_artifact.h json_branch_.c:json_branch.h json_config_.c:json_config.h json_diff_.c:json_diff.h json_dir_.c:json_dir.h json_finfo_.c:json_finfo.h json_login_.c:json_login.h json_query_.c:json_query.h json_report_.c:json_report.h json_status_.c:json_status.h json_tag_.c:json_tag.h json_timeline_.c:json_timeline.h json_user_.c:json_user.h json_wiki_.c:json_wiki.h leaf_.c:leaf.h loadctrl_.c:loadctrl.h login_.c:login.h lookslike_.c:lookslike.h main_.c:main.h manifest_.c:manifest.h markdown_.c:markdown.h markdown_html_.c:markdown_html.h md5_.c:md5.h merge_.c:merge.h merge3_.c:merge3.h moderate_.c:moderate.h name_.c:name.h path_.c:path.h piechart_.c:piechart.h pikchr_.c:pikchr.h pikchrshow_.c:pikchrshow.h pivot_.c:pivot.h popen_.c:popen.h pqueue_.c:pqueue.h printf_.c:printf.h publish_.c:publish.h purge_.c:purge.h rebuild_.c:rebuild.h regexp_.c:regexp.h repolist_.c:repolist.h report_.c:report.h rss_.c:rss.h schema_.c:schema.h search_.c:search.h security_audit_.c:security_audit.h setup_.c:setup.h setupuser_.c:setupuser.h sha1_.c:sha1.h sha1hard_.c:sha1hard.h sha3_.c:sha3.h shun_.c:shun.h sitemap_.c:sitemap.h skins_.c:skins.h smtp_.c:smtp.h sqlcmd_.c:sqlcmd.h stash_.c:stash.h stat_.c:stat.h statrep_.c:statrep.h style_.c:style.h sync_.c:sync.h tag_.c:tag.h tar_.c:tar.h terminal_.c:terminal.h th_main_.c:th_main.h timeline_.c:timeline.h tkt_.c:tkt.h tktsetup_.c:tktsetup.h undo_.c:undo.h unicode_.c:unicode.h unversioned_.c:unversioned.h update_.c:update.h url_.c:url.h user_.c:user.h utf8_.c:utf8.h util_.c:util.h verify_.c:verify.h vfile_.c:vfile.h wiki_.c:wiki.h wikiformat_.c:wikiformat.h winfile_.c:winfile.h winhttp_.c:winhttp.h xfer_.c:xfer.h xfersetup_.c:xfersetup.h zip_.c:zip.h $(SRCDIR)\sqlite3.h $(SRCDIR)\th.h VERSION.h $(SRCDIR)\cson_amalgamation.h
10171011
@copy /Y nul: headers
10181012
--- win/Makefile.dmc
+++ win/Makefile.dmc
@@ -28,13 +28,13 @@
28
29 SQLITE_OPTIONS = -DNDEBUG=1 -DSQLITE_DQS=0 -DSQLITE_THREADSAFE=0 -DSQLITE_DEFAULT_MEMSTATUS=0 -DSQLITE_DEFAULT_WAL_SYNCHRONOUS=1 -DSQLITE_LIKE_DOESNT_MATCH_BLOBS -DSQLITE_OMIT_DECLTYPE -DSQLITE_OMIT_DEPRECATED -DSQLITE_OMIT_PROGRESS_CALLBACK -DSQLITE_OMIT_SHARED_CACHE -DSQLITE_OMIT_LOAD_EXTENSION -DSQLITE_MAX_EXPR_DEPTH=0 -DSQLITE_USE_ALLOCA -DSQLITE_ENABLE_LOCKING_STYLE=0 -DSQLITE_DEFAULT_FILE_FORMAT=4 -DSQLITE_ENABLE_EXPLAIN_COMMENTS -DSQLITE_ENABLE_FTS4 -DSQLITE_ENABLE_DBSTAT_VTAB -DSQLITE_ENABLE_JSON1 -DSQLITE_ENABLE_FTS5 -DSQLITE_ENABLE_STMTVTAB -DSQLITE_HAVE_ZLIB -DSQLITE_INTROSPECTION_PRAGMAS -DSQLITE_ENABLE_DBPAGE_VTAB -DSQLITE_TRUSTED_SCHEMA=0
30
31 SHELL_OPTIONS = -DNDEBUG=1 -DSQLITE_DQS=0 -DSQLITE_THREADSAFE=0 -DSQLITE_DEFAULT_MEMSTATUS=0 -DSQLITE_DEFAULT_WAL_SYNCHRONOUS=1 -DSQLITE_LIKE_DOESNT_MATCH_BLOBS -DSQLITE_OMIT_DECLTYPE -DSQLITE_OMIT_DEPRECATED -DSQLITE_OMIT_PROGRESS_CALLBACK -DSQLITE_OMIT_SHARED_CACHE -DSQLITE_OMIT_LOAD_EXTENSION -DSQLITE_MAX_EXPR_DEPTH=0 -DSQLITE_USE_ALLOCA -DSQLITE_ENABLE_LOCKING_STYLE=0 -DSQLITE_DEFAULT_FILE_FORMAT=4 -DSQLITE_ENABLE_EXPLAIN_COMMENTS -DSQLITE_ENABLE_FTS4 -DSQLITE_ENABLE_DBSTAT_VTAB -DSQLITE_ENABLE_JSON1 -DSQLITE_ENABLE_FTS5 -DSQLITE_ENABLE_STMTVTAB -DSQLITE_HAVE_ZLIB -DSQLITE_INTROSPECTION_PRAGMAS -DSQLITE_ENABLE_DBPAGE_VTAB -DSQLITE_TRUSTED_SCHEMA=0 -Dmain=sqlite3_shell -DSQLITE_SHELL_IS_UTF8=1 -DSQLITE_OMIT_LOAD_EXTENSION=1 -DUSE_SYSTEM_SQLITE=$(USE_SYSTEM_SQLITE) -DSQLITE_SHELL_DBNAME_PROC=sqlcmd_get_dbname -DSQLITE_SHELL_INIT_PROC=sqlcmd_init_proc -Daccess=file_access -Dsystem=fossil_system -Dgetenv=fossil_getenv -Dfopen=fossil_fopen
32
33 SRC = add_.c ajax_.c alerts_.c allrepo_.c attach_.c backlink_.c backoffice_.c bag_.c bisect_.c blob_.c branch_.c browse_.c builtin_.c bundle_.c cache_.c capabilities_.c captcha_.c cgi_.c chat_.c checkin_.c checkout_.c clearsign_.c clone_.c color_.c comformat_.c configure_.c content_.c cookies_.c db_.c delta_.c deltacmd_.c deltafunc_.c descendants_.c diff_.c diffcmd_.c dispatch_.c doc_.c encode_.c etag_.c event_.c export_.c extcgi_.c file_.c fileedit_.c finfo_.c foci_.c forum_.c fshell_.c fusefs_.c fuzz_.c glob_.c graph_.c gzip_.c hname_.c hook_.c http_.c http_socket_.c http_ssl_.c http_transport_.c import_.c info_.c interwiki_.c json_.c json_artifact_.c json_branch_.c json_config_.c json_diff_.c json_dir_.c json_finfo_.c json_login_.c json_query_.c json_report_.c json_status_.c json_tag_.c json_timeline_.c json_user_.c json_wiki_.c leaf_.c loadctrl_.c login_.c lookslike_.c main_.c manifest_.c markdown_.c markdown_html_.c md5_.c merge_.c merge3_.c moderate_.c name_.c path_.c piechart_.c pikchr_.c pikchrshow_.c pivot_.c popen_.c pqueue_.c printf_.c publish_.c purge_.c rebuild_.c regexp_.c repolist_.c report_.c rss_.c schema_.c search_.c security_audit_.c setup_.c setupuser_.c sha1_.c sha1hard_.c sha3_.c shun_.c sitemap_.c skins_.c smtp_.c sqlcmd_.c stash_.c stat_.c statrep_.c style_.c sync_.c tag_.c tar_.c terminal_.c th_main_.c timeline_.c tkt_.c tktsetup_.c undo_.c unicode_.c unversioned_.c update_.c url_.c user_.c utf8_.c util_.c verify_.c vfile_.c webmail_.c wiki_.c wikiformat_.c winfile_.c winhttp_.c xfer_.c xfersetup_.c zip_.c
34
35 OBJ = $(OBJDIR)\add$O $(OBJDIR)\ajax$O $(OBJDIR)\alerts$O $(OBJDIR)\allrepo$O $(OBJDIR)\attach$O $(OBJDIR)\backlink$O $(OBJDIR)\backoffice$O $(OBJDIR)\bag$O $(OBJDIR)\bisect$O $(OBJDIR)\blob$O $(OBJDIR)\branch$O $(OBJDIR)\browse$O $(OBJDIR)\builtin$O $(OBJDIR)\bundle$O $(OBJDIR)\cache$O $(OBJDIR)\capabilities$O $(OBJDIR)\captcha$O $(OBJDIR)\cgi$O $(OBJDIR)\chat$O $(OBJDIR)\checkin$O $(OBJDIR)\checkout$O $(OBJDIR)\clearsign$O $(OBJDIR)\clone$O $(OBJDIR)\color$O $(OBJDIR)\comformat$O $(OBJDIR)\configure$O $(OBJDIR)\content$O $(OBJDIR)\cookies$O $(OBJDIR)\db$O $(OBJDIR)\delta$O $(OBJDIR)\deltacmd$O $(OBJDIR)\deltafunc$O $(OBJDIR)\descendants$O $(OBJDIR)\diff$O $(OBJDIR)\diffcmd$O $(OBJDIR)\dispatch$O $(OBJDIR)\doc$O $(OBJDIR)\encode$O $(OBJDIR)\etag$O $(OBJDIR)\event$O $(OBJDIR)\export$O $(OBJDIR)\extcgi$O $(OBJDIR)\file$O $(OBJDIR)\fileedit$O $(OBJDIR)\finfo$O $(OBJDIR)\foci$O $(OBJDIR)\forum$O $(OBJDIR)\fshell$O $(OBJDIR)\fusefs$O $(OBJDIR)\fuzz$O $(OBJDIR)\glob$O $(OBJDIR)\graph$O $(OBJDIR)\gzip$O $(OBJDIR)\hname$O $(OBJDIR)\hook$O $(OBJDIR)\http$O $(OBJDIR)\http_socket$O $(OBJDIR)\http_ssl$O $(OBJDIR)\http_transport$O $(OBJDIR)\import$O $(OBJDIR)\info$O $(OBJDIR)\interwiki$O $(OBJDIR)\json$O $(OBJDIR)\json_artifact$O $(OBJDIR)\json_branch$O $(OBJDIR)\json_config$O $(OBJDIR)\json_diff$O $(OBJDIR)\json_dir$O $(OBJDIR)\json_finfo$O $(OBJDIR)\json_login$O $(OBJDIR)\json_query$O $(OBJDIR)\json_report$O $(OBJDIR)\json_status$O $(OBJDIR)\json_tag$O $(OBJDIR)\json_timeline$O $(OBJDIR)\json_user$O $(OBJDIR)\json_wiki$O $(OBJDIR)\leaf$O $(OBJDIR)\loadctrl$O $(OBJDIR)\login$O $(OBJDIR)\lookslike$O $(OBJDIR)\main$O $(OBJDIR)\manifest$O $(OBJDIR)\markdown$O $(OBJDIR)\markdown_html$O $(OBJDIR)\md5$O $(OBJDIR)\merge$O $(OBJDIR)\merge3$O $(OBJDIR)\moderate$O $(OBJDIR)\name$O $(OBJDIR)\path$O $(OBJDIR)\piechart$O $(OBJDIR)\pikchr$O $(OBJDIR)\pikchrshow$O $(OBJDIR)\pivot$O $(OBJDIR)\popen$O $(OBJDIR)\pqueue$O $(OBJDIR)\printf$O $(OBJDIR)\publish$O $(OBJDIR)\purge$O $(OBJDIR)\rebuild$O $(OBJDIR)\regexp$O $(OBJDIR)\repolist$O $(OBJDIR)\report$O $(OBJDIR)\rss$O $(OBJDIR)\schema$O $(OBJDIR)\search$O $(OBJDIR)\security_audit$O $(OBJDIR)\setup$O $(OBJDIR)\setupuser$O $(OBJDIR)\sha1$O $(OBJDIR)\sha1hard$O $(OBJDIR)\sha3$O $(OBJDIR)\shun$O $(OBJDIR)\sitemap$O $(OBJDIR)\skins$O $(OBJDIR)\smtp$O $(OBJDIR)\sqlcmd$O $(OBJDIR)\stash$O $(OBJDIR)\stat$O $(OBJDIR)\statrep$O $(OBJDIR)\style$O $(OBJDIR)\sync$O $(OBJDIR)\tag$O $(OBJDIR)\tar$O $(OBJDIR)\terminal$O $(OBJDIR)\th_main$O $(OBJDIR)\timeline$O $(OBJDIR)\tkt$O $(OBJDIR)\tktsetup$O $(OBJDIR)\undo$O $(OBJDIR)\unicode$O $(OBJDIR)\unversioned$O $(OBJDIR)\update$O $(OBJDIR)\url$O $(OBJDIR)\user$O $(OBJDIR)\utf8$O $(OBJDIR)\util$O $(OBJDIR)\verify$O $(OBJDIR)\vfile$O $(OBJDIR)\webmail$O $(OBJDIR)\wiki$O $(OBJDIR)\wikiformat$O $(OBJDIR)\winfile$O $(OBJDIR)\winhttp$O $(OBJDIR)\xfer$O $(OBJDIR)\xfersetup$O $(OBJDIR)\zip$O $(OBJDIR)\shell$O $(OBJDIR)\sqlite3$O $(OBJDIR)\th$O $(OBJDIR)\th_lang$O
36
37
38 RC=$(DMDIR)\bin\rcc
39 RCFLAGS=-32 -w1 -I$(SRCDIR) /D__DMC__
40
@@ -49,11 +49,11 @@
49
50 $(OBJDIR)\fossil.res: $B\win\fossil.rc
51 $(RC) $(RCFLAGS) -o$@ $**
52
53 $(OBJDIR)\link: $B\win\Makefile.dmc $(OBJDIR)\fossil.res
54 +echo add ajax alerts allrepo attach backlink backoffice bag bisect blob branch browse builtin bundle cache capabilities captcha cgi chat checkin checkout clearsign clone color comformat configure content cookies db delta deltacmd deltafunc descendants diff diffcmd dispatch doc encode etag event export extcgi file fileedit finfo foci forum fshell fusefs fuzz glob graph gzip hname hook http http_socket http_ssl http_transport import info interwiki json json_artifact json_branch json_config json_diff json_dir json_finfo json_login json_query json_report json_status json_tag json_timeline json_user json_wiki leaf loadctrl login lookslike main manifest markdown markdown_html md5 merge merge3 moderate name path piechart pikchr pikchrshow pivot popen pqueue printf publish purge rebuild regexp repolist report rss schema search security_audit setup setupuser sha1 sha1hard sha3 shun sitemap skins smtp sqlcmd stash stat statrep style sync tag tar terminal th_main timeline tkt tktsetup undo unicode unversioned update url user utf8 util verify vfile webmail wiki wikiformat winfile winhttp xfer xfersetup zip shell sqlite3 th th_lang > $@
55 +echo fossil >> $@
56 +echo fossil >> $@
57 +echo $(LIBS) >> $@
58 +echo. >> $@
59 +echo fossil >> $@
@@ -962,16 +962,10 @@
962 $(TCC) -o$@ -c vfile_.c
963
964 vfile_.c : $(SRCDIR)\vfile.c
965 +translate$E $** > $@
966
967 $(OBJDIR)\webmail$O : webmail_.c webmail.h
968 $(TCC) -o$@ -c webmail_.c
969
970 webmail_.c : $(SRCDIR)\webmail.c
971 +translate$E $** > $@
972
973 $(OBJDIR)\wiki$O : wiki_.c wiki.h
974 $(TCC) -o$@ -c wiki_.c
975
976 wiki_.c : $(SRCDIR)\wiki.c
977 +translate$E $** > $@
@@ -1011,7 +1005,7 @@
1011
1012 zip_.c : $(SRCDIR)\zip.c
1013 +translate$E $** > $@
1014
1015 headers: makeheaders$E page_index.h builtin_data.h VERSION.h
1016 +makeheaders$E add_.c:add.h ajax_.c:ajax.h alerts_.c:alerts.h allrepo_.c:allrepo.h attach_.c:attach.h backlink_.c:backlink.h backoffice_.c:backoffice.h bag_.c:bag.h bisect_.c:bisect.h blob_.c:blob.h branch_.c:branch.h browse_.c:browse.h builtin_.c:builtin.h bundle_.c:bundle.h cache_.c:cache.h capabilities_.c:capabilities.h captcha_.c:captcha.h cgi_.c:cgi.h chat_.c:chat.h checkin_.c:checkin.h checkout_.c:checkout.h clearsign_.c:clearsign.h clone_.c:clone.h color_.c:color.h comformat_.c:comformat.h configure_.c:configure.h content_.c:content.h cookies_.c:cookies.h db_.c:db.h delta_.c:delta.h deltacmd_.c:deltacmd.h deltafunc_.c:deltafunc.h descendants_.c:descendants.h diff_.c:diff.h diffcmd_.c:diffcmd.h dispatch_.c:dispatch.h doc_.c:doc.h encode_.c:encode.h etag_.c:etag.h event_.c:event.h export_.c:export.h extcgi_.c:extcgi.h file_.c:file.h fileedit_.c:fileedit.h finfo_.c:finfo.h foci_.c:foci.h forum_.c:forum.h fshell_.c:fshell.h fusefs_.c:fusefs.h fuzz_.c:fuzz.h glob_.c:glob.h graph_.c:graph.h gzip_.c:gzip.h hname_.c:hname.h hook_.c:hook.h http_.c:http.h http_socket_.c:http_socket.h http_ssl_.c:http_ssl.h http_transport_.c:http_transport.h import_.c:import.h info_.c:info.h interwiki_.c:interwiki.h json_.c:json.h json_artifact_.c:json_artifact.h json_branch_.c:json_branch.h json_config_.c:json_config.h json_diff_.c:json_diff.h json_dir_.c:json_dir.h json_finfo_.c:json_finfo.h json_login_.c:json_login.h json_query_.c:json_query.h json_report_.c:json_report.h json_status_.c:json_status.h json_tag_.c:json_tag.h json_timeline_.c:json_timeline.h json_user_.c:json_user.h json_wiki_.c:json_wiki.h leaf_.c:leaf.h loadctrl_.c:loadctrl.h login_.c:login.h lookslike_.c:lookslike.h main_.c:main.h manifest_.c:manifest.h markdown_.c:markdown.h markdown_html_.c:markdown_html.h md5_.c:md5.h merge_.c:merge.h merge3_.c:merge3.h moderate_.c:moderate.h name_.c:name.h path_.c:path.h piechart_.c:piechart.h pikchr_.c:pikchr.h pikchrshow_.c:pikchrshow.h pivot_.c:pivot.h popen_.c:popen.h pqueue_.c:pqueue.h printf_.c:printf.h publish_.c:publish.h purge_.c:purge.h rebuild_.c:rebuild.h regexp_.c:regexp.h repolist_.c:repolist.h report_.c:report.h rss_.c:rss.h schema_.c:schema.h search_.c:search.h security_audit_.c:security_audit.h setup_.c:setup.h setupuser_.c:setupuser.h sha1_.c:sha1.h sha1hard_.c:sha1hard.h sha3_.c:sha3.h shun_.c:shun.h sitemap_.c:sitemap.h skins_.c:skins.h smtp_.c:smtp.h sqlcmd_.c:sqlcmd.h stash_.c:stash.h stat_.c:stat.h statrep_.c:statrep.h style_.c:style.h sync_.c:sync.h tag_.c:tag.h tar_.c:tar.h terminal_.c:terminal.h th_main_.c:th_main.h timeline_.c:timeline.h tkt_.c:tkt.h tktsetup_.c:tktsetup.h undo_.c:undo.h unicode_.c:unicode.h unversioned_.c:unversioned.h update_.c:update.h url_.c:url.h user_.c:user.h utf8_.c:utf8.h util_.c:util.h verify_.c:verify.h vfile_.c:vfile.h webmail_.c:webmail.h wiki_.c:wiki.h wikiformat_.c:wikiformat.h winfile_.c:winfile.h winhttp_.c:winhttp.h xfer_.c:xfer.h xfersetup_.c:xfersetup.h zip_.c:zip.h $(SRCDIR)\sqlite3.h $(SRCDIR)\th.h VERSION.h $(SRCDIR)\cson_amalgamation.h
1017 @copy /Y nul: headers
1018
--- win/Makefile.dmc
+++ win/Makefile.dmc
@@ -28,13 +28,13 @@
28
29 SQLITE_OPTIONS = -DNDEBUG=1 -DSQLITE_DQS=0 -DSQLITE_THREADSAFE=0 -DSQLITE_DEFAULT_MEMSTATUS=0 -DSQLITE_DEFAULT_WAL_SYNCHRONOUS=1 -DSQLITE_LIKE_DOESNT_MATCH_BLOBS -DSQLITE_OMIT_DECLTYPE -DSQLITE_OMIT_DEPRECATED -DSQLITE_OMIT_PROGRESS_CALLBACK -DSQLITE_OMIT_SHARED_CACHE -DSQLITE_OMIT_LOAD_EXTENSION -DSQLITE_MAX_EXPR_DEPTH=0 -DSQLITE_USE_ALLOCA -DSQLITE_ENABLE_LOCKING_STYLE=0 -DSQLITE_DEFAULT_FILE_FORMAT=4 -DSQLITE_ENABLE_EXPLAIN_COMMENTS -DSQLITE_ENABLE_FTS4 -DSQLITE_ENABLE_DBSTAT_VTAB -DSQLITE_ENABLE_JSON1 -DSQLITE_ENABLE_FTS5 -DSQLITE_ENABLE_STMTVTAB -DSQLITE_HAVE_ZLIB -DSQLITE_INTROSPECTION_PRAGMAS -DSQLITE_ENABLE_DBPAGE_VTAB -DSQLITE_TRUSTED_SCHEMA=0
30
31 SHELL_OPTIONS = -DNDEBUG=1 -DSQLITE_DQS=0 -DSQLITE_THREADSAFE=0 -DSQLITE_DEFAULT_MEMSTATUS=0 -DSQLITE_DEFAULT_WAL_SYNCHRONOUS=1 -DSQLITE_LIKE_DOESNT_MATCH_BLOBS -DSQLITE_OMIT_DECLTYPE -DSQLITE_OMIT_DEPRECATED -DSQLITE_OMIT_PROGRESS_CALLBACK -DSQLITE_OMIT_SHARED_CACHE -DSQLITE_OMIT_LOAD_EXTENSION -DSQLITE_MAX_EXPR_DEPTH=0 -DSQLITE_USE_ALLOCA -DSQLITE_ENABLE_LOCKING_STYLE=0 -DSQLITE_DEFAULT_FILE_FORMAT=4 -DSQLITE_ENABLE_EXPLAIN_COMMENTS -DSQLITE_ENABLE_FTS4 -DSQLITE_ENABLE_DBSTAT_VTAB -DSQLITE_ENABLE_JSON1 -DSQLITE_ENABLE_FTS5 -DSQLITE_ENABLE_STMTVTAB -DSQLITE_HAVE_ZLIB -DSQLITE_INTROSPECTION_PRAGMAS -DSQLITE_ENABLE_DBPAGE_VTAB -DSQLITE_TRUSTED_SCHEMA=0 -Dmain=sqlite3_shell -DSQLITE_SHELL_IS_UTF8=1 -DSQLITE_OMIT_LOAD_EXTENSION=1 -DUSE_SYSTEM_SQLITE=$(USE_SYSTEM_SQLITE) -DSQLITE_SHELL_DBNAME_PROC=sqlcmd_get_dbname -DSQLITE_SHELL_INIT_PROC=sqlcmd_init_proc -Daccess=file_access -Dsystem=fossil_system -Dgetenv=fossil_getenv -Dfopen=fossil_fopen
32
33 SRC = add_.c ajax_.c alerts_.c allrepo_.c attach_.c backlink_.c backoffice_.c bag_.c bisect_.c blob_.c branch_.c browse_.c builtin_.c bundle_.c cache_.c capabilities_.c captcha_.c cgi_.c chat_.c checkin_.c checkout_.c clearsign_.c clone_.c color_.c comformat_.c configure_.c content_.c cookies_.c db_.c delta_.c deltacmd_.c deltafunc_.c descendants_.c diff_.c diffcmd_.c dispatch_.c doc_.c encode_.c etag_.c event_.c export_.c extcgi_.c file_.c fileedit_.c finfo_.c foci_.c forum_.c fshell_.c fusefs_.c fuzz_.c glob_.c graph_.c gzip_.c hname_.c hook_.c http_.c http_socket_.c http_ssl_.c http_transport_.c import_.c info_.c interwiki_.c json_.c json_artifact_.c json_branch_.c json_config_.c json_diff_.c json_dir_.c json_finfo_.c json_login_.c json_query_.c json_report_.c json_status_.c json_tag_.c json_timeline_.c json_user_.c json_wiki_.c leaf_.c loadctrl_.c login_.c lookslike_.c main_.c manifest_.c markdown_.c markdown_html_.c md5_.c merge_.c merge3_.c moderate_.c name_.c path_.c piechart_.c pikchr_.c pikchrshow_.c pivot_.c popen_.c pqueue_.c printf_.c publish_.c purge_.c rebuild_.c regexp_.c repolist_.c report_.c rss_.c schema_.c search_.c security_audit_.c setup_.c setupuser_.c sha1_.c sha1hard_.c sha3_.c shun_.c sitemap_.c skins_.c smtp_.c sqlcmd_.c stash_.c stat_.c statrep_.c style_.c sync_.c tag_.c tar_.c terminal_.c th_main_.c timeline_.c tkt_.c tktsetup_.c undo_.c unicode_.c unversioned_.c update_.c url_.c user_.c utf8_.c util_.c verify_.c vfile_.c wiki_.c wikiformat_.c winfile_.c winhttp_.c xfer_.c xfersetup_.c zip_.c
34
35 OBJ = $(OBJDIR)\add$O $(OBJDIR)\ajax$O $(OBJDIR)\alerts$O $(OBJDIR)\allrepo$O $(OBJDIR)\attach$O $(OBJDIR)\backlink$O $(OBJDIR)\backoffice$O $(OBJDIR)\bag$O $(OBJDIR)\bisect$O $(OBJDIR)\blob$O $(OBJDIR)\branch$O $(OBJDIR)\browse$O $(OBJDIR)\builtin$O $(OBJDIR)\bundle$O $(OBJDIR)\cache$O $(OBJDIR)\capabilities$O $(OBJDIR)\captcha$O $(OBJDIR)\cgi$O $(OBJDIR)\chat$O $(OBJDIR)\checkin$O $(OBJDIR)\checkout$O $(OBJDIR)\clearsign$O $(OBJDIR)\clone$O $(OBJDIR)\color$O $(OBJDIR)\comformat$O $(OBJDIR)\configure$O $(OBJDIR)\content$O $(OBJDIR)\cookies$O $(OBJDIR)\db$O $(OBJDIR)\delta$O $(OBJDIR)\deltacmd$O $(OBJDIR)\deltafunc$O $(OBJDIR)\descendants$O $(OBJDIR)\diff$O $(OBJDIR)\diffcmd$O $(OBJDIR)\dispatch$O $(OBJDIR)\doc$O $(OBJDIR)\encode$O $(OBJDIR)\etag$O $(OBJDIR)\event$O $(OBJDIR)\export$O $(OBJDIR)\extcgi$O $(OBJDIR)\file$O $(OBJDIR)\fileedit$O $(OBJDIR)\finfo$O $(OBJDIR)\foci$O $(OBJDIR)\forum$O $(OBJDIR)\fshell$O $(OBJDIR)\fusefs$O $(OBJDIR)\fuzz$O $(OBJDIR)\glob$O $(OBJDIR)\graph$O $(OBJDIR)\gzip$O $(OBJDIR)\hname$O $(OBJDIR)\hook$O $(OBJDIR)\http$O $(OBJDIR)\http_socket$O $(OBJDIR)\http_ssl$O $(OBJDIR)\http_transport$O $(OBJDIR)\import$O $(OBJDIR)\info$O $(OBJDIR)\interwiki$O $(OBJDIR)\json$O $(OBJDIR)\json_artifact$O $(OBJDIR)\json_branch$O $(OBJDIR)\json_config$O $(OBJDIR)\json_diff$O $(OBJDIR)\json_dir$O $(OBJDIR)\json_finfo$O $(OBJDIR)\json_login$O $(OBJDIR)\json_query$O $(OBJDIR)\json_report$O $(OBJDIR)\json_status$O $(OBJDIR)\json_tag$O $(OBJDIR)\json_timeline$O $(OBJDIR)\json_user$O $(OBJDIR)\json_wiki$O $(OBJDIR)\leaf$O $(OBJDIR)\loadctrl$O $(OBJDIR)\login$O $(OBJDIR)\lookslike$O $(OBJDIR)\main$O $(OBJDIR)\manifest$O $(OBJDIR)\markdown$O $(OBJDIR)\markdown_html$O $(OBJDIR)\md5$O $(OBJDIR)\merge$O $(OBJDIR)\merge3$O $(OBJDIR)\moderate$O $(OBJDIR)\name$O $(OBJDIR)\path$O $(OBJDIR)\piechart$O $(OBJDIR)\pikchr$O $(OBJDIR)\pikchrshow$O $(OBJDIR)\pivot$O $(OBJDIR)\popen$O $(OBJDIR)\pqueue$O $(OBJDIR)\printf$O $(OBJDIR)\publish$O $(OBJDIR)\purge$O $(OBJDIR)\rebuild$O $(OBJDIR)\regexp$O $(OBJDIR)\repolist$O $(OBJDIR)\report$O $(OBJDIR)\rss$O $(OBJDIR)\schema$O $(OBJDIR)\search$O $(OBJDIR)\security_audit$O $(OBJDIR)\setup$O $(OBJDIR)\setupuser$O $(OBJDIR)\sha1$O $(OBJDIR)\sha1hard$O $(OBJDIR)\sha3$O $(OBJDIR)\shun$O $(OBJDIR)\sitemap$O $(OBJDIR)\skins$O $(OBJDIR)\smtp$O $(OBJDIR)\sqlcmd$O $(OBJDIR)\stash$O $(OBJDIR)\stat$O $(OBJDIR)\statrep$O $(OBJDIR)\style$O $(OBJDIR)\sync$O $(OBJDIR)\tag$O $(OBJDIR)\tar$O $(OBJDIR)\terminal$O $(OBJDIR)\th_main$O $(OBJDIR)\timeline$O $(OBJDIR)\tkt$O $(OBJDIR)\tktsetup$O $(OBJDIR)\undo$O $(OBJDIR)\unicode$O $(OBJDIR)\unversioned$O $(OBJDIR)\update$O $(OBJDIR)\url$O $(OBJDIR)\user$O $(OBJDIR)\utf8$O $(OBJDIR)\util$O $(OBJDIR)\verify$O $(OBJDIR)\vfile$O $(OBJDIR)\wiki$O $(OBJDIR)\wikiformat$O $(OBJDIR)\winfile$O $(OBJDIR)\winhttp$O $(OBJDIR)\xfer$O $(OBJDIR)\xfersetup$O $(OBJDIR)\zip$O $(OBJDIR)\shell$O $(OBJDIR)\sqlite3$O $(OBJDIR)\th$O $(OBJDIR)\th_lang$O
36
37
38 RC=$(DMDIR)\bin\rcc
39 RCFLAGS=-32 -w1 -I$(SRCDIR) /D__DMC__
40
@@ -49,11 +49,11 @@
49
50 $(OBJDIR)\fossil.res: $B\win\fossil.rc
51 $(RC) $(RCFLAGS) -o$@ $**
52
53 $(OBJDIR)\link: $B\win\Makefile.dmc $(OBJDIR)\fossil.res
54 +echo add ajax alerts allrepo attach backlink backoffice bag bisect blob branch browse builtin bundle cache capabilities captcha cgi chat checkin checkout clearsign clone color comformat configure content cookies db delta deltacmd deltafunc descendants diff diffcmd dispatch doc encode etag event export extcgi file fileedit finfo foci forum fshell fusefs fuzz glob graph gzip hname hook http http_socket http_ssl http_transport import info interwiki json json_artifact json_branch json_config json_diff json_dir json_finfo json_login json_query json_report json_status json_tag json_timeline json_user json_wiki leaf loadctrl login lookslike main manifest markdown markdown_html md5 merge merge3 moderate name path piechart pikchr pikchrshow pivot popen pqueue printf publish purge rebuild regexp repolist report rss schema search security_audit setup setupuser sha1 sha1hard sha3 shun sitemap skins smtp sqlcmd stash stat statrep style sync tag tar terminal th_main timeline tkt tktsetup undo unicode unversioned update url user utf8 util verify vfile wiki wikiformat winfile winhttp xfer xfersetup zip shell sqlite3 th th_lang > $@
55 +echo fossil >> $@
56 +echo fossil >> $@
57 +echo $(LIBS) >> $@
58 +echo. >> $@
59 +echo fossil >> $@
@@ -962,16 +962,10 @@
962 $(TCC) -o$@ -c vfile_.c
963
964 vfile_.c : $(SRCDIR)\vfile.c
965 +translate$E $** > $@
966
 
 
 
 
 
 
967 $(OBJDIR)\wiki$O : wiki_.c wiki.h
968 $(TCC) -o$@ -c wiki_.c
969
970 wiki_.c : $(SRCDIR)\wiki.c
971 +translate$E $** > $@
@@ -1011,7 +1005,7 @@
1005
1006 zip_.c : $(SRCDIR)\zip.c
1007 +translate$E $** > $@
1008
1009 headers: makeheaders$E page_index.h builtin_data.h VERSION.h
1010 +makeheaders$E add_.c:add.h ajax_.c:ajax.h alerts_.c:alerts.h allrepo_.c:allrepo.h attach_.c:attach.h backlink_.c:backlink.h backoffice_.c:backoffice.h bag_.c:bag.h bisect_.c:bisect.h blob_.c:blob.h branch_.c:branch.h browse_.c:browse.h builtin_.c:builtin.h bundle_.c:bundle.h cache_.c:cache.h capabilities_.c:capabilities.h captcha_.c:captcha.h cgi_.c:cgi.h chat_.c:chat.h checkin_.c:checkin.h checkout_.c:checkout.h clearsign_.c:clearsign.h clone_.c:clone.h color_.c:color.h comformat_.c:comformat.h configure_.c:configure.h content_.c:content.h cookies_.c:cookies.h db_.c:db.h delta_.c:delta.h deltacmd_.c:deltacmd.h deltafunc_.c:deltafunc.h descendants_.c:descendants.h diff_.c:diff.h diffcmd_.c:diffcmd.h dispatch_.c:dispatch.h doc_.c:doc.h encode_.c:encode.h etag_.c:etag.h event_.c:event.h export_.c:export.h extcgi_.c:extcgi.h file_.c:file.h fileedit_.c:fileedit.h finfo_.c:finfo.h foci_.c:foci.h forum_.c:forum.h fshell_.c:fshell.h fusefs_.c:fusefs.h fuzz_.c:fuzz.h glob_.c:glob.h graph_.c:graph.h gzip_.c:gzip.h hname_.c:hname.h hook_.c:hook.h http_.c:http.h http_socket_.c:http_socket.h http_ssl_.c:http_ssl.h http_transport_.c:http_transport.h import_.c:import.h info_.c:info.h interwiki_.c:interwiki.h json_.c:json.h json_artifact_.c:json_artifact.h json_branch_.c:json_branch.h json_config_.c:json_config.h json_diff_.c:json_diff.h json_dir_.c:json_dir.h json_finfo_.c:json_finfo.h json_login_.c:json_login.h json_query_.c:json_query.h json_report_.c:json_report.h json_status_.c:json_status.h json_tag_.c:json_tag.h json_timeline_.c:json_timeline.h json_user_.c:json_user.h json_wiki_.c:json_wiki.h leaf_.c:leaf.h loadctrl_.c:loadctrl.h login_.c:login.h lookslike_.c:lookslike.h main_.c:main.h manifest_.c:manifest.h markdown_.c:markdown.h markdown_html_.c:markdown_html.h md5_.c:md5.h merge_.c:merge.h merge3_.c:merge3.h moderate_.c:moderate.h name_.c:name.h path_.c:path.h piechart_.c:piechart.h pikchr_.c:pikchr.h pikchrshow_.c:pikchrshow.h pivot_.c:pivot.h popen_.c:popen.h pqueue_.c:pqueue.h printf_.c:printf.h publish_.c:publish.h purge_.c:purge.h rebuild_.c:rebuild.h regexp_.c:regexp.h repolist_.c:repolist.h report_.c:report.h rss_.c:rss.h schema_.c:schema.h search_.c:search.h security_audit_.c:security_audit.h setup_.c:setup.h setupuser_.c:setupuser.h sha1_.c:sha1.h sha1hard_.c:sha1hard.h sha3_.c:sha3.h shun_.c:shun.h sitemap_.c:sitemap.h skins_.c:skins.h smtp_.c:smtp.h sqlcmd_.c:sqlcmd.h stash_.c:stash.h stat_.c:stat.h statrep_.c:statrep.h style_.c:style.h sync_.c:sync.h tag_.c:tag.h tar_.c:tar.h terminal_.c:terminal.h th_main_.c:th_main.h timeline_.c:timeline.h tkt_.c:tkt.h tktsetup_.c:tktsetup.h undo_.c:undo.h unicode_.c:unicode.h unversioned_.c:unversioned.h update_.c:update.h url_.c:url.h user_.c:user.h utf8_.c:utf8.h util_.c:util.h verify_.c:verify.h vfile_.c:vfile.h wiki_.c:wiki.h wikiformat_.c:wikiformat.h winfile_.c:winfile.h winhttp_.c:winhttp.h xfer_.c:xfer.h xfersetup_.c:xfersetup.h zip_.c:zip.h $(SRCDIR)\sqlite3.h $(SRCDIR)\th.h VERSION.h $(SRCDIR)\cson_amalgamation.h
1011 @copy /Y nul: headers
1012
--- win/Makefile.mingw
+++ win/Makefile.mingw
@@ -563,11 +563,10 @@
563563
$(SRCDIR)/user.c \
564564
$(SRCDIR)/utf8.c \
565565
$(SRCDIR)/util.c \
566566
$(SRCDIR)/verify.c \
567567
$(SRCDIR)/vfile.c \
568
- $(SRCDIR)/webmail.c \
569568
$(SRCDIR)/wiki.c \
570569
$(SRCDIR)/wikiformat.c \
571570
$(SRCDIR)/winfile.c \
572571
$(SRCDIR)/winhttp.c \
573572
$(SRCDIR)/xfer.c \
@@ -819,11 +818,10 @@
819818
$(OBJDIR)/user_.c \
820819
$(OBJDIR)/utf8_.c \
821820
$(OBJDIR)/util_.c \
822821
$(OBJDIR)/verify_.c \
823822
$(OBJDIR)/vfile_.c \
824
- $(OBJDIR)/webmail_.c \
825823
$(OBJDIR)/wiki_.c \
826824
$(OBJDIR)/wikiformat_.c \
827825
$(OBJDIR)/winfile_.c \
828826
$(OBJDIR)/winhttp_.c \
829827
$(OBJDIR)/xfer_.c \
@@ -969,11 +967,10 @@
969967
$(OBJDIR)/user.o \
970968
$(OBJDIR)/utf8.o \
971969
$(OBJDIR)/util.o \
972970
$(OBJDIR)/verify.o \
973971
$(OBJDIR)/vfile.o \
974
- $(OBJDIR)/webmail.o \
975972
$(OBJDIR)/wiki.o \
976973
$(OBJDIR)/wikiformat.o \
977974
$(OBJDIR)/winfile.o \
978975
$(OBJDIR)/winhttp.o \
979976
$(OBJDIR)/xfer.o \
@@ -1334,11 +1331,10 @@
13341331
$(OBJDIR)/user_.c:$(OBJDIR)/user.h \
13351332
$(OBJDIR)/utf8_.c:$(OBJDIR)/utf8.h \
13361333
$(OBJDIR)/util_.c:$(OBJDIR)/util.h \
13371334
$(OBJDIR)/verify_.c:$(OBJDIR)/verify.h \
13381335
$(OBJDIR)/vfile_.c:$(OBJDIR)/vfile.h \
1339
- $(OBJDIR)/webmail_.c:$(OBJDIR)/webmail.h \
13401336
$(OBJDIR)/wiki_.c:$(OBJDIR)/wiki.h \
13411337
$(OBJDIR)/wikiformat_.c:$(OBJDIR)/wikiformat.h \
13421338
$(OBJDIR)/winfile_.c:$(OBJDIR)/winfile.h \
13431339
$(OBJDIR)/winhttp_.c:$(OBJDIR)/winhttp.h \
13441340
$(OBJDIR)/xfer_.c:$(OBJDIR)/xfer.h \
@@ -2471,18 +2467,10 @@
24712467
$(OBJDIR)/vfile.o: $(OBJDIR)/vfile_.c $(OBJDIR)/vfile.h $(SRCDIR)/config.h
24722468
$(XTCC) -o $(OBJDIR)/vfile.o -c $(OBJDIR)/vfile_.c
24732469
24742470
$(OBJDIR)/vfile.h: $(OBJDIR)/headers
24752471
2476
-$(OBJDIR)/webmail_.c: $(SRCDIR)/webmail.c $(TRANSLATE)
2477
- $(TRANSLATE) $(SRCDIR)/webmail.c >$@
2478
-
2479
-$(OBJDIR)/webmail.o: $(OBJDIR)/webmail_.c $(OBJDIR)/webmail.h $(SRCDIR)/config.h
2480
- $(XTCC) -o $(OBJDIR)/webmail.o -c $(OBJDIR)/webmail_.c
2481
-
2482
-$(OBJDIR)/webmail.h: $(OBJDIR)/headers
2483
-
24842472
$(OBJDIR)/wiki_.c: $(SRCDIR)/wiki.c $(TRANSLATE)
24852473
$(TRANSLATE) $(SRCDIR)/wiki.c >$@
24862474
24872475
$(OBJDIR)/wiki.o: $(OBJDIR)/wiki_.c $(OBJDIR)/wiki.h $(SRCDIR)/config.h
24882476
$(XTCC) -o $(OBJDIR)/wiki.o -c $(OBJDIR)/wiki_.c
24892477
--- win/Makefile.mingw
+++ win/Makefile.mingw
@@ -563,11 +563,10 @@
563 $(SRCDIR)/user.c \
564 $(SRCDIR)/utf8.c \
565 $(SRCDIR)/util.c \
566 $(SRCDIR)/verify.c \
567 $(SRCDIR)/vfile.c \
568 $(SRCDIR)/webmail.c \
569 $(SRCDIR)/wiki.c \
570 $(SRCDIR)/wikiformat.c \
571 $(SRCDIR)/winfile.c \
572 $(SRCDIR)/winhttp.c \
573 $(SRCDIR)/xfer.c \
@@ -819,11 +818,10 @@
819 $(OBJDIR)/user_.c \
820 $(OBJDIR)/utf8_.c \
821 $(OBJDIR)/util_.c \
822 $(OBJDIR)/verify_.c \
823 $(OBJDIR)/vfile_.c \
824 $(OBJDIR)/webmail_.c \
825 $(OBJDIR)/wiki_.c \
826 $(OBJDIR)/wikiformat_.c \
827 $(OBJDIR)/winfile_.c \
828 $(OBJDIR)/winhttp_.c \
829 $(OBJDIR)/xfer_.c \
@@ -969,11 +967,10 @@
969 $(OBJDIR)/user.o \
970 $(OBJDIR)/utf8.o \
971 $(OBJDIR)/util.o \
972 $(OBJDIR)/verify.o \
973 $(OBJDIR)/vfile.o \
974 $(OBJDIR)/webmail.o \
975 $(OBJDIR)/wiki.o \
976 $(OBJDIR)/wikiformat.o \
977 $(OBJDIR)/winfile.o \
978 $(OBJDIR)/winhttp.o \
979 $(OBJDIR)/xfer.o \
@@ -1334,11 +1331,10 @@
1334 $(OBJDIR)/user_.c:$(OBJDIR)/user.h \
1335 $(OBJDIR)/utf8_.c:$(OBJDIR)/utf8.h \
1336 $(OBJDIR)/util_.c:$(OBJDIR)/util.h \
1337 $(OBJDIR)/verify_.c:$(OBJDIR)/verify.h \
1338 $(OBJDIR)/vfile_.c:$(OBJDIR)/vfile.h \
1339 $(OBJDIR)/webmail_.c:$(OBJDIR)/webmail.h \
1340 $(OBJDIR)/wiki_.c:$(OBJDIR)/wiki.h \
1341 $(OBJDIR)/wikiformat_.c:$(OBJDIR)/wikiformat.h \
1342 $(OBJDIR)/winfile_.c:$(OBJDIR)/winfile.h \
1343 $(OBJDIR)/winhttp_.c:$(OBJDIR)/winhttp.h \
1344 $(OBJDIR)/xfer_.c:$(OBJDIR)/xfer.h \
@@ -2471,18 +2467,10 @@
2471 $(OBJDIR)/vfile.o: $(OBJDIR)/vfile_.c $(OBJDIR)/vfile.h $(SRCDIR)/config.h
2472 $(XTCC) -o $(OBJDIR)/vfile.o -c $(OBJDIR)/vfile_.c
2473
2474 $(OBJDIR)/vfile.h: $(OBJDIR)/headers
2475
2476 $(OBJDIR)/webmail_.c: $(SRCDIR)/webmail.c $(TRANSLATE)
2477 $(TRANSLATE) $(SRCDIR)/webmail.c >$@
2478
2479 $(OBJDIR)/webmail.o: $(OBJDIR)/webmail_.c $(OBJDIR)/webmail.h $(SRCDIR)/config.h
2480 $(XTCC) -o $(OBJDIR)/webmail.o -c $(OBJDIR)/webmail_.c
2481
2482 $(OBJDIR)/webmail.h: $(OBJDIR)/headers
2483
2484 $(OBJDIR)/wiki_.c: $(SRCDIR)/wiki.c $(TRANSLATE)
2485 $(TRANSLATE) $(SRCDIR)/wiki.c >$@
2486
2487 $(OBJDIR)/wiki.o: $(OBJDIR)/wiki_.c $(OBJDIR)/wiki.h $(SRCDIR)/config.h
2488 $(XTCC) -o $(OBJDIR)/wiki.o -c $(OBJDIR)/wiki_.c
2489
--- win/Makefile.mingw
+++ win/Makefile.mingw
@@ -563,11 +563,10 @@
563 $(SRCDIR)/user.c \
564 $(SRCDIR)/utf8.c \
565 $(SRCDIR)/util.c \
566 $(SRCDIR)/verify.c \
567 $(SRCDIR)/vfile.c \
 
568 $(SRCDIR)/wiki.c \
569 $(SRCDIR)/wikiformat.c \
570 $(SRCDIR)/winfile.c \
571 $(SRCDIR)/winhttp.c \
572 $(SRCDIR)/xfer.c \
@@ -819,11 +818,10 @@
818 $(OBJDIR)/user_.c \
819 $(OBJDIR)/utf8_.c \
820 $(OBJDIR)/util_.c \
821 $(OBJDIR)/verify_.c \
822 $(OBJDIR)/vfile_.c \
 
823 $(OBJDIR)/wiki_.c \
824 $(OBJDIR)/wikiformat_.c \
825 $(OBJDIR)/winfile_.c \
826 $(OBJDIR)/winhttp_.c \
827 $(OBJDIR)/xfer_.c \
@@ -969,11 +967,10 @@
967 $(OBJDIR)/user.o \
968 $(OBJDIR)/utf8.o \
969 $(OBJDIR)/util.o \
970 $(OBJDIR)/verify.o \
971 $(OBJDIR)/vfile.o \
 
972 $(OBJDIR)/wiki.o \
973 $(OBJDIR)/wikiformat.o \
974 $(OBJDIR)/winfile.o \
975 $(OBJDIR)/winhttp.o \
976 $(OBJDIR)/xfer.o \
@@ -1334,11 +1331,10 @@
1331 $(OBJDIR)/user_.c:$(OBJDIR)/user.h \
1332 $(OBJDIR)/utf8_.c:$(OBJDIR)/utf8.h \
1333 $(OBJDIR)/util_.c:$(OBJDIR)/util.h \
1334 $(OBJDIR)/verify_.c:$(OBJDIR)/verify.h \
1335 $(OBJDIR)/vfile_.c:$(OBJDIR)/vfile.h \
 
1336 $(OBJDIR)/wiki_.c:$(OBJDIR)/wiki.h \
1337 $(OBJDIR)/wikiformat_.c:$(OBJDIR)/wikiformat.h \
1338 $(OBJDIR)/winfile_.c:$(OBJDIR)/winfile.h \
1339 $(OBJDIR)/winhttp_.c:$(OBJDIR)/winhttp.h \
1340 $(OBJDIR)/xfer_.c:$(OBJDIR)/xfer.h \
@@ -2471,18 +2467,10 @@
2467 $(OBJDIR)/vfile.o: $(OBJDIR)/vfile_.c $(OBJDIR)/vfile.h $(SRCDIR)/config.h
2468 $(XTCC) -o $(OBJDIR)/vfile.o -c $(OBJDIR)/vfile_.c
2469
2470 $(OBJDIR)/vfile.h: $(OBJDIR)/headers
2471
 
 
 
 
 
 
 
 
2472 $(OBJDIR)/wiki_.c: $(SRCDIR)/wiki.c $(TRANSLATE)
2473 $(TRANSLATE) $(SRCDIR)/wiki.c >$@
2474
2475 $(OBJDIR)/wiki.o: $(OBJDIR)/wiki_.c $(OBJDIR)/wiki.h $(SRCDIR)/config.h
2476 $(XTCC) -o $(OBJDIR)/wiki.o -c $(OBJDIR)/wiki_.c
2477
--- win/Makefile.mingw.mistachkin
+++ win/Makefile.mingw.mistachkin
@@ -166,15 +166,12 @@
166166
ifndef FOSSIL_ENABLE_MINIZ
167167
SSLCONFIG += --with-zlib-lib=$(PWD)/$(ZLIBDIR) --with-zlib-include=$(PWD)/$(ZLIBDIR) zlib
168168
endif
169169
170170
#### The directories where the OpenSSL include and library files are located.
171
-# The recommended usage here is to use the Sysinternals junction tool
172
-# to create a hard link between an "openssl-1.x" sub-directory of the
173
-# Fossil source code directory and the target OpenSSL source directory.
174171
#
175
-OPENSSLDIR = $(SRCDIR)/../compat/openssl-1.1.1g
172
+OPENSSLDIR = $(SRCDIR)/../compat/openssl
176173
OPENSSLINCDIR = $(OPENSSLDIR)/include
177174
OPENSSLLIBDIR = $(OPENSSLDIR)
178175
179176
#### Either the directory where the Tcl library is installed or the Tcl
180177
# source code directory resides (depending on the value of the macro
@@ -444,14 +441,16 @@
444441
$(SRCDIR)/bundle.c \
445442
$(SRCDIR)/cache.c \
446443
$(SRCDIR)/capabilities.c \
447444
$(SRCDIR)/captcha.c \
448445
$(SRCDIR)/cgi.c \
446
+ $(SRCDIR)/chat.c \
449447
$(SRCDIR)/checkin.c \
450448
$(SRCDIR)/checkout.c \
451449
$(SRCDIR)/clearsign.c \
452450
$(SRCDIR)/clone.c \
451
+ $(SRCDIR)/color.c \
453452
$(SRCDIR)/comformat.c \
454453
$(SRCDIR)/configure.c \
455454
$(SRCDIR)/content.c \
456455
$(SRCDIR)/cookies.c \
457456
$(SRCDIR)/db.c \
@@ -564,21 +563,19 @@
564563
$(SRCDIR)/user.c \
565564
$(SRCDIR)/utf8.c \
566565
$(SRCDIR)/util.c \
567566
$(SRCDIR)/verify.c \
568567
$(SRCDIR)/vfile.c \
569
- $(SRCDIR)/webmail.c \
570568
$(SRCDIR)/wiki.c \
571569
$(SRCDIR)/wikiformat.c \
572570
$(SRCDIR)/winfile.c \
573571
$(SRCDIR)/winhttp.c \
574572
$(SRCDIR)/xfer.c \
575573
$(SRCDIR)/xfersetup.c \
576574
$(SRCDIR)/zip.c
577575
578576
EXTRA_FILES = \
579
- $(SRCDIR)/../skins/aht/details.txt \
580577
$(SRCDIR)/../skins/ardoise/css.txt \
581578
$(SRCDIR)/../skins/ardoise/details.txt \
582579
$(SRCDIR)/../skins/ardoise/footer.txt \
583580
$(SRCDIR)/../skins/ardoise/header.txt \
584581
$(SRCDIR)/../skins/black_and_white/css.txt \
@@ -588,32 +585,26 @@
588585
$(SRCDIR)/../skins/blitz/css.txt \
589586
$(SRCDIR)/../skins/blitz/details.txt \
590587
$(SRCDIR)/../skins/blitz/footer.txt \
591588
$(SRCDIR)/../skins/blitz/header.txt \
592589
$(SRCDIR)/../skins/blitz/ticket.txt \
593
- $(SRCDIR)/../skins/blitz_no_logo/css.txt \
594
- $(SRCDIR)/../skins/blitz_no_logo/details.txt \
595
- $(SRCDIR)/../skins/blitz_no_logo/footer.txt \
596
- $(SRCDIR)/../skins/blitz_no_logo/header.txt \
597
- $(SRCDIR)/../skins/blitz_no_logo/ticket.txt \
598590
$(SRCDIR)/../skins/bootstrap/css.txt \
599591
$(SRCDIR)/../skins/bootstrap/details.txt \
600592
$(SRCDIR)/../skins/bootstrap/footer.txt \
601593
$(SRCDIR)/../skins/bootstrap/header.txt \
594
+ $(SRCDIR)/../skins/darkmode/css.txt \
595
+ $(SRCDIR)/../skins/darkmode/details.txt \
596
+ $(SRCDIR)/../skins/darkmode/footer.txt \
597
+ $(SRCDIR)/../skins/darkmode/header.txt \
602598
$(SRCDIR)/../skins/default/css.txt \
603599
$(SRCDIR)/../skins/default/details.txt \
604600
$(SRCDIR)/../skins/default/footer.txt \
605601
$(SRCDIR)/../skins/default/header.txt \
606
- $(SRCDIR)/../skins/default/js.txt \
607602
$(SRCDIR)/../skins/eagle/css.txt \
608603
$(SRCDIR)/../skins/eagle/details.txt \
609604
$(SRCDIR)/../skins/eagle/footer.txt \
610605
$(SRCDIR)/../skins/eagle/header.txt \
611
- $(SRCDIR)/../skins/enhanced1/css.txt \
612
- $(SRCDIR)/../skins/enhanced1/details.txt \
613
- $(SRCDIR)/../skins/enhanced1/footer.txt \
614
- $(SRCDIR)/../skins/enhanced1/header.txt \
615606
$(SRCDIR)/../skins/khaki/css.txt \
616607
$(SRCDIR)/../skins/khaki/details.txt \
617608
$(SRCDIR)/../skins/khaki/footer.txt \
618609
$(SRCDIR)/../skins/khaki/header.txt \
619610
$(SRCDIR)/../skins/original/css.txt \
@@ -622,19 +613,20 @@
622613
$(SRCDIR)/../skins/original/header.txt \
623614
$(SRCDIR)/../skins/plain_gray/css.txt \
624615
$(SRCDIR)/../skins/plain_gray/details.txt \
625616
$(SRCDIR)/../skins/plain_gray/footer.txt \
626617
$(SRCDIR)/../skins/plain_gray/header.txt \
627
- $(SRCDIR)/../skins/rounded1/css.txt \
628
- $(SRCDIR)/../skins/rounded1/details.txt \
629
- $(SRCDIR)/../skins/rounded1/footer.txt \
630
- $(SRCDIR)/../skins/rounded1/header.txt \
631618
$(SRCDIR)/../skins/xekri/css.txt \
632619
$(SRCDIR)/../skins/xekri/details.txt \
633620
$(SRCDIR)/../skins/xekri/footer.txt \
634621
$(SRCDIR)/../skins/xekri/header.txt \
635622
$(SRCDIR)/accordion.js \
623
+ $(SRCDIR)/alerts/bflat2.wav \
624
+ $(SRCDIR)/alerts/bflat3.wav \
625
+ $(SRCDIR)/alerts/bloop.wav \
626
+ $(SRCDIR)/alerts/plunk.wav \
627
+ $(SRCDIR)/chat.js \
636628
$(SRCDIR)/ci_edit.js \
637629
$(SRCDIR)/copybtn.js \
638630
$(SRCDIR)/default.css \
639631
$(SRCDIR)/diff.tcl \
640632
$(SRCDIR)/forum.js \
@@ -641,10 +633,11 @@
641633
$(SRCDIR)/fossil.bootstrap.js \
642634
$(SRCDIR)/fossil.confirmer.js \
643635
$(SRCDIR)/fossil.copybutton.js \
644636
$(SRCDIR)/fossil.dom.js \
645637
$(SRCDIR)/fossil.fetch.js \
638
+ $(SRCDIR)/fossil.info-diff.js \
646639
$(SRCDIR)/fossil.numbered-lines.js \
647640
$(SRCDIR)/fossil.page.fileedit.js \
648641
$(SRCDIR)/fossil.page.forumpost.js \
649642
$(SRCDIR)/fossil.page.pikchrshow.js \
650643
$(SRCDIR)/fossil.page.wikiedit.js \
@@ -652,10 +645,11 @@
652645
$(SRCDIR)/fossil.popupwidget.js \
653646
$(SRCDIR)/fossil.storage.js \
654647
$(SRCDIR)/fossil.tabs.js \
655648
$(SRCDIR)/fossil.wikiedit-wysiwyg.js \
656649
$(SRCDIR)/graph.js \
650
+ $(SRCDIR)/hbmenu.js \
657651
$(SRCDIR)/href.js \
658652
$(SRCDIR)/login.js \
659653
$(SRCDIR)/markdown.md \
660654
$(SRCDIR)/menu.js \
661655
$(SRCDIR)/sbsdiff.js \
@@ -702,14 +696,16 @@
702696
$(OBJDIR)/bundle_.c \
703697
$(OBJDIR)/cache_.c \
704698
$(OBJDIR)/capabilities_.c \
705699
$(OBJDIR)/captcha_.c \
706700
$(OBJDIR)/cgi_.c \
701
+ $(OBJDIR)/chat_.c \
707702
$(OBJDIR)/checkin_.c \
708703
$(OBJDIR)/checkout_.c \
709704
$(OBJDIR)/clearsign_.c \
710705
$(OBJDIR)/clone_.c \
706
+ $(OBJDIR)/color_.c \
711707
$(OBJDIR)/comformat_.c \
712708
$(OBJDIR)/configure_.c \
713709
$(OBJDIR)/content_.c \
714710
$(OBJDIR)/cookies_.c \
715711
$(OBJDIR)/db_.c \
@@ -822,11 +818,10 @@
822818
$(OBJDIR)/user_.c \
823819
$(OBJDIR)/utf8_.c \
824820
$(OBJDIR)/util_.c \
825821
$(OBJDIR)/verify_.c \
826822
$(OBJDIR)/vfile_.c \
827
- $(OBJDIR)/webmail_.c \
828823
$(OBJDIR)/wiki_.c \
829824
$(OBJDIR)/wikiformat_.c \
830825
$(OBJDIR)/winfile_.c \
831826
$(OBJDIR)/winhttp_.c \
832827
$(OBJDIR)/xfer_.c \
@@ -850,14 +845,16 @@
850845
$(OBJDIR)/bundle.o \
851846
$(OBJDIR)/cache.o \
852847
$(OBJDIR)/capabilities.o \
853848
$(OBJDIR)/captcha.o \
854849
$(OBJDIR)/cgi.o \
850
+ $(OBJDIR)/chat.o \
855851
$(OBJDIR)/checkin.o \
856852
$(OBJDIR)/checkout.o \
857853
$(OBJDIR)/clearsign.o \
858854
$(OBJDIR)/clone.o \
855
+ $(OBJDIR)/color.o \
859856
$(OBJDIR)/comformat.o \
860857
$(OBJDIR)/configure.o \
861858
$(OBJDIR)/content.o \
862859
$(OBJDIR)/cookies.o \
863860
$(OBJDIR)/db.o \
@@ -970,11 +967,10 @@
970967
$(OBJDIR)/user.o \
971968
$(OBJDIR)/utf8.o \
972969
$(OBJDIR)/util.o \
973970
$(OBJDIR)/verify.o \
974971
$(OBJDIR)/vfile.o \
975
- $(OBJDIR)/webmail.o \
976972
$(OBJDIR)/wiki.o \
977973
$(OBJDIR)/wikiformat.o \
978974
$(OBJDIR)/winfile.o \
979975
$(OBJDIR)/winhttp.o \
980976
$(OBJDIR)/xfer.o \
@@ -1163,13 +1159,13 @@
11631159
11641160
ifdef FOSSIL_BUILD_SSL
11651161
APPTARGETS += openssl
11661162
endif
11671163
1168
-$(APPNAME): $(APPTARGETS) $(OBJDIR)/headers $(CODECHECK1) $(OBJ) $(EXTRAOBJ) $(OBJDIR)/fossil.o
1164
+$(APPNAME): $(APPTARGETS) $(OBJDIR)/headers $(CODECHECK1) $(EXTRAOBJ) $(OBJ) $(OBJDIR)/fossil.o
11691165
$(CODECHECK1) $(TRANS_SRC)
1170
- $(TCC) -o $@ $(OBJ) $(EXTRAOBJ) $(OBJDIR)/fossil.o $(LIB)
1166
+ $(TCC) -o $@ $(EXTRAOBJ) $(OBJ) $(OBJDIR)/fossil.o $(LIB)
11711167
11721168
# This rule prevents make from using its default rules to try build
11731169
# an executable named "manifest" out of the file named "manifest.c"
11741170
#
11751171
$(SRCDIR)/../manifest:
@@ -1213,14 +1209,16 @@
12131209
$(OBJDIR)/bundle_.c:$(OBJDIR)/bundle.h \
12141210
$(OBJDIR)/cache_.c:$(OBJDIR)/cache.h \
12151211
$(OBJDIR)/capabilities_.c:$(OBJDIR)/capabilities.h \
12161212
$(OBJDIR)/captcha_.c:$(OBJDIR)/captcha.h \
12171213
$(OBJDIR)/cgi_.c:$(OBJDIR)/cgi.h \
1214
+ $(OBJDIR)/chat_.c:$(OBJDIR)/chat.h \
12181215
$(OBJDIR)/checkin_.c:$(OBJDIR)/checkin.h \
12191216
$(OBJDIR)/checkout_.c:$(OBJDIR)/checkout.h \
12201217
$(OBJDIR)/clearsign_.c:$(OBJDIR)/clearsign.h \
12211218
$(OBJDIR)/clone_.c:$(OBJDIR)/clone.h \
1219
+ $(OBJDIR)/color_.c:$(OBJDIR)/color.h \
12221220
$(OBJDIR)/comformat_.c:$(OBJDIR)/comformat.h \
12231221
$(OBJDIR)/configure_.c:$(OBJDIR)/configure.h \
12241222
$(OBJDIR)/content_.c:$(OBJDIR)/content.h \
12251223
$(OBJDIR)/cookies_.c:$(OBJDIR)/cookies.h \
12261224
$(OBJDIR)/db_.c:$(OBJDIR)/db.h \
@@ -1333,11 +1331,10 @@
13331331
$(OBJDIR)/user_.c:$(OBJDIR)/user.h \
13341332
$(OBJDIR)/utf8_.c:$(OBJDIR)/utf8.h \
13351333
$(OBJDIR)/util_.c:$(OBJDIR)/util.h \
13361334
$(OBJDIR)/verify_.c:$(OBJDIR)/verify.h \
13371335
$(OBJDIR)/vfile_.c:$(OBJDIR)/vfile.h \
1338
- $(OBJDIR)/webmail_.c:$(OBJDIR)/webmail.h \
13391336
$(OBJDIR)/wiki_.c:$(OBJDIR)/wiki.h \
13401337
$(OBJDIR)/wikiformat_.c:$(OBJDIR)/wikiformat.h \
13411338
$(OBJDIR)/winfile_.c:$(OBJDIR)/winfile.h \
13421339
$(OBJDIR)/winhttp_.c:$(OBJDIR)/winhttp.h \
13431340
$(OBJDIR)/xfer_.c:$(OBJDIR)/xfer.h \
@@ -1493,10 +1490,18 @@
14931490
14941491
$(OBJDIR)/cgi.o: $(OBJDIR)/cgi_.c $(OBJDIR)/cgi.h $(SRCDIR)/config.h
14951492
$(XTCC) -o $(OBJDIR)/cgi.o -c $(OBJDIR)/cgi_.c
14961493
14971494
$(OBJDIR)/cgi.h: $(OBJDIR)/headers
1495
+
1496
+$(OBJDIR)/chat_.c: $(SRCDIR)/chat.c $(TRANSLATE)
1497
+ $(TRANSLATE) $(SRCDIR)/chat.c >$@
1498
+
1499
+$(OBJDIR)/chat.o: $(OBJDIR)/chat_.c $(OBJDIR)/chat.h $(SRCDIR)/config.h
1500
+ $(XTCC) -o $(OBJDIR)/chat.o -c $(OBJDIR)/chat_.c
1501
+
1502
+$(OBJDIR)/chat.h: $(OBJDIR)/headers
14981503
14991504
$(OBJDIR)/checkin_.c: $(SRCDIR)/checkin.c $(TRANSLATE)
15001505
$(TRANSLATE) $(SRCDIR)/checkin.c >$@
15011506
15021507
$(OBJDIR)/checkin.o: $(OBJDIR)/checkin_.c $(OBJDIR)/checkin.h $(SRCDIR)/config.h
@@ -1525,10 +1530,18 @@
15251530
15261531
$(OBJDIR)/clone.o: $(OBJDIR)/clone_.c $(OBJDIR)/clone.h $(SRCDIR)/config.h
15271532
$(XTCC) -o $(OBJDIR)/clone.o -c $(OBJDIR)/clone_.c
15281533
15291534
$(OBJDIR)/clone.h: $(OBJDIR)/headers
1535
+
1536
+$(OBJDIR)/color_.c: $(SRCDIR)/color.c $(TRANSLATE)
1537
+ $(TRANSLATE) $(SRCDIR)/color.c >$@
1538
+
1539
+$(OBJDIR)/color.o: $(OBJDIR)/color_.c $(OBJDIR)/color.h $(SRCDIR)/config.h
1540
+ $(XTCC) -o $(OBJDIR)/color.o -c $(OBJDIR)/color_.c
1541
+
1542
+$(OBJDIR)/color.h: $(OBJDIR)/headers
15301543
15311544
$(OBJDIR)/comformat_.c: $(SRCDIR)/comformat.c $(TRANSLATE)
15321545
$(TRANSLATE) $(SRCDIR)/comformat.c >$@
15331546
15341547
$(OBJDIR)/comformat.o: $(OBJDIR)/comformat_.c $(OBJDIR)/comformat.h $(SRCDIR)/config.h
@@ -2454,18 +2467,10 @@
24542467
$(OBJDIR)/vfile.o: $(OBJDIR)/vfile_.c $(OBJDIR)/vfile.h $(SRCDIR)/config.h
24552468
$(XTCC) -o $(OBJDIR)/vfile.o -c $(OBJDIR)/vfile_.c
24562469
24572470
$(OBJDIR)/vfile.h: $(OBJDIR)/headers
24582471
2459
-$(OBJDIR)/webmail_.c: $(SRCDIR)/webmail.c $(TRANSLATE)
2460
- $(TRANSLATE) $(SRCDIR)/webmail.c >$@
2461
-
2462
-$(OBJDIR)/webmail.o: $(OBJDIR)/webmail_.c $(OBJDIR)/webmail.h $(SRCDIR)/config.h
2463
- $(XTCC) -o $(OBJDIR)/webmail.o -c $(OBJDIR)/webmail_.c
2464
-
2465
-$(OBJDIR)/webmail.h: $(OBJDIR)/headers
2466
-
24672472
$(OBJDIR)/wiki_.c: $(SRCDIR)/wiki.c $(TRANSLATE)
24682473
$(TRANSLATE) $(SRCDIR)/wiki.c >$@
24692474
24702475
$(OBJDIR)/wiki.o: $(OBJDIR)/wiki_.c $(OBJDIR)/wiki.h $(SRCDIR)/config.h
24712476
$(XTCC) -o $(OBJDIR)/wiki.o -c $(OBJDIR)/wiki_.c
24722477
--- win/Makefile.mingw.mistachkin
+++ win/Makefile.mingw.mistachkin
@@ -166,15 +166,12 @@
166 ifndef FOSSIL_ENABLE_MINIZ
167 SSLCONFIG += --with-zlib-lib=$(PWD)/$(ZLIBDIR) --with-zlib-include=$(PWD)/$(ZLIBDIR) zlib
168 endif
169
170 #### The directories where the OpenSSL include and library files are located.
171 # The recommended usage here is to use the Sysinternals junction tool
172 # to create a hard link between an "openssl-1.x" sub-directory of the
173 # Fossil source code directory and the target OpenSSL source directory.
174 #
175 OPENSSLDIR = $(SRCDIR)/../compat/openssl-1.1.1g
176 OPENSSLINCDIR = $(OPENSSLDIR)/include
177 OPENSSLLIBDIR = $(OPENSSLDIR)
178
179 #### Either the directory where the Tcl library is installed or the Tcl
180 # source code directory resides (depending on the value of the macro
@@ -444,14 +441,16 @@
444 $(SRCDIR)/bundle.c \
445 $(SRCDIR)/cache.c \
446 $(SRCDIR)/capabilities.c \
447 $(SRCDIR)/captcha.c \
448 $(SRCDIR)/cgi.c \
 
449 $(SRCDIR)/checkin.c \
450 $(SRCDIR)/checkout.c \
451 $(SRCDIR)/clearsign.c \
452 $(SRCDIR)/clone.c \
 
453 $(SRCDIR)/comformat.c \
454 $(SRCDIR)/configure.c \
455 $(SRCDIR)/content.c \
456 $(SRCDIR)/cookies.c \
457 $(SRCDIR)/db.c \
@@ -564,21 +563,19 @@
564 $(SRCDIR)/user.c \
565 $(SRCDIR)/utf8.c \
566 $(SRCDIR)/util.c \
567 $(SRCDIR)/verify.c \
568 $(SRCDIR)/vfile.c \
569 $(SRCDIR)/webmail.c \
570 $(SRCDIR)/wiki.c \
571 $(SRCDIR)/wikiformat.c \
572 $(SRCDIR)/winfile.c \
573 $(SRCDIR)/winhttp.c \
574 $(SRCDIR)/xfer.c \
575 $(SRCDIR)/xfersetup.c \
576 $(SRCDIR)/zip.c
577
578 EXTRA_FILES = \
579 $(SRCDIR)/../skins/aht/details.txt \
580 $(SRCDIR)/../skins/ardoise/css.txt \
581 $(SRCDIR)/../skins/ardoise/details.txt \
582 $(SRCDIR)/../skins/ardoise/footer.txt \
583 $(SRCDIR)/../skins/ardoise/header.txt \
584 $(SRCDIR)/../skins/black_and_white/css.txt \
@@ -588,32 +585,26 @@
588 $(SRCDIR)/../skins/blitz/css.txt \
589 $(SRCDIR)/../skins/blitz/details.txt \
590 $(SRCDIR)/../skins/blitz/footer.txt \
591 $(SRCDIR)/../skins/blitz/header.txt \
592 $(SRCDIR)/../skins/blitz/ticket.txt \
593 $(SRCDIR)/../skins/blitz_no_logo/css.txt \
594 $(SRCDIR)/../skins/blitz_no_logo/details.txt \
595 $(SRCDIR)/../skins/blitz_no_logo/footer.txt \
596 $(SRCDIR)/../skins/blitz_no_logo/header.txt \
597 $(SRCDIR)/../skins/blitz_no_logo/ticket.txt \
598 $(SRCDIR)/../skins/bootstrap/css.txt \
599 $(SRCDIR)/../skins/bootstrap/details.txt \
600 $(SRCDIR)/../skins/bootstrap/footer.txt \
601 $(SRCDIR)/../skins/bootstrap/header.txt \
 
 
 
 
602 $(SRCDIR)/../skins/default/css.txt \
603 $(SRCDIR)/../skins/default/details.txt \
604 $(SRCDIR)/../skins/default/footer.txt \
605 $(SRCDIR)/../skins/default/header.txt \
606 $(SRCDIR)/../skins/default/js.txt \
607 $(SRCDIR)/../skins/eagle/css.txt \
608 $(SRCDIR)/../skins/eagle/details.txt \
609 $(SRCDIR)/../skins/eagle/footer.txt \
610 $(SRCDIR)/../skins/eagle/header.txt \
611 $(SRCDIR)/../skins/enhanced1/css.txt \
612 $(SRCDIR)/../skins/enhanced1/details.txt \
613 $(SRCDIR)/../skins/enhanced1/footer.txt \
614 $(SRCDIR)/../skins/enhanced1/header.txt \
615 $(SRCDIR)/../skins/khaki/css.txt \
616 $(SRCDIR)/../skins/khaki/details.txt \
617 $(SRCDIR)/../skins/khaki/footer.txt \
618 $(SRCDIR)/../skins/khaki/header.txt \
619 $(SRCDIR)/../skins/original/css.txt \
@@ -622,19 +613,20 @@
622 $(SRCDIR)/../skins/original/header.txt \
623 $(SRCDIR)/../skins/plain_gray/css.txt \
624 $(SRCDIR)/../skins/plain_gray/details.txt \
625 $(SRCDIR)/../skins/plain_gray/footer.txt \
626 $(SRCDIR)/../skins/plain_gray/header.txt \
627 $(SRCDIR)/../skins/rounded1/css.txt \
628 $(SRCDIR)/../skins/rounded1/details.txt \
629 $(SRCDIR)/../skins/rounded1/footer.txt \
630 $(SRCDIR)/../skins/rounded1/header.txt \
631 $(SRCDIR)/../skins/xekri/css.txt \
632 $(SRCDIR)/../skins/xekri/details.txt \
633 $(SRCDIR)/../skins/xekri/footer.txt \
634 $(SRCDIR)/../skins/xekri/header.txt \
635 $(SRCDIR)/accordion.js \
 
 
 
 
 
636 $(SRCDIR)/ci_edit.js \
637 $(SRCDIR)/copybtn.js \
638 $(SRCDIR)/default.css \
639 $(SRCDIR)/diff.tcl \
640 $(SRCDIR)/forum.js \
@@ -641,10 +633,11 @@
641 $(SRCDIR)/fossil.bootstrap.js \
642 $(SRCDIR)/fossil.confirmer.js \
643 $(SRCDIR)/fossil.copybutton.js \
644 $(SRCDIR)/fossil.dom.js \
645 $(SRCDIR)/fossil.fetch.js \
 
646 $(SRCDIR)/fossil.numbered-lines.js \
647 $(SRCDIR)/fossil.page.fileedit.js \
648 $(SRCDIR)/fossil.page.forumpost.js \
649 $(SRCDIR)/fossil.page.pikchrshow.js \
650 $(SRCDIR)/fossil.page.wikiedit.js \
@@ -652,10 +645,11 @@
652 $(SRCDIR)/fossil.popupwidget.js \
653 $(SRCDIR)/fossil.storage.js \
654 $(SRCDIR)/fossil.tabs.js \
655 $(SRCDIR)/fossil.wikiedit-wysiwyg.js \
656 $(SRCDIR)/graph.js \
 
657 $(SRCDIR)/href.js \
658 $(SRCDIR)/login.js \
659 $(SRCDIR)/markdown.md \
660 $(SRCDIR)/menu.js \
661 $(SRCDIR)/sbsdiff.js \
@@ -702,14 +696,16 @@
702 $(OBJDIR)/bundle_.c \
703 $(OBJDIR)/cache_.c \
704 $(OBJDIR)/capabilities_.c \
705 $(OBJDIR)/captcha_.c \
706 $(OBJDIR)/cgi_.c \
 
707 $(OBJDIR)/checkin_.c \
708 $(OBJDIR)/checkout_.c \
709 $(OBJDIR)/clearsign_.c \
710 $(OBJDIR)/clone_.c \
 
711 $(OBJDIR)/comformat_.c \
712 $(OBJDIR)/configure_.c \
713 $(OBJDIR)/content_.c \
714 $(OBJDIR)/cookies_.c \
715 $(OBJDIR)/db_.c \
@@ -822,11 +818,10 @@
822 $(OBJDIR)/user_.c \
823 $(OBJDIR)/utf8_.c \
824 $(OBJDIR)/util_.c \
825 $(OBJDIR)/verify_.c \
826 $(OBJDIR)/vfile_.c \
827 $(OBJDIR)/webmail_.c \
828 $(OBJDIR)/wiki_.c \
829 $(OBJDIR)/wikiformat_.c \
830 $(OBJDIR)/winfile_.c \
831 $(OBJDIR)/winhttp_.c \
832 $(OBJDIR)/xfer_.c \
@@ -850,14 +845,16 @@
850 $(OBJDIR)/bundle.o \
851 $(OBJDIR)/cache.o \
852 $(OBJDIR)/capabilities.o \
853 $(OBJDIR)/captcha.o \
854 $(OBJDIR)/cgi.o \
 
855 $(OBJDIR)/checkin.o \
856 $(OBJDIR)/checkout.o \
857 $(OBJDIR)/clearsign.o \
858 $(OBJDIR)/clone.o \
 
859 $(OBJDIR)/comformat.o \
860 $(OBJDIR)/configure.o \
861 $(OBJDIR)/content.o \
862 $(OBJDIR)/cookies.o \
863 $(OBJDIR)/db.o \
@@ -970,11 +967,10 @@
970 $(OBJDIR)/user.o \
971 $(OBJDIR)/utf8.o \
972 $(OBJDIR)/util.o \
973 $(OBJDIR)/verify.o \
974 $(OBJDIR)/vfile.o \
975 $(OBJDIR)/webmail.o \
976 $(OBJDIR)/wiki.o \
977 $(OBJDIR)/wikiformat.o \
978 $(OBJDIR)/winfile.o \
979 $(OBJDIR)/winhttp.o \
980 $(OBJDIR)/xfer.o \
@@ -1163,13 +1159,13 @@
1163
1164 ifdef FOSSIL_BUILD_SSL
1165 APPTARGETS += openssl
1166 endif
1167
1168 $(APPNAME): $(APPTARGETS) $(OBJDIR)/headers $(CODECHECK1) $(OBJ) $(EXTRAOBJ) $(OBJDIR)/fossil.o
1169 $(CODECHECK1) $(TRANS_SRC)
1170 $(TCC) -o $@ $(OBJ) $(EXTRAOBJ) $(OBJDIR)/fossil.o $(LIB)
1171
1172 # This rule prevents make from using its default rules to try build
1173 # an executable named "manifest" out of the file named "manifest.c"
1174 #
1175 $(SRCDIR)/../manifest:
@@ -1213,14 +1209,16 @@
1213 $(OBJDIR)/bundle_.c:$(OBJDIR)/bundle.h \
1214 $(OBJDIR)/cache_.c:$(OBJDIR)/cache.h \
1215 $(OBJDIR)/capabilities_.c:$(OBJDIR)/capabilities.h \
1216 $(OBJDIR)/captcha_.c:$(OBJDIR)/captcha.h \
1217 $(OBJDIR)/cgi_.c:$(OBJDIR)/cgi.h \
 
1218 $(OBJDIR)/checkin_.c:$(OBJDIR)/checkin.h \
1219 $(OBJDIR)/checkout_.c:$(OBJDIR)/checkout.h \
1220 $(OBJDIR)/clearsign_.c:$(OBJDIR)/clearsign.h \
1221 $(OBJDIR)/clone_.c:$(OBJDIR)/clone.h \
 
1222 $(OBJDIR)/comformat_.c:$(OBJDIR)/comformat.h \
1223 $(OBJDIR)/configure_.c:$(OBJDIR)/configure.h \
1224 $(OBJDIR)/content_.c:$(OBJDIR)/content.h \
1225 $(OBJDIR)/cookies_.c:$(OBJDIR)/cookies.h \
1226 $(OBJDIR)/db_.c:$(OBJDIR)/db.h \
@@ -1333,11 +1331,10 @@
1333 $(OBJDIR)/user_.c:$(OBJDIR)/user.h \
1334 $(OBJDIR)/utf8_.c:$(OBJDIR)/utf8.h \
1335 $(OBJDIR)/util_.c:$(OBJDIR)/util.h \
1336 $(OBJDIR)/verify_.c:$(OBJDIR)/verify.h \
1337 $(OBJDIR)/vfile_.c:$(OBJDIR)/vfile.h \
1338 $(OBJDIR)/webmail_.c:$(OBJDIR)/webmail.h \
1339 $(OBJDIR)/wiki_.c:$(OBJDIR)/wiki.h \
1340 $(OBJDIR)/wikiformat_.c:$(OBJDIR)/wikiformat.h \
1341 $(OBJDIR)/winfile_.c:$(OBJDIR)/winfile.h \
1342 $(OBJDIR)/winhttp_.c:$(OBJDIR)/winhttp.h \
1343 $(OBJDIR)/xfer_.c:$(OBJDIR)/xfer.h \
@@ -1493,10 +1490,18 @@
1493
1494 $(OBJDIR)/cgi.o: $(OBJDIR)/cgi_.c $(OBJDIR)/cgi.h $(SRCDIR)/config.h
1495 $(XTCC) -o $(OBJDIR)/cgi.o -c $(OBJDIR)/cgi_.c
1496
1497 $(OBJDIR)/cgi.h: $(OBJDIR)/headers
 
 
 
 
 
 
 
 
1498
1499 $(OBJDIR)/checkin_.c: $(SRCDIR)/checkin.c $(TRANSLATE)
1500 $(TRANSLATE) $(SRCDIR)/checkin.c >$@
1501
1502 $(OBJDIR)/checkin.o: $(OBJDIR)/checkin_.c $(OBJDIR)/checkin.h $(SRCDIR)/config.h
@@ -1525,10 +1530,18 @@
1525
1526 $(OBJDIR)/clone.o: $(OBJDIR)/clone_.c $(OBJDIR)/clone.h $(SRCDIR)/config.h
1527 $(XTCC) -o $(OBJDIR)/clone.o -c $(OBJDIR)/clone_.c
1528
1529 $(OBJDIR)/clone.h: $(OBJDIR)/headers
 
 
 
 
 
 
 
 
1530
1531 $(OBJDIR)/comformat_.c: $(SRCDIR)/comformat.c $(TRANSLATE)
1532 $(TRANSLATE) $(SRCDIR)/comformat.c >$@
1533
1534 $(OBJDIR)/comformat.o: $(OBJDIR)/comformat_.c $(OBJDIR)/comformat.h $(SRCDIR)/config.h
@@ -2454,18 +2467,10 @@
2454 $(OBJDIR)/vfile.o: $(OBJDIR)/vfile_.c $(OBJDIR)/vfile.h $(SRCDIR)/config.h
2455 $(XTCC) -o $(OBJDIR)/vfile.o -c $(OBJDIR)/vfile_.c
2456
2457 $(OBJDIR)/vfile.h: $(OBJDIR)/headers
2458
2459 $(OBJDIR)/webmail_.c: $(SRCDIR)/webmail.c $(TRANSLATE)
2460 $(TRANSLATE) $(SRCDIR)/webmail.c >$@
2461
2462 $(OBJDIR)/webmail.o: $(OBJDIR)/webmail_.c $(OBJDIR)/webmail.h $(SRCDIR)/config.h
2463 $(XTCC) -o $(OBJDIR)/webmail.o -c $(OBJDIR)/webmail_.c
2464
2465 $(OBJDIR)/webmail.h: $(OBJDIR)/headers
2466
2467 $(OBJDIR)/wiki_.c: $(SRCDIR)/wiki.c $(TRANSLATE)
2468 $(TRANSLATE) $(SRCDIR)/wiki.c >$@
2469
2470 $(OBJDIR)/wiki.o: $(OBJDIR)/wiki_.c $(OBJDIR)/wiki.h $(SRCDIR)/config.h
2471 $(XTCC) -o $(OBJDIR)/wiki.o -c $(OBJDIR)/wiki_.c
2472
--- win/Makefile.mingw.mistachkin
+++ win/Makefile.mingw.mistachkin
@@ -166,15 +166,12 @@
166 ifndef FOSSIL_ENABLE_MINIZ
167 SSLCONFIG += --with-zlib-lib=$(PWD)/$(ZLIBDIR) --with-zlib-include=$(PWD)/$(ZLIBDIR) zlib
168 endif
169
170 #### The directories where the OpenSSL include and library files are located.
 
 
 
171 #
172 OPENSSLDIR = $(SRCDIR)/../compat/openssl
173 OPENSSLINCDIR = $(OPENSSLDIR)/include
174 OPENSSLLIBDIR = $(OPENSSLDIR)
175
176 #### Either the directory where the Tcl library is installed or the Tcl
177 # source code directory resides (depending on the value of the macro
@@ -444,14 +441,16 @@
441 $(SRCDIR)/bundle.c \
442 $(SRCDIR)/cache.c \
443 $(SRCDIR)/capabilities.c \
444 $(SRCDIR)/captcha.c \
445 $(SRCDIR)/cgi.c \
446 $(SRCDIR)/chat.c \
447 $(SRCDIR)/checkin.c \
448 $(SRCDIR)/checkout.c \
449 $(SRCDIR)/clearsign.c \
450 $(SRCDIR)/clone.c \
451 $(SRCDIR)/color.c \
452 $(SRCDIR)/comformat.c \
453 $(SRCDIR)/configure.c \
454 $(SRCDIR)/content.c \
455 $(SRCDIR)/cookies.c \
456 $(SRCDIR)/db.c \
@@ -564,21 +563,19 @@
563 $(SRCDIR)/user.c \
564 $(SRCDIR)/utf8.c \
565 $(SRCDIR)/util.c \
566 $(SRCDIR)/verify.c \
567 $(SRCDIR)/vfile.c \
 
568 $(SRCDIR)/wiki.c \
569 $(SRCDIR)/wikiformat.c \
570 $(SRCDIR)/winfile.c \
571 $(SRCDIR)/winhttp.c \
572 $(SRCDIR)/xfer.c \
573 $(SRCDIR)/xfersetup.c \
574 $(SRCDIR)/zip.c
575
576 EXTRA_FILES = \
 
577 $(SRCDIR)/../skins/ardoise/css.txt \
578 $(SRCDIR)/../skins/ardoise/details.txt \
579 $(SRCDIR)/../skins/ardoise/footer.txt \
580 $(SRCDIR)/../skins/ardoise/header.txt \
581 $(SRCDIR)/../skins/black_and_white/css.txt \
@@ -588,32 +585,26 @@
585 $(SRCDIR)/../skins/blitz/css.txt \
586 $(SRCDIR)/../skins/blitz/details.txt \
587 $(SRCDIR)/../skins/blitz/footer.txt \
588 $(SRCDIR)/../skins/blitz/header.txt \
589 $(SRCDIR)/../skins/blitz/ticket.txt \
 
 
 
 
 
590 $(SRCDIR)/../skins/bootstrap/css.txt \
591 $(SRCDIR)/../skins/bootstrap/details.txt \
592 $(SRCDIR)/../skins/bootstrap/footer.txt \
593 $(SRCDIR)/../skins/bootstrap/header.txt \
594 $(SRCDIR)/../skins/darkmode/css.txt \
595 $(SRCDIR)/../skins/darkmode/details.txt \
596 $(SRCDIR)/../skins/darkmode/footer.txt \
597 $(SRCDIR)/../skins/darkmode/header.txt \
598 $(SRCDIR)/../skins/default/css.txt \
599 $(SRCDIR)/../skins/default/details.txt \
600 $(SRCDIR)/../skins/default/footer.txt \
601 $(SRCDIR)/../skins/default/header.txt \
 
602 $(SRCDIR)/../skins/eagle/css.txt \
603 $(SRCDIR)/../skins/eagle/details.txt \
604 $(SRCDIR)/../skins/eagle/footer.txt \
605 $(SRCDIR)/../skins/eagle/header.txt \
 
 
 
 
606 $(SRCDIR)/../skins/khaki/css.txt \
607 $(SRCDIR)/../skins/khaki/details.txt \
608 $(SRCDIR)/../skins/khaki/footer.txt \
609 $(SRCDIR)/../skins/khaki/header.txt \
610 $(SRCDIR)/../skins/original/css.txt \
@@ -622,19 +613,20 @@
613 $(SRCDIR)/../skins/original/header.txt \
614 $(SRCDIR)/../skins/plain_gray/css.txt \
615 $(SRCDIR)/../skins/plain_gray/details.txt \
616 $(SRCDIR)/../skins/plain_gray/footer.txt \
617 $(SRCDIR)/../skins/plain_gray/header.txt \
 
 
 
 
618 $(SRCDIR)/../skins/xekri/css.txt \
619 $(SRCDIR)/../skins/xekri/details.txt \
620 $(SRCDIR)/../skins/xekri/footer.txt \
621 $(SRCDIR)/../skins/xekri/header.txt \
622 $(SRCDIR)/accordion.js \
623 $(SRCDIR)/alerts/bflat2.wav \
624 $(SRCDIR)/alerts/bflat3.wav \
625 $(SRCDIR)/alerts/bloop.wav \
626 $(SRCDIR)/alerts/plunk.wav \
627 $(SRCDIR)/chat.js \
628 $(SRCDIR)/ci_edit.js \
629 $(SRCDIR)/copybtn.js \
630 $(SRCDIR)/default.css \
631 $(SRCDIR)/diff.tcl \
632 $(SRCDIR)/forum.js \
@@ -641,10 +633,11 @@
633 $(SRCDIR)/fossil.bootstrap.js \
634 $(SRCDIR)/fossil.confirmer.js \
635 $(SRCDIR)/fossil.copybutton.js \
636 $(SRCDIR)/fossil.dom.js \
637 $(SRCDIR)/fossil.fetch.js \
638 $(SRCDIR)/fossil.info-diff.js \
639 $(SRCDIR)/fossil.numbered-lines.js \
640 $(SRCDIR)/fossil.page.fileedit.js \
641 $(SRCDIR)/fossil.page.forumpost.js \
642 $(SRCDIR)/fossil.page.pikchrshow.js \
643 $(SRCDIR)/fossil.page.wikiedit.js \
@@ -652,10 +645,11 @@
645 $(SRCDIR)/fossil.popupwidget.js \
646 $(SRCDIR)/fossil.storage.js \
647 $(SRCDIR)/fossil.tabs.js \
648 $(SRCDIR)/fossil.wikiedit-wysiwyg.js \
649 $(SRCDIR)/graph.js \
650 $(SRCDIR)/hbmenu.js \
651 $(SRCDIR)/href.js \
652 $(SRCDIR)/login.js \
653 $(SRCDIR)/markdown.md \
654 $(SRCDIR)/menu.js \
655 $(SRCDIR)/sbsdiff.js \
@@ -702,14 +696,16 @@
696 $(OBJDIR)/bundle_.c \
697 $(OBJDIR)/cache_.c \
698 $(OBJDIR)/capabilities_.c \
699 $(OBJDIR)/captcha_.c \
700 $(OBJDIR)/cgi_.c \
701 $(OBJDIR)/chat_.c \
702 $(OBJDIR)/checkin_.c \
703 $(OBJDIR)/checkout_.c \
704 $(OBJDIR)/clearsign_.c \
705 $(OBJDIR)/clone_.c \
706 $(OBJDIR)/color_.c \
707 $(OBJDIR)/comformat_.c \
708 $(OBJDIR)/configure_.c \
709 $(OBJDIR)/content_.c \
710 $(OBJDIR)/cookies_.c \
711 $(OBJDIR)/db_.c \
@@ -822,11 +818,10 @@
818 $(OBJDIR)/user_.c \
819 $(OBJDIR)/utf8_.c \
820 $(OBJDIR)/util_.c \
821 $(OBJDIR)/verify_.c \
822 $(OBJDIR)/vfile_.c \
 
823 $(OBJDIR)/wiki_.c \
824 $(OBJDIR)/wikiformat_.c \
825 $(OBJDIR)/winfile_.c \
826 $(OBJDIR)/winhttp_.c \
827 $(OBJDIR)/xfer_.c \
@@ -850,14 +845,16 @@
845 $(OBJDIR)/bundle.o \
846 $(OBJDIR)/cache.o \
847 $(OBJDIR)/capabilities.o \
848 $(OBJDIR)/captcha.o \
849 $(OBJDIR)/cgi.o \
850 $(OBJDIR)/chat.o \
851 $(OBJDIR)/checkin.o \
852 $(OBJDIR)/checkout.o \
853 $(OBJDIR)/clearsign.o \
854 $(OBJDIR)/clone.o \
855 $(OBJDIR)/color.o \
856 $(OBJDIR)/comformat.o \
857 $(OBJDIR)/configure.o \
858 $(OBJDIR)/content.o \
859 $(OBJDIR)/cookies.o \
860 $(OBJDIR)/db.o \
@@ -970,11 +967,10 @@
967 $(OBJDIR)/user.o \
968 $(OBJDIR)/utf8.o \
969 $(OBJDIR)/util.o \
970 $(OBJDIR)/verify.o \
971 $(OBJDIR)/vfile.o \
 
972 $(OBJDIR)/wiki.o \
973 $(OBJDIR)/wikiformat.o \
974 $(OBJDIR)/winfile.o \
975 $(OBJDIR)/winhttp.o \
976 $(OBJDIR)/xfer.o \
@@ -1163,13 +1159,13 @@
1159
1160 ifdef FOSSIL_BUILD_SSL
1161 APPTARGETS += openssl
1162 endif
1163
1164 $(APPNAME): $(APPTARGETS) $(OBJDIR)/headers $(CODECHECK1) $(EXTRAOBJ) $(OBJ) $(OBJDIR)/fossil.o
1165 $(CODECHECK1) $(TRANS_SRC)
1166 $(TCC) -o $@ $(EXTRAOBJ) $(OBJ) $(OBJDIR)/fossil.o $(LIB)
1167
1168 # This rule prevents make from using its default rules to try build
1169 # an executable named "manifest" out of the file named "manifest.c"
1170 #
1171 $(SRCDIR)/../manifest:
@@ -1213,14 +1209,16 @@
1209 $(OBJDIR)/bundle_.c:$(OBJDIR)/bundle.h \
1210 $(OBJDIR)/cache_.c:$(OBJDIR)/cache.h \
1211 $(OBJDIR)/capabilities_.c:$(OBJDIR)/capabilities.h \
1212 $(OBJDIR)/captcha_.c:$(OBJDIR)/captcha.h \
1213 $(OBJDIR)/cgi_.c:$(OBJDIR)/cgi.h \
1214 $(OBJDIR)/chat_.c:$(OBJDIR)/chat.h \
1215 $(OBJDIR)/checkin_.c:$(OBJDIR)/checkin.h \
1216 $(OBJDIR)/checkout_.c:$(OBJDIR)/checkout.h \
1217 $(OBJDIR)/clearsign_.c:$(OBJDIR)/clearsign.h \
1218 $(OBJDIR)/clone_.c:$(OBJDIR)/clone.h \
1219 $(OBJDIR)/color_.c:$(OBJDIR)/color.h \
1220 $(OBJDIR)/comformat_.c:$(OBJDIR)/comformat.h \
1221 $(OBJDIR)/configure_.c:$(OBJDIR)/configure.h \
1222 $(OBJDIR)/content_.c:$(OBJDIR)/content.h \
1223 $(OBJDIR)/cookies_.c:$(OBJDIR)/cookies.h \
1224 $(OBJDIR)/db_.c:$(OBJDIR)/db.h \
@@ -1333,11 +1331,10 @@
1331 $(OBJDIR)/user_.c:$(OBJDIR)/user.h \
1332 $(OBJDIR)/utf8_.c:$(OBJDIR)/utf8.h \
1333 $(OBJDIR)/util_.c:$(OBJDIR)/util.h \
1334 $(OBJDIR)/verify_.c:$(OBJDIR)/verify.h \
1335 $(OBJDIR)/vfile_.c:$(OBJDIR)/vfile.h \
 
1336 $(OBJDIR)/wiki_.c:$(OBJDIR)/wiki.h \
1337 $(OBJDIR)/wikiformat_.c:$(OBJDIR)/wikiformat.h \
1338 $(OBJDIR)/winfile_.c:$(OBJDIR)/winfile.h \
1339 $(OBJDIR)/winhttp_.c:$(OBJDIR)/winhttp.h \
1340 $(OBJDIR)/xfer_.c:$(OBJDIR)/xfer.h \
@@ -1493,10 +1490,18 @@
1490
1491 $(OBJDIR)/cgi.o: $(OBJDIR)/cgi_.c $(OBJDIR)/cgi.h $(SRCDIR)/config.h
1492 $(XTCC) -o $(OBJDIR)/cgi.o -c $(OBJDIR)/cgi_.c
1493
1494 $(OBJDIR)/cgi.h: $(OBJDIR)/headers
1495
1496 $(OBJDIR)/chat_.c: $(SRCDIR)/chat.c $(TRANSLATE)
1497 $(TRANSLATE) $(SRCDIR)/chat.c >$@
1498
1499 $(OBJDIR)/chat.o: $(OBJDIR)/chat_.c $(OBJDIR)/chat.h $(SRCDIR)/config.h
1500 $(XTCC) -o $(OBJDIR)/chat.o -c $(OBJDIR)/chat_.c
1501
1502 $(OBJDIR)/chat.h: $(OBJDIR)/headers
1503
1504 $(OBJDIR)/checkin_.c: $(SRCDIR)/checkin.c $(TRANSLATE)
1505 $(TRANSLATE) $(SRCDIR)/checkin.c >$@
1506
1507 $(OBJDIR)/checkin.o: $(OBJDIR)/checkin_.c $(OBJDIR)/checkin.h $(SRCDIR)/config.h
@@ -1525,10 +1530,18 @@
1530
1531 $(OBJDIR)/clone.o: $(OBJDIR)/clone_.c $(OBJDIR)/clone.h $(SRCDIR)/config.h
1532 $(XTCC) -o $(OBJDIR)/clone.o -c $(OBJDIR)/clone_.c
1533
1534 $(OBJDIR)/clone.h: $(OBJDIR)/headers
1535
1536 $(OBJDIR)/color_.c: $(SRCDIR)/color.c $(TRANSLATE)
1537 $(TRANSLATE) $(SRCDIR)/color.c >$@
1538
1539 $(OBJDIR)/color.o: $(OBJDIR)/color_.c $(OBJDIR)/color.h $(SRCDIR)/config.h
1540 $(XTCC) -o $(OBJDIR)/color.o -c $(OBJDIR)/color_.c
1541
1542 $(OBJDIR)/color.h: $(OBJDIR)/headers
1543
1544 $(OBJDIR)/comformat_.c: $(SRCDIR)/comformat.c $(TRANSLATE)
1545 $(TRANSLATE) $(SRCDIR)/comformat.c >$@
1546
1547 $(OBJDIR)/comformat.o: $(OBJDIR)/comformat_.c $(OBJDIR)/comformat.h $(SRCDIR)/config.h
@@ -2454,18 +2467,10 @@
2467 $(OBJDIR)/vfile.o: $(OBJDIR)/vfile_.c $(OBJDIR)/vfile.h $(SRCDIR)/config.h
2468 $(XTCC) -o $(OBJDIR)/vfile.o -c $(OBJDIR)/vfile_.c
2469
2470 $(OBJDIR)/vfile.h: $(OBJDIR)/headers
2471
 
 
 
 
 
 
 
 
2472 $(OBJDIR)/wiki_.c: $(SRCDIR)/wiki.c $(TRANSLATE)
2473 $(TRANSLATE) $(SRCDIR)/wiki.c >$@
2474
2475 $(OBJDIR)/wiki.o: $(OBJDIR)/wiki_.c $(OBJDIR)/wiki.h $(SRCDIR)/config.h
2476 $(XTCC) -o $(OBJDIR)/wiki.o -c $(OBJDIR)/wiki_.c
2477
+20 -13
--- win/Makefile.msc
+++ win/Makefile.msc
@@ -14,11 +14,11 @@
1414
OBJDIR = $(T)
1515
OX = $(OBJDIR)
1616
O = .obj
1717
E = .exe
1818
P = .pdb
19
-OPTLEVEL= /Os
19
+DBGOPTS = /Od
2020
2121
INSTALLDIR = .
2222
!ifdef DESTDIR
2323
INSTALLDIR = $(DESTDIR)\$(INSTALLDIR)
2424
!endif
@@ -37,10 +37,15 @@
3737
# Perl is only necessary if OpenSSL support is enabled and it is built from
3838
# source code. The PERLDIR environment variable, if it exists, should point
3939
# to the directory containing the main Perl executable specified here (i.e.
4040
# "perl.exe").
4141
PERL = perl.exe
42
+
43
+# Enable use of available compiler optimizations?
44
+!ifndef OPTIMIZATIONS
45
+OPTIMIZATIONS = 2
46
+!endif
4247
4348
# Enable debugging symbols?
4449
!ifndef DEBUG
4550
DEBUG = 0
4651
!endif
@@ -209,16 +214,28 @@
209214
CRTFLAGS = /MTd
210215
!else
211216
CRTFLAGS = /MT
212217
!endif
213218
!endif
219
+
220
+!if $(OPTIMIZATIONS)>3
221
+RELOPTS = /Os
222
+!elseif $(OPTIMIZATIONS)>2
223
+RELOPTS = /Ox
224
+!elseif $(OPTIMIZATIONS)>1
225
+RELOPTS = /O2
226
+!elseif $(OPTIMIZATIONS)>0
227
+RELOPTS = /O1
228
+!else
229
+RELOPTS =
230
+!endif
214231
215232
!if $(DEBUG)!=0
216
-CFLAGS = $(CFLAGS) /Zi $(CRTFLAGS) /Od /DFOSSIL_DEBUG
233
+CFLAGS = $(CFLAGS) /Zi $(CRTFLAGS) $(DBGOPTS) /DFOSSIL_DEBUG /DTH_MEMDEBUG
217234
LDFLAGS = $(LDFLAGS) /DEBUG
218235
!else
219
-CFLAGS = $(CFLAGS) $(CRTFLAGS) $(OPTLEVEL)
236
+CFLAGS = $(CFLAGS) $(CRTFLAGS) $(RELOPTS)
220237
!endif
221238
222239
BCC = $(CC) $(CFLAGS)
223240
TCC = $(CC) /c $(CFLAGS) $(MSCDEF) $(INCL)
224241
RCC = $(RC) /D_WIN32 /D_MSC_VER $(MSCDEF) $(INCL)
@@ -489,11 +506,10 @@
489506
"$(OX)\user_.c" \
490507
"$(OX)\utf8_.c" \
491508
"$(OX)\util_.c" \
492509
"$(OX)\verify_.c" \
493510
"$(OX)\vfile_.c" \
494
- "$(OX)\webmail_.c" \
495511
"$(OX)\wiki_.c" \
496512
"$(OX)\wikiformat_.c" \
497513
"$(OX)\winfile_.c" \
498514
"$(OX)\winhttp_.c" \
499515
"$(OX)\xfer_.c" \
@@ -749,11 +765,10 @@
749765
"$(OX)\user$O" \
750766
"$(OX)\utf8$O" \
751767
"$(OX)\util$O" \
752768
"$(OX)\verify$O" \
753769
"$(OX)\vfile$O" \
754
- "$(OX)\webmail$O" \
755770
"$(OX)\wiki$O" \
756771
"$(OX)\wikiformat$O" \
757772
"$(OX)\winfile$O" \
758773
"$(OX)\winhttp$O" \
759774
"$(OX)\xfer$O" \
@@ -980,11 +995,10 @@
980995
echo "$(OX)\user.obj" >> $@
981996
echo "$(OX)\utf8.obj" >> $@
982997
echo "$(OX)\util.obj" >> $@
983998
echo "$(OX)\verify.obj" >> $@
984999
echo "$(OX)\vfile.obj" >> $@
985
- echo "$(OX)\webmail.obj" >> $@
9861000
echo "$(OX)\wiki.obj" >> $@
9871001
echo "$(OX)\wikiformat.obj" >> $@
9881002
echo "$(OX)\winfile.obj" >> $@
9891003
echo "$(OX)\winhttp.obj" >> $@
9901004
echo "$(OX)\xfer.obj" >> $@
@@ -2049,16 +2063,10 @@
20492063
$(TCC) /Fo$@ /Fd$(@D)\ -c "$(OX)\vfile_.c"
20502064
20512065
"$(OX)\vfile_.c" : "$(SRCDIR)\vfile.c"
20522066
"$(OBJDIR)\translate$E" $** > $@
20532067
2054
-"$(OX)\webmail$O" : "$(OX)\webmail_.c" "$(OX)\webmail.h"
2055
- $(TCC) /Fo$@ /Fd$(@D)\ -c "$(OX)\webmail_.c"
2056
-
2057
-"$(OX)\webmail_.c" : "$(SRCDIR)\webmail.c"
2058
- "$(OBJDIR)\translate$E" $** > $@
2059
-
20602068
"$(OX)\wiki$O" : "$(OX)\wiki_.c" "$(OX)\wiki.h"
20612069
$(TCC) /Fo$@ /Fd$(@D)\ -c "$(OX)\wiki_.c"
20622070
20632071
"$(OX)\wiki_.c" : "$(SRCDIR)\wiki.c"
20642072
"$(OBJDIR)\translate$E" $** > $@
@@ -2241,11 +2249,10 @@
22412249
"$(OX)\user_.c":"$(OX)\user.h" \
22422250
"$(OX)\utf8_.c":"$(OX)\utf8.h" \
22432251
"$(OX)\util_.c":"$(OX)\util.h" \
22442252
"$(OX)\verify_.c":"$(OX)\verify.h" \
22452253
"$(OX)\vfile_.c":"$(OX)\vfile.h" \
2246
- "$(OX)\webmail_.c":"$(OX)\webmail.h" \
22472254
"$(OX)\wiki_.c":"$(OX)\wiki.h" \
22482255
"$(OX)\wikiformat_.c":"$(OX)\wikiformat.h" \
22492256
"$(OX)\winfile_.c":"$(OX)\winfile.h" \
22502257
"$(OX)\winhttp_.c":"$(OX)\winhttp.h" \
22512258
"$(OX)\xfer_.c":"$(OX)\xfer.h" \
22522259
--- win/Makefile.msc
+++ win/Makefile.msc
@@ -14,11 +14,11 @@
14 OBJDIR = $(T)
15 OX = $(OBJDIR)
16 O = .obj
17 E = .exe
18 P = .pdb
19 OPTLEVEL= /Os
20
21 INSTALLDIR = .
22 !ifdef DESTDIR
23 INSTALLDIR = $(DESTDIR)\$(INSTALLDIR)
24 !endif
@@ -37,10 +37,15 @@
37 # Perl is only necessary if OpenSSL support is enabled and it is built from
38 # source code. The PERLDIR environment variable, if it exists, should point
39 # to the directory containing the main Perl executable specified here (i.e.
40 # "perl.exe").
41 PERL = perl.exe
 
 
 
 
 
42
43 # Enable debugging symbols?
44 !ifndef DEBUG
45 DEBUG = 0
46 !endif
@@ -209,16 +214,28 @@
209 CRTFLAGS = /MTd
210 !else
211 CRTFLAGS = /MT
212 !endif
213 !endif
 
 
 
 
 
 
 
 
 
 
 
 
214
215 !if $(DEBUG)!=0
216 CFLAGS = $(CFLAGS) /Zi $(CRTFLAGS) /Od /DFOSSIL_DEBUG
217 LDFLAGS = $(LDFLAGS) /DEBUG
218 !else
219 CFLAGS = $(CFLAGS) $(CRTFLAGS) $(OPTLEVEL)
220 !endif
221
222 BCC = $(CC) $(CFLAGS)
223 TCC = $(CC) /c $(CFLAGS) $(MSCDEF) $(INCL)
224 RCC = $(RC) /D_WIN32 /D_MSC_VER $(MSCDEF) $(INCL)
@@ -489,11 +506,10 @@
489 "$(OX)\user_.c" \
490 "$(OX)\utf8_.c" \
491 "$(OX)\util_.c" \
492 "$(OX)\verify_.c" \
493 "$(OX)\vfile_.c" \
494 "$(OX)\webmail_.c" \
495 "$(OX)\wiki_.c" \
496 "$(OX)\wikiformat_.c" \
497 "$(OX)\winfile_.c" \
498 "$(OX)\winhttp_.c" \
499 "$(OX)\xfer_.c" \
@@ -749,11 +765,10 @@
749 "$(OX)\user$O" \
750 "$(OX)\utf8$O" \
751 "$(OX)\util$O" \
752 "$(OX)\verify$O" \
753 "$(OX)\vfile$O" \
754 "$(OX)\webmail$O" \
755 "$(OX)\wiki$O" \
756 "$(OX)\wikiformat$O" \
757 "$(OX)\winfile$O" \
758 "$(OX)\winhttp$O" \
759 "$(OX)\xfer$O" \
@@ -980,11 +995,10 @@
980 echo "$(OX)\user.obj" >> $@
981 echo "$(OX)\utf8.obj" >> $@
982 echo "$(OX)\util.obj" >> $@
983 echo "$(OX)\verify.obj" >> $@
984 echo "$(OX)\vfile.obj" >> $@
985 echo "$(OX)\webmail.obj" >> $@
986 echo "$(OX)\wiki.obj" >> $@
987 echo "$(OX)\wikiformat.obj" >> $@
988 echo "$(OX)\winfile.obj" >> $@
989 echo "$(OX)\winhttp.obj" >> $@
990 echo "$(OX)\xfer.obj" >> $@
@@ -2049,16 +2063,10 @@
2049 $(TCC) /Fo$@ /Fd$(@D)\ -c "$(OX)\vfile_.c"
2050
2051 "$(OX)\vfile_.c" : "$(SRCDIR)\vfile.c"
2052 "$(OBJDIR)\translate$E" $** > $@
2053
2054 "$(OX)\webmail$O" : "$(OX)\webmail_.c" "$(OX)\webmail.h"
2055 $(TCC) /Fo$@ /Fd$(@D)\ -c "$(OX)\webmail_.c"
2056
2057 "$(OX)\webmail_.c" : "$(SRCDIR)\webmail.c"
2058 "$(OBJDIR)\translate$E" $** > $@
2059
2060 "$(OX)\wiki$O" : "$(OX)\wiki_.c" "$(OX)\wiki.h"
2061 $(TCC) /Fo$@ /Fd$(@D)\ -c "$(OX)\wiki_.c"
2062
2063 "$(OX)\wiki_.c" : "$(SRCDIR)\wiki.c"
2064 "$(OBJDIR)\translate$E" $** > $@
@@ -2241,11 +2249,10 @@
2241 "$(OX)\user_.c":"$(OX)\user.h" \
2242 "$(OX)\utf8_.c":"$(OX)\utf8.h" \
2243 "$(OX)\util_.c":"$(OX)\util.h" \
2244 "$(OX)\verify_.c":"$(OX)\verify.h" \
2245 "$(OX)\vfile_.c":"$(OX)\vfile.h" \
2246 "$(OX)\webmail_.c":"$(OX)\webmail.h" \
2247 "$(OX)\wiki_.c":"$(OX)\wiki.h" \
2248 "$(OX)\wikiformat_.c":"$(OX)\wikiformat.h" \
2249 "$(OX)\winfile_.c":"$(OX)\winfile.h" \
2250 "$(OX)\winhttp_.c":"$(OX)\winhttp.h" \
2251 "$(OX)\xfer_.c":"$(OX)\xfer.h" \
2252
--- win/Makefile.msc
+++ win/Makefile.msc
@@ -14,11 +14,11 @@
14 OBJDIR = $(T)
15 OX = $(OBJDIR)
16 O = .obj
17 E = .exe
18 P = .pdb
19 DBGOPTS = /Od
20
21 INSTALLDIR = .
22 !ifdef DESTDIR
23 INSTALLDIR = $(DESTDIR)\$(INSTALLDIR)
24 !endif
@@ -37,10 +37,15 @@
37 # Perl is only necessary if OpenSSL support is enabled and it is built from
38 # source code. The PERLDIR environment variable, if it exists, should point
39 # to the directory containing the main Perl executable specified here (i.e.
40 # "perl.exe").
41 PERL = perl.exe
42
43 # Enable use of available compiler optimizations?
44 !ifndef OPTIMIZATIONS
45 OPTIMIZATIONS = 2
46 !endif
47
48 # Enable debugging symbols?
49 !ifndef DEBUG
50 DEBUG = 0
51 !endif
@@ -209,16 +214,28 @@
214 CRTFLAGS = /MTd
215 !else
216 CRTFLAGS = /MT
217 !endif
218 !endif
219
220 !if $(OPTIMIZATIONS)>3
221 RELOPTS = /Os
222 !elseif $(OPTIMIZATIONS)>2
223 RELOPTS = /Ox
224 !elseif $(OPTIMIZATIONS)>1
225 RELOPTS = /O2
226 !elseif $(OPTIMIZATIONS)>0
227 RELOPTS = /O1
228 !else
229 RELOPTS =
230 !endif
231
232 !if $(DEBUG)!=0
233 CFLAGS = $(CFLAGS) /Zi $(CRTFLAGS) $(DBGOPTS) /DFOSSIL_DEBUG /DTH_MEMDEBUG
234 LDFLAGS = $(LDFLAGS) /DEBUG
235 !else
236 CFLAGS = $(CFLAGS) $(CRTFLAGS) $(RELOPTS)
237 !endif
238
239 BCC = $(CC) $(CFLAGS)
240 TCC = $(CC) /c $(CFLAGS) $(MSCDEF) $(INCL)
241 RCC = $(RC) /D_WIN32 /D_MSC_VER $(MSCDEF) $(INCL)
@@ -489,11 +506,10 @@
506 "$(OX)\user_.c" \
507 "$(OX)\utf8_.c" \
508 "$(OX)\util_.c" \
509 "$(OX)\verify_.c" \
510 "$(OX)\vfile_.c" \
 
511 "$(OX)\wiki_.c" \
512 "$(OX)\wikiformat_.c" \
513 "$(OX)\winfile_.c" \
514 "$(OX)\winhttp_.c" \
515 "$(OX)\xfer_.c" \
@@ -749,11 +765,10 @@
765 "$(OX)\user$O" \
766 "$(OX)\utf8$O" \
767 "$(OX)\util$O" \
768 "$(OX)\verify$O" \
769 "$(OX)\vfile$O" \
 
770 "$(OX)\wiki$O" \
771 "$(OX)\wikiformat$O" \
772 "$(OX)\winfile$O" \
773 "$(OX)\winhttp$O" \
774 "$(OX)\xfer$O" \
@@ -980,11 +995,10 @@
995 echo "$(OX)\user.obj" >> $@
996 echo "$(OX)\utf8.obj" >> $@
997 echo "$(OX)\util.obj" >> $@
998 echo "$(OX)\verify.obj" >> $@
999 echo "$(OX)\vfile.obj" >> $@
 
1000 echo "$(OX)\wiki.obj" >> $@
1001 echo "$(OX)\wikiformat.obj" >> $@
1002 echo "$(OX)\winfile.obj" >> $@
1003 echo "$(OX)\winhttp.obj" >> $@
1004 echo "$(OX)\xfer.obj" >> $@
@@ -2049,16 +2063,10 @@
2063 $(TCC) /Fo$@ /Fd$(@D)\ -c "$(OX)\vfile_.c"
2064
2065 "$(OX)\vfile_.c" : "$(SRCDIR)\vfile.c"
2066 "$(OBJDIR)\translate$E" $** > $@
2067
 
 
 
 
 
 
2068 "$(OX)\wiki$O" : "$(OX)\wiki_.c" "$(OX)\wiki.h"
2069 $(TCC) /Fo$@ /Fd$(@D)\ -c "$(OX)\wiki_.c"
2070
2071 "$(OX)\wiki_.c" : "$(SRCDIR)\wiki.c"
2072 "$(OBJDIR)\translate$E" $** > $@
@@ -2241,11 +2249,10 @@
2249 "$(OX)\user_.c":"$(OX)\user.h" \
2250 "$(OX)\utf8_.c":"$(OX)\utf8.h" \
2251 "$(OX)\util_.c":"$(OX)\util.h" \
2252 "$(OX)\verify_.c":"$(OX)\verify.h" \
2253 "$(OX)\vfile_.c":"$(OX)\vfile.h" \
 
2254 "$(OX)\wiki_.c":"$(OX)\wiki.h" \
2255 "$(OX)\wikiformat_.c":"$(OX)\wikiformat.h" \
2256 "$(OX)\winfile_.c":"$(OX)\winfile.h" \
2257 "$(OX)\winhttp_.c":"$(OX)\winhttp.h" \
2258 "$(OX)\xfer_.c":"$(OX)\xfer.h" \
2259
--- win/buildmsvc.bat
+++ win/buildmsvc.bat
@@ -33,11 +33,11 @@
3333
3434
REM
3535
REM Visual C++ ????
3636
REM
3737
IF DEFINED VCINSTALLDIR IF EXIST "%VCINSTALLDIR%" (
38
- %_AECHO% Build environment appears to be setup.
38
+ %_AECHO% Build environment appears to be set up.
3939
GOTO skip_setupVisualStudio
4040
)
4141
4242
REM
4343
REM Visual Studio ????
@@ -45,10 +45,22 @@
4545
IF DEFINED VSVARS32 IF EXIST "%VSVARS32%" (
4646
%_AECHO% Build environment batch file manually overridden to "%VSVARS32%"...
4747
GOTO skip_detectVisualStudio
4848
)
4949
50
+REM
51
+REM Visual Studio 2017 / 2019
52
+REM
53
+CALL :fn_TryUseVsWhereExe
54
+IF NOT DEFINED VSWHEREINSTALLDIR GOTO skip_detectVisualStudio2017
55
+SET VSVARS32=%VSWHEREINSTALLDIR%\Common7\Tools\VsDevCmd.bat
56
+IF EXIST "%VSVARS32%" (
57
+ %_AECHO% Using Visual Studio 2017 / 2019...
58
+ GOTO skip_detectVisualStudio
59
+)
60
+:skip_detectVisualStudio2017
61
+
5062
REM
5163
REM Visual Studio 2015
5264
REM
5365
IF NOT DEFINED VS140COMNTOOLS GOTO skip_detectVisualStudio2015
5466
SET VSVARS32=%VS140COMNTOOLS%\vsvars32.bat
@@ -341,10 +353,38 @@
341353
GOTO :EOF
342354
343355
:fn_SetErrorLevel
344356
VERIFY MAYBE 2> NUL
345357
GOTO :EOF
358
+
359
+:fn_TryUseVsWhereExe
360
+ IF DEFINED VSWHERE_EXE GOTO skip_setVsWhereExe
361
+ SET VSWHERE_EXE=%ProgramFiles(x86)%\Microsoft Visual Studio\Installer\vswhere.exe
362
+ IF NOT EXIST "%VSWHERE_EXE%" SET VSWHERE_EXE=%ProgramFiles%\Microsoft Visual Studio\Installer\vswhere.exe
363
+ :skip_setVsWhereExe
364
+ IF NOT EXIST "%VSWHERE_EXE%" (
365
+ %_AECHO% The "VsWhere" tool does not appear to be installed.
366
+ GOTO :EOF
367
+ )
368
+ SET VS_WHEREIS_CMD="%VSWHERE_EXE%" -products * -requires Microsoft.VisualStudio.Component.VC.Tools.x86.x64 -property installationPath -latest
369
+ IF DEFINED __ECHO (
370
+ %__ECHO% %VS_WHEREIS_CMD%
371
+ REM
372
+ REM NOTE: This will not be executed, any reasonable fake path will work.
373
+ REM
374
+ SET VSWHEREINSTALLDIR=C:\Program Files\Microsoft Visual Studio\2017\Community
375
+ GOTO skip_setVsWhereInstallDir
376
+ )
377
+ FOR /F "delims=" %%D IN ('%VS_WHEREIS_CMD%') DO (SET VSWHEREINSTALLDIR=%%D)
378
+ :skip_setVsWhereInstallDir
379
+ %_VECHO% VsWhereInstallDir = '%VSWHEREINSTALLDIR%'
380
+ IF NOT DEFINED VSWHEREINSTALLDIR (
381
+ %_AECHO% Visual Studio 2017 / 2019 is not installed.
382
+ GOTO :EOF
383
+ )
384
+ %_AECHO% Visual Studio 2017 / 2019 is installed.
385
+ GOTO :EOF
346386
347387
:usage
348388
ECHO.
349389
ECHO Usage: %~nx0 [...]
350390
ECHO.
351391
--- win/buildmsvc.bat
+++ win/buildmsvc.bat
@@ -33,11 +33,11 @@
33
34 REM
35 REM Visual C++ ????
36 REM
37 IF DEFINED VCINSTALLDIR IF EXIST "%VCINSTALLDIR%" (
38 %_AECHO% Build environment appears to be setup.
39 GOTO skip_setupVisualStudio
40 )
41
42 REM
43 REM Visual Studio ????
@@ -45,10 +45,22 @@
45 IF DEFINED VSVARS32 IF EXIST "%VSVARS32%" (
46 %_AECHO% Build environment batch file manually overridden to "%VSVARS32%"...
47 GOTO skip_detectVisualStudio
48 )
49
 
 
 
 
 
 
 
 
 
 
 
 
50 REM
51 REM Visual Studio 2015
52 REM
53 IF NOT DEFINED VS140COMNTOOLS GOTO skip_detectVisualStudio2015
54 SET VSVARS32=%VS140COMNTOOLS%\vsvars32.bat
@@ -341,10 +353,38 @@
341 GOTO :EOF
342
343 :fn_SetErrorLevel
344 VERIFY MAYBE 2> NUL
345 GOTO :EOF
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
346
347 :usage
348 ECHO.
349 ECHO Usage: %~nx0 [...]
350 ECHO.
351
--- win/buildmsvc.bat
+++ win/buildmsvc.bat
@@ -33,11 +33,11 @@
33
34 REM
35 REM Visual C++ ????
36 REM
37 IF DEFINED VCINSTALLDIR IF EXIST "%VCINSTALLDIR%" (
38 %_AECHO% Build environment appears to be set up.
39 GOTO skip_setupVisualStudio
40 )
41
42 REM
43 REM Visual Studio ????
@@ -45,10 +45,22 @@
45 IF DEFINED VSVARS32 IF EXIST "%VSVARS32%" (
46 %_AECHO% Build environment batch file manually overridden to "%VSVARS32%"...
47 GOTO skip_detectVisualStudio
48 )
49
50 REM
51 REM Visual Studio 2017 / 2019
52 REM
53 CALL :fn_TryUseVsWhereExe
54 IF NOT DEFINED VSWHEREINSTALLDIR GOTO skip_detectVisualStudio2017
55 SET VSVARS32=%VSWHEREINSTALLDIR%\Common7\Tools\VsDevCmd.bat
56 IF EXIST "%VSVARS32%" (
57 %_AECHO% Using Visual Studio 2017 / 2019...
58 GOTO skip_detectVisualStudio
59 )
60 :skip_detectVisualStudio2017
61
62 REM
63 REM Visual Studio 2015
64 REM
65 IF NOT DEFINED VS140COMNTOOLS GOTO skip_detectVisualStudio2015
66 SET VSVARS32=%VS140COMNTOOLS%\vsvars32.bat
@@ -341,10 +353,38 @@
353 GOTO :EOF
354
355 :fn_SetErrorLevel
356 VERIFY MAYBE 2> NUL
357 GOTO :EOF
358
359 :fn_TryUseVsWhereExe
360 IF DEFINED VSWHERE_EXE GOTO skip_setVsWhereExe
361 SET VSWHERE_EXE=%ProgramFiles(x86)%\Microsoft Visual Studio\Installer\vswhere.exe
362 IF NOT EXIST "%VSWHERE_EXE%" SET VSWHERE_EXE=%ProgramFiles%\Microsoft Visual Studio\Installer\vswhere.exe
363 :skip_setVsWhereExe
364 IF NOT EXIST "%VSWHERE_EXE%" (
365 %_AECHO% The "VsWhere" tool does not appear to be installed.
366 GOTO :EOF
367 )
368 SET VS_WHEREIS_CMD="%VSWHERE_EXE%" -products * -requires Microsoft.VisualStudio.Component.VC.Tools.x86.x64 -property installationPath -latest
369 IF DEFINED __ECHO (
370 %__ECHO% %VS_WHEREIS_CMD%
371 REM
372 REM NOTE: This will not be executed, any reasonable fake path will work.
373 REM
374 SET VSWHEREINSTALLDIR=C:\Program Files\Microsoft Visual Studio\2017\Community
375 GOTO skip_setVsWhereInstallDir
376 )
377 FOR /F "delims=" %%D IN ('%VS_WHEREIS_CMD%') DO (SET VSWHEREINSTALLDIR=%%D)
378 :skip_setVsWhereInstallDir
379 %_VECHO% VsWhereInstallDir = '%VSWHEREINSTALLDIR%'
380 IF NOT DEFINED VSWHEREINSTALLDIR (
381 %_AECHO% Visual Studio 2017 / 2019 is not installed.
382 GOTO :EOF
383 )
384 %_AECHO% Visual Studio 2017 / 2019 is installed.
385 GOTO :EOF
386
387 :usage
388 ECHO.
389 ECHO Usage: %~nx0 [...]
390 ECHO.
391
+7 -4
--- www/build.wiki
+++ www/build.wiki
@@ -151,21 +151,24 @@
151151
152152
<i>HINT</i>: Do <u>not</u> use MinGW-4.x, it may compile but the Fossil binary
153153
will not work correctly, see
154154
[https://fossil-scm.org/home/tktview/18cff45a4e210430e24c | ticket].
155155
156
-<li><p><i>MSVC</i> → Use the MSVC makefile. First
156
+<li><p><i>MSVC</i> → Use the MSVC makefile.
157
+
158
+Run all of the following from a "x64 Native Tools Command Prompt".
159
+
160
+First
157161
change to the "win/" subdirectory ("<b>cd win</b>") then run
158162
"<b>nmake /f Makefile.msc</b>".<br><br>Alternatively, the batch
159163
file "<b>win\buildmsvc.bat</b>" may be used and it will attempt to
160164
detect and use the latest installed version of MSVC.<br><br>To enable
161165
the optional <a href="https://www.openssl.org/">OpenSSL</a> support,
162166
first <a href="https://www.openssl.org/source/">download the official
163167
source code for OpenSSL</a> and extract it to an appropriately named
164
-"<b>openssl-X.Y.ZA</b>" subdirectory within the local
165
-[/tree?ci=trunk&name=compat | compat] directory (e.g.
166
-"<b>compat/openssl-1.1.1g</b>"), then make sure that some recent
168
+"<b>openssl</b>" subdirectory within the local
169
+[/tree?ci=trunk&name=compat | compat] directory then make sure that some recent
167170
<a href="http://www.perl.org/">Perl</a> binaries are installed locally,
168171
and finally run one of the following commands:
169172
<blockquote><pre>
170173
nmake /f Makefile.msc FOSSIL_ENABLE_SSL=1 FOSSIL_BUILD_SSL=1 PERLDIR=C:\full\path\to\Perl\bin
171174
</pre></blockquote>
172175
--- www/build.wiki
+++ www/build.wiki
@@ -151,21 +151,24 @@
151
152 <i>HINT</i>: Do <u>not</u> use MinGW-4.x, it may compile but the Fossil binary
153 will not work correctly, see
154 [https://fossil-scm.org/home/tktview/18cff45a4e210430e24c | ticket].
155
156 <li><p><i>MSVC</i> → Use the MSVC makefile. First
 
 
 
 
157 change to the "win/" subdirectory ("<b>cd win</b>") then run
158 "<b>nmake /f Makefile.msc</b>".<br><br>Alternatively, the batch
159 file "<b>win\buildmsvc.bat</b>" may be used and it will attempt to
160 detect and use the latest installed version of MSVC.<br><br>To enable
161 the optional <a href="https://www.openssl.org/">OpenSSL</a> support,
162 first <a href="https://www.openssl.org/source/">download the official
163 source code for OpenSSL</a> and extract it to an appropriately named
164 "<b>openssl-X.Y.ZA</b>" subdirectory within the local
165 [/tree?ci=trunk&name=compat | compat] directory (e.g.
166 "<b>compat/openssl-1.1.1g</b>"), then make sure that some recent
167 <a href="http://www.perl.org/">Perl</a> binaries are installed locally,
168 and finally run one of the following commands:
169 <blockquote><pre>
170 nmake /f Makefile.msc FOSSIL_ENABLE_SSL=1 FOSSIL_BUILD_SSL=1 PERLDIR=C:\full\path\to\Perl\bin
171 </pre></blockquote>
172
--- www/build.wiki
+++ www/build.wiki
@@ -151,21 +151,24 @@
151
152 <i>HINT</i>: Do <u>not</u> use MinGW-4.x, it may compile but the Fossil binary
153 will not work correctly, see
154 [https://fossil-scm.org/home/tktview/18cff45a4e210430e24c | ticket].
155
156 <li><p><i>MSVC</i> → Use the MSVC makefile.
157
158 Run all of the following from a "x64 Native Tools Command Prompt".
159
160 First
161 change to the "win/" subdirectory ("<b>cd win</b>") then run
162 "<b>nmake /f Makefile.msc</b>".<br><br>Alternatively, the batch
163 file "<b>win\buildmsvc.bat</b>" may be used and it will attempt to
164 detect and use the latest installed version of MSVC.<br><br>To enable
165 the optional <a href="https://www.openssl.org/">OpenSSL</a> support,
166 first <a href="https://www.openssl.org/source/">download the official
167 source code for OpenSSL</a> and extract it to an appropriately named
168 "<b>openssl</b>" subdirectory within the local
169 [/tree?ci=trunk&name=compat | compat] directory then make sure that some recent
 
170 <a href="http://www.perl.org/">Perl</a> binaries are installed locally,
171 and finally run one of the following commands:
172 <blockquote><pre>
173 nmake /f Makefile.msc FOSSIL_ENABLE_SSL=1 FOSSIL_BUILD_SSL=1 PERLDIR=C:\full\path\to\Perl\bin
174 </pre></blockquote>
175
+32 -2
--- www/event.wiki
+++ www/event.wiki
@@ -31,11 +31,12 @@
3131
* <b>Process Checkpoints</b>. For projects that have a formal process,
3232
technotes can be used to record the completion or the initiation of
3333
various process steps. For example, a technote can be used to record
3434
the successful completion of a long-running test, perhaps with
3535
performance results and details of where the test was run and who
36
- ran it recorded in the wiki content.
36
+ ran it recorded in the wiki content. The technote can be added
37
+ from a script.
3738
3839
* <b>News Articles</b>. Significant occurrences in the life cycle of
3940
a project can be recorded as news articles using technotes. Perhaps the
4041
domain name of the canonical website for a project changes, or new
4142
server hardware is obtained. Such happenings are appropriate for
@@ -58,18 +59,47 @@
5859
and technotes.
5960
6061
Technotes show up on the timeline. Click on the hyperlink beside the
6162
technote title to see the complete text.
6263
63
-<h2>Creating And Editing Technotes</h2>
64
+<h2>Creating, Editing and Viewing Technotes</h2>
6465
6566
There is a hyperlink under the /wikihelp menu that can be used to create
6667
new technotes. And there is a submenu hyperlink on technote displays for
6768
editing existing technotes.
69
+
70
+Technotes can also be created using the <b>wiki create</b> command:
71
+
72
+<blockquote>
73
+<b>
74
+fossil wiki create TestTechnote -t now --technote-bgcolor lightgreen technote.md<br>
75
+<tt>Created new tech note 2021-03-15 13:05:56</tt><br>
76
+</b>
77
+</blockquote>
78
+
79
+This command inserts a light green technote in the timeline at 2021-03-15 13:05:56, with
80
+the contents of file <b>technote.md</b> and comment "TestTechnote". Specifying a different time using
81
+<b>-t DATETIME</b> will insert the technote at the specified timestamp location in the timeline.
82
+Different technotes can have the same timestamp.
83
+
84
+The first argument to create, <b>TECHNOTE-COMMENT</b>, is the title text for the technote
85
+that appears in the timeline.
86
+
87
+To view technotes, use the <b>wiki ls</b> command:
88
+
89
+<blockquote>
90
+<b>
91
+fossil wiki ls --technote --show-technote-ids<br>
92
+<tt>z739263a134bf0da1d28e939f4c4367f51ef4c51 2020-12-19 13:20:19</tt><br>
93
+<tt>e15a918a8bed71c2ac091d74dc397b8d3340d5e1 2018-09-22 17:40:10</tt><br>
94
+</b>
95
+</blockquote>
96
+
97
+A technote ID is the UUID of the technote.
6898
6999
Users must have check-in privileges (permission "i") in order to
70100
create or edit technotes. In addition, users must have create-wiki
71101
privilege (permission "f") to create new technotes and edit-wiki
72102
privilege (permission "k") in order to edit existing technotes.
73103
74104
Technote content may be formatted as [/wiki_rules | Fossil wiki],
75105
[/md_rules | Markdown], or a plain text.
76106
--- www/event.wiki
+++ www/event.wiki
@@ -31,11 +31,12 @@
31 * <b>Process Checkpoints</b>. For projects that have a formal process,
32 technotes can be used to record the completion or the initiation of
33 various process steps. For example, a technote can be used to record
34 the successful completion of a long-running test, perhaps with
35 performance results and details of where the test was run and who
36 ran it recorded in the wiki content.
 
37
38 * <b>News Articles</b>. Significant occurrences in the life cycle of
39 a project can be recorded as news articles using technotes. Perhaps the
40 domain name of the canonical website for a project changes, or new
41 server hardware is obtained. Such happenings are appropriate for
@@ -58,18 +59,47 @@
58 and technotes.
59
60 Technotes show up on the timeline. Click on the hyperlink beside the
61 technote title to see the complete text.
62
63 <h2>Creating And Editing Technotes</h2>
64
65 There is a hyperlink under the /wikihelp menu that can be used to create
66 new technotes. And there is a submenu hyperlink on technote displays for
67 editing existing technotes.
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
68
69 Users must have check-in privileges (permission "i") in order to
70 create or edit technotes. In addition, users must have create-wiki
71 privilege (permission "f") to create new technotes and edit-wiki
72 privilege (permission "k") in order to edit existing technotes.
73
74 Technote content may be formatted as [/wiki_rules | Fossil wiki],
75 [/md_rules | Markdown], or a plain text.
76
--- www/event.wiki
+++ www/event.wiki
@@ -31,11 +31,12 @@
31 * <b>Process Checkpoints</b>. For projects that have a formal process,
32 technotes can be used to record the completion or the initiation of
33 various process steps. For example, a technote can be used to record
34 the successful completion of a long-running test, perhaps with
35 performance results and details of where the test was run and who
36 ran it recorded in the wiki content. The technote can be added
37 from a script.
38
39 * <b>News Articles</b>. Significant occurrences in the life cycle of
40 a project can be recorded as news articles using technotes. Perhaps the
41 domain name of the canonical website for a project changes, or new
42 server hardware is obtained. Such happenings are appropriate for
@@ -58,18 +59,47 @@
59 and technotes.
60
61 Technotes show up on the timeline. Click on the hyperlink beside the
62 technote title to see the complete text.
63
64 <h2>Creating, Editing and Viewing Technotes</h2>
65
66 There is a hyperlink under the /wikihelp menu that can be used to create
67 new technotes. And there is a submenu hyperlink on technote displays for
68 editing existing technotes.
69
70 Technotes can also be created using the <b>wiki create</b> command:
71
72 <blockquote>
73 <b>
74 fossil wiki create TestTechnote -t now --technote-bgcolor lightgreen technote.md<br>
75 <tt>Created new tech note 2021-03-15 13:05:56</tt><br>
76 </b>
77 </blockquote>
78
79 This command inserts a light green technote in the timeline at 2021-03-15 13:05:56, with
80 the contents of file <b>technote.md</b> and comment "TestTechnote". Specifying a different time using
81 <b>-t DATETIME</b> will insert the technote at the specified timestamp location in the timeline.
82 Different technotes can have the same timestamp.
83
84 The first argument to create, <b>TECHNOTE-COMMENT</b>, is the title text for the technote
85 that appears in the timeline.
86
87 To view technotes, use the <b>wiki ls</b> command:
88
89 <blockquote>
90 <b>
91 fossil wiki ls --technote --show-technote-ids<br>
92 <tt>z739263a134bf0da1d28e939f4c4367f51ef4c51 2020-12-19 13:20:19</tt><br>
93 <tt>e15a918a8bed71c2ac091d74dc397b8d3340d5e1 2018-09-22 17:40:10</tt><br>
94 </b>
95 </blockquote>
96
97 A technote ID is the UUID of the technote.
98
99 Users must have check-in privileges (permission "i") in order to
100 create or edit technotes. In addition, users must have create-wiki
101 privilege (permission "f") to create new technotes and edit-wiki
102 privilege (permission "k") in order to edit existing technotes.
103
104 Technote content may be formatted as [/wiki_rules | Fossil wiki],
105 [/md_rules | Markdown], or a plain text.
106
+39 -27
--- www/gsoc-ideas.md
+++ www/gsoc-ideas.md
@@ -1,27 +1,50 @@
1
-# Project Ideas for Google Summer of Code 2021
2
-
3
-This list was made for the Fossil project's application for [Google Summer of
4
-Code](https://summerofcode.withgoogle.com/) in 2021. GSoC pays students to
5
-contribute to free software projects during the Northern Hemiphere summer. If
6
-you are a student, you will be able to apply for GSoC starting March 29th 2021.
7
-
8
-This page applies to the two implementations of Fossil: [the classic Fossil](https://fossil-scm.org)
9
-and [libfossil](https://fossil.wanderinghorse.net/r/libfossil). The two implementations
10
-have an identical implementation of the Fossil data model, are 100% compatible in terms of
11
-data access since they use the same SQL, and are 100% binary compatible in terms of on-disk storage.
12
-
13
-# General Features
14
-
1
+# List of Projects and Tasks
2
+
3
+This list was made for the Fossil project's application for [Google Summer of Code](https://summerofcode.withgoogle.com/) in 2021. That application was
4
+unsuccessful, but still this list is a starting point for anyone looking
5
+for a place to start. We welcome newcomers, and invite developers to follow the simple
6
+[procedures for contributing to Fossil](./contribute.wiki). The
7
+[hacker how-to](./hacker-howto.wiki) is recommended reading.
8
+
9
+There are two implementations of the Fossil data model:
10
+
11
+* [the classic Fossil project](https://fossil-scm.org) , which is where this file is maintained and
12
+ which is as of 2021 how everyone interacts with Fossil objects
13
+* [libfossil](https://fossil.wanderinghorse.net/r/libfossil), which is an independent project to manipulate Fossil objects from a library, or using commandline tools which are thin wrappers to the library
14
+
15
+As of 2021 the two implementations have an identical implementation of the
16
+Fossil data model, are 100% compatible in terms of data access since they use
17
+the same SQL, and are 100% binary compatible in terms of on-disk storage.
18
+
19
+The projects listed here are grouped by functionality - User Interface, Integration, Email,
20
+etc. If you are looking for something easy to start with, then depending where
21
+your interests lie, there are some small libfossil tasks and small
22
+features to work on in the UI.
23
+
24
+# UI, Look and Feel
25
+
26
+Tasks for those interested in graphic/web design:
27
+
28
+* Add a quote button to the Forum, such as [discussed in this thread](https://fossil-scm.org/forum/forumpost/7ad03cd73d)
1529
* Improve the documentation history-browsing page to enable selection of 2 arbitrary versions to diff, similar to the [Mediawiki history feature enabled on Wikipedia](https://en.wikipedia.org/w/index.php?title=Fossil_(software)&action=history)
1630
* Allow diffing of Forum posts
17
-* Develop a test suite for the draft JSON API in libfossil. This JSON API is a way of integrating many kinds of systems with Fossil
18
-* Re-implement the draft JSON API in libfossil to use the JSON capability in SQLite, now that SQLite has JSON. This is a large project and would start with feasibility analysis
31
+* General touch-ups in the existing skins. This may, depending on how deep one
32
+ cares to dig, require digging into C code to find, and potentially modify, how
33
+ the HTML is generated.
34
+* Creation of one or more new skins. This does not specifically require any C
35
+ know-how.
36
+* Complete per-feature CSS facilities in [the Inskinerator](https://tangentsoft.com/inskinerator/dir) and add features to the Inskinerator
37
+
38
+# Projects Relating to Fossil Integration
39
+
1940
* Fossil hooks for pipelines with CI/CD such as static analysis, Buildbot, Gerrit, Travis and Jenkins are not well-documented and may need some further development. Make this work better, with configuration examples
2041
* Create a [Pandoc](https://pandoc.org) filter that handles Fossil-style Markdown
2142
* Create a [Pandoc filter that handles Pikchr](https://groups.google.com/g/pandoc-discuss/c/zZSspnHHsg0?pli=1) (Pikchr can be used with many kinds of layout, not just Markdown)
2243
* Editor integration: [improve the Fossil VSCode plugin](https://marketplace.visualstudio.com/items?itemName=koog1000.fossil) or [create a Fossil plugin for Eclipse](https://marketplace.eclipse.org/taxonomy/term/26%2C31)
44
+* Develop a test suite for the draft JSON API in libfossil. This JSON API is a way of integrating many kinds of systems with Fossil
45
+* Re-implement the draft JSON API in libfossil to use the JSON capability in SQLite, now that SQLite has JSON. This is a large project and would start with feasibility analysis
2346
2447
# Adding Inbound (Receiving) Email to Fossil
2548
2649
This task involves designing a new feature and working with Fossil developers to
2750
see how it can be feasible in practice.
@@ -85,21 +108,10 @@
85108
* Alternatively, instead of improving Fossil's cli, implement a comprehensive ticket commandline with [libfossil's primitives](https://fossil.wanderinghorse.net/r/libfossil/wiki/home), look under the f-apps/ directory.
86109
* Improving the Fossil web UI for ticketing, which is clunky to say the least. Fossil tries not be a heavy user of Javascript and Javascript libraries, but the wikiedit, chat and Forum code are all more advanced than ticketing,
87110
and have UI features that would improve ticketing
88111
* If there is an inbound email system as per the previous section "Adding Inbound (Receiving) Email to Fossil", then implement this system for ticketing
89112
90
-# Look and Feel
91
-
92
-Tasks for those interested in graphic/web design:
93
-
94
-* General touch-ups in the existing skins. This may, depending on how deep one
95
- cares to dig, require digging into C code to find, and potentially modify, how
96
- the HTML is generated.
97
-* Creation of one or more new skins. This does not specifically require any C
98
- know-how.
99
-* Complete per-feature CSS facilities in [the Inskinerator](https://tangentsoft.com/inskinerator/dir) and add features to the Inskinerator
100
-
101113
# Tasks Requiring Fossil Data Model Knowledge
102114
103115
The Fossil data model concepts are simple, but the implications are quite subtle and impressive. The data model
104116
is designed to [endure for centuries](./fileformat.wiki),
105117
be [easily accessible](./fossil-v-git.wiki#durable), and is [non-relational](./fossil-is-not-relational.md).
106118
--- www/gsoc-ideas.md
+++ www/gsoc-ideas.md
@@ -1,27 +1,50 @@
1 # Project Ideas for Google Summer of Code 2021
2
3 This list was made for the Fossil project's application for [Google Summer of
4 Code](https://summerofcode.withgoogle.com/) in 2021. GSoC pays students to
5 contribute to free software projects during the Northern Hemiphere summer. If
6 you are a student, you will be able to apply for GSoC starting March 29th 2021.
7
8 This page applies to the two implementations of Fossil: [the classic Fossil](https://fossil-scm.org)
9 and [libfossil](https://fossil.wanderinghorse.net/r/libfossil). The two implementations
10 have an identical implementation of the Fossil data model, are 100% compatible in terms of
11 data access since they use the same SQL, and are 100% binary compatible in terms of on-disk storage.
12
13 # General Features
14
 
 
 
 
 
 
 
 
 
 
 
 
 
 
15 * Improve the documentation history-browsing page to enable selection of 2 arbitrary versions to diff, similar to the [Mediawiki history feature enabled on Wikipedia](https://en.wikipedia.org/w/index.php?title=Fossil_(software)&action=history)
16 * Allow diffing of Forum posts
17 * Develop a test suite for the draft JSON API in libfossil. This JSON API is a way of integrating many kinds of systems with Fossil
18 * Re-implement the draft JSON API in libfossil to use the JSON capability in SQLite, now that SQLite has JSON. This is a large project and would start with feasibility analysis
 
 
 
 
 
 
 
19 * Fossil hooks for pipelines with CI/CD such as static analysis, Buildbot, Gerrit, Travis and Jenkins are not well-documented and may need some further development. Make this work better, with configuration examples
20 * Create a [Pandoc](https://pandoc.org) filter that handles Fossil-style Markdown
21 * Create a [Pandoc filter that handles Pikchr](https://groups.google.com/g/pandoc-discuss/c/zZSspnHHsg0?pli=1) (Pikchr can be used with many kinds of layout, not just Markdown)
22 * Editor integration: [improve the Fossil VSCode plugin](https://marketplace.visualstudio.com/items?itemName=koog1000.fossil) or [create a Fossil plugin for Eclipse](https://marketplace.eclipse.org/taxonomy/term/26%2C31)
 
 
23
24 # Adding Inbound (Receiving) Email to Fossil
25
26 This task involves designing a new feature and working with Fossil developers to
27 see how it can be feasible in practice.
@@ -85,21 +108,10 @@
85 * Alternatively, instead of improving Fossil's cli, implement a comprehensive ticket commandline with [libfossil's primitives](https://fossil.wanderinghorse.net/r/libfossil/wiki/home), look under the f-apps/ directory.
86 * Improving the Fossil web UI for ticketing, which is clunky to say the least. Fossil tries not be a heavy user of Javascript and Javascript libraries, but the wikiedit, chat and Forum code are all more advanced than ticketing,
87 and have UI features that would improve ticketing
88 * If there is an inbound email system as per the previous section "Adding Inbound (Receiving) Email to Fossil", then implement this system for ticketing
89
90 # Look and Feel
91
92 Tasks for those interested in graphic/web design:
93
94 * General touch-ups in the existing skins. This may, depending on how deep one
95 cares to dig, require digging into C code to find, and potentially modify, how
96 the HTML is generated.
97 * Creation of one or more new skins. This does not specifically require any C
98 know-how.
99 * Complete per-feature CSS facilities in [the Inskinerator](https://tangentsoft.com/inskinerator/dir) and add features to the Inskinerator
100
101 # Tasks Requiring Fossil Data Model Knowledge
102
103 The Fossil data model concepts are simple, but the implications are quite subtle and impressive. The data model
104 is designed to [endure for centuries](./fileformat.wiki),
105 be [easily accessible](./fossil-v-git.wiki#durable), and is [non-relational](./fossil-is-not-relational.md).
106
--- www/gsoc-ideas.md
+++ www/gsoc-ideas.md
@@ -1,27 +1,50 @@
1 # List of Projects and Tasks
2
3 This list was made for the Fossil project's application for [Google Summer of Code](https://summerofcode.withgoogle.com/) in 2021. That application was
4 unsuccessful, but still this list is a starting point for anyone looking
5 for a place to start. We welcome newcomers, and invite developers to follow the simple
6 [procedures for contributing to Fossil](./contribute.wiki). The
7 [hacker how-to](./hacker-howto.wiki) is recommended reading.
8
9 There are two implementations of the Fossil data model:
10
11 * [the classic Fossil project](https://fossil-scm.org) , which is where this file is maintained and
12 which is as of 2021 how everyone interacts with Fossil objects
13 * [libfossil](https://fossil.wanderinghorse.net/r/libfossil), which is an independent project to manipulate Fossil objects from a library, or using commandline tools which are thin wrappers to the library
14
15 As of 2021 the two implementations have an identical implementation of the
16 Fossil data model, are 100% compatible in terms of data access since they use
17 the same SQL, and are 100% binary compatible in terms of on-disk storage.
18
19 The projects listed here are grouped by functionality - User Interface, Integration, Email,
20 etc. If you are looking for something easy to start with, then depending where
21 your interests lie, there are some small libfossil tasks and small
22 features to work on in the UI.
23
24 # UI, Look and Feel
25
26 Tasks for those interested in graphic/web design:
27
28 * Add a quote button to the Forum, such as [discussed in this thread](https://fossil-scm.org/forum/forumpost/7ad03cd73d)
29 * Improve the documentation history-browsing page to enable selection of 2 arbitrary versions to diff, similar to the [Mediawiki history feature enabled on Wikipedia](https://en.wikipedia.org/w/index.php?title=Fossil_(software)&action=history)
30 * Allow diffing of Forum posts
31 * General touch-ups in the existing skins. This may, depending on how deep one
32 cares to dig, require digging into C code to find, and potentially modify, how
33 the HTML is generated.
34 * Creation of one or more new skins. This does not specifically require any C
35 know-how.
36 * Complete per-feature CSS facilities in [the Inskinerator](https://tangentsoft.com/inskinerator/dir) and add features to the Inskinerator
37
38 # Projects Relating to Fossil Integration
39
40 * Fossil hooks for pipelines with CI/CD such as static analysis, Buildbot, Gerrit, Travis and Jenkins are not well-documented and may need some further development. Make this work better, with configuration examples
41 * Create a [Pandoc](https://pandoc.org) filter that handles Fossil-style Markdown
42 * Create a [Pandoc filter that handles Pikchr](https://groups.google.com/g/pandoc-discuss/c/zZSspnHHsg0?pli=1) (Pikchr can be used with many kinds of layout, not just Markdown)
43 * Editor integration: [improve the Fossil VSCode plugin](https://marketplace.visualstudio.com/items?itemName=koog1000.fossil) or [create a Fossil plugin for Eclipse](https://marketplace.eclipse.org/taxonomy/term/26%2C31)
44 * Develop a test suite for the draft JSON API in libfossil. This JSON API is a way of integrating many kinds of systems with Fossil
45 * Re-implement the draft JSON API in libfossil to use the JSON capability in SQLite, now that SQLite has JSON. This is a large project and would start with feasibility analysis
46
47 # Adding Inbound (Receiving) Email to Fossil
48
49 This task involves designing a new feature and working with Fossil developers to
50 see how it can be feasible in practice.
@@ -85,21 +108,10 @@
108 * Alternatively, instead of improving Fossil's cli, implement a comprehensive ticket commandline with [libfossil's primitives](https://fossil.wanderinghorse.net/r/libfossil/wiki/home), look under the f-apps/ directory.
109 * Improving the Fossil web UI for ticketing, which is clunky to say the least. Fossil tries not be a heavy user of Javascript and Javascript libraries, but the wikiedit, chat and Forum code are all more advanced than ticketing,
110 and have UI features that would improve ticketing
111 * If there is an inbound email system as per the previous section "Adding Inbound (Receiving) Email to Fossil", then implement this system for ticketing
112
 
 
 
 
 
 
 
 
 
 
 
113 # Tasks Requiring Fossil Data Model Knowledge
114
115 The Fossil data model concepts are simple, but the implications are quite subtle and impressive. The data model
116 is designed to [endure for centuries](./fileformat.wiki),
117 be [easily accessible](./fossil-v-git.wiki#durable), and is [non-relational](./fossil-is-not-relational.md).
118
--- www/server/windows/service.md
+++ www/server/windows/service.md
@@ -85,7 +85,24 @@
8585
8686
in the PowerShell console.
8787
8888
Congratulations, you now have a base http accessible Fossil server running on
8989
Windows.
90
+
91
+### Removing the Windows Service
92
+
93
+If you want to remove the Fossil service, execute the following from an
94
+Administrative PowerShell or Command Prompt console:
95
+
96
+```
97
+sc.exe delete fossil
98
+```
99
+
100
+If you have Powershell version 6.0 or later, you can use:
101
+
102
+```PowerShell
103
+Remove-Service -Name fossil
104
+```
105
+
106
+with the same effect.
90107
91108
*[Return to the top-level Fossil server article.](../)*
92109
--- www/server/windows/service.md
+++ www/server/windows/service.md
@@ -85,7 +85,24 @@
85
86 in the PowerShell console.
87
88 Congratulations, you now have a base http accessible Fossil server running on
89 Windows.
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
90
91 *[Return to the top-level Fossil server article.](../)*
92
--- www/server/windows/service.md
+++ www/server/windows/service.md
@@ -85,7 +85,24 @@
85
86 in the PowerShell console.
87
88 Congratulations, you now have a base http accessible Fossil server running on
89 Windows.
90
91 ### Removing the Windows Service
92
93 If you want to remove the Fossil service, execute the following from an
94 Administrative PowerShell or Command Prompt console:
95
96 ```
97 sc.exe delete fossil
98 ```
99
100 If you have Powershell version 6.0 or later, you can use:
101
102 ```PowerShell
103 Remove-Service -Name fossil
104 ```
105
106 with the same effect.
107
108 *[Return to the top-level Fossil server article.](../)*
109
--- www/server/windows/stunnel.md
+++ www/server/windows/stunnel.md
@@ -39,10 +39,24 @@
3939
need to enter the following to avoid rebooting the server:
4040
4141
```PowerShell
4242
Start-Service -Name fossil-secure
4343
```
44
+
45
+To remove the service, run the following in a Powershell or cmd console:
46
+
47
+```
48
+sc.exe delete fossil
49
+```
50
+
51
+or (in a Powershell console)
52
+
53
+```PowerShell
54
+Remove-Service -Name fossil
55
+```
56
+
57
+if your version of Powershell is 6.0 or above.
4458
4559
## Install stunnel 5.55
4660
4761
Download stunnel from the [downloads](https://www.stunnel.org/downloads.html)
4862
page. Select the latest stunnel windows package (at the time of writing this is
4963
--- www/server/windows/stunnel.md
+++ www/server/windows/stunnel.md
@@ -39,10 +39,24 @@
39 need to enter the following to avoid rebooting the server:
40
41 ```PowerShell
42 Start-Service -Name fossil-secure
43 ```
 
 
 
 
 
 
 
 
 
 
 
 
 
 
44
45 ## Install stunnel 5.55
46
47 Download stunnel from the [downloads](https://www.stunnel.org/downloads.html)
48 page. Select the latest stunnel windows package (at the time of writing this is
49
--- www/server/windows/stunnel.md
+++ www/server/windows/stunnel.md
@@ -39,10 +39,24 @@
39 need to enter the following to avoid rebooting the server:
40
41 ```PowerShell
42 Start-Service -Name fossil-secure
43 ```
44
45 To remove the service, run the following in a Powershell or cmd console:
46
47 ```
48 sc.exe delete fossil
49 ```
50
51 or (in a Powershell console)
52
53 ```PowerShell
54 Remove-Service -Name fossil
55 ```
56
57 if your version of Powershell is 6.0 or above.
58
59 ## Install stunnel 5.55
60
61 Download stunnel from the [downloads](https://www.stunnel.org/downloads.html)
62 page. Select the latest stunnel windows package (at the time of writing this is
63

Keyboard Shortcuts

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