Fossil

Check-in [4f2c9841]
Login

Many hyperlinks are disabled.
Use anonymous login to enable hyperlinks.

Overview
Comment:Anti-robot defenses are now CSP-safe.
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | main.js
Files: files | file ages | folders
SHA3-256:4f2c9841323513544e41ab779c759b81488c7498d4dbaa106309bb2a8597d1ec
User & Date: drh 2017-12-05 01:05:25
Context
2017-12-05
01:24
Remove unnecessary "antibot" class names from anchors. Closed-Leaf check-in: 52a47db1 user: drh tags: main.js
01:05
Anti-robot defenses are now CSP-safe. check-in: 4f2c9841 user: drh tags: main.js
2017-12-04
21:08
Attempt to add a separate JS file and source it just prior to </body> check-in: 487aa43f user: drh tags: main.js
Changes
Hide Diffs Side-by-Side Diffs Ignore Whitespace Patch

Changes to src/codecheck1.c.

   310    310     unsigned fmtFlags;     /* Processing flags */
   311    311   } aFmtFunc[] = {
   312    312     { "admin_log",               1, 0 },
   313    313     { "blob_append_sql",         2, FMT_NO_S },
   314    314     { "blob_appendf",            2, 0 },
   315    315     { "cgi_panic",               1, 0 },
   316    316     { "cgi_redirectf",           1, 0 },
          317  +  { "chref",                   2, 0 },
   317    318     { "db_blob",                 2, FMT_NO_S },
   318    319     { "db_double",               2, FMT_NO_S },
   319    320     { "db_err",                  1, 0 },
   320    321     { "db_exists",               1, FMT_NO_S },
   321    322     { "db_int",                  2, FMT_NO_S },
   322    323     { "db_int64",                2, FMT_NO_S },
   323    324     { "db_multi_exec",           1, FMT_NO_S },

Changes to src/info.c.

   796    796       verboseFlag = !verboseFlag;
   797    797       zPage = "ci";
   798    798       zPageHide = "vinfo";
   799    799     }
   800    800     diffFlags = construct_diff_flags(verboseFlag, sideBySide);
   801    801     zW = (diffFlags&DIFF_IGNORE_ALLWS)?"&w":"";
   802    802     if( verboseFlag ){
   803         -    @ %z(xhref("class='button'","%R/%s/%T",zPageHide,zName))
          803  +    @ %z(chref("button","%R/%s/%T",zPageHide,zName))
   804    804       @ Hide&nbsp;Diffs</a>
   805    805       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))
   807    807         @ Unified&nbsp;Diffs</a>
   808    808       }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))
   810    810         @ Side-by-Side&nbsp;Diffs</a>
   811    811       }
   812    812       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))
   814    814         @ Show&nbsp;Whitespace&nbsp;Changes</a>
   815    815       }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))
   817    817         @ Ignore&nbsp;Whitespace</a>
   818    818       }
   819    819     }else{
   820         -    @ %z(xhref("class='button'","%R/%s/%T?sbs=0",zPage,zName))
          820  +    @ %z(chref("button","%R/%s/%T?sbs=0",zPage,zName))
   821    821       @ 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))
   823    823       @ Show&nbsp;Side-by-Side&nbsp;Diffs</a>
   824    824     }
   825    825     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))
   827    827       @ Patch</a>
   828    828     }
   829    829     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>
   831    831     }
   832    832     @</div>
   833    833     if( pRe ){
   834    834       @ <p><b>Only differences that match regular expression "%h(zRe)"
   835    835       @ are shown.</b></p>
   836    836     }
   837    837     db_prepare(&q3,

Changes to src/main.js.

     1      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         -    }
            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");
    25     24     }
    26     25   }
    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
           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);
    32     35     }
    33     36   }

Changes to src/style.c.

    81     81   static int sideboxUsed = 0;
    82     82   
    83     83   /*
    84     84   ** Ad-unit styles.
    85     85   */
    86     86   static unsigned adUnitFlags = 0;
    87     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     88   
   104     89   /*
   105     90   ** Generate and return a anchor tag like this:
   106     91   **
   107     92   **        <a href="URL">
   108     93   **  or    <a id="ID">
   109     94   **
................................................................................
   129    114   **
   130    115   **      @ %z(href("%R/artifact/%s",zUuid))%h(zFN)</a>
   131    116   **
   132    117   ** Note %z format.  The string returned by this function is always
   133    118   ** obtained from fossil_malloc() so rendering it with %z will reclaim
   134    119   ** that memory space.
   135    120   **
   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
   138    126   **
   139    127   ** g.perm.Hyperlink is true if the user has the Hyperlink (h) property.
   140    128   ** Most logged in users should have this property, since we can assume
   141    129   ** that a logged in user is not a bot.  Only "nobody" lacks g.perm.Hyperlink,
   142    130   ** typically.
   143    131   */
   144    132   char *xhref(const char *zExtra, const char *zFormat, ...){
................................................................................
   148    136     zUrl = vmprintf(zFormat, ap);
   149    137     va_end(ap);
   150    138     if( g.perm.Hyperlink && !g.javascriptHyperlink ){
   151    139       char *zHUrl = mprintf("<a %s href=\"%h\">", zExtra, zUrl);
   152    140       fossil_free(zUrl);
   153    141       return zHUrl;
   154    142     }
   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;
   158    156     }
   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);
   161    159   }
   162    160   char *href(const char *zFormat, ...){
   163    161     char *zUrl;
   164    162     va_list ap;
   165    163     va_start(ap, zFormat);
   166    164     zUrl = vmprintf(zFormat, ap);
   167    165     va_end(ap);
   168    166     if( g.perm.Hyperlink && !g.javascriptHyperlink ){
   169    167       char *zHUrl = mprintf("<a href=\"%h\">", zUrl);
   170    168       fossil_free(zUrl);
   171    169       return zHUrl;
   172    170     }
   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);
   179    173   }
   180    174   
   181    175   /*
   182    176   ** Generate <form method="post" action=ARG>.  The ARG value is inserted
   183    177   ** by javascript.
   184    178   */
   185    179   void form_begin(const char *zOtherArgs, const char *zAction, ...){
................................................................................
   188    182     if( zOtherArgs==0 ) zOtherArgs = "";
   189    183     va_start(ap, zAction);
   190    184     zLink = vmprintf(zAction, ap);
   191    185     va_end(ap);
   192    186     if( g.perm.Hyperlink && !g.javascriptHyperlink ){
   193    187       @ <form method="POST" action="%z(zLink)" %s(zOtherArgs)>
   194    188     }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)>
          189  +    @ <form method="POST" data-action='%s(zLink)' action='%R/login' \
          190  +    @ %s(zOtherArgs)>
   200    191     }
   201    192   }
   202    193   
   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    194   /*
   260    195   ** Add a new element to the submenu
   261    196   */
   262    197   void style_submenu_element(
   263    198     const char *zLabel,
   264    199     const char *zLink,
   265    200     ...
................................................................................
   574    509   /*
   575    510   ** Draw the footer at the bottom of the page.
   576    511   */
   577    512   void style_footer(void){
   578    513     const char *zFooter;
   579    514     const char *zAd = 0;
   580    515     unsigned int mAdFlags = 0;
          516  +  int bMouseover = 0;            /* Active hyperlinks after mouseover */
   581    517   
   582    518     if( !headerHasBeenGenerated ) return;
   583    519   
   584    520     /* Go back and put the submenu at the top of the page.  We delay the
   585    521     ** creation of the submenu until the end so that we can add elements
   586    522     ** to the submenu while generating page text.
   587    523     */
................................................................................
   724    660       ** the additional clear/both is needed to extend the content
   725    661       ** part to the end of an optional sidebox.
   726    662       */
   727    663       @ <div class="endContent"></div>
   728    664     }
   729    665     @ </div>
   730    666   
          667  +#if 0
   731    668     /* Set the href= field on hyperlinks.  Do this before the footer since
   732    669     ** the footer will be generating </html> */
   733    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  +
   734    690   
   735    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  +  }
   736    695     if( g.thTrace ) Th_Trace("BEGIN_FOOTER<br />\n", -1);
   737    696     Th_Render(zFooter);
   738    697     if( g.thTrace ) Th_Trace("END_FOOTER<br />\n", -1);
   739    698   
   740    699     /* Render trace log if TH1 tracing is enabled. */
   741    700     if( g.thTrace ){
   742    701       cgi_append_content("<span class=\"thTrace\"><hr />\n", -1);
   743    702       cgi_append_content(blob_str(&g.thLog), blob_size(&g.thLog));
   744    703       cgi_append_content("</span>\n", -1);
   745    704     }
   746    705   
   747    706     /* Add document end mark if it was not in the footer */
   748    707     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    708       @ <script src='%s(g.zBaseURL)/main.js' type='application/javascript'>\
   754         -    @ <script>
          709  +    @ </script>
   755    710       @ </body>
   756    711       @ </html>
   757    712     }
   758    713   }
   759    714   
   760    715   /*
   761    716   ** Begin a side-box on the right-hand side of a page.  The title and

Changes to src/tag.c.

   667    667       " AND tagname GLOB 'sym-*'"
   668    668       " ORDER BY tagname"
   669    669     );
   670    670     @ <ul>
   671    671     while( db_step(&q)==SQLITE_ROW ){
   672    672       const char *zName = db_column_text(&q, 0);
   673    673       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))
   675    675         @ %h(zName)</a></li>
   676    676       }else{
   677    677         @ <li><span class="tagDsp">%h(zName)</span></li>
   678    678       }
   679    679     }
   680    680     @ </ul>
   681    681     db_finalize(&q);

Changes to src/timeline.c.

    48     48   }
    49     49   
    50     50   /*
    51     51   ** Generate a hyperlink to a version.
    52     52   */
    53     53   void hyperlink_to_uuid(const char *zUuid){
    54     54     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>
    56     56     }else{
    57     57       @ <span class="timelineHistDsp">[%S(zUuid)]</span>
    58     58     }
    59     59   }
    60     60   
    61     61   /*
    62     62   ** Generate a hyperlink to a date & time.
................................................................................
   385    385       if( zType[0]=='e' && tagid ){
   386    386         char *zId;
   387    387         zId = db_text(0, "SELECT substr(tagname, 7) FROM tag WHERE tagid=%d",
   388    388                           tagid);
   389    389         zDateLink = href("%R/technote/%s",zId);
   390    390         free(zId);
   391    391       }else if( zUuid ){
   392         -      zDateLink = xhref("class='timelineHistLink'", "%R/info/%!S", zUuid);
          392  +      zDateLink = chref("timelineHistLink", "%R/info/%!S", zUuid);
   393    393       }else{
   394    394         zDateLink = mprintf("<a>");
   395    395       }
   396    396       /* WAS: zDateLink = href("%R/timeline?c=%!S&unhide", zUuid); */
   397    397       @ <td class="timelineTime">%z(zDateLink)%s(zTime)</a></td>
   398    398       @ <td class="timelineGraph">
   399    399       if( tmFlags & TIMELINE_UCOLOR )  zBgClr = zUser ? hash_color(zUser) : 0;
................................................................................
  2293   2293   
  2294   2294     /* Report any errors. */
  2295   2295     if( zError ){
  2296   2296       @ <p class="generalError">%h(zError)</p>
  2297   2297     }
  2298   2298   
  2299   2299     if( zNewerButton ){
  2300         -    @ %z(xhref("class='button'","%z",zNewerButton))More&nbsp;&uarr;</a>
         2300  +    @ %z(chref("button","%z",zNewerButton))More&nbsp;&uarr;</a>
  2301   2301     }
  2302   2302     www_print_timeline(&q, tmFlags, zThisUser, zThisTag, selectedRid, 0);
  2303   2303     db_finalize(&q);
  2304   2304     if( zOlderButton ){
  2305         -    @ %z(xhref("class='button'","%z",zOlderButton))More&nbsp;&darr;</a>
         2305  +    @ %z(chref("button","%z",zOlderButton))More&nbsp;&darr;</a>
  2306   2306     }
  2307   2307     style_footer();
  2308   2308   }
  2309   2309   
  2310   2310   /*
  2311   2311   ** The input query q selects various records.  Print a human-readable
  2312   2312   ** summary of those records.