Fossil SCM

Anti-robot defenses are now CSP-safe.

drh 2017-12-05 01:05 UTC main.js
Commit 4f2c9841323513544e41ab779c759b81488c7498d4dbaa106309bb2a8597d1ec
--- src/codecheck1.c
+++ src/codecheck1.c
@@ -312,10 +312,11 @@
312312
{ "admin_log", 1, 0 },
313313
{ "blob_append_sql", 2, FMT_NO_S },
314314
{ "blob_appendf", 2, 0 },
315315
{ "cgi_panic", 1, 0 },
316316
{ "cgi_redirectf", 1, 0 },
317
+ { "chref", 2, 0 },
317318
{ "db_blob", 2, FMT_NO_S },
318319
{ "db_double", 2, FMT_NO_S },
319320
{ "db_err", 1, 0 },
320321
{ "db_exists", 1, FMT_NO_S },
321322
{ "db_int", 2, FMT_NO_S },
322323
--- src/codecheck1.c
+++ src/codecheck1.c
@@ -312,10 +312,11 @@
312 { "admin_log", 1, 0 },
313 { "blob_append_sql", 2, FMT_NO_S },
314 { "blob_appendf", 2, 0 },
315 { "cgi_panic", 1, 0 },
316 { "cgi_redirectf", 1, 0 },
 
317 { "db_blob", 2, FMT_NO_S },
318 { "db_double", 2, FMT_NO_S },
319 { "db_err", 1, 0 },
320 { "db_exists", 1, FMT_NO_S },
321 { "db_int", 2, FMT_NO_S },
322
--- src/codecheck1.c
+++ src/codecheck1.c
@@ -312,10 +312,11 @@
312 { "admin_log", 1, 0 },
313 { "blob_append_sql", 2, FMT_NO_S },
314 { "blob_appendf", 2, 0 },
315 { "cgi_panic", 1, 0 },
316 { "cgi_redirectf", 1, 0 },
317 { "chref", 2, 0 },
318 { "db_blob", 2, FMT_NO_S },
319 { "db_double", 2, FMT_NO_S },
320 { "db_err", 1, 0 },
321 { "db_exists", 1, FMT_NO_S },
322 { "db_int", 2, FMT_NO_S },
323
+9 -9
--- src/info.c
+++ src/info.c
@@ -798,38 +798,38 @@
798798
zPageHide = "vinfo";
799799
}
800800
diffFlags = construct_diff_flags(verboseFlag, sideBySide);
801801
zW = (diffFlags&DIFF_IGNORE_ALLWS)?"&w":"";
802802
if( verboseFlag ){
803
- @ %z(xhref("class='button'","%R/%s/%T",zPageHide,zName))
803
+ @ %z(chref("button","%R/%s/%T",zPageHide,zName))
804804
@ Hide&nbsp;Diffs</a>
805805
if( sideBySide ){
806
- @ %z(xhref("class='button'","%R/%s/%T?sbs=0%s",zPage,zName,zW))
806
+ @ %z(chref("button","%R/%s/%T?sbs=0%s",zPage,zName,zW))
807807
@ Unified&nbsp;Diffs</a>
808808
}else{
809
- @ %z(xhref("class='button'","%R/%s/%T?sbs=1%s",zPage,zName,zW))
809
+ @ %z(chref("button","%R/%s/%T?sbs=1%s",zPage,zName,zW))
810810
@ Side-by-Side&nbsp;Diffs</a>
811811
}
812812
if( *zW ){
813
- @ %z(xhref("class='button'","%R/%s/%T?sbs=%d",zPage,zName,sideBySide))
813
+ @ %z(chref("button","%R/%s/%T?sbs=%d",zPage,zName,sideBySide))
814814
@ Show&nbsp;Whitespace&nbsp;Changes</a>
815815
}else{
816
- @ %z(xhref("class='button'","%R/%s/%T?sbs=%d&w",zPage,zName,sideBySide))
816
+ @ %z(chref("button","%R/%s/%T?sbs=%d&w",zPage,zName,sideBySide))
817817
@ Ignore&nbsp;Whitespace</a>
818818
}
819819
}else{
820
- @ %z(xhref("class='button'","%R/%s/%T?sbs=0",zPage,zName))
820
+ @ %z(chref("button","%R/%s/%T?sbs=0",zPage,zName))
821821
@ Show&nbsp;Unified&nbsp;Diffs</a>
822
- @ %z(xhref("class='button'","%R/%s/%T?sbs=1",zPage,zName))
822
+ @ %z(chref("button","%R/%s/%T?sbs=1",zPage,zName))
823823
@ Show&nbsp;Side-by-Side&nbsp;Diffs</a>
824824
}
825825
if( zParent ){
826
- @ %z(xhref("class='button'","%R/vpatch?from=%!S&to=%!S",zParent,zUuid))
826
+ @ %z(chref("button","%R/vpatch?from=%!S&to=%!S",zParent,zUuid))
827827
@ Patch</a>
828828
}
829829
if( g.perm.Admin ){
830
- @ %z(xhref("class='button'","%R/mlink?ci=%!S",zUuid))MLink Table</a>
830
+ @ %z(chref("button","%R/mlink?ci=%!S",zUuid))MLink Table</a>
831831
}
832832
@</div>
833833
if( pRe ){
834834
@ <p><b>Only differences that match regular expression "%h(zRe)"
835835
@ are shown.</b></p>
836836
--- src/info.c
+++ src/info.c
@@ -798,38 +798,38 @@
798 zPageHide = "vinfo";
799 }
800 diffFlags = construct_diff_flags(verboseFlag, sideBySide);
801 zW = (diffFlags&DIFF_IGNORE_ALLWS)?"&w":"";
802 if( verboseFlag ){
803 @ %z(xhref("class='button'","%R/%s/%T",zPageHide,zName))
804 @ Hide&nbsp;Diffs</a>
805 if( sideBySide ){
806 @ %z(xhref("class='button'","%R/%s/%T?sbs=0%s",zPage,zName,zW))
807 @ Unified&nbsp;Diffs</a>
808 }else{
809 @ %z(xhref("class='button'","%R/%s/%T?sbs=1%s",zPage,zName,zW))
810 @ Side-by-Side&nbsp;Diffs</a>
811 }
812 if( *zW ){
813 @ %z(xhref("class='button'","%R/%s/%T?sbs=%d",zPage,zName,sideBySide))
814 @ Show&nbsp;Whitespace&nbsp;Changes</a>
815 }else{
816 @ %z(xhref("class='button'","%R/%s/%T?sbs=%d&w",zPage,zName,sideBySide))
817 @ Ignore&nbsp;Whitespace</a>
818 }
819 }else{
820 @ %z(xhref("class='button'","%R/%s/%T?sbs=0",zPage,zName))
821 @ Show&nbsp;Unified&nbsp;Diffs</a>
822 @ %z(xhref("class='button'","%R/%s/%T?sbs=1",zPage,zName))
823 @ Show&nbsp;Side-by-Side&nbsp;Diffs</a>
824 }
825 if( zParent ){
826 @ %z(xhref("class='button'","%R/vpatch?from=%!S&to=%!S",zParent,zUuid))
827 @ Patch</a>
828 }
829 if( g.perm.Admin ){
830 @ %z(xhref("class='button'","%R/mlink?ci=%!S",zUuid))MLink Table</a>
831 }
832 @</div>
833 if( pRe ){
834 @ <p><b>Only differences that match regular expression "%h(zRe)"
835 @ are shown.</b></p>
836
--- src/info.c
+++ src/info.c
@@ -798,38 +798,38 @@
798 zPageHide = "vinfo";
799 }
800 diffFlags = construct_diff_flags(verboseFlag, sideBySide);
801 zW = (diffFlags&DIFF_IGNORE_ALLWS)?"&w":"";
802 if( verboseFlag ){
803 @ %z(chref("button","%R/%s/%T",zPageHide,zName))
804 @ Hide&nbsp;Diffs</a>
805 if( sideBySide ){
806 @ %z(chref("button","%R/%s/%T?sbs=0%s",zPage,zName,zW))
807 @ Unified&nbsp;Diffs</a>
808 }else{
809 @ %z(chref("button","%R/%s/%T?sbs=1%s",zPage,zName,zW))
810 @ Side-by-Side&nbsp;Diffs</a>
811 }
812 if( *zW ){
813 @ %z(chref("button","%R/%s/%T?sbs=%d",zPage,zName,sideBySide))
814 @ Show&nbsp;Whitespace&nbsp;Changes</a>
815 }else{
816 @ %z(chref("button","%R/%s/%T?sbs=%d&w",zPage,zName,sideBySide))
817 @ Ignore&nbsp;Whitespace</a>
818 }
819 }else{
820 @ %z(chref("button","%R/%s/%T?sbs=0",zPage,zName))
821 @ Show&nbsp;Unified&nbsp;Diffs</a>
822 @ %z(chref("button","%R/%s/%T?sbs=1",zPage,zName))
823 @ Show&nbsp;Side-by-Side&nbsp;Diffs</a>
824 }
825 if( zParent ){
826 @ %z(chref("button","%R/vpatch?from=%!S&to=%!S",zParent,zUuid))
827 @ Patch</a>
828 }
829 if( g.perm.Admin ){
830 @ %z(chref("button","%R/mlink?ci=%!S",zUuid))MLink Table</a>
831 }
832 @</div>
833 if( pRe ){
834 @ <p><b>Only differences that match regular expression "%h(zRe)"
835 @ are shown.</b></p>
836
+33 -30
--- src/main.js
+++ src/main.js
@@ -1,33 +1,36 @@
11
/* This script is sourced just prior to the </body> in every Fossil webpage */
2
-var pageDataObj = document.getElementById('page-data');
3
-var links = new Array();
4
-if( pageDataObj ){
5
- var pageData = JSON.parse(pageDataObj.textContent || pageDataObj.innerText);
6
- var r;
7
- for(r in pageData){
8
- switch(r.op){
9
- "setAllHrefs": {
10
- links = r.links;
11
- if(r.mouseMove){
12
- document.getElementsByTagName("body")[0].onmousemove=function(){
13
- setTimeout("setAllHrefs();",r.nDelay);
14
- }
15
- }else{
16
- setTimeout("setAllHrefs();",r.nDelay);
17
- }
18
- break;
19
- }
20
- "no-op": {
21
- alert('finished processing page-data');
22
- break;
23
- }
24
- }
25
- }
26
-}
27
-function setAllHrefs(){
28
- var x;
29
- for(x in links){
30
- var y = document.getElementById(x.id);
31
- if(y) y.href=x.href
2
+var x = document.getElementById("page-data");
3
+var jx = x.textContent || x.innerText;
4
+var g = JSON.parse(jx);
5
+
6
+/* As an anti-robot defense, <a> elements are initially coded with the
7
+** href= set to the honeypot, and <form> elements are initialized with
8
+** action= set to the login page. The real values for href= and action=
9
+** are held in data-href= and data-action=. The following code moves
10
+** data-href= into href= and data-action= into action= for all
11
+** <a> and <form> elements, after delay and maybe also after mouse
12
+** movement is seen.
13
+*/
14
+function setAllHrefs(){
15
+ var anchors = document.getElementsByTagName("a");
16
+ for(var i=0; i<anchors.length; i++){
17
+ var j = anchors[i];
18
+ if(j.hasAttribute("data-href")) j.href=j.getAttribute("data-href");
19
+ }
20
+ var forms = document.getElementsByTagName("form");
21
+ for(var i=0; i<forms.length; i++){
22
+ var j = forms[i];
23
+ if(j.hasAttribute("data-action")) j.action=j.getAttribute("data-action");
24
+ }
25
+}
26
+if(g.antibot.enable){
27
+ var isOperaMini =
28
+ Object.prototype.toString.call(window.operamini)==="[object OperaMini]";
29
+ if(g.antibot.mouseover && !isOperaMini){
30
+ document.getElementByTagName("body")[0].onmousemove=function(){
31
+ setTimeout("setAllHrefs();",g.antibot.delay);
32
+ }
33
+ }else{
34
+ setTimeout("setAllHrefs();",g.antibot.delay);
3235
}
3336
}
3437
--- src/main.js
+++ src/main.js
@@ -1,33 +1,36 @@
1 /* This script is sourced just prior to the </body> in every Fossil webpage */
2 var pageDataObj = document.getElementById('page-data');
3 var links = new Array();
4 if( pageDataObj ){
5 var pageData = JSON.parse(pageDataObj.textContent || pageDataObj.innerText);
6 var r;
7 for(r in pageData){
8 switch(r.op){
9 "setAllHrefs": {
10 links = r.links;
11 if(r.mouseMove){
12 document.getElementsByTagName("body")[0].onmousemove=function(){
13 setTimeout("setAllHrefs();",r.nDelay);
14 }
15 }else{
16 setTimeout("setAllHrefs();",r.nDelay);
17 }
18 break;
19 }
20 "no-op": {
21 alert('finished processing page-data');
22 break;
23 }
24 }
25 }
26 }
27 function setAllHrefs(){
28 var x;
29 for(x in links){
30 var y = document.getElementById(x.id);
31 if(y) y.href=x.href
 
 
 
32 }
33 }
34
--- src/main.js
+++ src/main.js
@@ -1,33 +1,36 @@
1 /* This script is sourced just prior to the </body> in every Fossil webpage */
2 var x = document.getElementById("page-data");
3 var jx = x.textContent || x.innerText;
4 var g = JSON.parse(jx);
5
6 /* As an anti-robot defense, <a> elements are initially coded with the
7 ** href= set to the honeypot, and <form> elements are initialized with
8 ** action= set to the login page. The real values for href= and action=
9 ** are held in data-href= and data-action=. The following code moves
10 ** data-href= into href= and data-action= into action= for all
11 ** <a> and <form> elements, after delay and maybe also after mouse
12 ** movement is seen.
13 */
14 function setAllHrefs(){
15 var anchors = document.getElementsByTagName("a");
16 for(var i=0; i<anchors.length; i++){
17 var j = anchors[i];
18 if(j.hasAttribute("data-href")) j.href=j.getAttribute("data-href");
19 }
20 var forms = document.getElementsByTagName("form");
21 for(var i=0; i<forms.length; i++){
22 var j = forms[i];
23 if(j.hasAttribute("data-action")) j.action=j.getAttribute("data-action");
24 }
25 }
26 if(g.antibot.enable){
27 var isOperaMini =
28 Object.prototype.toString.call(window.operamini)==="[object OperaMini]";
29 if(g.antibot.mouseover && !isOperaMini){
30 document.getElementByTagName("body")[0].onmousemove=function(){
31 setTimeout("setAllHrefs();",g.antibot.delay);
32 }
33 }else{
34 setTimeout("setAllHrefs();",g.antibot.delay);
35 }
36 }
37
+50 -95
--- src/style.c
+++ src/style.c
@@ -83,25 +83,10 @@
8383
/*
8484
** Ad-unit styles.
8585
*/
8686
static unsigned adUnitFlags = 0;
8787
88
-/*
89
-** Page data JSON
90
-*/
91
-static Blob pageDataJson = BLOB_INITIALIZER;
92
-
93
-
94
-/*
95
-** List of hyperlinks and forms that need to be resolved by javascript in
96
-** the footer.
97
-*/
98
-char **aHref = 0;
99
-int nHref = 0;
100
-int nHrefAlloc = 0;
101
-char **aFormAction = 0;
102
-int nFormAction = 0;
10388
10489
/*
10590
** Generate and return a anchor tag like this:
10691
**
10792
** <a href="URL">
@@ -131,12 +116,15 @@
131116
**
132117
** Note %z format. The string returned by this function is always
133118
** obtained from fossil_malloc() so rendering it with %z will reclaim
134119
** that memory space.
135120
**
136
-** There are two versions of this routine: href() does a plain hyperlink
137
-** and xhref() adds extra attribute text.
121
+** There are three versions of this routine:
122
+**
123
+** (1) href() does a plain hyperlink
124
+** (2) xhref() adds extra attribute text
125
+** (3) chref() adds a class name
138126
**
139127
** g.perm.Hyperlink is true if the user has the Hyperlink (h) property.
140128
** Most logged in users should have this property, since we can assume
141129
** that a logged in user is not a bot. Only "nobody" lacks g.perm.Hyperlink,
142130
** typically.
@@ -150,16 +138,26 @@
150138
if( g.perm.Hyperlink && !g.javascriptHyperlink ){
151139
char *zHUrl = mprintf("<a %s href=\"%h\">", zExtra, zUrl);
152140
fossil_free(zUrl);
153141
return zHUrl;
154142
}
155
- if( nHref>=nHrefAlloc ){
156
- nHrefAlloc = nHrefAlloc*2 + 10;
157
- aHref = fossil_realloc(aHref, nHrefAlloc*sizeof(aHref[0]));
143
+ return mprintf("<a %s class='antibot' data-href='%z' href='%R/honeypot'>",
144
+ zExtra, zUrl);
145
+}
146
+char *chref(const char *zExtra, const char *zFormat, ...){
147
+ char *zUrl;
148
+ va_list ap;
149
+ va_start(ap, zFormat);
150
+ zUrl = vmprintf(zFormat, ap);
151
+ va_end(ap);
152
+ if( g.perm.Hyperlink && !g.javascriptHyperlink ){
153
+ char *zHUrl = mprintf("<a %s href=\"%h\">", zExtra, zUrl);
154
+ fossil_free(zUrl);
155
+ return zHUrl;
158156
}
159
- aHref[nHref++] = zUrl;
160
- return mprintf("<a %s id='a%d' href='%R/honeypot'>", zExtra, nHref);
157
+ return mprintf("<a class='antibot %s' data-href='%z' href='%R/honeypot'>",
158
+ zExtra, zUrl);
161159
}
162160
char *href(const char *zFormat, ...){
163161
char *zUrl;
164162
va_list ap;
165163
va_start(ap, zFormat);
@@ -168,16 +166,12 @@
168166
if( g.perm.Hyperlink && !g.javascriptHyperlink ){
169167
char *zHUrl = mprintf("<a href=\"%h\">", zUrl);
170168
fossil_free(zUrl);
171169
return zHUrl;
172170
}
173
- if( nHref>=nHrefAlloc ){
174
- nHrefAlloc = nHrefAlloc*2 + 10;
175
- aHref = fossil_realloc(aHref, nHrefAlloc*sizeof(aHref[0]));
176
- }
177
- aHref[nHref++] = zUrl;
178
- return mprintf("<a id='a%d' href='%R/honeypot'>", nHref);
171
+ return mprintf("<a class='antibot' data-href='%s' href='%R/honeypot'>",
172
+ zUrl);
179173
}
180174
181175
/*
182176
** Generate <form method="post" action=ARG>. The ARG value is inserted
183177
** by javascript.
@@ -190,72 +184,13 @@
190184
zLink = vmprintf(zAction, ap);
191185
va_end(ap);
192186
if( g.perm.Hyperlink && !g.javascriptHyperlink ){
193187
@ <form method="POST" action="%z(zLink)" %s(zOtherArgs)>
194188
}else{
195
- int n;
196
- aFormAction = fossil_realloc(aFormAction, (nFormAction+1)*sizeof(char*));
197
- aFormAction[nFormAction++] = zLink;
198
- n = nFormAction;
199
- @ <form id="form%d(n)" method="POST" action='%R/login' %s(zOtherArgs)>
200
- }
201
-}
202
-
203
-/*
204
-** Append page-data JSON
205
-*/
206
-void style_pagedata_appendf(const char *zFormat, ...){
207
- va_list ap;
208
- if( blob_size(&pageDataJson)==0 ){
209
- blob_append(&pageDataJson, "[", 1);
210
- }
211
- va_start(ap, zFormat);
212
- blob_vappendf(&pageDataJson, zFormat, ap);
213
- va_end(ap);
214
-}
215
-
216
-/*
217
-** Generate javascript that will set the href= attribute on all anchors.
218
-*/
219
-void style_resolve_href(void){
220
- int i;
221
- int nDelay = db_get_int("auto-hyperlink-delay",10);
222
- if( !g.perm.Hyperlink ) return;
223
- if( nHref==0 && nFormAction==0 ) return;
224
- @ <script>
225
- @ function setAllHrefs(){
226
- if( g.javascriptHyperlink ){
227
- for(i=0; i<nHref; i++){
228
- @ gebi("a%d(i+1)").href="%s(aHref[i])";
229
- }
230
- }
231
- for(i=0; i<nFormAction; i++){
232
- @ gebi("form%d(i+1)").action="%s(aFormAction[i])";
233
- }
234
- @ }
235
- if( sqlite3_strglob("*Opera Mini/[1-9]*", PD("HTTP_USER_AGENT",""))==0 ){
236
- /* Special case for Opera Mini, which executes JS server-side */
237
- @ var isOperaMini = Object.prototype.toString.call(window.operamini)
238
- @ === "[object OperaMini]";
239
- @ if( isOperaMini ){
240
- @ setTimeout("setAllHrefs();",%d(nDelay));
241
- @ }
242
- }else if( db_get_boolean("auto-hyperlink-ishuman",0) && g.isHuman ){
243
- /* Active hyperlinks after a delay */
244
- @ setTimeout("setAllHrefs();",%d(nDelay));
245
- }else if( db_get_boolean("auto-hyperlink-mouseover",0) ){
246
- /* Require mouse movement before starting the teim that will
247
- ** activating hyperlinks */
248
- @ document.getElementsByTagName("body")[0].onmousemove=function(){
249
- @ setTimeout("setAllHrefs();",%d(nDelay));
250
- @ this.onmousemove = null;
251
- @ }
252
- }else{
253
- /* Active hyperlinks after a delay */
254
- @ setTimeout("setAllHrefs();",%d(nDelay));
255
- }
256
- @ </script>
189
+ @ <form method="POST" data-action='%s(zLink)' action='%R/login' \
190
+ @ %s(zOtherArgs)>
191
+ }
257192
}
258193
259194
/*
260195
** Add a new element to the submenu
261196
*/
@@ -576,10 +511,11 @@
576511
*/
577512
void style_footer(void){
578513
const char *zFooter;
579514
const char *zAd = 0;
580515
unsigned int mAdFlags = 0;
516
+ int bMouseover = 0; /* Active hyperlinks after mouseover */
581517
582518
if( !headerHasBeenGenerated ) return;
583519
584520
/* Go back and put the submenu at the top of the page. We delay the
585521
** creation of the submenu until the end so that we can add elements
@@ -726,15 +662,38 @@
726662
*/
727663
@ <div class="endContent"></div>
728664
}
729665
@ </div>
730666
667
+#if 0
731668
/* Set the href= field on hyperlinks. Do this before the footer since
732669
** the footer will be generating </html> */
733670
style_resolve_href();
671
+#endif
672
+
673
+ /* Load up the page data */
674
+ @ <script id='page-data' type='application/json'>
675
+ if( !g.javascriptHyperlink ){
676
+ @ {"antibot":{"enable":0},
677
+ }else{
678
+ int nDelay = db_get_int("auto-hyperlink-delay",0);
679
+ int bMouseover;
680
+ bMouseover = (!g.isHuman || db_get_boolean("auto-hyperlink-ishuman",0))
681
+ && db_get_boolean("auto-hyperlink-mouseover",0);
682
+ @ {"antibot":
683
+ @ {"enable":1,
684
+ @ "delay":%d(nDelay),
685
+ @ "mouseover":%d(bMouseover)},
686
+ }
687
+ @ "noop":0}
688
+ @ </script>
689
+
734690
735691
zFooter = skin_get("footer");
692
+ if( sqlite3_strlike("%</body>%", zFooter, 0)==0 ){
693
+ @ <script src='%s(g.zBaseURL)/main.js' type='application/javascript'>\
694
+ }
736695
if( g.thTrace ) Th_Trace("BEGIN_FOOTER<br />\n", -1);
737696
Th_Render(zFooter);
738697
if( g.thTrace ) Th_Trace("END_FOOTER<br />\n", -1);
739698
740699
/* Render trace log if TH1 tracing is enabled. */
@@ -744,16 +703,12 @@
744703
cgi_append_content("</span>\n", -1);
745704
}
746705
747706
/* Add document end mark if it was not in the footer */
748707
if( sqlite3_strlike("%</body>%", zFooter, 0)!=0 ){
749
- style_pagedata_appendf("{'op':'no-op'}]");
750
- @ <script type='application/json' id='page-data'>
751
- @ %s(blob_str(&pageDataJson))
752
- @ </script>
753708
@ <script src='%s(g.zBaseURL)/main.js' type='application/javascript'>\
754
- @ <script>
709
+ @ </script>
755710
@ </body>
756711
@ </html>
757712
}
758713
}
759714
760715
--- src/style.c
+++ src/style.c
@@ -83,25 +83,10 @@
83 /*
84 ** Ad-unit styles.
85 */
86 static unsigned adUnitFlags = 0;
87
88 /*
89 ** Page data JSON
90 */
91 static Blob pageDataJson = BLOB_INITIALIZER;
92
93
94 /*
95 ** List of hyperlinks and forms that need to be resolved by javascript in
96 ** the footer.
97 */
98 char **aHref = 0;
99 int nHref = 0;
100 int nHrefAlloc = 0;
101 char **aFormAction = 0;
102 int nFormAction = 0;
103
104 /*
105 ** Generate and return a anchor tag like this:
106 **
107 ** <a href="URL">
@@ -131,12 +116,15 @@
131 **
132 ** Note %z format. The string returned by this function is always
133 ** obtained from fossil_malloc() so rendering it with %z will reclaim
134 ** that memory space.
135 **
136 ** There are two versions of this routine: href() does a plain hyperlink
137 ** and xhref() adds extra attribute text.
 
 
 
138 **
139 ** g.perm.Hyperlink is true if the user has the Hyperlink (h) property.
140 ** Most logged in users should have this property, since we can assume
141 ** that a logged in user is not a bot. Only "nobody" lacks g.perm.Hyperlink,
142 ** typically.
@@ -150,16 +138,26 @@
150 if( g.perm.Hyperlink && !g.javascriptHyperlink ){
151 char *zHUrl = mprintf("<a %s href=\"%h\">", zExtra, zUrl);
152 fossil_free(zUrl);
153 return zHUrl;
154 }
155 if( nHref>=nHrefAlloc ){
156 nHrefAlloc = nHrefAlloc*2 + 10;
157 aHref = fossil_realloc(aHref, nHrefAlloc*sizeof(aHref[0]));
 
 
 
 
 
 
 
 
 
 
158 }
159 aHref[nHref++] = zUrl;
160 return mprintf("<a %s id='a%d' href='%R/honeypot'>", zExtra, nHref);
161 }
162 char *href(const char *zFormat, ...){
163 char *zUrl;
164 va_list ap;
165 va_start(ap, zFormat);
@@ -168,16 +166,12 @@
168 if( g.perm.Hyperlink && !g.javascriptHyperlink ){
169 char *zHUrl = mprintf("<a href=\"%h\">", zUrl);
170 fossil_free(zUrl);
171 return zHUrl;
172 }
173 if( nHref>=nHrefAlloc ){
174 nHrefAlloc = nHrefAlloc*2 + 10;
175 aHref = fossil_realloc(aHref, nHrefAlloc*sizeof(aHref[0]));
176 }
177 aHref[nHref++] = zUrl;
178 return mprintf("<a id='a%d' href='%R/honeypot'>", nHref);
179 }
180
181 /*
182 ** Generate <form method="post" action=ARG>. The ARG value is inserted
183 ** by javascript.
@@ -190,72 +184,13 @@
190 zLink = vmprintf(zAction, ap);
191 va_end(ap);
192 if( g.perm.Hyperlink && !g.javascriptHyperlink ){
193 @ <form method="POST" action="%z(zLink)" %s(zOtherArgs)>
194 }else{
195 int n;
196 aFormAction = fossil_realloc(aFormAction, (nFormAction+1)*sizeof(char*));
197 aFormAction[nFormAction++] = zLink;
198 n = nFormAction;
199 @ <form id="form%d(n)" method="POST" action='%R/login' %s(zOtherArgs)>
200 }
201 }
202
203 /*
204 ** Append page-data JSON
205 */
206 void style_pagedata_appendf(const char *zFormat, ...){
207 va_list ap;
208 if( blob_size(&pageDataJson)==0 ){
209 blob_append(&pageDataJson, "[", 1);
210 }
211 va_start(ap, zFormat);
212 blob_vappendf(&pageDataJson, zFormat, ap);
213 va_end(ap);
214 }
215
216 /*
217 ** Generate javascript that will set the href= attribute on all anchors.
218 */
219 void style_resolve_href(void){
220 int i;
221 int nDelay = db_get_int("auto-hyperlink-delay",10);
222 if( !g.perm.Hyperlink ) return;
223 if( nHref==0 && nFormAction==0 ) return;
224 @ <script>
225 @ function setAllHrefs(){
226 if( g.javascriptHyperlink ){
227 for(i=0; i<nHref; i++){
228 @ gebi("a%d(i+1)").href="%s(aHref[i])";
229 }
230 }
231 for(i=0; i<nFormAction; i++){
232 @ gebi("form%d(i+1)").action="%s(aFormAction[i])";
233 }
234 @ }
235 if( sqlite3_strglob("*Opera Mini/[1-9]*", PD("HTTP_USER_AGENT",""))==0 ){
236 /* Special case for Opera Mini, which executes JS server-side */
237 @ var isOperaMini = Object.prototype.toString.call(window.operamini)
238 @ === "[object OperaMini]";
239 @ if( isOperaMini ){
240 @ setTimeout("setAllHrefs();",%d(nDelay));
241 @ }
242 }else if( db_get_boolean("auto-hyperlink-ishuman",0) && g.isHuman ){
243 /* Active hyperlinks after a delay */
244 @ setTimeout("setAllHrefs();",%d(nDelay));
245 }else if( db_get_boolean("auto-hyperlink-mouseover",0) ){
246 /* Require mouse movement before starting the teim that will
247 ** activating hyperlinks */
248 @ document.getElementsByTagName("body")[0].onmousemove=function(){
249 @ setTimeout("setAllHrefs();",%d(nDelay));
250 @ this.onmousemove = null;
251 @ }
252 }else{
253 /* Active hyperlinks after a delay */
254 @ setTimeout("setAllHrefs();",%d(nDelay));
255 }
256 @ </script>
257 }
258
259 /*
260 ** Add a new element to the submenu
261 */
@@ -576,10 +511,11 @@
576 */
577 void style_footer(void){
578 const char *zFooter;
579 const char *zAd = 0;
580 unsigned int mAdFlags = 0;
 
581
582 if( !headerHasBeenGenerated ) return;
583
584 /* Go back and put the submenu at the top of the page. We delay the
585 ** creation of the submenu until the end so that we can add elements
@@ -726,15 +662,38 @@
726 */
727 @ <div class="endContent"></div>
728 }
729 @ </div>
730
 
731 /* Set the href= field on hyperlinks. Do this before the footer since
732 ** the footer will be generating </html> */
733 style_resolve_href();
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
734
735 zFooter = skin_get("footer");
 
 
 
736 if( g.thTrace ) Th_Trace("BEGIN_FOOTER<br />\n", -1);
737 Th_Render(zFooter);
738 if( g.thTrace ) Th_Trace("END_FOOTER<br />\n", -1);
739
740 /* Render trace log if TH1 tracing is enabled. */
@@ -744,16 +703,12 @@
744 cgi_append_content("</span>\n", -1);
745 }
746
747 /* Add document end mark if it was not in the footer */
748 if( sqlite3_strlike("%</body>%", zFooter, 0)!=0 ){
749 style_pagedata_appendf("{'op':'no-op'}]");
750 @ <script type='application/json' id='page-data'>
751 @ %s(blob_str(&pageDataJson))
752 @ </script>
753 @ <script src='%s(g.zBaseURL)/main.js' type='application/javascript'>\
754 @ <script>
755 @ </body>
756 @ </html>
757 }
758 }
759
760
--- src/style.c
+++ src/style.c
@@ -83,25 +83,10 @@
83 /*
84 ** Ad-unit styles.
85 */
86 static unsigned adUnitFlags = 0;
87
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
88
89 /*
90 ** Generate and return a anchor tag like this:
91 **
92 ** <a href="URL">
@@ -131,12 +116,15 @@
116 **
117 ** Note %z format. The string returned by this function is always
118 ** obtained from fossil_malloc() so rendering it with %z will reclaim
119 ** that memory space.
120 **
121 ** There are three versions of this routine:
122 **
123 ** (1) href() does a plain hyperlink
124 ** (2) xhref() adds extra attribute text
125 ** (3) chref() adds a class name
126 **
127 ** g.perm.Hyperlink is true if the user has the Hyperlink (h) property.
128 ** Most logged in users should have this property, since we can assume
129 ** that a logged in user is not a bot. Only "nobody" lacks g.perm.Hyperlink,
130 ** typically.
@@ -150,16 +138,26 @@
138 if( g.perm.Hyperlink && !g.javascriptHyperlink ){
139 char *zHUrl = mprintf("<a %s href=\"%h\">", zExtra, zUrl);
140 fossil_free(zUrl);
141 return zHUrl;
142 }
143 return mprintf("<a %s class='antibot' data-href='%z' href='%R/honeypot'>",
144 zExtra, zUrl);
145 }
146 char *chref(const char *zExtra, const char *zFormat, ...){
147 char *zUrl;
148 va_list ap;
149 va_start(ap, zFormat);
150 zUrl = vmprintf(zFormat, ap);
151 va_end(ap);
152 if( g.perm.Hyperlink && !g.javascriptHyperlink ){
153 char *zHUrl = mprintf("<a %s href=\"%h\">", zExtra, zUrl);
154 fossil_free(zUrl);
155 return zHUrl;
156 }
157 return mprintf("<a class='antibot %s' data-href='%z' href='%R/honeypot'>",
158 zExtra, zUrl);
159 }
160 char *href(const char *zFormat, ...){
161 char *zUrl;
162 va_list ap;
163 va_start(ap, zFormat);
@@ -168,16 +166,12 @@
166 if( g.perm.Hyperlink && !g.javascriptHyperlink ){
167 char *zHUrl = mprintf("<a href=\"%h\">", zUrl);
168 fossil_free(zUrl);
169 return zHUrl;
170 }
171 return mprintf("<a class='antibot' data-href='%s' href='%R/honeypot'>",
172 zUrl);
 
 
 
 
173 }
174
175 /*
176 ** Generate <form method="post" action=ARG>. The ARG value is inserted
177 ** by javascript.
@@ -190,72 +184,13 @@
184 zLink = vmprintf(zAction, ap);
185 va_end(ap);
186 if( g.perm.Hyperlink && !g.javascriptHyperlink ){
187 @ <form method="POST" action="%z(zLink)" %s(zOtherArgs)>
188 }else{
189 @ <form method="POST" data-action='%s(zLink)' action='%R/login' \
190 @ %s(zOtherArgs)>
191 }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
192 }
193
194 /*
195 ** Add a new element to the submenu
196 */
@@ -576,10 +511,11 @@
511 */
512 void style_footer(void){
513 const char *zFooter;
514 const char *zAd = 0;
515 unsigned int mAdFlags = 0;
516 int bMouseover = 0; /* Active hyperlinks after mouseover */
517
518 if( !headerHasBeenGenerated ) return;
519
520 /* Go back and put the submenu at the top of the page. We delay the
521 ** creation of the submenu until the end so that we can add elements
@@ -726,15 +662,38 @@
662 */
663 @ <div class="endContent"></div>
664 }
665 @ </div>
666
667 #if 0
668 /* Set the href= field on hyperlinks. Do this before the footer since
669 ** the footer will be generating </html> */
670 style_resolve_href();
671 #endif
672
673 /* Load up the page data */
674 @ <script id='page-data' type='application/json'>
675 if( !g.javascriptHyperlink ){
676 @ {"antibot":{"enable":0},
677 }else{
678 int nDelay = db_get_int("auto-hyperlink-delay",0);
679 int bMouseover;
680 bMouseover = (!g.isHuman || db_get_boolean("auto-hyperlink-ishuman",0))
681 && db_get_boolean("auto-hyperlink-mouseover",0);
682 @ {"antibot":
683 @ {"enable":1,
684 @ "delay":%d(nDelay),
685 @ "mouseover":%d(bMouseover)},
686 }
687 @ "noop":0}
688 @ </script>
689
690
691 zFooter = skin_get("footer");
692 if( sqlite3_strlike("%</body>%", zFooter, 0)==0 ){
693 @ <script src='%s(g.zBaseURL)/main.js' type='application/javascript'>\
694 }
695 if( g.thTrace ) Th_Trace("BEGIN_FOOTER<br />\n", -1);
696 Th_Render(zFooter);
697 if( g.thTrace ) Th_Trace("END_FOOTER<br />\n", -1);
698
699 /* Render trace log if TH1 tracing is enabled. */
@@ -744,16 +703,12 @@
703 cgi_append_content("</span>\n", -1);
704 }
705
706 /* Add document end mark if it was not in the footer */
707 if( sqlite3_strlike("%</body>%", zFooter, 0)!=0 ){
 
 
 
 
708 @ <script src='%s(g.zBaseURL)/main.js' type='application/javascript'>\
709 @ </script>
710 @ </body>
711 @ </html>
712 }
713 }
714
715
+1 -1
--- src/tag.c
+++ src/tag.c
@@ -669,11 +669,11 @@
669669
);
670670
@ <ul>
671671
while( db_step(&q)==SQLITE_ROW ){
672672
const char *zName = db_column_text(&q, 0);
673673
if( g.perm.Hyperlink ){
674
- @ <li>%z(xhref("class='taglink'","%R/timeline?t=%T&n=200",zName))
674
+ @ <li>%z(chref("taglink","%R/timeline?t=%T&n=200",zName))
675675
@ %h(zName)</a></li>
676676
}else{
677677
@ <li><span class="tagDsp">%h(zName)</span></li>
678678
}
679679
}
680680
--- src/tag.c
+++ src/tag.c
@@ -669,11 +669,11 @@
669 );
670 @ <ul>
671 while( db_step(&q)==SQLITE_ROW ){
672 const char *zName = db_column_text(&q, 0);
673 if( g.perm.Hyperlink ){
674 @ <li>%z(xhref("class='taglink'","%R/timeline?t=%T&n=200",zName))
675 @ %h(zName)</a></li>
676 }else{
677 @ <li><span class="tagDsp">%h(zName)</span></li>
678 }
679 }
680
--- src/tag.c
+++ src/tag.c
@@ -669,11 +669,11 @@
669 );
670 @ <ul>
671 while( db_step(&q)==SQLITE_ROW ){
672 const char *zName = db_column_text(&q, 0);
673 if( g.perm.Hyperlink ){
674 @ <li>%z(chref("taglink","%R/timeline?t=%T&n=200",zName))
675 @ %h(zName)</a></li>
676 }else{
677 @ <li><span class="tagDsp">%h(zName)</span></li>
678 }
679 }
680
+4 -4
--- src/timeline.c
+++ src/timeline.c
@@ -50,11 +50,11 @@
5050
/*
5151
** Generate a hyperlink to a version.
5252
*/
5353
void hyperlink_to_uuid(const char *zUuid){
5454
if( g.perm.Hyperlink ){
55
- @ %z(xhref("class='timelineHistLink'","%R/info/%!S",zUuid))[%S(zUuid)]</a>
55
+ @ %z(chref("timelineHistLink","%R/info/%!S",zUuid))[%S(zUuid)]</a>
5656
}else{
5757
@ <span class="timelineHistDsp">[%S(zUuid)]</span>
5858
}
5959
}
6060
@@ -387,11 +387,11 @@
387387
zId = db_text(0, "SELECT substr(tagname, 7) FROM tag WHERE tagid=%d",
388388
tagid);
389389
zDateLink = href("%R/technote/%s",zId);
390390
free(zId);
391391
}else if( zUuid ){
392
- zDateLink = xhref("class='timelineHistLink'", "%R/info/%!S", zUuid);
392
+ zDateLink = chref("timelineHistLink", "%R/info/%!S", zUuid);
393393
}else{
394394
zDateLink = mprintf("<a>");
395395
}
396396
/* WAS: zDateLink = href("%R/timeline?c=%!S&unhide", zUuid); */
397397
@ <td class="timelineTime">%z(zDateLink)%s(zTime)</a></td>
@@ -2295,16 +2295,16 @@
22952295
if( zError ){
22962296
@ <p class="generalError">%h(zError)</p>
22972297
}
22982298
22992299
if( zNewerButton ){
2300
- @ %z(xhref("class='button'","%z",zNewerButton))More&nbsp;&uarr;</a>
2300
+ @ %z(chref("button","%z",zNewerButton))More&nbsp;&uarr;</a>
23012301
}
23022302
www_print_timeline(&q, tmFlags, zThisUser, zThisTag, selectedRid, 0);
23032303
db_finalize(&q);
23042304
if( zOlderButton ){
2305
- @ %z(xhref("class='button'","%z",zOlderButton))More&nbsp;&darr;</a>
2305
+ @ %z(chref("button","%z",zOlderButton))More&nbsp;&darr;</a>
23062306
}
23072307
style_footer();
23082308
}
23092309
23102310
/*
23112311
--- src/timeline.c
+++ src/timeline.c
@@ -50,11 +50,11 @@
50 /*
51 ** Generate a hyperlink to a version.
52 */
53 void hyperlink_to_uuid(const char *zUuid){
54 if( g.perm.Hyperlink ){
55 @ %z(xhref("class='timelineHistLink'","%R/info/%!S",zUuid))[%S(zUuid)]</a>
56 }else{
57 @ <span class="timelineHistDsp">[%S(zUuid)]</span>
58 }
59 }
60
@@ -387,11 +387,11 @@
387 zId = db_text(0, "SELECT substr(tagname, 7) FROM tag WHERE tagid=%d",
388 tagid);
389 zDateLink = href("%R/technote/%s",zId);
390 free(zId);
391 }else if( zUuid ){
392 zDateLink = xhref("class='timelineHistLink'", "%R/info/%!S", zUuid);
393 }else{
394 zDateLink = mprintf("<a>");
395 }
396 /* WAS: zDateLink = href("%R/timeline?c=%!S&unhide", zUuid); */
397 @ <td class="timelineTime">%z(zDateLink)%s(zTime)</a></td>
@@ -2295,16 +2295,16 @@
2295 if( zError ){
2296 @ <p class="generalError">%h(zError)</p>
2297 }
2298
2299 if( zNewerButton ){
2300 @ %z(xhref("class='button'","%z",zNewerButton))More&nbsp;&uarr;</a>
2301 }
2302 www_print_timeline(&q, tmFlags, zThisUser, zThisTag, selectedRid, 0);
2303 db_finalize(&q);
2304 if( zOlderButton ){
2305 @ %z(xhref("class='button'","%z",zOlderButton))More&nbsp;&darr;</a>
2306 }
2307 style_footer();
2308 }
2309
2310 /*
2311
--- src/timeline.c
+++ src/timeline.c
@@ -50,11 +50,11 @@
50 /*
51 ** Generate a hyperlink to a version.
52 */
53 void hyperlink_to_uuid(const char *zUuid){
54 if( g.perm.Hyperlink ){
55 @ %z(chref("timelineHistLink","%R/info/%!S",zUuid))[%S(zUuid)]</a>
56 }else{
57 @ <span class="timelineHistDsp">[%S(zUuid)]</span>
58 }
59 }
60
@@ -387,11 +387,11 @@
387 zId = db_text(0, "SELECT substr(tagname, 7) FROM tag WHERE tagid=%d",
388 tagid);
389 zDateLink = href("%R/technote/%s",zId);
390 free(zId);
391 }else if( zUuid ){
392 zDateLink = chref("timelineHistLink", "%R/info/%!S", zUuid);
393 }else{
394 zDateLink = mprintf("<a>");
395 }
396 /* WAS: zDateLink = href("%R/timeline?c=%!S&unhide", zUuid); */
397 @ <td class="timelineTime">%z(zDateLink)%s(zTime)</a></td>
@@ -2295,16 +2295,16 @@
2295 if( zError ){
2296 @ <p class="generalError">%h(zError)</p>
2297 }
2298
2299 if( zNewerButton ){
2300 @ %z(chref("button","%z",zNewerButton))More&nbsp;&uarr;</a>
2301 }
2302 www_print_timeline(&q, tmFlags, zThisUser, zThisTag, selectedRid, 0);
2303 db_finalize(&q);
2304 if( zOlderButton ){
2305 @ %z(chref("button","%z",zOlderButton))More&nbsp;&darr;</a>
2306 }
2307 style_footer();
2308 }
2309
2310 /*
2311

Keyboard Shortcuts

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