Fossil

Check-in [368c78a8]
Login

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

Overview
Comment:merge trunk
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | openssl-1.1
Files: files | file ages | folders
SHA3-256:368c78a855cf65e9cacee8c898a6f1bbae91461c8eadf6be5fd58b277b3d956a
User & Date: jan.nijtmans 2018-09-11 14:54:21
Context
2018-09-15
20:27
add support for TLS 1.3, when compiled with OpenSSL 1.1.1 (LTS). Windows build adapted to use OpenSSL 1.1.1 by default. Still compiles and runs with older OpenSSL as well check-in: 115544e9 user: jan.nijtmans tags: trunk
2018-09-11
14:54
merge trunk Closed-Leaf check-in: 368c78a8 user: jan.nijtmans tags: openssl-1.1
14:29
Compile openssl with option no-weak-ssl-ciphers (or -DOPENSSL_NO_WEAK_SSL_CIPHERS) Update custum Makefile.mingw check-in: 58b7b4e5 user: jan.nijtmans tags: trunk
2018-08-24
08:29
Merge trunk. Undo unintended changes in previous commit check-in: 5583448c user: jan.nijtmans tags: openssl-1.1
Changes
Hide Diffs Side-by-Side Diffs Ignore Whitespace Patch

Changes to Makefile.in.

    32     32   E = @EXEEXT@
    33     33   
    34     34   TCC = @CC@
    35     35   
    36     36   #### Tcl shell for use in running the fossil testsuite.  If you do not
    37     37   #    care about testing the end result, this can be blank.
    38     38   #
    39         -TCLSH = tclsh
           39  +TCLSH = @TCLSH@
    40     40   
    41     41   CFLAGS = @CFLAGS@
    42     42   LIB =	@LDFLAGS@ @EXTRA_LDFLAGS@ @LIBS@
    43     43   BCCFLAGS =	@CPPFLAGS@ $(CFLAGS)
    44     44   TCCFLAGS =	@EXTRA_CFLAGS@ @CPPFLAGS@ $(CFLAGS) -DHAVE_AUTOCONFIG_H -D_HAVE_SQLITE_CONFIG_H
    45     45   INSTALLDIR = $(DESTDIR)@prefix@/bin
    46     46   USE_SYSTEM_SQLITE = @USE_SYSTEM_SQLITE@

Changes to auto.def.

    31     31   cc-with {-includes {stdint.h inttypes.h}} {
    32     32       cc-check-types uint32_t uint16_t int16_t uint8_t
    33     33   }
    34     34   
    35     35   # Use pread/pwrite system calls in place of seek + read/write if possible
    36     36   define USE_PREAD [cc-check-functions pread]
    37     37   
    38         -# Find tclsh for the test suite. Can't yet use jimsh for this.
           38  +# Find tclsh for the test suite.
           39  +#
           40  +# We can't use jimsh for this: the test suite uses features of Tcl that
           41  +# Jim doesn't support, either statically or due to the way it's built by
           42  +# autosetup.  For example, Jim supports `file normalize`, but only if
           43  +# you build it with HAVE_REALPATH, which won't ever be defined in this
           44  +# context because autosetup doesn't try to discover platform-specific
           45  +# details like that before it decides to build jimsh0.  Besides which,
           46  +# autosetup won't build jimsh0 at all if it can find tclsh itself.
           47  +# Ironically, this means we may right now be running under either jimsh0
           48  +# or a version of tclsh that we find unsuitable below!
    39     49   cc-check-progs tclsh
           50  +set hbtd /usr/local/Cellar/tcl-tk
           51  +if {[string equal false [get-define TCLSH]]} {
           52  +    msg-result "WARNING: 'make test' will not run here."
           53  +} else {
           54  +    set v [exec /bin/sh -c "echo 'puts \$tcl_version' | tclsh"]
           55  +    if {[expr $v >= 8.6]} {
           56  +        msg-result "Found Tclsh version $v in the PATH."
           57  +        define TCLSH tclsh
           58  +    } elseif {[file isdirectory $hbtd]} {
           59  +        # This is a macOS system with the Homebrew version of Tcl/Tk
           60  +        # installed.  Select the newest version.  It won't normally be
           61  +        # in the PATH to avoid shadowing /usr/bin/tclsh, and even if it
           62  +        # were in the PATH, it's bad practice to put /usr/local/bin (the
           63  +        # Homebrew default) ahead of /usr/bin, especially given that
           64  +        # it's user-writeable by default with Homebrew.  Thus, we can be
           65  +        # pretty sure the only way to call it is with an absolute path.
           66  +        set v [exec ls -tr $hbtd | tail -1]
           67  +        set path "$hbtd/$v/bin/tclsh"
           68  +        define TCLSH $path
           69  +        msg-result "Using Homebrew Tcl/Tk version $path."
           70  +    } else {
           71  +        msg-result "WARNING: tclsh $v found; need >= 8.6 for 'make test'."
           72  +        define TCLSH false     ;# force "make test" failure via /usr/bin/false
           73  +    }
           74  +}
    40     75   
    41     76   define EXTRA_CFLAGS "-Wall"
    42     77   define EXTRA_LDFLAGS ""
    43     78   define USE_SYSTEM_SQLITE 0
    44     79   define USE_LINENOISE 0
    45     80   define FOSSIL_ENABLE_MINIZ 0
    46     81   define USE_MMAN_H 0

Changes to skins/ardoise/header.txt.

    39     39   }
    40     40   if {[hascap oh]} {
    41     41     menulink /dir?ci=tip Files
    42     42   }
    43     43   if {[hascap o]} {
    44     44     menulink  /brlist Branches
    45     45     menulink  /taglist Tags
           46  +}
           47  +if {[anycap 23456] || [anoncap 2] || [anoncap 3]} {
           48  +  menulink /forum Forum
    46     49   }
    47     50   if {[hascap r]} {
    48     51     menulink /ticket Tickets
    49     52   }
    50     53   if {[hascap j]} {
    51     54     menulink /wiki Wiki
    52     55   }

Changes to skins/black_and_white/header.txt.

    20     20   }
    21     21   if {[anoncap oh]} {
    22     22     html "<a href='$home/tree?ci=tip'>Files</a>\n"
    23     23   }
    24     24   if {[anoncap o]} {
    25     25     html "<a href='$home/brlist'>Branches</a>\n"
    26     26     html "<a href='$home/taglist'>Tags</a>\n"
           27  +}
           28  +if {[anycap 23456] || [anoncap 2] || [anoncap 3]} {
           29  +  html "<a href='$home/forum'>Forum</a>\n"
    27     30   }
    28     31   if {[anoncap r]} {
    29     32     html "<a href='$home/ticket'>Tickets</a>\n"
    30     33   }
    31     34   if {[anoncap j]} {
    32     35     html "<a href='$home/wiki'>Wiki</a>\n"
    33     36   }

Changes to skins/blitz/header.txt.

    43     43   }
    44     44   if {[hascap oh]} {
    45     45     menulink /dir?ci=tip Files
    46     46   }
    47     47   if {[hascap o]} {
    48     48     menulink  /brlist Branches
    49     49     menulink  /taglist Tags
           50  +}
           51  +if {[anycap 23456] || [anoncap 2] || [anoncap 3]} {
           52  +  menulink /forum Forum
    50     53   }
    51     54   if {[hascap r]} {
    52     55     menulink /ticket Tickets
    53     56   }
    54     57   if {[hascap j]} {
    55     58     menulink /wiki Wiki
    56     59   }

Changes to skins/blitz_no_logo/header.txt.

    40     40   }
    41     41   if {[hascap oh]} {
    42     42     menulink /dir?ci=tip Files
    43     43   }
    44     44   if {[hascap o]} {
    45     45     menulink  /brlist Branches
    46     46     menulink  /taglist Tags
           47  +}
           48  +if {[anycap 23456] || [anoncap 2] || [anoncap 3]} {
           49  +  menulink /forum Forum
    47     50   }
    48     51   if {[hascap r]} {
    49     52     menulink /ticket Tickets
    50     53   }
    51     54   if {[hascap j]} {
    52     55     menulink /wiki Wiki
    53     56   }

Changes to skins/bootstrap/header.txt.

    75     75                       html "<li><a href='$home/brlist'>Branches</a></li>\n"
    76     76                     }
    77     77                     if {[string compare $current_page "taglist"] == 0} {
    78     78                       html "<li class='active'><a href='$home/taglist'>Tags</a></li>\n"
    79     79                     } else {
    80     80                       html "<li><a href='$home/taglist'>Tags</a></li>\n"
    81     81                     }
           82  +                }
           83  +                if {[anycap 23456] || [anoncap 2] || [anoncap 3]} {
           84  +                  if {[string compare $current_page "forum"] == 0} {
           85  +                    html "<li class='active'><a href='$home/forum'>Forum</a></li>\n"
           86  +                  } else {
           87  +                    html "<li><a href='$home/forum'>Forum</a></li>\n"
           88  +                  }
    82     89                   }
    83     90                   if {[hascap r]} {
    84     91                     if {[string compare $current_page "reportlist"] == 0} {
    85     92                       html "<li class='active'><a href='$home/reportlist'>Tickets</a></li>\n"
    86     93                     } else {
    87     94                       html "<li><a href='$home/reportlist'>Tickets</a></li>\n"
    88     95                     }

Changes to skins/default/css.txt.

     1      1   body {
     2      2       margin: 0 auto;
     3         -    padding: 0px 20px;
     4      3       background-color: white;
     5      4       font-family: sans-serif;
     6      5       font-size:14pt;
     7      6       -moz-text-size-adjust: none;
     8      7       -webkit-text-size-adjust: none;
     9      8       -mx-text-size-adjust: none;
    10      9   }
................................................................................
    13     12       color: #4183C4;
    14     13       text-decoration: none;
    15     14   }
    16     15   a:hover {
    17     16       color: #4183C4;
    18     17       text-decoration: underline;
    19     18   }
           19  +div.forumPosts a:visited {
           20  +    color: #6A7F94;
           21  +}
    20     22   
    21     23   hr {
    22     24       color: #eee;
    23     25   }
    24     26   
    25     27   .title {
    26     28       color: #4183C4;
    27     29       float:left;
    28         -    padding-top: 30px;
    29         -    padding-bottom: 10px;
    30     30   }
    31     31   .title h1 {
    32     32       display:inline;
    33     33   }
    34     34   .title h1:after {
    35     35       content: " / ";
    36     36       color: #777;
................................................................................
    70     70       display: inline-block;
    71     71       margin-right: 1em;
    72     72   }
    73     73   
    74     74   .status {
    75     75       float:right;
    76     76       font-size:.7em;
    77         -    padding-top:50px;
    78     77   }
    79     78   
    80     79   .mainmenu {
    81     80       font-size:.8em;
    82     81       clear:both;
    83         -    padding:10px;
    84     82       background:#eaeaea linear-gradient(#fafafa, #eaeaea) repeat-x;
    85     83       border:1px solid #eaeaea;
    86     84       border-radius:5px;
    87     85       overflow-x: auto;
           86  +    overflow-y: hidden;
    88     87       white-space: nowrap;
           88  +    z-index: 21;  /* just above hbdrop */
    89     89   }
    90     90   
    91     91   .mainmenu a {
    92         -    padding: 10px 20px;
    93     92       text-decoration:none;
    94     93       color: #777;
    95     94       border-right:1px solid #eaeaea;
    96     95   }
    97     96   .mainmenu a.active,
    98     97   .mainmenu a:hover {
    99     98       color: #000;
   100     99       border-bottom:2px solid #D26911;
   101    100   }
          101  +
          102  +div#hbdrop {
          103  +    background-color: white;
          104  +    border: 1px solid black;
          105  +    border-top: white;
          106  +    border-radius: 0 0 0.5em 0.5em;
          107  +    display: none;
          108  +    font-size: 80%;
          109  +    left: 2em;
          110  +    width: 90%;
          111  +    padding-right: 1em;
          112  +    position: absolute;
          113  +    z-index: 20;  /* just below mainmenu, but above timeline bubbles */
          114  +}
   102    115   
   103    116   .submenu {
   104    117       font-size: .7em;
   105         -    margin-top: 10px;
   106    118       padding: 10px;
   107    119       border-bottom: 1px solid #ccc;
   108    120   }
   109    121   
   110    122   .submenu a, .submenu label {
   111    123       padding: 10px 11px;
   112    124       text-decoration:none;
................................................................................
   205    217   }
   206    218   span.submenuctrl {
   207    219     white-space: nowrap;
   208    220   }
   209    221   div.submenu label {
   210    222     white-space: nowrap;
   211    223   }
          224  +
          225  +@media screen and (max-width: 600px) {
          226  +  /* Spacing for mobile */
          227  +  body {
          228  +    padding-left: 4px;
          229  +    padding-right: 4px;
          230  +  }
          231  +  .title {
          232  +    padding-top: 0px;
          233  +    padding-bottom: 0px;
          234  +  }
          235  +  .status {padding-top: 0px;}
          236  +  .mainmenu a {
          237  +    padding: 10px 10px;
          238  +  }
          239  +  .mainmenu {
          240  +    padding: 10px;
          241  +  }
          242  +  .desktoponly {
          243  +    display: none;
          244  +  }
          245  +}
          246  +@media screen and (min-width: 600px) {
          247  +  /* Spacing for desktop */
          248  +  body {
          249  +    padding-left: 20px;
          250  +    padding-right: 20px;
          251  +  }
          252  +  .title {
          253  +    padding-top: 10px;
          254  +    padding-bottom: 10px;
          255  +  }
          256  +  .status {padding-top: 30px;}
          257  +  .mainmenu a {
          258  +    padding: 10px 20px;
          259  +  }
          260  +  .mainmenu {
          261  +    padding: 10px;
          262  +  }
          263  +}
          264  +@media screen and (max-width: 1200px) {
          265  +  /* Special declarations for narrow desktop or wide mobile */
          266  +  .wideonly {
          267  +    display: none;
          268  +  }
          269  +}

Changes to skins/default/footer.txt.

     1      1   <div class="footer">
     2      2   This page was generated in about
     3      3   <th1>puts [expr {([utime]+[stime]+1000)/1000*0.001}]</th1>s by
     4      4   Fossil $release_version $manifest_version $manifest_date
     5      5   </div>
            6  +<script nonce="$nonce">
            7  +<th1>styleScript</th1>
            8  +</script>

Changes to skins/default/header.txt.

     6      6    } else {
     7      7      html "<a href='$home/login'>Login</a>\n"
     8      8    }
     9      9       </th1></div>
    10     10   </div>
    11     11   <div class="mainmenu">
    12     12   <th1>
    13         -proc menulink {url name} {
           13  +proc menulink {url name cls} {
    14     14     upvar current_page current
    15     15     upvar home home
    16     16     if {[string range $url 0 [string length $current]] eq "/$current"} {
    17         -    html "<a href='$home$url' class='active'>$name</a>\n"
           17  +    html "<a href='$home$url' class='active $cls'>$name</a>\n"
    18     18     } else {
    19         -    html "<a href='$home$url'>$name</a>\n"
           19  +    html "<a href='$home$url' class='$cls'>$name</a>\n"
    20     20     }
    21     21   }
    22         -menulink $index_page Home
           22  +html "<a href='#'>&#9776;</a>"
           23  +menulink $index_page Home {}
    23     24   if {[anycap jor]} {
    24         -  menulink /timeline Timeline
           25  +  menulink /timeline Timeline {}
    25     26   }
    26     27   if {[hascap oh]} {
    27         -  menulink /dir?ci=tip Files
           28  +  menulink /dir?ci=tip Files desktoponly
    28     29   }
    29     30   if {[hascap o]} {
    30         -  menulink  /brlist Branches
    31         -  menulink  /taglist Tags
           31  +  menulink  /brlist Branches desktoponly
           32  +  menulink  /taglist Tags wideonly
           33  +}
           34  +if {[anycap 23456] || [anoncap 2] || [anoncap 3]} {
           35  +  menulink /forum Forum wideonly
    32     36   }
    33     37   if {[hascap r]} {
    34         -  menulink /ticket Tickets
           38  +  menulink /ticket Tickets wideonly
    35     39   }
    36     40   if {[hascap j]} {
    37         -  menulink /wiki Wiki
           41  +  menulink /wiki Wiki wideonly
    38     42   }
    39     43   if {[hascap s]} {
    40         -  menulink /setup Admin
           44  +  menulink /setup Admin {}
    41     45   } elseif {[hascap a]} {
    42         -  menulink /setup_ulist Users
           46  +  menulink /setup_ulist Users {}
    43     47   }
    44     48   </th1></div>
           49  +<div id='hbdrop'></div>

Added skins/default/js.txt.

            1  +(function() {
            2  +  var home='$home';
            3  +  var panel = document.getElementById("hbdrop");
            4  +  if (!panel) return;   // site admin might've nuked it
            5  +  var panelBorder = panel.style.border;
            6  +  var animate = panel.style.hasOwnProperty('transition');
            7  +  var animMS = 400;
            8  +
            9  +  // Calculate panel height despite its being hidden at call time.
           10  +  // Based on https://stackoverflow.com/a/29047447/142454
           11  +  var panelHeight;  // computed on sitemap load
           12  +  function calculatePanelHeight() {
           13  +    // Get initial panel styles so we can restore them below.
           14  +    var es   = window.getComputedStyle(panel),
           15  +        edis = es.display,
           16  +        epos = es.position,
           17  +        evis = es.visibility;
           18  +
           19  +    // Restyle the panel so we can measure its height while invisible.
           20  +    panel.style.visibility = 'hidden';
           21  +    panel.style.position   = 'absolute';
           22  +    panel.style.display    = 'block';
           23  +    panelHeight = panel.offsetHeight + 'px';
           24  +
           25  +    // Revert styles now that job is done.
           26  +    panel.style.display    = edis;
           27  +    panel.style.position   = epos;
           28  +    panel.style.visibility = evis;
           29  +  }
           30  +
           31  +  // Show the panel by changing the panel height, which kicks off the
           32  +  // slide-open/closed transition set up in the XHR onload handler.
           33  +  //
           34  +  // Schedule the change for a near-future time in case this is the
           35  +  // first call, where the div was initially invisible.  That causes
           36  +  // the browser to consider the height change as part of the same
           37  +  // state change as the visibility change, so it doesn't see a state
           38  +  // *transition*, hence never kicks off the *CSS* transition:
           39  +  //
           40  +  // https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Transitions/Using_CSS_transitions#JavaScript_examples
           41  +  function showPanel() {
           42  +    if (animate) {
           43  +      setTimeout(function() {
           44  +        panel.style.maxHeight = panelHeight;
           45  +        panel.style.border    = panelBorder;
           46  +      }, 40);   // 25ms is insufficient with Firefox 62
           47  +    }
           48  +    else {
           49  +      panel.style.display = 'block';
           50  +    }
           51  +  }
           52  +
           53  +  // Return true if the panel is showing.
           54  +  function panelShowing() {
           55  +    if (animate) {
           56  +      return panel.style.maxHeight == panelHeight;
           57  +    }
           58  +    else {
           59  +      return panel.style.display == 'block';
           60  +    }
           61  +  }
           62  +
           63  +  // Click handler for the hamburger button.
           64  +  var needSitemapHTML = true;
           65  +  document.querySelector("div.mainmenu > a").onclick = function() {
           66  +    if (panelShowing()) {
           67  +      // Transition back to hidden state.
           68  +      if (animate) {
           69  +        panel.style.maxHeight = '0';
           70  +        setTimeout(function() {
           71  +          // Browsers show a 1px high border line when maxHeight == 0,
           72  +          // our "hidden" state, so hide the borders in that state, too.
           73  +          panel.style.border = 'none';
           74  +        }, animMS);
           75  +      }
           76  +      else {
           77  +        panel.style.display = 'none';
           78  +      }
           79  +    }
           80  +    else if (needSitemapHTML) {
           81  +      // Only get it once per page load: it isn't likely to
           82  +      // change on us.
           83  +      var xhr = new XMLHttpRequest();
           84  +      xhr.onload = function() {
           85  +        var doc = xhr.responseXML;
           86  +        if (doc) {
           87  +          var sm = doc.querySelector("ul#sitemap");
           88  +          if (sm && xhr.status == 200) {
           89  +            // Got sitemap.  Insert it into the drop-down panel.
           90  +            needSitemapHTML = false;
           91  +            panel.innerHTML = sm.outerHTML;
           92  +            if (window.setAllHrefs) {
           93  +              setAllHrefs();   // don't need anti-robot defense here
           94  +            }
           95  +
           96  +            // Display the panel
           97  +            if (animate) {
           98  +              // Set up a CSS transition to animate the panel open and
           99  +              // closed.  Only needs to be done once per page load.
          100  +              // Based on https://stackoverflow.com/a/29047447/142454
          101  +              calculatePanelHeight();
          102  +              panel.style.transition = 'max-height ' +
          103  +                  (animMS / 1000) + 's ease-in-out';
          104  +              panel.style.overflowY  = 'hidden';
          105  +              panel.style.maxHeight  = '0';
          106  +              showPanel();
          107  +            }
          108  +            panel.style.display = 'block';
          109  +          }
          110  +        }
          111  +        // else, can't parse response as HTML or XML
          112  +      }
          113  +      xhr.open("POST", home + "/sitemap");
          114  +      xhr.responseType = "document";
          115  +      xhr.send("popup=1");
          116  +    }
          117  +    else {
          118  +      showPanel();   // just show what we built above
          119  +    }
          120  +    return false;  // prevent browser from acting on <a> click
          121  +  }
          122  +})();

Changes to skins/eagle/header.txt.

    69     69        if {[info exists login]} {
    70     70          puts "Logged in as $login"
    71     71        } else {
    72     72          puts "Not logged in"
    73     73        }
    74     74     </th1></nobr><small><div id="clock"></div></small></div>
    75     75   </div>
    76         -<script>
           76  +<th1>html "<script nonce='$nonce'>"</th1>
    77     77   function updateClock(){
    78     78     var e = document.getElementById("clock");
    79     79     if(e){
    80     80       var d = new Date();
    81     81       function f(n) {
    82     82         return n < 10 ? '0' + n : n;
    83     83       }
................................................................................
   104    104   }
   105    105   if {[anoncap oh]} {
   106    106     menulink /dir?ci=tip Files
   107    107   }
   108    108   if {[anoncap o]} {
   109    109     menulink /brlist Branches
   110    110     menulink /taglist Tags
          111  +}
          112  +if {[anycap 23456] || [anoncap 2] || [anoncap 3]} {
          113  +  menulink /forum Forum
   111    114   }
   112    115   if {[anoncap r]} {
   113    116     menulink /ticket Tickets
   114    117   }
   115    118   if {[anoncap j]} {
   116    119     menulink /wiki Wiki
   117    120   }

Changes to skins/enhanced1/header.txt.

    69     69        if {[info exists login]} {
    70     70          puts "Logged in as $login"
    71     71        } else {
    72     72          puts "Not logged in"
    73     73        }
    74     74     </th1></nobr><small><div id="clock"></div></small></div>
    75     75   </div>
    76         -<script>
           76  +<th1>html "<script nonce='$nonce'>"</th1>
    77     77   function updateClock(){
    78     78     var e = document.getElementById("clock");
    79     79     if(e){
    80     80       var d = new Date();
    81     81       function f(n) {
    82     82         return n < 10 ? '0' + n : n;
    83     83       }
................................................................................
   104    104   }
   105    105   if {[anoncap oh]} {
   106    106     menulink /dir?ci=tip Files
   107    107   }
   108    108   if {[anoncap o]} {
   109    109     menulink /brlist Branches
   110    110     menulink /taglist Tags
          111  +}
          112  +if {[anycap 23456] || [anoncap 2] || [anoncap 3]} {
          113  +  menulink /forum Forum
   111    114   }
   112    115   if {[anoncap r]} {
   113    116     menulink /ticket Tickets
   114    117   }
   115    118   if {[anoncap j]} {
   116    119     menulink /wiki Wiki
   117    120   }

Changes to skins/khaki/header.txt.

    18     18   }
    19     19   if {[anoncap oh]} {
    20     20     html "<a href='$home/tree?ci=tip'>Files</a>\n"
    21     21   }
    22     22   if {[anoncap o]} {
    23     23     html "<a href='$home/brlist'>Branches</a>\n"
    24     24     html "<a href='$home/taglist'>Tags</a>\n"
           25  +}
           26  +if {[anycap 23456] || [anoncap 2] || [anoncap 3]} {
           27  +  html "<a href='$home/forum'>Forum</a>\n"
    25     28   }
    26     29   if {[anoncap r]} {
    27     30     html "<a href='$home/ticket'>Tickets</a>\n"
    28     31   }
    29     32   if {[anoncap j]} {
    30     33     html "<a href='$home/wiki'>Wiki</a>\n"
    31     34   }

Changes to skins/original/header.txt.

    19     19   }
    20     20   if {[anoncap oh]} {
    21     21     html "<a href='$home/tree?ci=tip'>Files</a>\n"
    22     22   }
    23     23   if {[anoncap o]} {
    24     24     html "<a href='$home/brlist'>Branches</a>\n"
    25     25     html "<a href='$home/taglist'>Tags</a>\n"
           26  +}
           27  +if {[anycap 23456] || [anoncap 2] || [anoncap 3]} {
           28  +  html "<a href='$home/forum'>Forum</a>\n"
    26     29   }
    27     30   if {[anoncap r]} {
    28     31     html "<a href='$home/ticket'>Tickets</a>\n"
    29     32   }
    30     33   if {[anoncap j]} {
    31     34     html "<a href='$home/wiki'>Wiki</a>\n"
    32     35   }

Changes to skins/plain_gray/header.txt.

    16     16   }
    17     17   if {[anoncap oh]} {
    18     18     html "<a href='$home/tree?ci=tip'>Files</a>\n"
    19     19   }
    20     20   if {[anoncap o]} {
    21     21     html "<a href='$home/brlist'>Branches</a>\n"
    22     22     html "<a href='$home/taglist'>Tags</a>\n"
           23  +}
           24  +if {[anycap 23456] || [anoncap 2] || [anoncap 3]} {
           25  +  html "<a href='$home/forum'>Forum</a>\n"
    23     26   }
    24     27   if {[anoncap r]} {
    25     28     html "<a href='$home/ticket'>Tickets</a>\n"
    26     29   }
    27     30   if {[anoncap j]} {
    28     31     html "<a href='$home/wiki'>Wiki</a>\n"
    29     32   }

Changes to skins/rounded1/header.txt.

    20     20   }
    21     21   if {[anoncap oh]} {
    22     22     html "<a href='$home/tree?ci=tip'>Files</a>\n"
    23     23   }
    24     24   if {[anoncap o]} {
    25     25     html "<a href='$home/brlist'>Branches</a>\n"
    26     26     html "<a href='$home/taglist'>Tags</a>\n"
           27  +}
           28  +if {[anycap 23456] || [anoncap 2] || [anoncap 3]} {
           29  +  html "<a href='$home/forum'>Forum</a>\n"
    27     30   }
    28     31   if {[anoncap r]} {
    29     32     html "<a href='$home/ticket'>Tickets</a>\n"
    30     33   }
    31     34   if {[anoncap j]} {
    32     35     html "<a href='$home/wiki'>Wiki</a>\n"
    33     36   }

Changes to skins/xekri/header.txt.

    69     69        if {[info exists login]} {
    70     70          puts "Logged in as $login"
    71     71        } else {
    72     72          puts "Not logged in"
    73     73        }
    74     74     </th1></nobr><small><div id="clock"></div></small></div>
    75     75   </div>
    76         -<script>
           76  +<th1>html "<script nonce='$nonce'>"</th1>
    77     77   function updateClock(){
    78     78     var e = document.getElementById("clock");
    79     79     if(e){
    80     80       var d = new Date();
    81     81       function f(n) {
    82     82         return n < 10 ? '0' + n : n;
    83     83       }
................................................................................
   108    108   }
   109    109   if {[anoncap oh]} {
   110    110     menulink /dir?ci=tip Files
   111    111   }
   112    112   if {[anoncap o]} {
   113    113     menulink  /brlist Branches
   114    114     menulink  /taglist Tags
          115  +}
          116  +if {[anycap 23456] || [anoncap 2] || [anoncap 3]} {
          117  +  menulink /forum Forum
   115    118   }
   116    119   if {[anoncap r]} {
   117    120     menulink /ticket Tickets
   118    121   }
   119    122   if {[anoncap j]} {
   120    123     menulink /wiki Wiki
   121    124   }

Name change from src/email.c to src/alerts.c.

    19     19   **
    20     20   ** Are you looking for the code that reads and writes the internet
    21     21   ** email protocol?  That is not here.  See the "smtp.c" file instead.
    22     22   ** Yes, the choice of source code filenames is not the greatest, but
    23     23   ** it is not so bad that changing them seems justified.
    24     24   */ 
    25     25   #include "config.h"
    26         -#include "email.h"
           26  +#include "alerts.h"
    27     27   #include <assert.h>
    28     28   #include <time.h>
    29     29   
    30     30   /*
    31     31   ** Maximum size of the subscriberCode blob, in bytes
    32     32   */
    33     33   #define SUBSCRIBER_CODE_SZ 32
    34     34   
    35     35   /*
    36     36   ** SQL code to implement the tables needed by the email notification
    37     37   ** system.
    38     38   */
    39         -static const char zEmailInit[] =
           39  +static const char zAlertInit[] =
    40     40   @ DROP TABLE IF EXISTS repository.subscriber;
    41     41   @ -- Subscribers are distinct from users.  A person can have a log-in in
    42     42   @ -- the USER table without being a subscriber.  Or a person can be a
    43     43   @ -- subscriber without having a USER table entry.  Or they can have both.
    44     44   @ -- In the last case the suname column points from the subscriber entry
    45     45   @ -- to the USER entry.
    46     46   @ --
................................................................................
    80     80   @ CREATE TABLE repository.pending_alert(
    81     81   @   eventid TEXT PRIMARY KEY,         -- Object that changed
    82     82   @   sentSep BOOLEAN DEFAULT false,    -- individual alert sent
    83     83   @   sentDigest BOOLEAN DEFAULT false, -- digest alert sent
    84     84   @   sentMod BOOLEAN DEFAULT false     -- pending moderation alert sent
    85     85   @ ) WITHOUT ROWID;
    86     86   @ 
    87         -@ DROP TABLE IF EXISTS repository.email_bounce;
           87  +@ DROP TABLE IF EXISTS repository.alert_bounce;
    88     88   @ -- Record bounced emails.  If too many bounces are received within
    89     89   @ -- some defined time range, then cancel the subscription.  Older
    90     90   @ -- entries are periodically purged.
    91     91   @ --
    92         -@ CREATE TABLE repository.email_bounce(
           92  +@ CREATE TABLE repository.alert_bounce(
    93     93   @   subscriberId INTEGER, -- to whom the email was sent.
    94     94   @   sendTime INTEGER,     -- seconds since 1970 when email was sent
    95     95   @   rcvdTime INTEGER      -- seconds since 1970 when bounce was received
    96     96   @ );
    97     97   ;
    98     98   
    99     99   /*
   100    100   ** Return true if the email notification tables exist.
   101    101   */
   102         -int email_tables_exist(void){
          102  +int alert_tables_exist(void){
   103    103     return db_table_exists("repository", "subscriber");
   104    104   }
   105    105   
   106    106   /*
   107    107   ** Make sure the table needed for email notification exist in the repository.
   108    108   **
   109    109   ** If the bOnlyIfEnabled option is true, then tables are only created
   110    110   ** if the email-send-method is something other than "off".
   111    111   */
   112         -void email_schema(int bOnlyIfEnabled){
   113         -  if( !email_tables_exist() ){
          112  +void alert_schema(int bOnlyIfEnabled){
          113  +  if( !alert_tables_exist() ){
   114    114       if( bOnlyIfEnabled
   115    115        && fossil_strcmp(db_get("email-send-method","off"),"off")==0
   116    116       ){
   117    117         return;  /* Don't create table for disabled email */
   118    118       }
   119         -    db_multi_exec(zEmailInit/*works-like:""*/);
   120         -    email_triggers_enable();
          119  +    db_multi_exec(zAlertInit/*works-like:""*/);
          120  +    alert_triggers_enable();
   121    121     }else if( !db_table_has_column("repository","pending_alert","sentMod") ){
   122    122       db_multi_exec(
   123    123         "ALTER TABLE repository.pending_alert"
   124    124         " ADD COLUMN sentMod BOOLEAN DEFAULT false;"
   125    125       );
   126    126     }
   127    127   }
   128    128   
   129    129   /*
   130    130   ** Enable triggers that automatically populate the pending_alert
   131    131   ** table.
   132    132   */
   133         -void email_triggers_enable(void){
          133  +void alert_triggers_enable(void){
   134    134     if( !db_table_exists("repository","pending_alert") ) return;
   135    135     db_multi_exec(
   136         -    "CREATE TRIGGER IF NOT EXISTS repository.email_trigger1\n"
          136  +    "CREATE TRIGGER IF NOT EXISTS repository.alert_trigger1\n"
   137    137       "AFTER INSERT ON event BEGIN\n"
   138    138       "  INSERT INTO pending_alert(eventid)\n"
   139    139       "    SELECT printf('%%.1c%%d',new.type,new.objid) WHERE true\n"
   140    140       "    ON CONFLICT(eventId) DO NOTHING;\n"
   141    141       "END;"
   142    142     );
   143    143   }
................................................................................
   144    144   
   145    145   /*
   146    146   ** Disable triggers the event_pending triggers.
   147    147   **
   148    148   ** This must be called before rebuilding the EVENT table, for example
   149    149   ** via the "fossil rebuild" command.
   150    150   */
   151         -void email_triggers_disable(void){
          151  +void alert_triggers_disable(void){
   152    152     db_multi_exec(
   153         -    "DROP TRIGGER IF EXISTS repository.email_trigger1;\n"
          153  +    "DROP TRIGGER IF EXISTS repository.alert_trigger1;\n"
          154  +    "DROP TRIGGER IF EXISTS repository.email_trigger1;\n" // Legacy
   154    155     );
   155    156   }
   156    157   
   157    158   /*
   158    159   ** Return true if email alerts are active.
   159    160   */
   160         -int email_enabled(void){
   161         -  if( !email_tables_exist() ) return 0;
          161  +int alert_enabled(void){
          162  +  if( !alert_tables_exist() ) return 0;
   162    163     if( fossil_strcmp(db_get("email-send-method","off"),"off")==0 ) return 0;
   163    164     return 1;
   164    165   }
   165    166   
   166    167   /*
   167    168   ** If the subscriber table does not exist, then paint an error message
   168    169   ** web page and return true.
   169    170   **
   170    171   ** If the subscriber table does exist, return 0 without doing anything.
   171    172   */
   172         -static int email_webpages_disabled(void){
   173         -  if( email_tables_exist() ) return 0;
          173  +static int alert_webpages_disabled(void){
          174  +  if( alert_tables_exist() ) return 0;
   174    175     style_header("Email Alerts Are Disabled");
   175    176     @ <p>Email alerts are disabled on this server</p>
   176    177     style_footer();
   177    178     return 1;
   178    179   }
   179    180   
   180    181   /*
   181    182   ** Insert a "Subscriber List" submenu link if the current user
   182    183   ** is an administrator.
   183    184   */
   184         -void email_submenu_common(void){
          185  +void alert_submenu_common(void){
   185    186     if( g.perm.Admin ){
   186    187       if( fossil_strcmp(g.zPath,"subscribers") ){
   187    188         style_submenu_element("List Subscribers","%R/subscribers");
   188    189       }
   189    190       if( fossil_strcmp(g.zPath,"subscribe") ){
   190    191         style_submenu_element("Add New Subscriber","%R/subscribe");
   191    192       }
................................................................................
   210    211     login_check_credentials();
   211    212     if( !g.perm.Setup ){
   212    213       login_needed(0);
   213    214       return;
   214    215     }
   215    216     db_begin_transaction();
   216    217   
   217         -  email_submenu_common();
          218  +  alert_submenu_common();
   218    219     style_submenu_element("Send Announcement","%R/announce");
   219    220     style_header("Email Notification Setup");
   220    221     @ <h1>Status</h1>
   221    222     @ <table class="label-value">
   222         -  if( email_enabled() ){
          223  +  if( alert_enabled() ){
   223    224       stats_for_email();
   224    225     }else{
   225    226       @ <th>Disabled</th>
   226    227     }
   227    228     @ </table>
   228    229     @ <hr>
   229    230     @ <h1> Configuration </h1>
................................................................................
   263    264   
   264    265     multiple_choice_attribute("Email Send Method", "email-send-method", "esm",
   265    266          "off", count(azSendMethods)/2, azSendMethods);
   266    267     @ <p>How to send email.  Requires auxiliary information from the fields
   267    268     @ that follow.  Hint: Use the <a href="%R/announce">/announce</a> page
   268    269     @ to send test message to debug this setting.
   269    270     @ (Property: "email-send-method")</p>
   270         -  email_schema(1);
          271  +  alert_schema(1);
   271    272     entry_attribute("Pipe Email Text Into This Command", 60, "email-send-command",
   272    273                      "ecmd", "sendmail -ti", 0);
   273    274     @ <p>When the send method is "pipe to a command", this is the command
   274    275     @ that is run.  Email messages are piped into the standard input of this
   275    276     @ command.  The command is expected to extract the sender address,
   276    277     @ recepient addresses, and subject from the header of the piped email
   277    278     @ text.  (Property: "email-send-command")</p>
................................................................................
   370    371   # define pclose _pclose
   371    372   #endif
   372    373   
   373    374   #if INTERFACE
   374    375   /*
   375    376   ** An instance of the following object is used to send emails.
   376    377   */
   377         -struct EmailSender {
          378  +struct AlertSender {
   378    379     sqlite3 *db;               /* Database emails are sent to */
   379    380     sqlite3_stmt *pStmt;       /* Stmt to insert into the database */
   380    381     const char *zDest;         /* How to send email. */
   381    382     const char *zDb;           /* Name of database file */
   382    383     const char *zDir;          /* Directory in which to store as email files */
   383    384     const char *zCmd;          /* Command to run for each email */
   384    385     const char *zFrom;         /* Emails come from here */
................................................................................
   385    386     SmtpSession *pSmtp;        /* SMTP relay connection */
   386    387     Blob out;                  /* For zDest=="blob" */
   387    388     char *zErr;                /* Error message */
   388    389     u32 mFlags;                /* Flags */
   389    390     int bImmediateFail;        /* On any error, call fossil_fatal() */
   390    391   };
   391    392   
   392         -/* Allowed values for mFlags to email_sender_new().
          393  +/* Allowed values for mFlags to alert_sender_new().
   393    394   */
   394         -#define EMAIL_IMMEDIATE_FAIL   0x0001   /* Call fossil_fatal() on any error */
   395         -#define EMAIL_TRACE            0x0002   /* Log sending process on console */
          395  +#define ALERT_IMMEDIATE_FAIL   0x0001   /* Call fossil_fatal() on any error */
          396  +#define ALERT_TRACE            0x0002   /* Log sending process on console */
   396    397   
   397    398   #endif /* INTERFACE */
   398    399   
   399    400   /*
   400    401   ** Shutdown an emailer.  Clear all information other than the error message.
   401    402   */
   402         -static void emailerShutdown(EmailSender *p){
          403  +static void emailerShutdown(AlertSender *p){
   403    404     sqlite3_finalize(p->pStmt);
   404    405     p->pStmt = 0;
   405    406     sqlite3_close(p->db);
   406    407     p->db = 0;
   407    408     p->zDb = 0;
   408    409     p->zDir = 0;
   409    410     p->zCmd = 0;
................................................................................
   412    413       smtp_session_free(p->pSmtp);
   413    414       p->pSmtp = 0;
   414    415     }
   415    416     blob_reset(&p->out);
   416    417   }
   417    418   
   418    419   /*
   419         -** Put the EmailSender into an error state.
          420  +** Put the AlertSender into an error state.
   420    421   */
   421         -static void emailerError(EmailSender *p, const char *zFormat, ...){
          422  +static void emailerError(AlertSender *p, const char *zFormat, ...){
   422    423     va_list ap;
   423    424     fossil_free(p->zErr);
   424    425     va_start(ap, zFormat);
   425    426     p->zErr = vmprintf(zFormat, ap);
   426    427     va_end(ap);
   427    428     emailerShutdown(p);
   428         -  if( p->mFlags & EMAIL_IMMEDIATE_FAIL ){
          429  +  if( p->mFlags & ALERT_IMMEDIATE_FAIL ){
   429    430       fossil_fatal("%s", p->zErr);
   430    431     }
   431    432   }
   432    433   
   433    434   /*
   434    435   ** Free an email sender object
   435    436   */
   436         -void email_sender_free(EmailSender *p){
          437  +void alert_sender_free(AlertSender *p){
   437    438     if( p ){
   438    439       emailerShutdown(p);
   439    440       fossil_free(p->zErr);
   440    441       fossil_free(p);
   441    442     }
   442    443   }
   443    444   
   444    445   /*
   445    446   ** Get an email setting value.  Report an error if not configured.
   446    447   ** Return 0 on success and one if there is an error.
   447    448   */
   448    449   static int emailerGetSetting(
   449         -  EmailSender *p,        /* Where to report the error */
          450  +  AlertSender *p,        /* Where to report the error */
   450    451     const char **pzVal,    /* Write the setting value here */
   451    452     const char *zName      /* Name of the setting */
   452    453   ){
   453    454     const char *z = db_get(zName, 0);
   454    455     int rc = 0;
   455    456     if( z==0 || z[0]==0 ){
   456    457       emailerError(p, "missing \"%s\" setting", zName);
................................................................................
   458    459     }else{
   459    460       *pzVal = z;
   460    461     }
   461    462     return rc;
   462    463   }
   463    464   
   464    465   /*
   465         -** Create a new EmailSender object.
          466  +** Create a new AlertSender object.
   466    467   **
   467    468   ** The method used for sending email is determined by various email-*
   468    469   ** settings, and especially email-send-method.  The repository
   469    470   ** email-send-method can be overridden by the zAltDest argument to
   470    471   ** cause a different sending mechanism to be used.  Pass "stdout" to
   471    472   ** zAltDest to cause all emails to be printed to the console for
   472    473   ** debugging purposes.
   473    474   **
   474         -** The EmailSender object returned must be freed using email_sender_free().
          475  +** The AlertSender object returned must be freed using alert_sender_free().
   475    476   */
   476         -EmailSender *email_sender_new(const char *zAltDest, u32 mFlags){
   477         -  EmailSender *p;
          477  +AlertSender *alert_sender_new(const char *zAltDest, u32 mFlags){
          478  +  AlertSender *p;
   478    479   
   479    480     p = fossil_malloc(sizeof(*p));
   480    481     memset(p, 0, sizeof(*p));
   481    482     blob_init(&p->out, 0, 0);
   482    483     p->mFlags = mFlags;
   483    484     if( zAltDest ){
   484    485       p->zDest = zAltDest;
................................................................................
   519    520     }else if( fossil_strcmp(p->zDest, "blob")==0 ){
   520    521       blob_init(&p->out, 0, 0);
   521    522     }else if( fossil_strcmp(p->zDest, "relay")==0 ){
   522    523       const char *zRelay = 0;
   523    524       emailerGetSetting(p, &zRelay, "email-send-relayhost");
   524    525       if( zRelay ){
   525    526         u32 smtpFlags = SMTP_DIRECT;
   526         -      if( mFlags & EMAIL_TRACE ) smtpFlags |= SMTP_TRACE_STDOUT;
          527  +      if( mFlags & ALERT_TRACE ) smtpFlags |= SMTP_TRACE_STDOUT;
   527    528         p->pSmtp = smtp_session_new(p->zFrom, zRelay, smtpFlags);
   528    529         smtp_client_startup(p->pSmtp);
   529    530       }
   530    531     }
   531    532     return p;
   532    533   }
   533    534   
................................................................................
   627    628   
   628    629   /*
   629    630   ** Scan the input string for a valid email address enclosed in <...>
   630    631   ** If the string contains one or more email addresses, extract the first
   631    632   ** one into memory obtained from mprintf() and return a pointer to it.
   632    633   ** If no valid email address can be found, return NULL.
   633    634   */
   634         -char *email_find_emailaddr(const char *zIn){
          635  +char *alert_find_emailaddr(const char *zIn){
   635    636     char *zOut = 0;
   636    637     while( zIn!=0 ){
   637    638        zIn = (const char*)strchr(zIn, '<');
   638    639        if( zIn==0 ) break;
   639    640        zIn++;
   640    641        zOut = email_copy_addr(zIn, '>');
   641    642        if( zOut!=0 ) break;
................................................................................
   645    646   
   646    647   /*
   647    648   ** SQL function:  find_emailaddr(X)
   648    649   **
   649    650   ** Return the first valid email address of the form <...> in input string
   650    651   ** X.  Or return NULL if not found.
   651    652   */
   652         -void email_find_emailaddr_func(
          653  +void alert_find_emailaddr_func(
   653    654     sqlite3_context *context,
   654    655     int argc,
   655    656     sqlite3_value **argv
   656    657   ){
   657    658     const char *zIn = (const char*)sqlite3_value_text(argv[0]);
   658         -  char *zOut = email_find_emailaddr(zIn);
          659  +  char *zOut = alert_find_emailaddr(zIn);
   659    660     if( zOut ){
   660    661       sqlite3_result_text(context, zOut, -1, fossil_free);
   661    662     }
   662    663   }
   663    664   
   664    665   /*
   665    666   ** Return the hostname portion of an email address - the part following
   666    667   ** the @
   667    668   */
   668         -char *email_hostname(const char *zAddr){
          669  +char *alert_hostname(const char *zAddr){
   669    670     char *z = strchr(zAddr, '@');
   670    671     if( z ){
   671    672       z++;
   672    673     }else{
   673    674       z = (char*)zAddr;
   674    675     }
   675    676     return z;
................................................................................
   680    681   ** to human-readable name zFromName.  The fake mailbox name is based
   681    682   ** on a hash.  No huge problems arise if there is a hash collisions,
   682    683   ** but it is still better if collisions can be avoided.
   683    684   **
   684    685   ** The returned string is held in a static buffer and is overwritten
   685    686   ** by each subsequent call to this routine.
   686    687   */
   687         -static char *email_mailbox_name(const char *zFromName){
          688  +static char *alert_mailbox_name(const char *zFromName){
   688    689     static char zHash[20];
   689    690     unsigned int x = 0;
   690    691     int n = 0;
   691    692     while( zFromName[0] ){
   692    693       n++;
   693    694       x = x*1103515245 + 12345 + ((unsigned char*)zFromName)[0];
   694    695       zFromName++;
................................................................................
   701    702   /*
   702    703   ** COMMAND: test-mailbox-hashname
   703    704   **
   704    705   ** Usage: %fossil test-mailbox-hashname HUMAN-NAME ...
   705    706   **
   706    707   ** Return the mailbox hash name corresponding to each human-readable
   707    708   ** name on the command line.  This is a test interface for the
   708         -** email_mailbox_name() function.
          709  +** alert_mailbox_name() function.
   709    710   */
   710         -void email_test_mailbox_hashname(void){
          711  +void alert_test_mailbox_hashname(void){
   711    712     int i;
   712    713     for(i=2; i<g.argc; i++){
   713         -    fossil_print("%30s: %s\n", g.argv[i], email_mailbox_name(g.argv[i]));
          714  +    fossil_print("%30s: %s\n", g.argv[i], alert_mailbox_name(g.argv[i]));
   714    715     }
   715    716   }
   716    717   
   717    718   /*
   718    719   ** Extract all To: header values from the email header supplied.
   719    720   ** Store them in the array list.
   720    721   */
................................................................................
   777    778   ** email address based on a hash of zFromName and the domain of email-self,
   778    779   ** and an additional "X-Fossil-From:" field is inserted with the email-self
   779    780   ** address.  Downstream software might use the X-Fossil-From header to set
   780    781   ** the envelope-from address of the email.  If zFromName is a NULL pointer, 
   781    782   ** then the "From:" is set to the email-self value and X-Fossil-From is
   782    783   ** omitted.
   783    784   */
   784         -void email_send(
   785         -  EmailSender *p,           /* Emailer context */
          785  +void alert_send(
          786  +  AlertSender *p,           /* Emailer context */
   786    787     Blob *pHdr,               /* Email header (incomplete) */
   787    788     Blob *pBody,              /* Email body */
   788    789     const char *zFromName     /* Optional human-readable name of sender */
   789    790   ){
   790    791     Blob all, *pOut;
   791    792     u64 r1, r2;
   792         -  if( p->mFlags & EMAIL_TRACE ){
          793  +  if( p->mFlags & ALERT_TRACE ){
   793    794       fossil_print("Sending email\n");
   794    795     }
   795    796     if( fossil_strcmp(p->zDest, "off")==0 ){
   796    797       return;
   797    798     }
   798    799     if( fossil_strcmp(p->zDest, "blob")==0 ){
   799    800       pOut = &p->out;
................................................................................
   803    804     }else{
   804    805       blob_init(&all, 0, 0);
   805    806       pOut = &all;
   806    807     }
   807    808     blob_append(pOut, blob_buffer(pHdr), blob_size(pHdr));
   808    809     if( zFromName ){
   809    810       blob_appendf(pOut, "From: %s <%s@%s>\r\n",
   810         -       zFromName, email_mailbox_name(zFromName), email_hostname(p->zFrom));
          811  +       zFromName, alert_mailbox_name(zFromName), alert_hostname(p->zFrom));
   811    812       blob_appendf(pOut, "X-Fossil-From: <%s>\r\n", p->zFrom);
   812    813     }else{
   813    814       blob_appendf(pOut, "From: <%s>\r\n", p->zFrom);
   814    815     }
   815    816     blob_appendf(pOut, "Date: %z\r\n", cgi_rfc822_datestamp(time(0)));
   816    817     if( strstr(blob_str(pHdr), "\r\nMessage-Id:")==0 ){
   817    818       /* Message-id format:  "<$(date)x$(random)@$(from-host)>" where $(date) is
   818    819       ** the current unix-time in hex, $(random) is a 64-bit random number,
   819    820       ** and $(from) is the domain part of the email-self setting. */
   820    821       sqlite3_randomness(sizeof(r1), &r1);
   821    822       r2 = time(0);
   822    823       blob_appendf(pOut, "Message-Id: <%llxx%016llx@%s>\r\n",
   823         -                 r2, r1, email_hostname(p->zFrom));
          824  +                 r2, r1, alert_hostname(p->zFrom));
   824    825     }
   825    826     blob_add_final_newline(pBody);
   826    827     blob_appendf(pOut, "MIME-Version: 1.0\r\n");
   827    828     blob_appendf(pOut, "Content-Type: text/plain; charset=\"UTF-8\"\r\n");
   828    829   #if 0
   829    830     blob_appendf(pOut, "Content-Transfer-Encoding: base64\r\n\r\n");
   830    831     append_base64(pOut, pBody);
................................................................................
   960    961   **                              --body FILENAME
   961    962   **                              --smtp-trace
   962    963   **                              --stdout
   963    964   **                              --subject|-S SUBJECT
   964    965   **
   965    966   **    unsubscribe EMAIL       Remove a single subscriber with the given EMAIL.
   966    967   */
   967         -void email_cmd(void){
          968  +void alert_cmd(void){
   968    969     const char *zCmd;
   969    970     int nCmd;
   970    971     db_find_and_open_repository(0, 0);
   971         -  email_schema(0);
          972  +  alert_schema(0);
   972    973     zCmd = g.argc>=3 ? g.argv[2] : "x";
   973    974     nCmd = (int)strlen(zCmd);
   974    975     if( strncmp(zCmd, "pending", nCmd)==0 ){
   975    976       Stmt q;
   976    977       verify_all_options();
   977    978       if( g.argc!=3 ) usage("pending");
   978    979       db_prepare(&q,"SELECT eventid, sentSep, sentDigest, sentMod"
................................................................................
   999   1000             "deleting all subscriber information.  The information will be\n"
  1000   1001             "unrecoverable.\n");
  1001   1002         prompt_user("Continue? (y/N) ", &yn);
  1002   1003         c = blob_str(&yn)[0];
  1003   1004         blob_reset(&yn);
  1004   1005       }
  1005   1006       if( c=='y' ){
  1006         -      email_triggers_disable();
         1007  +      alert_triggers_disable();
  1007   1008         db_multi_exec(
  1008   1009           "DROP TABLE IF EXISTS subscriber;\n"
  1009   1010           "DROP TABLE IF EXISTS pending_alert;\n"
  1010         -        "DROP TABLE IF EXISTS email_bounce;\n"
         1011  +        "DROP TABLE IF EXISTS alert_bounce;\n"
  1011   1012           /* Legacy */
  1012         -        "DROP TABLE IF EXISTS email_pending;\n"
         1013  +        "DROP TABLE IF EXISTS alert_pending;\n"
  1013   1014           "DROP TABLE IF EXISTS subscription;\n"
  1014   1015         );
  1015         -      email_schema(0);
         1016  +      alert_schema(0);
  1016   1017       }
  1017   1018     }else
  1018   1019     if( strncmp(zCmd, "send", nCmd)==0 ){
  1019   1020       u32 eFlags = 0;
  1020   1021       if( find_option("digest",0,0)!=0 ) eFlags |= SENDALERT_DIGEST;
  1021   1022       if( find_option("test",0,0)!=0 ){
  1022   1023         eFlags |= SENDALERT_PRESERVE|SENDALERT_STDOUT;
  1023   1024       }
  1024   1025       verify_all_options();
  1025         -    email_send_alerts(eFlags);
         1026  +    alert_send_alerts(eFlags);
  1026   1027     }else
  1027   1028     if( strncmp(zCmd, "settings", nCmd)==0 ){
  1028   1029       int isGlobal = find_option("global",0,0)!=0;
  1029   1030       int nSetting;
  1030   1031       const Setting *pSetting = setting_info(&nSetting);
  1031   1032       db_open_config(1, 0);
  1032   1033       verify_all_options();
................................................................................
  1090   1091       }
  1091   1092       db_finalize(&q);
  1092   1093     }else
  1093   1094     if( strncmp(zCmd, "test-message", nCmd)==0 ){
  1094   1095       Blob prompt, body, hdr;
  1095   1096       const char *zDest = find_option("stdout",0,0)!=0 ? "stdout" : 0;
  1096   1097       int i;
  1097         -    u32 mFlags = EMAIL_IMMEDIATE_FAIL;
         1098  +    u32 mFlags = ALERT_IMMEDIATE_FAIL;
  1098   1099       const char *zSubject = find_option("subject", "S", 1);
  1099   1100       const char *zSource = find_option("body", 0, 1);
  1100         -    EmailSender *pSender;
  1101         -    if( find_option("smtp-trace",0,0)!=0 ) mFlags |= EMAIL_TRACE;
         1101  +    AlertSender *pSender;
         1102  +    if( find_option("smtp-trace",0,0)!=0 ) mFlags |= ALERT_TRACE;
  1102   1103       verify_all_options();
  1103   1104       blob_init(&prompt, 0, 0);
  1104   1105       blob_init(&body, 0, 0);
  1105   1106       blob_init(&hdr, 0, 0);
  1106   1107       blob_appendf(&hdr,"To: ");
  1107   1108       for(i=3; i<g.argc; i++){
  1108   1109         if( i>3 ) blob_append(&hdr, ", ", 2);
................................................................................
  1113   1114       blob_appendf(&hdr, "Subject: %s\r\n", zSubject);
  1114   1115       if( zSource ){
  1115   1116         blob_read_from_file(&body, zSource, ExtFILE);
  1116   1117       }else{
  1117   1118         prompt_for_user_comment(&body, &prompt);
  1118   1119       }
  1119   1120       blob_add_final_newline(&body);
  1120         -    pSender = email_sender_new(zDest, mFlags);
  1121         -    email_send(pSender, &hdr, &body, 0);
  1122         -    email_sender_free(pSender);
         1121  +    pSender = alert_sender_new(zDest, mFlags);
         1122  +    alert_send(pSender, &hdr, &body, 0);
         1123  +    alert_sender_free(pSender);
  1123   1124       blob_reset(&hdr);
  1124   1125       blob_reset(&body);
  1125   1126       blob_reset(&prompt);
  1126   1127     }else
  1127   1128     if( strncmp(zCmd, "unsubscribe", nCmd)==0 ){
  1128   1129       verify_all_options();
  1129   1130       if( g.argc!=4 ) usage("unsubscribe EMAIL");
................................................................................
  1230   1231   @
  1231   1232   ;
  1232   1233   
  1233   1234   /*
  1234   1235   ** Append the text of an email confirmation message to the given
  1235   1236   ** Blob.  The security code is in zCode.
  1236   1237   */
  1237         -void email_append_confirmation_message(Blob *pMsg, const char *zCode){
         1238  +void alert_append_confirmation_message(Blob *pMsg, const char *zCode){
  1238   1239     blob_appendf(pMsg, zConfirmMsg/*works-like:"%s%s%s"*/,
  1239   1240                      g.zBaseURL, g.zBaseURL, zCode);
  1240   1241   }
  1241   1242   
  1242   1243   /*
  1243   1244   ** WEBPAGE: subscribe
  1244   1245   **
................................................................................
  1248   1249   ** A logged-in user can add email notifications on the /alerts page.
  1249   1250   ** Access to this page by a logged in user (other than an
  1250   1251   ** administrator) results in a redirect to the /alerts page.
  1251   1252   **
  1252   1253   ** Administrators can visit this page in order to sign up other
  1253   1254   ** users.
  1254   1255   **
  1255         -** The Email-Alerts permission ("7") is required to access this
         1256  +** The Alerts permission ("7") is required to access this
  1256   1257   ** page.  To allow anonymous passers-by to sign up for email
  1257   1258   ** notification, set Email-Alerts on user "nobody" or "anonymous".
  1258   1259   */
  1259   1260   void subscribe_page(void){
  1260   1261     int needCaptcha;
  1261   1262     unsigned int uSeed;
  1262   1263     const char *zDecoded;
  1263   1264     char *zCaptcha = 0;
  1264   1265     char *zErr = 0;
  1265   1266     int eErr = 0;
         1267  +  int di;
  1266   1268   
  1267         -  if( email_webpages_disabled() ) return;
         1269  +  if( alert_webpages_disabled() ) return;
  1268   1270     login_check_credentials();
  1269   1271     if( !g.perm.EmailAlert ){
  1270   1272       login_needed(g.anon.EmailAlert);
  1271   1273       return;
  1272   1274     }
  1273   1275     if( login_is_individual()
  1274   1276      && db_exists("SELECT 1 FROM subscriber WHERE suname=%Q",g.zLogin)
................................................................................
  1286   1288       }else{
  1287   1289         /* Everybody else jumps to the page to administer their own
  1288   1290         ** account only. */
  1289   1291         cgi_redirectf("%R/alerts");
  1290   1292         return;
  1291   1293       }
  1292   1294     }
  1293         -  email_submenu_common();
         1295  +  alert_submenu_common();
  1294   1296     needCaptcha = !login_is_individual();
  1295   1297     if( P("submit")
  1296   1298      && cgi_csrf_safe(1)
  1297   1299      && subscribe_error_check(&eErr,&zErr,needCaptcha)
  1298   1300     ){
  1299   1301       /* A validated request for a new subscription has been received. */
  1300   1302       char ssub[20];
................................................................................
  1331   1333         ** No verification is required.  Jump immediately to /alerts page.
  1332   1334         */
  1333   1335         cgi_redirectf("%R/alerts/%s", zCode);
  1334   1336         return;
  1335   1337       }else{
  1336   1338         /* We need to send a verification email */
  1337   1339         Blob hdr, body;
  1338         -      EmailSender *pSender = email_sender_new(0,0);
         1340  +      AlertSender *pSender = alert_sender_new(0,0);
  1339   1341         blob_init(&hdr,0,0);
  1340   1342         blob_init(&body,0,0);
  1341   1343         blob_appendf(&hdr, "To: <%s>\n", zEAddr);
  1342   1344         blob_appendf(&hdr, "Subject: Subscription verification\n");
  1343         -      email_append_confirmation_message(&body, zCode);
  1344         -      email_send(pSender, &hdr, &body, 0);
         1345  +      alert_append_confirmation_message(&body, zCode);
         1346  +      alert_send(pSender, &hdr, &body, 0);
  1345   1347         style_header("Email Alert Verification");
  1346   1348         if( pSender->zErr ){
  1347   1349           @ <h1>Internal Error</h1>
  1348   1350           @ <p>The following internal error was encountered while trying
  1349   1351           @ to send the confirmation email:
  1350   1352           @ <blockquote><pre>
  1351   1353           @ %h(pSender->zErr)
  1352   1354           @ </pre></blockquote>
  1353   1355         }else{
  1354   1356           @ <p>An email has been sent to "%h(zEAddr)". That email contains a
  1355   1357           @ hyperlink that you must click on in order to activate your
  1356   1358           @ subscription.</p>
  1357   1359         }
  1358         -      email_sender_free(pSender);
         1360  +      alert_sender_free(pSender);
  1359   1361         style_footer();
  1360   1362       }
  1361   1363       return;
  1362   1364     }
  1363   1365     style_header("Signup For Email Alerts");
  1364   1366     if( P("submit")==0 ){
  1365   1367       /* If this is the first visit to this page (if this HTTP request did not
................................................................................
  1374   1376     @ <p>To receive email notifications for changes to this
  1375   1377     @ repository, fill out the form below and press "Submit" button.</p>
  1376   1378     form_begin(0, "%R/subscribe");
  1377   1379     @ <table class="subscribe">
  1378   1380     @ <tr>
  1379   1381     @  <td class="form_label">Email&nbsp;Address:</td>
  1380   1382     @  <td><input type="text" name="e" value="%h(PD("e",""))" size="30"></td>
         1383  +  @ <tr>
  1381   1384     if( eErr==1 ){
  1382         -    @  <td><span class="loginError">&larr; %h(zErr)</span></td>
         1385  +    @ <tr><td><td><span class='loginError'>&uarr; %h(zErr)</span></td></tr>
  1383   1386     }
  1384   1387     @ </tr>
  1385   1388     if( needCaptcha ){
  1386   1389       uSeed = captcha_seed();
  1387   1390       zDecoded = captcha_decode(uSeed);
  1388   1391       zCaptcha = captcha_render(zDecoded);
  1389   1392       @ <tr>
  1390   1393       @  <td class="form_label">Security Code:</td>
  1391   1394       @  <td><input type="text" name="captcha" value="" size="30">
  1392   1395       @  <input type="hidden" name="captchaseed" value="%u(uSeed)"></td>
         1396  +    @ </tr>
  1393   1397       if( eErr==2 ){
  1394         -      @  <td><span class="loginError">&larr; %h(zErr)</span></td>
         1398  +      @ <tr><td><td><span class='loginError'>&uarr; %h(zErr)</span></td></tr>
  1395   1399       }
  1396   1400       @ </tr>
  1397   1401     }
  1398   1402     if( g.perm.Admin ){
  1399   1403       @ <tr>
  1400   1404       @  <td class="form_label">User:</td>
  1401   1405       @  <td><input type="text" name="suname" value="%h(PD("suname",g.zLogin))" \
  1402   1406       @  size="30"></td>
         1407  +    @ </tr>
  1403   1408       if( eErr==3 ){
  1404         -      @  <td><span class="loginError">&larr; %h(zErr)</span></td>
         1409  +      @ <tr><td><td><span class='loginError'>&uarr; %h(zErr)</span></td></tr>
  1405   1410       }
  1406   1411       @ </tr>
  1407   1412     }
  1408   1413     @ <tr>
  1409         -  @  <td class="form_label">Options:</td>
         1414  +  @  <td class="form_label">Topics:</td>
  1410   1415     @  <td><label><input type="checkbox" name="sa" %s(PCK("sa"))> \
  1411   1416     @  Announcements</label><br>
  1412   1417     if( g.perm.Read ){
  1413   1418       @  <label><input type="checkbox" name="sc" %s(PCK("sc"))> \
  1414   1419       @  Check-ins</label><br>
  1415   1420     }
  1416   1421     if( g.perm.RdForum ){
................................................................................
  1421   1426       @  <label><input type="checkbox" name="st" %s(PCK("st"))> \
  1422   1427       @  Ticket changes</label><br>
  1423   1428     }
  1424   1429     if( g.perm.RdWiki ){
  1425   1430       @  <label><input type="checkbox" name="sw" %s(PCK("sw"))> \
  1426   1431       @  Wiki</label><br>
  1427   1432     }
  1428         -  @  <label><input type="checkbox" name="di" %s(PCK("di"))> \
  1429         -  @  Daily digest only</label><br>
         1433  +  di = PB("di");
         1434  +  @ </td></tr>
         1435  +  @ <tr>
         1436  +  @  <td class="form_label">Delivery:</td>
         1437  +  @  <td><select size="1" name="di">
         1438  +  @     <option value="0" %s(di?"":"selected")>Individual Emails</option>
         1439  +  @     <option value="1" %s(di?"selected":"")>Daily Digest</option>
         1440  +  @     </select></td>
         1441  +  @ </tr>
  1430   1442     if( g.perm.Admin ){
         1443  +    @ <tr>
         1444  +    @  <td class="form_label">Admin Options:</td><td>
  1431   1445       @  <label><input type="checkbox" name="vi" %s(PCK("vi"))> \
  1432   1446       @  Verified</label><br>
  1433   1447       @  <label><input type="checkbox" name="dnc" %s(PCK("dnc"))> \
  1434         -    @  Do not call</label><br>
         1448  +    @  Do not call</label></td></tr>
  1435   1449     }
  1436         -  @ </td>
  1437         -  @ </tr>
  1438   1450     @ <tr>
  1439   1451     @  <td></td>
  1440         -  if( needCaptcha && !email_enabled() ){
         1452  +  if( needCaptcha && !alert_enabled() ){
  1441   1453       @  <td><input type="submit" name="submit" value="Submit" disabled>
  1442   1454       @  (Email current disabled)</td>
  1443   1455     }else{
  1444   1456       @  <td><input type="submit" name="submit" value="Submit"></td>
  1445   1457     }
  1446   1458     @ </tr>
  1447   1459     @ </table>
  1448   1460     if( needCaptcha ){
  1449         -    @ <div class="captcha"><table class="captcha"><tr><td><pre>
         1461  +    @ <div class="captcha"><table class="captcha"><tr><td><pre class="captcha">
  1450   1462       @ %h(zCaptcha)
  1451   1463       @ </pre>
  1452   1464       @ Enter the 8 characters above in the "Security Code" box
  1453   1465       @ </td></tr></table></div>
  1454   1466     }
  1455   1467     @ </form>
  1456   1468     fossil_free(zErr);
................................................................................
  1458   1470   }
  1459   1471   
  1460   1472   /*
  1461   1473   ** Either shutdown or completely delete a subscription entry given
  1462   1474   ** by the hex value zName.  Then paint a webpage that explains that
  1463   1475   ** the entry has been removed.
  1464   1476   */
  1465         -static void email_unsubscribe(const char *zName){
         1477  +static void alert_unsubscribe(const char *zName){
  1466   1478     char *zEmail;
  1467   1479     zEmail = db_text(0, "SELECT semail FROM subscriber"
  1468   1480                         " WHERE subscriberCode=hextoblob(%Q)", zName);
  1469   1481     if( zEmail==0 ){
  1470   1482       style_header("Unsubscribe Fail");
  1471   1483       @ <p>Unable to locate a subscriber with the requested key</p>
  1472   1484     }else{
................................................................................
  1492   1504   **    (1)  The name= query parameter contains the subscriberCode.
  1493   1505   **         
  1494   1506   **    (2)  The user is logged into an account other than "nobody" or
  1495   1507   **         "anonymous".  In that case the notification settings
  1496   1508   **         associated with that account can be edited without needing
  1497   1509   **         to know the subscriber code.
  1498   1510   */
  1499         -void alerts_page(void){
         1511  +void alert_page(void){
  1500   1512     const char *zName = P("name");
  1501   1513     Stmt q;
  1502   1514     int sa, sc, sf, st, sw;
  1503   1515     int sdigest, sdonotcall, sverified;
  1504   1516     const char *ssub;
  1505   1517     const char *semail;
  1506   1518     const char *smip;
  1507   1519     const char *suname;
  1508   1520     const char *mtime;
  1509   1521     const char *sctime;
  1510   1522     int eErr = 0;
  1511   1523     char *zErr = 0;
  1512   1524   
  1513         -  if( email_webpages_disabled() ) return;
         1525  +  if( alert_webpages_disabled() ) return;
  1514   1526     login_check_credentials();
         1527  +  if( !g.perm.EmailAlert ){
         1528  +    login_needed(g.anon.EmailAlert);
         1529  +    return;
         1530  +  }
  1515   1531     if( zName==0 && login_is_individual() ){
  1516   1532       zName = db_text(0, "SELECT hex(subscriberCode) FROM subscriber"
  1517   1533                          " WHERE suname=%Q", g.zLogin);
  1518   1534     }
  1519   1535     if( zName==0 || !validate16(zName, -1) ){
  1520   1536       cgi_redirect("subscribe");
  1521   1537       return;
  1522   1538     }
  1523         -  email_submenu_common();
         1539  +  alert_submenu_common();
  1524   1540     if( P("submit")!=0 && cgi_csrf_safe(1) ){
  1525   1541       int sdonotcall = PB("sdonotcall");
  1526   1542       int sdigest = PB("sdigest");
  1527   1543       char ssub[10];
  1528   1544       int nsub = 0;
  1529   1545       if( PB("sa") )                   ssub[nsub++] = 'a';
  1530   1546       if( g.perm.Read && PB("sc") )    ssub[nsub++] = 'c';
................................................................................
  1570   1586           zName
  1571   1587         );
  1572   1588       }
  1573   1589     }
  1574   1590     if( P("delete")!=0 && cgi_csrf_safe(1) ){
  1575   1591       if( !PB("dodelete") ){
  1576   1592         eErr = 9;
  1577         -      zErr = mprintf("Select this checkbox and press \"Unsubscribe\" to"
         1593  +      zErr = mprintf("Select this checkbox and press \"Unsubscribe\" again to"
  1578   1594                        " unsubscribe");
  1579   1595       }else{
  1580         -      email_unsubscribe(zName);
         1596  +      alert_unsubscribe(zName);
  1581   1597         return;
  1582   1598       }
  1583   1599     }
  1584   1600     db_prepare(&q,
  1585   1601       "SELECT"
  1586   1602       "  semail,"                       /* 0 */
  1587   1603       "  sverified,"                    /* 1 */
................................................................................
  1648   1664       @ <tr>
  1649   1665       @  <td class="form_label">User:</td>
  1650   1666       @  <td><input type="text" name="suname" value="%h(suname?suname:"")" \
  1651   1667       @  size="30"></td>
  1652   1668       @ </tr>
  1653   1669     }
  1654   1670     @ <tr>
  1655         -  @  <td class="form_label">Options:</td>
         1671  +  @  <td class="form_label">Topics:</td>
  1656   1672     @  <td><label><input type="checkbox" name="sa" %s(sa?"checked":"")>\
  1657   1673     @  Announcements</label><br>
  1658   1674     if( g.perm.Read ){
  1659   1675       @  <label><input type="checkbox" name="sc" %s(sc?"checked":"")>\
  1660   1676       @  Check-ins</label><br>
  1661   1677     }
  1662   1678     if( g.perm.RdForum ){
................................................................................
  1665   1681     }
  1666   1682     if( g.perm.RdTkt ){
  1667   1683       @  <label><input type="checkbox" name="st" %s(st?"checked":"")>\
  1668   1684       @  Ticket changes</label><br>
  1669   1685     }
  1670   1686     if( g.perm.RdWiki ){
  1671   1687       @  <label><input type="checkbox" name="sw" %s(sw?"checked":"")>\
  1672         -    @  Wiki</label><br>
         1688  +    @  Wiki</label>
  1673   1689     }
         1690  +  @ </td></tr>
         1691  +  @ <tr>
         1692  +  @  <td class="form_label">Delivery:</td>
         1693  +  @  <td><select size="1" name="sdigest">
         1694  +  @     <option value="0" %s(sdigest?"":"selected")>Individual Emails</option>
         1695  +  @     <option value="1" %s(sdigest?"selected":"")>Daily Digest</option>
         1696  +  @     </select></td>
         1697  +  @ </tr>
         1698  +#if 0
  1674   1699     @  <label><input type="checkbox" name="sdigest" %s(sdigest?"checked":"")>\
  1675   1700     @  Daily digest only</label><br>
         1701  +#endif
  1676   1702     if( g.perm.Admin ){
         1703  +    @ <tr>
         1704  +    @  <td class="form_label">Admin Options:</td><td>
  1677   1705       @  <label><input type="checkbox" name="sdonotcall" \
  1678   1706       @  %s(sdonotcall?"checked":"")> Do not call</label><br>
  1679   1707       @  <label><input type="checkbox" name="sverified" \
  1680   1708       @  %s(sverified?"checked":"")>\
  1681         -    @  Verified</label><br>
         1709  +    @  Verified</label></td></tr>
  1682   1710     }
  1683         -  @  <label><input type="checkbox" name="dodelete">
  1684         -  @  Unsubscribe</label> \
  1685   1711     if( eErr==9 ){
  1686         -    @ <span class="loginError">&larr; %h(zErr)</span>\
         1712  +    @ <tr>
         1713  +    @  <td class="form_label">Verify:</td><td>
         1714  +    @  <label><input type="checkbox" name="dodelete">
         1715  +    @  Unsubscribe</label>
         1716  +    @ <span class="loginError">&larr; %h(zErr)</span>
         1717  +    @ </td></tr>
  1687   1718     }
  1688         -  @ <br>
  1689         -  @ </td></tr>
  1690   1719     @ <tr>
  1691   1720     @  <td></td>
  1692   1721     @  <td><input type="submit" name="submit" value="Submit">
  1693   1722     @  <input type="submit" name="delete" value="Unsubscribe">
  1694   1723     @ </tr>
  1695   1724     @ </table>
  1696   1725     @ </form>
................................................................................
  1741   1770   
  1742   1771     /* If a valid subscriber code is supplied, then unsubscribe immediately.
  1743   1772     */
  1744   1773     if( zName 
  1745   1774      && db_exists("SELECT 1 FROM subscriber WHERE subscriberCode=hextoblob(%Q)",
  1746   1775                   zName)
  1747   1776     ){
  1748         -    email_unsubscribe(zName);
         1777  +    alert_unsubscribe(zName);
  1749   1778       return;
  1750   1779     }
  1751   1780   
  1752   1781     /* Logged in users are redirected to the /alerts page */
  1753   1782     login_check_credentials();
  1754   1783     if( login_is_individual() ){
  1755   1784       cgi_redirectf("%R/alerts");
................................................................................
  1775   1804         bSubmit = 0;
  1776   1805       }
  1777   1806     }
  1778   1807     if( bSubmit ){
  1779   1808       /* If we get this far, it means that a valid unsubscribe request has
  1780   1809       ** been submitted.  Send the appropriate email. */
  1781   1810       Blob hdr, body;
  1782         -    EmailSender *pSender = email_sender_new(0,0);
         1811  +    AlertSender *pSender = alert_sender_new(0,0);
  1783   1812       blob_init(&hdr,0,0);
  1784   1813       blob_init(&body,0,0);
  1785   1814       blob_appendf(&hdr, "To: <%s>\r\n", zEAddr);
  1786   1815       blob_appendf(&hdr, "Subject: Unsubscribe Instructions\r\n");
  1787   1816       blob_appendf(&body, zUnsubMsg/*works-like:"%s%s%s%s%s%s"*/,
  1788   1817                     g.zBaseURL, g.zBaseURL, zCode, g.zBaseURL, g.zBaseURL, zCode);
  1789         -    email_send(pSender, &hdr, &body, 0);
         1818  +    alert_send(pSender, &hdr, &body, 0);
  1790   1819       style_header("Unsubscribe Instructions Sent");
  1791   1820       if( pSender->zErr ){
  1792   1821         @ <h1>Internal Error</h1>
  1793   1822         @ <p>The following error was encountered while trying to send an
  1794   1823         @ email to %h(zEAddr):
  1795   1824         @ <blockquote><pre>
  1796   1825         @ %h(pSender->zErr)
  1797   1826         @ </pre></blockquote>
  1798   1827       }else{
  1799   1828         @ <p>An email has been sent to "%h(zEAddr)" that explains how to
  1800   1829         @ unsubscribe and/or modify your subscription settings</p>
  1801   1830       }
  1802         -    email_sender_free(pSender);
         1831  +    alert_sender_free(pSender);
  1803   1832       style_footer();
  1804   1833       return;
  1805   1834     }  
  1806   1835   
  1807   1836     /* Non-logged-in users have to enter an email address to which is
  1808   1837     ** sent a message containing the unsubscribe link.
  1809   1838     */
................................................................................
  1838   1867     @  <label><input type="radio" name="dx" value="1" %s(dx?"checked":"")>\
  1839   1868     @  Completely unsubscribe</label><br>
  1840   1869     @ <tr>
  1841   1870     @  <td></td>
  1842   1871     @  <td><input type="submit" name="submit" value="Submit"></td>
  1843   1872     @ </tr>
  1844   1873     @ </table>
  1845         -  @ <div class="captcha"><table class="captcha"><tr><td><pre>
         1874  +  @ <div class="captcha"><table class="captcha"><tr><td><pre class="captcha">
  1846   1875     @ %h(zCaptcha)
  1847   1876     @ </pre>
  1848   1877     @ Enter the 8 characters above in the "Security Code" box
  1849   1878     @ </td></tr></table></div>
  1850   1879     @ </form>
  1851   1880     fossil_free(zErr);
  1852   1881     style_footer();
................................................................................
  1861   1890   ** for that email where the delivery settings can be
  1862   1891   ** modified.
  1863   1892   */
  1864   1893   void subscriber_list_page(void){
  1865   1894     Blob sql;
  1866   1895     Stmt q;
  1867   1896     sqlite3_int64 iNow;
  1868         -  if( email_webpages_disabled() ) return;
         1897  +  if( alert_webpages_disabled() ) return;
  1869   1898     login_check_credentials();
  1870   1899     if( !g.perm.Admin ){
  1871   1900       login_needed(0);
  1872   1901       return;
  1873   1902     }
  1874         -  email_submenu_common();
         1903  +  alert_submenu_common();
  1875   1904     style_header("Subscriber List");
  1876   1905     blob_init(&sql, 0, 0);
  1877   1906     blob_append_sql(&sql,
  1878   1907       "SELECT hex(subscriberCode),"          /* 0 */
  1879   1908       "       semail,"                       /* 1 */
  1880   1909       "       ssub,"                         /* 2 */
  1881   1910       "       suname,"                       /* 3 */
................................................................................
  1939   1968     EmailEvent *pNext; /* Next in chronological order */
  1940   1969   };
  1941   1970   #endif
  1942   1971   
  1943   1972   /*
  1944   1973   ** Free a linked list of EmailEvent objects
  1945   1974   */
  1946         -void email_free_eventlist(EmailEvent *p){
         1975  +void alert_free_eventlist(EmailEvent *p){
  1947   1976     while( p ){
  1948   1977       EmailEvent *pNext = p->pNext;
  1949   1978       blob_reset(&p->txt);
  1950   1979       blob_reset(&p->hdr);
  1951   1980       fossil_free(p->zFromName);
  1952   1981       fossil_free(p);
  1953   1982       p = pNext;
................................................................................
  1957   1986   /*
  1958   1987   ** Compute and return a linked list of EmailEvent objects
  1959   1988   ** corresponding to the current content of the temp.wantalert
  1960   1989   ** table which should be defined as follows:
  1961   1990   **
  1962   1991   **     CREATE TEMP TABLE wantalert(eventId TEXT, needMod BOOLEAN);
  1963   1992   */
  1964         -EmailEvent *email_compute_event_text(int *pnEvent, int doDigest){
         1993  +EmailEvent *alert_compute_event_text(int *pnEvent, int doDigest){
  1965   1994     Stmt q;
  1966   1995     EmailEvent *p;
  1967   1996     EmailEvent anchor;
  1968   1997     EmailEvent *pLast;
  1969   1998     const char *zUrl = db_get("email-url","http://localhost:8080");
  1970   1999     const char *zFrom;
  1971   2000     const char *zSub;
................................................................................
  2082   2111       zTitle = db_column_text(&q, 3);
  2083   2112       if( p->needMod ){
  2084   2113         blob_appendf(&p->hdr, "Subject: %s Pending Moderation: %s\r\n",
  2085   2114                      zSub, zTitle);
  2086   2115       }else{
  2087   2116         blob_appendf(&p->hdr, "Subject: %s %s\r\n", zSub, zTitle);
  2088   2117         blob_appendf(&p->hdr, "Message-Id: <%.32s@%s>\r\n", 
  2089         -                   zUuid, email_hostname(zFrom));
         2118  +                   zUuid, alert_hostname(zFrom));
  2090   2119         zIrt = db_column_text(&q, 4);
  2091   2120         if( zIrt && zIrt[0] ){
  2092   2121           blob_appendf(&p->hdr, "In-Reply-To: <%.32s@%s>\r\n",
  2093         -                     zIrt, email_hostname(zFrom));
         2122  +                     zIrt, alert_hostname(zFrom));
  2094   2123         }
  2095   2124       }
  2096   2125       blob_init(&p->txt, 0, 0);
  2097   2126       if( p->needMod ){
  2098   2127         blob_appendf(&p->txt,
  2099   2128           "** Pending moderator approval (%s/modreq) **\n",
  2100   2129           zUrl
................................................................................
  2124   2153       db_get("email-url","http://localhost:8080"));
  2125   2154   }
  2126   2155   
  2127   2156   /*
  2128   2157   ** Append the "unsubscribe" notification and other footer text to
  2129   2158   ** the end of an email alert being assemblied in pOut.
  2130   2159   */
  2131         -void email_footer(Blob *pOut){
         2160  +void alert_footer(Blob *pOut){
  2132   2161     blob_appendf(pOut, "\n-- \nTo unsubscribe: %s/unsubscribe\n",
  2133   2162        db_get("email-url","http://localhost:8080"));
  2134   2163   }
  2135   2164   
  2136   2165   /*
  2137   2166   ** COMMAND:  test-alert
  2138   2167   **
................................................................................
  2159   2188     EmailEvent *pEvent, *p;
  2160   2189   
  2161   2190     doDigest = find_option("digest",0,0)!=0;
  2162   2191     needMod = find_option("needmod",0,0)!=0;
  2163   2192     db_find_and_open_repository(0, 0);
  2164   2193     verify_all_options();
  2165   2194     db_begin_transaction();
  2166         -  email_schema(0);
         2195  +  alert_schema(0);
  2167   2196     db_multi_exec("CREATE TEMP TABLE wantalert(eventid TEXT, needMod BOOLEAN)");
  2168   2197     if( g.argc==2 ){
  2169   2198       db_multi_exec(
  2170   2199         "INSERT INTO wantalert(eventId,needMod)"
  2171   2200         " SELECT eventid, %d FROM pending_alert", needMod);
  2172   2201     }else{
  2173   2202       int i;
................................................................................
  2174   2203       for(i=2; i<g.argc; i++){
  2175   2204         db_multi_exec("INSERT INTO wantalert(eventId,needMod) VALUES(%Q,%d)",
  2176   2205              g.argv[i], needMod);
  2177   2206       }
  2178   2207     }
  2179   2208     blob_init(&out, 0, 0);
  2180   2209     email_header(&out);
  2181         -  pEvent = email_compute_event_text(&nEvent, doDigest);
         2210  +  pEvent = alert_compute_event_text(&nEvent, doDigest);
  2182   2211     for(p=pEvent; p; p=p->pNext){
  2183   2212       blob_append(&out, "\n", 1);
  2184   2213       if( blob_size(&p->hdr) ){
  2185   2214         blob_append(&out, blob_buffer(&p->hdr), blob_size(&p->hdr));
  2186   2215         blob_append(&out, "\n", 1);
  2187   2216       }
  2188   2217       blob_append(&out, blob_buffer(&p->txt), blob_size(&p->txt));
  2189   2218     }
  2190         -  email_free_eventlist(pEvent);
  2191         -  email_footer(&out);
         2219  +  alert_free_eventlist(pEvent);
         2220  +  alert_footer(&out);
  2192   2221     fossil_print("%s", blob_str(&out));
  2193   2222     blob_reset(&out);
  2194   2223     db_end_transaction(0);
  2195   2224   }
  2196   2225   
  2197   2226   /*
  2198   2227   ** COMMAND:  test-add-alerts
................................................................................
  2206   2235   ** EVENTIDs are text.  The first character is 'c', 'f', 't', or 'w'
  2207   2236   ** for check-in, forum, ticket, or wiki.  The remaining text is a
  2208   2237   ** integer that references the EVENT.OBJID value for the event.
  2209   2238   ** Run /timeline?showid to see these OBJID values.
  2210   2239   **
  2211   2240   ** Options:
  2212   2241   **
  2213         -**    --backoffice        Run email_backoffice() after all alerts have
         2242  +**    --backoffice        Run alert_backoffice() after all alerts have
  2214   2243   **                        been added.  This will cause the alerts to be
  2215   2244   **                        sent out with the SENDALERT_TRACE option.
  2216   2245   **
  2217   2246   **    --debug             Like --backoffice, but add the SENDALERT_STDOUT
  2218   2247   **                        so that emails are printed to standard output
  2219   2248   **                        rather than being sent.
  2220   2249   **
................................................................................
  2230   2259     }
  2231   2260     if( find_option("digest",0,0)!=0 ){
  2232   2261       mFlags |= SENDALERT_DIGEST;
  2233   2262     }
  2234   2263     db_find_and_open_repository(0, 0);
  2235   2264     verify_all_options();
  2236   2265     db_begin_write();
  2237         -  email_schema(0);
         2266  +  alert_schema(0);
  2238   2267     for(i=2; i<g.argc; i++){
  2239   2268       db_multi_exec("REPLACE INTO pending_alert(eventId) VALUES(%Q)", g.argv[i]);
  2240   2269     }
  2241   2270     db_end_transaction(0);
  2242   2271     if( doAuto ){
  2243         -    email_backoffice(SENDALERT_TRACE|mFlags);
         2272  +    alert_backoffice(SENDALERT_TRACE|mFlags);
  2244   2273     }
  2245   2274   }
  2246   2275   
  2247   2276   #if INTERFACE
  2248   2277   /*
  2249         -** Flags for email_send_alerts()
         2278  +** Flags for alert_send_alerts()
  2250   2279   */
  2251   2280   #define SENDALERT_DIGEST      0x0001    /* Send a digest */
  2252   2281   #define SENDALERT_PRESERVE    0x0002    /* Do not mark the task as done */
  2253   2282   #define SENDALERT_STDOUT      0x0004    /* Print emails instead of sending */
  2254   2283   #define SENDALERT_TRACE       0x0008    /* Trace operation for debugging */
  2255   2284   
  2256   2285   #endif /* INTERFACE */
................................................................................
  2266   2295   **
  2267   2296   **   (1) Create a TEMP table wantalert(eventId,needMod) and fill it with
  2268   2297   **       all the events that we want to send alerts about.  The needMod
  2269   2298   **       flags is set if and only if the event is still awaiting
  2270   2299   **       moderator approval.  Events with the needMod flag are only
  2271   2300   **       shown to users that have moderator privileges.
  2272   2301   **
  2273         -**   (2) Call email_compute_event_text() to compute a list of EmailEvent
         2302  +**   (2) Call alert_compute_event_text() to compute a list of EmailEvent
  2274   2303   **       objects that describe all events about which we want to send
  2275   2304   **       alerts.
  2276   2305   **
  2277   2306   **   (3) Loop over all subscribers.  Compose and send one or more email
  2278   2307   **       messages to each subscriber that describe the events for
  2279   2308   **       which the subscriber has expressed interest and has
  2280   2309   **       appropriate privileges.
................................................................................
  2285   2314   ** Update 2018-08-09:  Do step (3) before step (4).  Update the
  2286   2315   ** pending_alerts table *before* the emails are sent.  That way, if
  2287   2316   ** the process malfunctions or crashes, some notifications may never
  2288   2317   ** be sent.  But that is better than some recurring bug causing
  2289   2318   ** subscribers to be flooded with repeated notifications every 60
  2290   2319   ** seconds!
  2291   2320   */
  2292         -void email_send_alerts(u32 flags){
         2321  +void alert_send_alerts(u32 flags){
  2293   2322     EmailEvent *pEvents, *p;
  2294   2323     int nEvent = 0;
  2295   2324     Stmt q;
  2296   2325     const char *zDigest = "false";
  2297   2326     Blob hdr, body;
  2298   2327     const char *zUrl;
  2299   2328     const char *zRepoName;
  2300   2329     const char *zFrom;
  2301   2330     const char *zDest = (flags & SENDALERT_STDOUT) ? "stdout" : 0;
  2302         -  EmailSender *pSender = 0;
         2331  +  AlertSender *pSender = 0;
  2303   2332     u32 senderFlags = 0;
  2304   2333   
  2305         -  if( g.fSqlTrace ) fossil_trace("-- BEGIN email_send_alerts(%u)\n", flags);
  2306         -  email_schema(0);
  2307         -  if( !email_enabled() ) goto send_alerts_done;
         2334  +  if( g.fSqlTrace ) fossil_trace("-- BEGIN alert_send_alerts(%u)\n", flags);
         2335  +  alert_schema(0);
         2336  +  if( !alert_enabled() ) goto send_alert_done;
  2308   2337     zUrl = db_get("email-url",0);
  2309         -  if( zUrl==0 ) goto send_alerts_done;
         2338  +  if( zUrl==0 ) goto send_alert_done;
  2310   2339     zRepoName = db_get("email-subname",0);
  2311         -  if( zRepoName==0 ) goto send_alerts_done;
         2340  +  if( zRepoName==0 ) goto send_alert_done;
  2312   2341     zFrom = db_get("email-self",0);
  2313         -  if( zFrom==0 ) goto send_alerts_done;
         2342  +  if( zFrom==0 ) goto send_alert_done;
  2314   2343     if( flags & SENDALERT_TRACE ){
  2315         -    senderFlags |= EMAIL_TRACE;
         2344  +    senderFlags |= ALERT_TRACE;
  2316   2345     }
  2317         -  pSender = email_sender_new(zDest, senderFlags);
         2346  +  pSender = alert_sender_new(zDest, senderFlags);
  2318   2347   
  2319   2348     /* Step (1):  Compute the alerts that need sending
  2320   2349     */
  2321   2350     db_multi_exec(
  2322   2351       "DROP TABLE IF EXISTS temp.wantalert;"
  2323   2352       "CREATE TEMP TABLE wantalert(eventId TEXT, needMod BOOLEAN, sentMod);"
  2324   2353     );
................................................................................
  2345   2374         "DELETE FROM wantalert WHERE needMod AND sentMod;"
  2346   2375       );
  2347   2376     }
  2348   2377   
  2349   2378     /* Step 2: compute EmailEvent objects for every notification that
  2350   2379     ** needs sending.
  2351   2380     */
  2352         -  pEvents = email_compute_event_text(&nEvent, (flags & SENDALERT_DIGEST)!=0);
  2353         -  if( nEvent==0 ) goto send_alerts_done;
         2381  +  pEvents = alert_compute_event_text(&nEvent, (flags & SENDALERT_DIGEST)!=0);
         2382  +  if( nEvent==0 ) goto send_alert_done;
  2354   2383   
  2355   2384     /* Step 4a: Update the pending_alerts table to designate the
  2356   2385     ** alerts as having all been sent.  This is done *before* step (3)
  2357   2386     ** so that a crash will not cause alerts to be sent multiple times.
  2358   2387     ** Better a missed alert than being spammed with hundreds of alerts
  2359   2388     ** due to a bug.
  2360   2389     */
................................................................................
  2427   2456           Blob fhdr, fbody;
  2428   2457           blob_init(&fhdr, 0, 0);
  2429   2458           blob_appendf(&fhdr, "To: <%s>\r\n", zEmail);
  2430   2459           blob_append(&fhdr, blob_buffer(&p->hdr), blob_size(&p->hdr));
  2431   2460           blob_init(&fbody, blob_buffer(&p->txt), blob_size(&p->txt));
  2432   2461           blob_appendf(&fbody, "\n-- \nSubscription info: %s/alerts/%s\n",
  2433   2462              zUrl, zCode);
  2434         -        email_send(pSender,&fhdr,&fbody,p->zFromName);
         2463  +        alert_send(pSender,&fhdr,&fbody,p->zFromName);
  2435   2464           blob_reset(&fhdr);
  2436   2465           blob_reset(&fbody);
  2437   2466         }else{
  2438   2467           /* Events other than forum posts are gathered together into
  2439   2468           ** a single email message */
  2440   2469           if( nHit==0 ){
  2441   2470             blob_appendf(&hdr,"To: <%s>\r\n", zEmail);
................................................................................
  2450   2479           blob_append(&body, "\n", 1);
  2451   2480           blob_append(&body, blob_buffer(&p->txt), blob_size(&p->txt));
  2452   2481         }
  2453   2482       }
  2454   2483       if( nHit==0 ) continue;
  2455   2484       blob_appendf(&body,"\n-- \nSubscription info: %s/alerts/%s\n",
  2456   2485            zUrl, zCode);
  2457         -    email_send(pSender,&hdr,&body,0);
         2486  +    alert_send(pSender,&hdr,&body,0);
  2458   2487       blob_truncate(&hdr, 0);
  2459   2488       blob_truncate(&body, 0);
  2460   2489     }
  2461   2490     blob_reset(&hdr);
  2462   2491     blob_reset(&body);
  2463   2492     db_finalize(&q);
  2464         -  email_free_eventlist(pEvents);
         2493  +  alert_free_eventlist(pEvents);
  2465   2494   
  2466   2495     /* Step 4b: Update the pending_alerts table to remove all of the
  2467   2496     ** alerts that have been completely sent.
  2468   2497     */
  2469   2498     db_multi_exec("DELETE FROM pending_alert WHERE sentDigest AND sentSep;");
  2470   2499   
  2471         -send_alerts_done:
  2472         -  email_sender_free(pSender);
  2473         -  if( g.fSqlTrace ) fossil_trace("-- END email_send_alerts(%u)\n", flags);
         2500  +send_alert_done:
         2501  +  alert_sender_free(pSender);
         2502  +  if( g.fSqlTrace ) fossil_trace("-- END alert_send_alerts(%u)\n", flags);
  2474   2503   }
  2475   2504   
  2476   2505   /*
  2477   2506   ** Do backoffice processing for email notifications.  In other words,
  2478   2507   ** check to see if any email notifications need to occur, and then
  2479   2508   ** do them.
  2480   2509   **
  2481   2510   ** This routine is intended to run in the background, after webpages.
  2482   2511   **
  2483   2512   ** The mFlags option is zero or more of the SENDALERT_* flags.  Normally
  2484   2513   ** this flag is zero, but the test-set-alert command sets it to
  2485   2514   ** SENDALERT_TRACE.
  2486   2515   */
  2487         -void email_backoffice(u32 mFlags){
         2516  +void alert_backoffice(u32 mFlags){
  2488   2517     int iJulianDay;
  2489         -  if( !email_tables_exist() ) return;
  2490         -  email_send_alerts(mFlags);
         2518  +  if( !alert_tables_exist() ) return;
         2519  +  alert_send_alerts(mFlags);
  2491   2520     iJulianDay = db_int(0, "SELECT julianday('now')");
  2492   2521     if( iJulianDay>db_get_int("email-last-digest",0) ){
  2493   2522       db_set_int("email-last-digest",iJulianDay,0);
  2494         -    email_send_alerts(SENDALERT_DIGEST|mFlags);
         2523  +    alert_send_alerts(SENDALERT_DIGEST|mFlags);
  2495   2524     }
  2496   2525   }
  2497   2526   
  2498   2527   /*
  2499   2528   ** WEBPAGE: contact_admin
  2500   2529   **
  2501   2530   ** A web-form to send an email message to the repository administrator,
................................................................................
  2518   2547      && P("subject")!=0
  2519   2548      && P("msg")!=0
  2520   2549      && P("from")!=0
  2521   2550      && cgi_csrf_safe(1)
  2522   2551      && captcha_is_correct(0)
  2523   2552     ){
  2524   2553       Blob hdr, body;
  2525         -    EmailSender *pSender = email_sender_new(0,0);
         2554  +    AlertSender *pSender = alert_sender_new(0,0);
  2526   2555       blob_init(&hdr, 0, 0);
  2527   2556       blob_appendf(&hdr, "To: <%s>\r\nSubject: %s administrator message\r\n",
  2528   2557                    zAdminEmail, db_get("email-subname","Fossil Repo"));
  2529   2558       blob_init(&body, 0, 0);
  2530   2559       blob_appendf(&body, "Message from [%s]\n", PT("from")/*safe-for-%s*/);
  2531   2560       blob_appendf(&body, "Subject: [%s]\n\n", PT("subject")/*safe-for-%s*/);
  2532   2561       blob_appendf(&body, "%s", PT("msg")/*safe-for-%s*/);
  2533         -    email_send(pSender, &hdr, &body, 0);
         2562  +    alert_send(pSender, &hdr, &body, 0);
  2534   2563       style_header("Message Sent");
  2535   2564       if( pSender->zErr ){
  2536   2565         @ <h1>Internal Error</h1>
  2537   2566         @ <p>The following error was reported by the system:
  2538   2567         @ <blockquote><pre>
  2539   2568         @ %h(pSender->zErr)
  2540   2569         @ </pre></blockquote>
  2541   2570       }else{
  2542   2571         @ <p>Your message has been sent to the repository administrator.
  2543   2572         @ Thank you for your input.</p>
  2544   2573       }
  2545         -    email_sender_free(pSender);
         2574  +    alert_sender_free(pSender);
  2546   2575       style_footer();
  2547   2576       return;
  2548   2577     }
  2549   2578     if( captcha_needed() ){
  2550   2579       uSeed = captcha_seed();
  2551   2580       zDecoded = captcha_decode(uSeed);
  2552   2581       zCaptcha = captcha_render(zDecoded);
................................................................................
  2578   2607     @ </tr>
  2579   2608     @ <tr>
  2580   2609     @   <td></td>
  2581   2610     @   <td><input type="submit" name="submit" value="Send Message">
  2582   2611     @ </tr>
  2583   2612     @ </table>
  2584   2613     if( zCaptcha ){
  2585         -    @ <div class="captcha"><table class="captcha"><tr><td><pre>
         2614  +    @ <div class="captcha"><table class="captcha"><tr><td><pre class="captcha">
  2586   2615       @ %h(zCaptcha)
  2587   2616       @ </pre>
  2588   2617       @ Enter the 8 characters above in the "Security Code" box
  2589   2618       @ </td></tr></table></div>
  2590   2619     }
  2591   2620     @ </form>
  2592   2621     style_footer();
  2593   2622   }
  2594   2623   
  2595   2624   /*
  2596   2625   ** Send an annoucement message described by query parameter.
  2597   2626   ** Permission to do this has already been verified.
  2598   2627   */
  2599         -static char *email_send_announcement(void){
  2600         -  EmailSender *pSender;
         2628  +static char *alert_send_announcement(void){
         2629  +  AlertSender *pSender;
  2601   2630     char *zErr;
  2602   2631     const char *zTo = PT("to");
  2603   2632     char *zSubject = PT("subject");
  2604   2633     int bAll = PB("all");
  2605   2634     int bAA = PB("aa");
  2606   2635     const char *zSub = db_get("email-subname", "[Fossil Repo]");
  2607   2636     int bTest2 = fossil_strcmp(P("name"),"test2")==0;
  2608   2637     Blob hdr, body;
  2609   2638     blob_init(&body, 0, 0);
  2610   2639     blob_init(&hdr, 0, 0);
  2611   2640     blob_appendf(&body, "%s", PT("msg")/*safe-for-%s*/);
  2612         -  pSender = email_sender_new(bTest2 ? "blob" : 0, 0);
         2641  +  pSender = alert_sender_new(bTest2 ? "blob" : 0, 0);
  2613   2642     if( zTo[0] ){
  2614   2643       blob_appendf(&hdr, "To: <%s>\r\nSubject: %s %s\r\n", zTo, zSub, zSubject);
  2615         -    email_send(pSender, &hdr, &body, 0);
         2644  +    alert_send(pSender, &hdr, &body, 0);
  2616   2645     }
  2617   2646     if( bAll || bAA ){
  2618   2647       Stmt q;
  2619   2648       int nUsed = blob_size(&body);
  2620   2649       const char *zURL =  db_get("email-url",0);
  2621   2650       db_prepare(&q, "SELECT semail, hex(subscriberCode) FROM subscriber "
  2622   2651                      " WHERE sverified AND NOT sdonotcall %s",
................................................................................
  2627   2656         blob_truncate(&hdr, 0);
  2628   2657         blob_appendf(&hdr, "To: <%s>\r\nSubject: %s %s\r\n", zTo, zSub, zSubject);
  2629   2658         if( zURL ){
  2630   2659           blob_truncate(&body, nUsed);
  2631   2660           blob_appendf(&body,"\n-- \nSubscription info: %s/alerts/%s\n",
  2632   2661              zURL, zCode);
  2633   2662         }
  2634         -      email_send(pSender, &hdr, &body, 0);
         2663  +      alert_send(pSender, &hdr, &body, 0);
  2635   2664       }
  2636   2665       db_finalize(&q);
  2637   2666     }
  2638   2667     if( bTest2 ){
  2639   2668       /* If the URL is /announce/test2 instead of just /announce, then no
  2640   2669       ** email is actually sent.  Instead, the text of the email that would
  2641   2670       ** have been sent is displayed in the result window. */
  2642   2671       @ <pre style='border: 2px solid blue; padding: 1ex'>
  2643   2672       @ %h(blob_str(&pSender->out))
  2644   2673       @ </pre>
  2645   2674     }
  2646   2675     zErr = pSender->zErr;
  2647   2676     pSender->zErr = 0;
  2648         -  email_sender_free(pSender);
         2677  +  alert_sender_free(pSender);
  2649   2678     return zErr;
  2650   2679   }
  2651   2680   
  2652   2681   
  2653   2682   /*
  2654   2683   ** WEBPAGE: announce
  2655   2684   **
................................................................................
  2669   2698     if( fossil_strcmp(P("name"),"test1")==0 ){
  2670   2699       /* Visit the /announce/test1 page to see the CGI variables */
  2671   2700       @ <p style='border: 1px solid black; padding: 1ex;'>
  2672   2701       cgi_print_all(0, 0);
  2673   2702       @ </p>
  2674   2703     }else
  2675   2704     if( P("submit")!=0 && cgi_csrf_safe(1) ){
  2676         -    char *zErr = email_send_announcement();
         2705  +    char *zErr = alert_send_announcement();
  2677   2706       style_header("Announcement Sent");
  2678   2707       if( zErr ){
  2679   2708         @ <h1>Internal Error</h1>
  2680   2709         @ <p>The following error was reported by the system:
  2681   2710         @ <blockquote><pre>
  2682   2711         @ %h(zErr)
  2683   2712         @ </pre></blockquote>

Changes to src/backoffice.c.

   517    517         char *zDate = db_text(0, "SELECT datetime('now');");
   518    518         fprintf(pLog, "%s (%d) backoffice running\n", zDate, GETPID());
   519    519         fclose(pLog);
   520    520       }
   521    521     }
   522    522   
   523    523     /* Here is where the actual work of the backoffice happens */
   524         -  email_backoffice(0);
          524  +  alert_backoffice(0);
   525    525     smtp_cleanup();
   526    526   }
   527    527   
   528    528   /*
   529    529   ** COMMAND: backoffice
   530    530   **
   531    531   ** Usage: backoffice [-R repository]

Changes to src/browse.c.

   119    119   **    type=TYPE        TYPE=flat: use this display
   120    120   **                     TYPE=tree: use the /tree display instead
   121    121   */
   122    122   void page_dir(void){
   123    123     char *zD = fossil_strdup(P("name"));
   124    124     int nD = zD ? strlen(zD)+1 : 0;
   125    125     int mxLen;
          126  +  int n;
   126    127     char *zPrefix;
   127    128     Stmt q;
   128    129     const char *zCI = P("ci");
   129    130     int rid = 0;
   130    131     char *zUuid = 0;
   131    132     Blob dirname;
   132    133     Manifest *pM = 0;
................................................................................
   265    266       );
   266    267     }
   267    268   
   268    269     /* Generate a multi-column table listing the contents of zD[]
   269    270     ** directory.
   270    271     */
   271    272     mxLen = db_int(12, "SELECT max(length(x)) FROM localfiles /*scan*/");
          273  +  n = db_int(1,"SELECT count(*) FROM localfiles; /*scan*/");
   272    274     if( mxLen<12 ) mxLen = 12;
          275  +  mxLen += (mxLen+9)/10;
   273    276     db_prepare(&q, "SELECT x, u FROM localfiles ORDER BY x /*scan*/");
   274         -  @ <div class="columns" style="column-width: %d(mxLen+(mxLen+9)/10)ex;">
          277  +  @ <div class="columns" style="columns: %d(mxLen)ex %d(n);">
   275    278     @ <ul class="browser">
   276    279     while( db_step(&q)==SQLITE_ROW ){
   277    280       const char *zFN;
   278    281       zFN = db_column_text(&q, 0);
   279    282       if( zFN[0]=='/' ){
   280    283         zFN++;
   281    284         @ <li class="dir">%z(href("%s%T",zSubdirLink,zFN))%h(zFN)</a></li>

Changes to src/capabilities.c.

   203    203     if( zIn==0 ) zIn = "";
   204    204     p = capability_add(0, zIn);
   205    205     capability_expand(p);
   206    206     zOut = capability_string(p);
   207    207     sqlite3_result_text(context, zOut, -1, fossil_free);
   208    208     capability_free(p);
   209    209   }
          210  +
          211  +#if INTERFACE
          212  +/*
          213  +** Capabilities are grouped into "classes" as follows:
          214  +*/
          215  +#define CAPCLASS_CODE  0x0001
          216  +#define CAPCLASS_WIKI  0x0002
          217  +#define CAPCLASS_TKT   0x0004
          218  +#define CAPCLASS_FORUM 0x0008
          219  +#define CAPCLASS_DATA  0x0010
          220  +#define CAPCLASS_ALERT 0x0020
          221  +#define CAPCLASS_OTHER 0x0040
          222  +#define CAPCLASS_SUPER 0x0080
          223  +#define CAPCLASS_ALL   0xffff
          224  +#endif /* INTERFACE */
          225  +
          226  +
          227  +/*
          228  +** The following structure holds descriptions of the various capabilities.
          229  +*/
          230  +static struct Caps {
          231  +  char cCap;              /* The capability letter */
          232  +  unsigned short eClass;  /* The "class" for this capability */
          233  +  char *zAbbrev;          /* Abbreviated mnemonic name */
          234  +  char *zOneLiner;        /* One-line summary */
          235  +} aCap[] = {
          236  +  { 'a', CAPCLASS_SUPER,
          237  +    "Admin", "Create and delete users" },
          238  +  { 'b', CAPCLASS_WIKI|CAPCLASS_TKT,
          239  +    "Attach", "Add attchments to wiki or tickets" },
          240  +  { 'c', CAPCLASS_TKT,
          241  +    "Append-Tkt", "Append to existing tickets" },
          242  +  { 'd', CAPCLASS_WIKI|CAPCLASS_TKT,
          243  +    "Delete", "Delete wiki or tickets" },
          244  +  { 'e', CAPCLASS_DATA,
          245  +    "View-PII", "View sensitive info such as email addresses" },
          246  +  { 'f', CAPCLASS_WIKI,
          247  +    "New-Wiki", "Create new wiki pages" },
          248  +  { 'g', CAPCLASS_DATA,
          249  +    "Clone", "Clone the repository" },
          250  +  { 'h', CAPCLASS_OTHER,
          251  +    "Hyperlinks", "Show hyperlinks to detailed repository history" },
          252  +  { 'i', CAPCLASS_CODE,
          253  +    "Check-In", "Check-in code changes" },
          254  +  { 'j', CAPCLASS_WIKI,
          255  +    "Read-Wiki", "View wiki pages" },
          256  +  { 'k', CAPCLASS_WIKI,
          257  +    "Write-Wiki", "Edit wiki pages" },
          258  +  { 'l', CAPCLASS_WIKI|CAPCLASS_SUPER,
          259  +    "Mod-Wiki", "Moderator for wiki pages" },
          260  +  { 'm', CAPCLASS_WIKI,
          261  +    "Append-Wiki", "Append to wiki pages" },
          262  +  { 'n', CAPCLASS_TKT,
          263  +    "New-Tkt", "Create new tickets" },
          264  +  { 'o', CAPCLASS_CODE,
          265  +    "Check-Out", "Check out code" },
          266  +  { 'p', CAPCLASS_OTHER,
          267  +    "Password", "Change your own password" },
          268  +  { 'q', CAPCLASS_TKT|CAPCLASS_SUPER,
          269  +    "Mod-Tkt", "Moderate tickets" },
          270  +  { 'r', CAPCLASS_TKT,
          271  +    "Read-Tkt", "View tickets" },
          272  +  { 's', CAPCLASS_SUPER,
          273  +    "Superuser", "Setup and configure the respository" },
          274  +  { 't', CAPCLASS_TKT,
          275  +    "Reports", "Create new ticket report formats" },
          276  +  { 'u', CAPCLASS_OTHER,
          277  +    "Reader", "Inherit all the capabilities of the \"reader\" user" },
          278  +  { 'v', CAPCLASS_OTHER,
          279  +    "Developer", "Inherit all capabilities of the \"developer\" user" },
          280  +  { 'w', CAPCLASS_TKT,
          281  +    "Write-Tkt", "Edit tickets" },
          282  +  { 'x', CAPCLASS_DATA,
          283  +    "Private", "Push and/or pull private branches" },
          284  +  { 'y', CAPCLASS_SUPER,
          285  +    "Write-UV", "Push unversioned content" },
          286  +  { 'z', CAPCLASS_CODE,
          287  +    "Zip-Download", "Download a ZIP archive, tarball, or SQL archive" },
          288  +  { '2', CAPCLASS_FORUM,
          289  +    "Forum-Read", "Read forum posts by others" },
          290  +  { '3', CAPCLASS_FORUM,
          291  +    "Forum-Write", "Create new forum messages" },
          292  +  { '4', CAPCLASS_FORUM,
          293  +    "Forum-Trusted", "Create forum messages that bypass moderation" },
          294  +  { '5', CAPCLASS_FORUM|CAPCLASS_SUPER,
          295  +    "Forum-Mod", "Moderator for forum messages" },
          296  +  { '6', CAPCLASS_FORUM|CAPCLASS_SUPER,
          297  +    "Forum-Admin", "Set or remove capability '4' from other users" },
          298  +  { '7', CAPCLASS_ALERT,
          299  +    "Alerts", "Sign up for email alerts" },
          300  +  { 'A', CAPCLASS_ALERT|CAPCLASS_SUPER,
          301  +    "Announce", "Send announcements to all subscribers" },
          302  +  { 'D', CAPCLASS_OTHER,
          303  +    "Debug", "Enable debugging features" },
          304  +};
          305  +
   210    306   
   211    307   /*
   212    308   ** Generate HTML that lists all of the capability letters together with
   213    309   ** a brief summary of what each letter means.
   214    310   */
   215         -void capabilities_table(void){
          311  +void capabilities_table(unsigned mClass){
          312  +  int i;
   216    313     @ <table>
   217         -     @ <tr><th valign="top">a</th>
   218         -     @   <td><i>Admin:</i> Create and delete users</td></tr>
   219         -     @ <tr><th valign="top">b</th>
   220         -     @   <td><i>Attach:</i> Add attachments to wiki or tickets</td></tr>
   221         -     @ <tr><th valign="top">c</th>
   222         -     @   <td><i>Append-Tkt:</i> Append to tickets</td></tr>
   223         -     @ <tr><th valign="top">d</th>
   224         -     @   <td><i>Delete:</i> Delete wiki and tickets</td></tr>
   225         -     @ <tr><th valign="top">e</th>
   226         -     @   <td><i>View-PII:</i> \
   227         -     @ View sensitive data such as email addresses</td></tr>
   228         -     @ <tr><th valign="top">f</th>
   229         -     @   <td><i>New-Wiki:</i> Create new wiki pages</td></tr>
   230         -     @ <tr><th valign="top">g</th>
   231         -     @   <td><i>Clone:</i> Clone the repository</td></tr>
   232         -     @ <tr><th valign="top">h</th>
   233         -     @   <td><i>Hyperlinks:</i> Show hyperlinks to detailed
   234         -     @   repository history</td></tr>
   235         -     @ <tr><th valign="top">i</th>
   236         -     @   <td><i>Check-In:</i> Commit new versions in the repository</td></tr>
   237         -     @ <tr><th valign="top">j</th>
   238         -     @   <td><i>Read-Wiki:</i> View wiki pages</td></tr>
   239         -     @ <tr><th valign="top">k</th>
   240         -     @   <td><i>Write-Wiki:</i> Edit wiki pages</td></tr>
   241         -     @ <tr><th valign="top">l</th>
   242         -     @   <td><i>Mod-Wiki:</i> Moderator for wiki pages</td></tr>
   243         -     @ <tr><th valign="top">m</th>
   244         -     @   <td><i>Append-Wiki:</i> Append to wiki pages</td></tr>
   245         -     @ <tr><th valign="top">n</th>
   246         -     @   <td><i>New-Tkt:</i> Create new tickets</td></tr>
   247         -     @ <tr><th valign="top">o</th>
   248         -     @   <td><i>Check-Out:</i> Check out versions</td></tr>
   249         -     @ <tr><th valign="top">p</th>
   250         -     @   <td><i>Password:</i> Change your own password</td></tr>
   251         -     @ <tr><th valign="top">q</th>
   252         -     @   <td><i>Mod-Tkt:</i> Moderator for tickets</td></tr>
   253         -     @ <tr><th valign="top">r</th>
   254         -     @   <td><i>Read-Tkt:</i> View tickets</td></tr>
   255         -     @ <tr><th valign="top">s</th>
   256         -     @   <td><i>Setup/Super-user:</i> Setup and configure this website</td></tr>
   257         -     @ <tr><th valign="top">t</th>
   258         -     @   <td><i>Tkt-Report:</i> Create new bug summary reports</td></tr>
   259         -     @ <tr><th valign="top">u</th>
   260         -     @   <td><i>Reader:</i> Inherit privileges of
   261         -     @   user <tt>reader</tt></td></tr>
   262         -     @ <tr><th valign="top">v</th>
   263         -     @   <td><i>Developer:</i> Inherit privileges of
   264         -     @   user <tt>developer</tt></td></tr>
   265         -     @ <tr><th valign="top">w</th>
   266         -     @   <td><i>Write-Tkt:</i> Edit tickets</td></tr>
   267         -     @ <tr><th valign="top">x</th>
   268         -     @   <td><i>Private:</i> Push and/or pull private branches</td></tr>
   269         -     @ <tr><th valign="top">y</th>
   270         -     @   <td><i>Write-Unver:</i> Push unversioned files</td></tr>
   271         -     @ <tr><th valign="top">z</th>
   272         -     @   <td><i>Zip download:</i> Download a ZIP archive or tarball</td></tr>
   273         -     @ <tr><th valign="top">2</th>
   274         -     @   <td><i>Forum-Read:</i> Read forum posts by others </td></tr>
   275         -     @ <tr><th valign="top">3</th>
   276         -     @   <td><i>Forum-Append:</i> Add new forum posts</td></tr>
   277         -     @ <tr><th valign="top">4</th>
   278         -     @   <td><i>Forum-Trusted:</i> Add pre-approved forum posts </td></tr>
   279         -     @ <tr><th valign="top">5</th>
   280         -     @   <td><i>Forum-Moderator:</i> Approve or disapprove forum posts</td></tr>
   281         -     @ <tr><th valign="top">6</th>
   282         -     @   <td><i>Forum-Supervisor:</i> \
   283         -     @ Forum administrator: Set or remove capability "4" for other users
   284         -     @ <tr><th valign="top">7</th>
   285         -     @   <td><i>Email-Alerts:</i> Sign up for email nofications</td></tr>
   286         -     @ <tr><th valign="top">A</th>
   287         -     @   <td><i>Announce:</i> Send announcements</td></tr>
   288         -     @ <tr><th valign="top">D</th>
   289         -     @   <td><i>Debug:</i> Enable debugging features</td></tr>
          314  +  for(i=0; i<sizeof(aCap)/sizeof(aCap[0]); i++){
          315  +    if( (aCap[i].eClass & mClass)==0 ) continue;
          316  +    @ <tr><th valign="top">%c(aCap[i].cCap)</th>
          317  +    @  <td><i>%h(aCap[i].zAbbrev):</i> %h(aCap[i].zOneLiner)</td></tr>
          318  +  }
   290    319     @ </table>
   291    320   }
   292    321   
   293    322   /*
   294    323   ** Generate a "capability summary table" that shows the major capabilities
   295    324   ** against the various user categories.
   296    325   */

Changes to src/captcha.c.

   537    537     const char *zDecoded;
   538    538     char *zCaptcha;
   539    539   
   540    540     if( !captcha_needed() ) return;
   541    541     uSeed = captcha_seed();
   542    542     zDecoded = captcha_decode(uSeed);
   543    543     zCaptcha = captcha_render(zDecoded);
   544         -  @ <div class="captcha"><table class="captcha"><tr><td><pre>
          544  +  @ <div class="captcha"><table class="captcha"><tr><td><pre class="captcha">
   545    545     @ %h(zCaptcha)
   546    546     @ </pre>
   547    547     @ Enter security code shown above:
   548    548     @ <input type="hidden" name="captchaseed" value="%u(uSeed)" />
   549    549     @ <input type="text" name="captcha" size=8 />
   550    550     if( showButton ){
   551    551       @ <input type="submit" value="Submit">

Changes to src/codecheck1.c.

   396    396     { "json_new_string_f",       1, 0 },
   397    397     { "json_set_err",            2, 0 },
   398    398     { "json_warn",               2, 0 },
   399    399     { "mprintf",                 1, 0 },
   400    400     { "socket_set_errmsg",       1, 0 },
   401    401     { "ssl_set_errmsg",          1, 0 },
   402    402     { "style_header",            1, FMT_HTML },
          403  +  { "style_js_onload",         1, FMT_HTML },
   403    404     { "style_set_current_page",  1, FMT_URL },
   404    405     { "style_submenu_element",   2, FMT_URL },
   405    406     { "style_submenu_sql",       3, FMT_SQL },
   406    407     { "webpage_error",           1, FMT_SAFE },
   407    408     { "xhref",                   2, FMT_URL },
   408    409   };
   409    410   

Changes to src/configure.c.

    97     97     { "background-image",       CONFIGSET_SKIN },
    98     98     { "timeline-block-markup",  CONFIGSET_SKIN },
    99     99     { "timeline-max-comment",   CONFIGSET_SKIN },
   100    100     { "timeline-plaintext",     CONFIGSET_SKIN },
   101    101     { "adunit",                 CONFIGSET_SKIN },
   102    102     { "adunit-omit-if-admin",   CONFIGSET_SKIN },
   103    103     { "adunit-omit-if-user",    CONFIGSET_SKIN },
          104  +  { "sitemap-docidx",         CONFIGSET_SKIN },
          105  +  { "sitemap-download",       CONFIGSET_SKIN },
          106  +  { "sitemap-license",        CONFIGSET_SKIN },
          107  +  { "sitemap-contact",        CONFIGSET_SKIN },
   104    108   
   105    109   #ifdef FOSSIL_ENABLE_TH1_DOCS
   106    110     { "th1-docs",               CONFIGSET_TH1 },
   107    111   #endif
   108    112   #ifdef FOSSIL_ENABLE_TH1_HOOKS
   109    113     { "th1-hooks",              CONFIGSET_TH1 },
   110    114   #endif
................................................................................
   396    400         thisMask = configure_is_exportable(azToken[1]);
   397    401       }else{
   398    402         thisMask = configure_is_exportable(aType[ii].zName);
   399    403       }
   400    404       if( (thisMask & groupMask)==0 ) return;
   401    405       if( (thisMask & checkMask)!=0 ){
   402    406         if( (thisMask & CONFIGSET_SCRIBER)!=0 ){
   403         -        email_schema(1);
          407  +        alert_schema(1);
   404    408         }
   405    409         checkMask &= ~thisMask;
   406    410       }
   407    411   
   408    412       blob_zero(&sql);
   409    413       if( groupMask & CONFIGSET_OVERWRITE ){
   410    414         if( (thisMask & configHasBeenReset)==0 && aType[ii].zName[0]!='/' ){
................................................................................
   413    417         }
   414    418         blob_append_sql(&sql, "REPLACE INTO ");
   415    419       }else{
   416    420         blob_append_sql(&sql, "INSERT OR IGNORE INTO ");
   417    421       }
   418    422       blob_append_sql(&sql, "\"%w\"(\"%w\",mtime",
   419    423            &zName[1], aType[ii].zPrimKey);
   420         -    if( fossil_stricmp(zName,"/subscriber") ) email_schema(0);
          424  +    if( fossil_stricmp(zName,"/subscriber") ) alert_schema(0);
   421    425       for(jj=2; jj<nToken; jj+=2){
   422    426          blob_append_sql(&sql, ",\"%w\"", azToken[jj]);
   423    427       }
   424    428       blob_append_sql(&sql,") VALUES(%s,%s",
   425    429          azToken[1] /*safe-for-%s*/, azToken[0]/*safe-for-%s*/);
   426    430       for(jj=2; jj<nToken; jj+=2){
   427    431          blob_append_sql(&sql, ",%s", azToken[jj+1] /*safe-for-%s*/);

Changes to src/db.c.

   999    999     sqlite3_create_function(db, "hextoblob", 1, SQLITE_UTF8, 0,
  1000   1000                             db_hextoblob, 0, 0);
  1001   1001     sqlite3_create_function(db, "capunion", 1, SQLITE_UTF8, 0,
  1002   1002                             0, capability_union_step, capability_union_finalize);
  1003   1003     sqlite3_create_function(db, "fullcap", 1, SQLITE_UTF8, 0,
  1004   1004                             capability_fullcap, 0, 0);
  1005   1005     sqlite3_create_function(db, "find_emailaddr", 1, SQLITE_UTF8, 0,
  1006         -                          email_find_emailaddr_func, 0, 0);
         1006  +                          alert_find_emailaddr_func, 0, 0);
  1007   1007   }
  1008   1008   
  1009   1009   #if USE_SEE
  1010   1010   /*
  1011   1011   ** This is a pointer to the saved database encryption key string.
  1012   1012   */
  1013   1013   static char *zSavedKey = 0;

Changes to src/default_css.txt.

   186    186   span.wikiTagCancelled {
   187    187     text-decoration: line-through;
   188    188   }
   189    189   div.columns {
   190    190     padding: 0 2em 0 2em;
   191    191     max-width: 1000px;
   192    192   }
   193         -div.columns ul {
          193  +div.columns > ul {
   194    194     margin: 0;
   195         -  padding: 0;
          195  +  padding: 0 0 0 1em;
   196    196   }
   197         -div.columns ul li:first-child {
          197  +div.columns > ul li:first-child {
   198    198     margin-top:0px;
   199    199   }
          200  +div.columns li {
          201  +  break-inside: avoid;
          202  +}
   200    203   .filetree {
   201    204     margin: 1em 0;
   202    205     line-height: 1.5;
   203    206   }
   204    207   .filetree > ul {
   205    208     display: inline-block;
   206    209   }
................................................................................
   301    304   table.captcha {
   302    305     margin: auto;
   303    306     padding: 10px;
   304    307     border-width: 4px;
   305    308     border-style: double;
   306    309     border-color: black;
   307    310   }
          311  +pre.captcha {
          312  +  font-size: 50%;
          313  +}
   308    314   td.login_out_label {
   309    315     text-align: center;
   310    316   }
   311    317   span.loginError {
   312    318     color: red;
   313    319   }
   314    320   span.note {

Changes to src/doc.c.

   465    465   ** Look for a file named zName in the check-in with RID=vid.  Load the content
   466    466   ** of that file into pContent and return the RID for the file.  Or return 0
   467    467   ** if the file is not found or could not be loaded.
   468    468   */
   469    469   int doc_load_content(int vid, const char *zName, Blob *pContent){
   470    470     int writable = db_is_writeable("repository");
   471    471     int rid;   /* The RID of the file being loaded */
          472  +  if( writable ){
          473  +    db_end_transaction(0);
          474  +    db_begin_write();
          475  +  }
   472    476     if( !db_table_exists("repository", "vcache") || !writable ){
   473    477       db_multi_exec(
   474    478         "CREATE %s TABLE IF NOT EXISTS vcache(\n"
   475    479         "  vid INTEGER,         -- check-in ID\n"
   476    480         "  fname TEXT,          -- filename\n"
   477    481         "  rid INTEGER,         -- artifact ID\n"
   478    482         "  PRIMARY KEY(vid,fname)\n"

Changes to src/forum.c.

   772    772   ** Edit an existing forum message.
   773    773   ** Query parameters:
   774    774   **
   775    775   **   fpid=X        Hash of the post to be editted.  REQUIRED
   776    776   */
   777    777   void forumedit_page(void){
   778    778     int fpid;
   779         -  Manifest *pPost;
          779  +  Manifest *pPost = 0;
   780    780     const char *zMimetype = 0;
   781    781     const char *zContent = 0;
   782    782     const char *zTitle = 0;
   783    783     int isCsrfSafe;
   784    784     int isDelete = 0;
   785    785   
   786    786     login_check_credentials();
................................................................................
   862    862         zTitle = fossil_strdup(pPost->zThreadTitle);
   863    863       }
   864    864       style_header("Edit %s", zTitle ? "Post" : "Reply");
   865    865       @ <h1>Original Post:</h1>
   866    866       forum_render(pPost->zThreadTitle, pPost->zMimetype, pPost->zWiki,
   867    867                    "forumEdit");
   868    868       if( P("preview") ){
   869         -      @ <h1>Preview Of Editted Post:</h1>
          869  +      @ <h1>Preview of Edited Post:</h1>
   870    870         forum_render(zTitle, zMimetype, zContent,"forumEdit");
   871    871       }
   872    872       @ <h1>Revised Message:</h1>
   873    873       @ <form action="%R/forume2" method="POST">
   874    874       @ <input type="hidden" name="fpid" value="%h(P("fpid"))">
   875    875       @ <input type="hidden" name="edit" value="1">
   876    876       forum_from_line();
................................................................................
   926    926   **
   927    927   **    n=N             The number of threads to show on each page
   928    928   **    x=X             Skip the first X threads
   929    929   */
   930    930   void forum_main_page(void){
   931    931     Stmt q;
   932    932     int iLimit, iOfst, iCnt;
          933  +  int srchFlags;
   933    934     login_check_credentials();
          935  +  srchFlags = search_restrict(SRCH_FORUM);
   934    936     if( !g.perm.RdForum ){
   935    937       login_needed(g.anon.RdForum);
   936    938       return;
   937    939     }
   938    940     style_header("Forum");
   939    941     if( g.perm.WrForum ){
   940    942       style_submenu_element("New Message","%R/forumnew");
   941    943     }
   942    944     if( g.perm.ModForum && moderation_needed() ){
   943    945       style_submenu_element("Moderation Requests", "%R/modreq");
   944    946     }
   945         -  if( search_screen(SRCH_FORUM, 0) ){
   946         -    style_submenu_element("Recent Threads","%R/forum");
   947         -    style_footer();
   948         -    return;
          947  +  if( (srchFlags & SRCH_FORUM)!=0 ){
          948  +    if( search_screen(SRCH_FORUM, 0) ){
          949  +      style_submenu_element("Recent Threads","%R/forum");
          950  +      style_footer();
          951  +      return;
          952  +    }
   949    953     }
   950    954     iLimit = atoi(PD("n","25"));
   951    955     iOfst = atoi(PD("x","0"));
   952    956     iCnt = 0;
   953    957     if( db_table_exists("repository","forumpost") ){
   954    958       db_prepare(&q,
   955    959         "WITH thread(age,duration,cnt,root,last) AS ("
................................................................................
   988    992         const char *zTitle = db_column_text(&q, 4);
   989    993         if( iCnt==0 ){
   990    994           if( iOfst>0 ){
   991    995             @ <h1>Threads at least %s(zAge) old</h1>
   992    996           }else{
   993    997             @ <h1>Most recent threads</h1>
   994    998           }
   995         -        @ <div class='fileage'><table width="100%%">
          999  +        @ <div class='forumPosts fileage'><table width="100%%">
   996   1000           if( iOfst>0 ){
   997   1001             if( iOfst>iLimit ){
   998   1002               @ <tr><td colspan="3">\
   999   1003               @ %z(href("%R/forum?x=%d&n=%d",iOfst-iLimit,iLimit))\
  1000   1004               @ &uarr; Newer...</a></td></tr>
  1001   1005             }else{
  1002   1006               @ <tr><td colspan="3">%z(href("%R/forum?n=%d",iLimit))\

Changes to src/json.c.

  1888   1888     ADD(WrTkt,"editTicket");
  1889   1889     ADD(ModTkt,"moderateTicket");
  1890   1890     ADD(Attach,"attachFile");
  1891   1891     ADD(TktFmt,"createTicketReport");
  1892   1892     ADD(RdAddr,"readPrivate");
  1893   1893     ADD(Zip,"zip");
  1894   1894     ADD(Private,"xferPrivate");
         1895  +  ADD(WrUnver,"writeUnversioned");
         1896  +  ADD(RdForum,"readForum");
         1897  +  ADD(WrForum,"writeForum");
         1898  +  ADD(WrTForum,"writeTrustedForum");
         1899  +  ADD(ModForum,"moderateForum");
         1900  +  ADD(AdminForum,"adminForum");
         1901  +  ADD(EmailAlert,"emailAlert");
         1902  +  ADD(Announce,"announce");
         1903  +  ADD(Debug,"debug");
  1895   1904   #undef ADD
  1896   1905     return payload;
  1897   1906   }
  1898   1907   
  1899   1908   /*
  1900   1909   ** Implementation of the /json/stat page/command.
  1901   1910   **

Changes to src/login.c.

   727    727       @ <tr>
   728    728       @   <td class="form_label">User ID:</td>
   729    729       if( anonFlag ){
   730    730         @ <td><input type="text" id="u" name="u" value="anonymous" size="30"></td>
   731    731       }else{
   732    732         @ <td><input type="text" id="u" name="u" value="" size="30" /></td>
   733    733       }
          734  +    @ </tr>
          735  +    @ <tr>
          736  +    @  <td class="form_label">Password:</td>
          737  +    @  <td><input type="password" id="p" name="p" value="" size="30" /></td>
          738  +    @ </tr>
   734    739       if( P("HTTPS")==0 ){
   735         -      @ <td width="15"><td rowspan="2">
   736         -      @ <p class='securityWarning'>
   737         -      @ Warning: Your password will be sent in the clear over an
          740  +      @ <tr><td class="form_label">Warning:</td>
          741  +      @ <td><span class='securityWarning'>
          742  +      @ Your password will be sent in the clear over an
   738    743         @ unencrypted connection.
   739    744         if( g.sslNotAvailable ){
   740    745           @ No encrypted connection is available on this server.
   741    746         }else{
   742    747           @ Consider logging in at
   743    748           @ <a href='%s(g.zHttpsURL)'>%h(g.zHttpsURL)</a> instead.
   744    749         }
   745         -      @ </p>
          750  +      @ </span></td></tr>
   746    751       }
   747         -    @ </tr>
   748         -    @ <tr>
   749         -    @  <td class="form_label">Password:</td>
   750         -    @  <td><input type="password" id="p" name="p" value="" size="30" /></td>
   751         -    @ </tr>
   752    752       if( g.zLogin==0 && (anonFlag || zGoto==0) ){
   753    753         zAnonPw = db_text(0, "SELECT pw FROM user"
   754    754                              " WHERE login='anonymous'"
   755    755                              "   AND cap!=''");
   756    756       }
   757    757       @ <tr>
   758    758       @   <td></td>
   759    759       @   <td><input type="submit" name="in" value="Login"></td>
   760         -    @   <td colspan="2">&larr; Pressing this button grants\
   761         -    @   permission to store a cookie
   762    760       @ </tr>
   763    761       if( !noAnon && login_self_register_available(0) ){
   764    762         @ <tr>
   765    763         @   <td></td>
   766    764         @   <td><input type="submit" name="self" value="Create A New Account">
   767         -      @   <td colspan="2"> \
   768         -      @   &larr; Don't have a login?  Click this button to create one.
   769    765         @ </tr>
   770    766       }
   771    767       @ </table>
   772    768       if( zAnonPw && !noAnon ){
   773    769         unsigned int uSeed = captcha_seed();
   774    770         const char *zDecoded = captcha_decode(uSeed);
   775    771         int bAutoCaptcha = db_get_boolean("auto-captcha", 0);
   776    772         char *zCaptcha = captcha_render(zDecoded);
   777    773     
   778    774         @ <p><input type="hidden" name="cs" value="%u(uSeed)" />
   779    775         @ Visitors may enter <b>anonymous</b> as the user-ID with
   780    776         @ the 8-character hexadecimal password shown below:</p>
   781         -      @ <div class="captcha"><table class="captcha"><tr><td><pre>
          777  +      @ <div class="captcha"><table class="captcha"><tr><td>\
          778  +      @ <pre class="captcha">
   782    779         @ %h(zCaptcha)
   783    780         @ </pre></td></tr></table>
   784    781         if( bAutoCaptcha ) {
   785    782            @ <input type="button" value="Fill out captcha" id='autofillButton' \
   786    783            @ data-af='%s(zDecoded)' />
   787    784            style_load_one_js_file("login.js");
   788    785         }
   789    786         @ </div>
   790    787         free(zCaptcha);
   791    788       }
   792    789       @ </form>
   793    790     }
   794         -  if( login_is_individual() && g.perm.Password ){
   795         -    if( email_enabled() ){
          791  +  if( login_is_individual() ){
          792  +    if( g.perm.EmailAlert && alert_enabled() ){
   796    793         @ <hr>
   797    794         @ <p>Configure <a href="%R/alerts">Email Alerts</a>
   798    795         @ for user <b>%h(g.zLogin)</b></p>
   799    796       }
   800         -    @ <hr />
   801         -    @ <p>Change Password for user <b>%h(g.zLogin)</b>:</p>
   802         -    form_begin(0, "%R/login");
   803         -    @ <table>
   804         -    @ <tr><td class="form_label">Old Password:</td>
   805         -    @ <td><input type="password" name="p" size="30" /></td></tr>
   806         -    @ <tr><td class="form_label">New Password:</td>
   807         -    @ <td><input type="password" name="n1" size="30" /></td></tr>
   808         -    @ <tr><td class="form_label">Repeat New Password:</td>
   809         -    @ <td><input type="password" name="n2" size="30" /></td></tr>
   810         -    @ <tr><td></td>
   811         -    @ <td><input type="submit" value="Change Password" /></td></tr>
   812         -    @ </table>
   813         -    @ </form>
          797  +    if( g.perm.Password ){
          798  +      @ <hr>
          799  +      @ <p>Change Password for user <b>%h(g.zLogin)</b>:</p>
          800  +      form_begin(0, "%R/login");
          801  +      @ <table>
          802  +      @ <tr><td class="form_label">Old Password:</td>
          803  +      @ <td><input type="password" name="p" size="30" /></td></tr>
          804  +      @ <tr><td class="form_label">New Password:</td>
          805  +      @ <td><input type="password" name="n1" size="30" /></td></tr>
          806  +      @ <tr><td class="form_label">Repeat New Password:</td>
          807  +      @ <td><input type="password" name="n2" size="30" /></td></tr>
          808  +      @ <tr><td></td>
          809  +      @ <td><input type="submit" value="Change Password" /></td></tr>
          810  +      @ </table>
          811  +      @ </form>
          812  +    }
   814    813     }
   815    814     style_footer();
   816    815   }
   817    816   
   818    817   /*
   819    818   ** Attempt to find login credentials for user zLogin on a peer repository
   820    819   ** with project code zCode.  Transfer those credentials to the local
................................................................................
  1192   1191     /* If the public-pages glob pattern is defined and REQUEST_URI matches
  1193   1192     ** one of the globs in public-pages, then also add in all default-perms
  1194   1193     ** permissions.
  1195   1194     */
  1196   1195     zPublicPages = db_get("public-pages",0);
  1197   1196     if( zPublicPages!=0 ){
  1198   1197       Glob *pGlob = glob_create(zPublicPages);
  1199         -    if( glob_match(pGlob, PD("REQUEST_URI","no-match")) ){
         1198  +    const char *zUri = PD("REQUEST_URI","");
         1199  +    zUri += (int)strlen(g.zTop);
         1200  +    if( glob_match(pGlob, zUri) ){
  1200   1201         login_set_capabilities(db_get("default-perms","u"), 0);
  1201   1202       }
  1202   1203       glob_free(pGlob);
  1203   1204     }
  1204   1205   }
  1205   1206   
  1206   1207   /*
................................................................................
  1260   1261                                p->RdWiki = p->WrWiki = p->NewWiki =
  1261   1262                                p->ApndWiki = p->Hyperlink = p->Clone =
  1262   1263                                p->NewTkt = p->Password = p->RdAddr =
  1263   1264                                p->TktFmt = p->Attach = p->ApndTkt =
  1264   1265                                p->ModWiki = p->ModTkt = p->Delete =
  1265   1266                                p->RdForum = p->WrForum = p->ModForum =
  1266   1267                                p->WrTForum = p->AdminForum =
  1267         -                             p->EmailAlert = p->Announce =
         1268  +                             p->EmailAlert = p->Announce = p->Debug =
  1268   1269                                p->WrUnver = p->Private = 1;
  1269   1270                                /* Fall thru into Read/Write */
  1270   1271         case 'i':   p->Read = p->Write = 1;                      break;
  1271   1272         case 'o':   p->Read = 1;                                 break;
  1272   1273         case 'z':   p->Zip = 1;                                  break;
  1273   1274   
  1274   1275         case 'd':   p->Delete = 1;                               break;
................................................................................
  1540   1541       style_footer();
  1541   1542       return;
  1542   1543     }
  1543   1544     zPerms = db_get("default-perms","u");
  1544   1545   
  1545   1546     /* Prompt the user for email alerts if this repository is configured for
  1546   1547     ** email alerts and if the default permissions include "7" */
  1547         -  canDoAlerts = email_tables_exist() && db_int(0,
         1548  +  canDoAlerts = alert_tables_exist() && db_int(0,
  1548   1549       "SELECT fullcap(%Q) GLOB '*7*'", zPerms
  1549   1550     );
  1550   1551     doAlerts = canDoAlerts && atoi(PD("alerts","1"))!=0;
  1551   1552   
  1552   1553     zUserID = PDT("u","");
  1553   1554     zPasswd = PDT("p","");
  1554   1555     zConfirm = PDT("cp","");
................................................................................
  1613   1614       fossil_free(zPass);
  1614   1615       db_multi_exec("%s", blob_sql_text(&sql));
  1615   1616       uid = db_int(0, "SELECT uid FROM user WHERE login=%Q", zUserID);
  1616   1617       login_set_user_cookie(zUserID, uid, NULL);
  1617   1618       if( doAlerts ){
  1618   1619         /* Also make the new user a subscriber. */
  1619   1620         Blob hdr, body;
  1620         -      EmailSender *pSender;
         1621  +      AlertSender *pSender;
  1621   1622         sqlite3_int64 id;   /* New subscriber Id */
  1622   1623         const char *zCode;  /* New subscriber code (in hex) */
  1623   1624         const char *zGoto = P("g");
  1624   1625         int nsub = 0;
  1625   1626         char ssub[20];
  1626   1627         ssub[nsub++] = 'a';
  1627   1628         if( g.perm.Read )    ssub[nsub++] = 'c';
................................................................................
  1651   1652           ** not necessary to repeat the verfication step */
  1652   1653           redirect_to_g();
  1653   1654         }
  1654   1655         zCode = db_text(0,
  1655   1656              "SELECT hex(subscriberCode) FROM subscriber WHERE subscriberId=%lld",
  1656   1657              id);
  1657   1658         /* A verification email */
  1658         -      pSender = email_sender_new(0,0);
         1659  +      pSender = alert_sender_new(0,0);
  1659   1660         blob_init(&hdr,0,0);
  1660   1661         blob_init(&body,0,0);
  1661   1662         blob_appendf(&hdr, "To: <%s>\n", zEAddr);
  1662   1663         blob_appendf(&hdr, "Subject: Subscription verification\n");
  1663         -      email_append_confirmation_message(&body, zCode);
  1664         -      email_send(pSender, &hdr, &body, 0);
         1664  +      alert_append_confirmation_message(&body, zCode);
         1665  +      alert_send(pSender, &hdr, &body, 0);
  1665   1666         style_header("Email Verification");
  1666   1667         if( pSender->zErr ){
  1667   1668           @ <h1>Internal Error</h1>
  1668   1669           @ <p>The following internal error was encountered while trying
  1669   1670           @ to send the confirmation email:
  1670   1671           @ <blockquote><pre>
  1671   1672           @ %h(pSender->zErr)
  1672   1673           @ </pre></blockquote>
  1673   1674         }else{
  1674   1675           @ <p>An email has been sent to "%h(zEAddr)". That email contains a
  1675   1676           @ hyperlink that you must click on in order to activate your
  1676   1677           @ subscription.</p>
  1677   1678         }
  1678         -      email_sender_free(pSender);
         1679  +      alert_sender_free(pSender);
  1679   1680         if( zGoto ){
  1680   1681           @ <p><a href='%h(zGoto)'>Continue</a>
  1681   1682         }
  1682   1683         style_footer();
  1683   1684         return;
  1684   1685       }
  1685   1686       redirect_to_g();
................................................................................
  1697   1698       @ <input type="hidden" name="g" value="%h(P("g"))" />
  1698   1699     }
  1699   1700     @ <p><input type="hidden" name="captchaseed" value="%u(uSeed)" />
  1700   1701     @ <table class="login_out">
  1701   1702     @ <tr>
  1702   1703     @   <td class="form_label" align="right">User ID:</td>
  1703   1704     @   <td><input type="text" name="u" value="%h(zUserID)" size="30"></td>
         1705  +  @
  1704   1706     if( iErrLine==1 ){
  1705         -    @   <td><span class='loginError'>&larr; %h(zErr)</span></td>
         1707  +    @ <tr><td><td><span class='loginError'>&uarr; %h(zErr)</span></td></tr>
  1706   1708     }
  1707         -  @ </tr>
  1708   1709     @ <tr>
  1709   1710     @   <td class="form_label" align="right">Display Name:</td>
  1710   1711     @   <td><input type="text" name="dn" value="%h(zDName)" size="30"></td>
         1712  +  @ </tr>
  1711   1713     if( iErrLine==2 ){
  1712         -    @   <td><span class='loginError'>&larr; %h(zErr)</span></td>
         1714  +    @ <tr><td><td><span class='loginError'>&uarr; %h(zErr)</span></td></tr>
  1713   1715     }
  1714   1716     @ </tr>
  1715   1717     @ <tr>
  1716   1718     @   <td class="form_label" align="right">Email Address:</td>
  1717   1719     @   <td><input type="text" name="ea" value="%h(zEAddr)" size="30"></td>
         1720  +  @ </tr>
  1718   1721     if( iErrLine==3 ){
  1719         -    @   <td><span class='loginError'>&larr; %h(zErr)</span></td>
         1722  +    @ <tr><td><td><span class='loginError'>&uarr; %h(zErr)</span></td></tr>
  1720   1723     }
  1721         -  @ </tr>
  1722   1724     if( canDoAlerts ){
  1723   1725       int a = atoi(PD("alerts","1"));
  1724   1726       @ <tr>
  1725         -    @   <td class="form_label" align="right">Receive Email Alerts?</td>
         1727  +    @   <td class="form_label" align="right">Email&nbsp;Alerts?</td>
  1726   1728       @   <td><select size='1' name='alerts'>
  1727   1729       @       <option value="1" %s(a?"selected":"")>Yes</option>
  1728   1730       @       <option value="0" %s(!a?"selected":"")>No</option>
  1729   1731       @   </select></td></tr>
  1730   1732     }
  1731   1733     @ <tr>
  1732   1734     @   <td class="form_label" align="right">Password:</td>
  1733   1735     @   <td><input type="password" name="p" value="%h(zPasswd)" size="30"></td>
         1736  +  @ <tr>
  1734   1737     if( iErrLine==4 ){
  1735         -    @   <td><span class='loginError'>&larr; %h(zErr)</span></td>
  1736         -  }else{
  1737         -    @   <td>&larr; Must be at least 6 characters</td>
         1738  +    @ <tr><td><td><span class='loginError'>&uarr; %h(zErr)</span></td></tr>
  1738   1739     }
  1739         -  @ </tr>
  1740   1740     @ <tr>
  1741         -  @   <td class="form_label" align="right">Confirm password:</td>
         1741  +  @   <td class="form_label" align="right">Confirm:</td>
  1742   1742     @   <td><input type="password" name="cp" value="%h(zConfirm)" size="30"></td>
         1743  +  @ </tr>
  1743   1744     if( iErrLine==5 ){
  1744         -    @   <td><span class='loginError'>&larr; %h(zErr)</span></td>
         1745  +    @ <tr><td><td><span class='loginError'>&uarr; %h(zErr)</span></td></tr>
  1745   1746     }
  1746         -  @ </tr>
  1747   1747     @ <tr>
  1748         -  @   <td class="form_label" align="right">Captcha text (below):</td>
         1748  +  @   <td class="form_label" align="right">Captcha:</td>
  1749   1749     @   <td><input type="text" name="captcha" value="" size="30"></td>
         1750  +  @ </tr>
  1750   1751     if( iErrLine==6 ){
  1751         -    @   <td><span class='loginError'>&larr; %h(zErr)</span></td>
         1752  +    @ <tr><td><td><span class='loginError'>&uarr; %h(zErr)</span></td></tr>
  1752   1753     }
  1753         -  @ </tr>
  1754   1754     @ <tr><td></td>
  1755   1755     @ <td><input type="submit" name="new" value="Register" /></td></tr>
  1756   1756     @ </table>
  1757         -  @ <div class="captcha"><table class="captcha"><tr><td><pre>
         1757  +  @ <div class="captcha"><table class="captcha"><tr><td><pre class="captcha">
  1758   1758     @ %h(zCaptcha)
  1759         -  @ </pre></td></tr></table></div>
         1759  +  @ </pre>
         1760  +  @ Enter this 8-letter code in the "Captcha" box above.
         1761  +  @ </td></tr></table></div>
  1760   1762     @ </form>
  1761   1763     style_footer();
  1762   1764   
  1763   1765     free(zCaptcha);
  1764   1766   }
  1765   1767   
  1766   1768   /*

Changes to src/main.mk.

     9      9   #
    10     10   # This file is included by primary Makefile.
    11     11   #
    12     12   
    13     13   XBCC = $(BCC) $(BCCFLAGS)
    14     14   XTCC = $(TCC) -I. -I$(SRCDIR) -I$(OBJDIR) $(TCCFLAGS)
    15     15   
           16  +TESTFLAGS := -quiet
    16     17   
    17     18   SRC = \
    18     19     $(SRCDIR)/add.c \
           20  +  $(SRCDIR)/alerts.c \
    19     21     $(SRCDIR)/allrepo.c \
    20     22     $(SRCDIR)/attach.c \
    21     23     $(SRCDIR)/backoffice.c \
    22     24     $(SRCDIR)/bag.c \
    23     25     $(SRCDIR)/bisect.c \
    24     26     $(SRCDIR)/blob.c \
    25     27     $(SRCDIR)/branch.c \
................................................................................
    42     44     $(SRCDIR)/delta.c \
    43     45     $(SRCDIR)/deltacmd.c \
    44     46     $(SRCDIR)/descendants.c \
    45     47     $(SRCDIR)/diff.c \
    46     48     $(SRCDIR)/diffcmd.c \
    47     49     $(SRCDIR)/dispatch.c \
    48     50     $(SRCDIR)/doc.c \
    49         -  $(SRCDIR)/email.c \
    50     51     $(SRCDIR)/encode.c \
    51     52     $(SRCDIR)/etag.c \
    52     53     $(SRCDIR)/event.c \
    53     54     $(SRCDIR)/export.c \
    54     55     $(SRCDIR)/file.c \
    55     56     $(SRCDIR)/finfo.c \
    56     57     $(SRCDIR)/foci.c \
................................................................................
   175    176     $(SRCDIR)/../skins/bootstrap/details.txt \
   176    177     $(SRCDIR)/../skins/bootstrap/footer.txt \
   177    178     $(SRCDIR)/../skins/bootstrap/header.txt \
   178    179     $(SRCDIR)/../skins/default/css.txt \
   179    180     $(SRCDIR)/../skins/default/details.txt \
   180    181     $(SRCDIR)/../skins/default/footer.txt \
   181    182     $(SRCDIR)/../skins/default/header.txt \
          183  +  $(SRCDIR)/../skins/default/js.txt \
   182    184     $(SRCDIR)/../skins/eagle/css.txt \
   183    185     $(SRCDIR)/../skins/eagle/details.txt \
   184    186     $(SRCDIR)/../skins/eagle/footer.txt \
   185    187     $(SRCDIR)/../skins/eagle/header.txt \
   186    188     $(SRCDIR)/../skins/enhanced1/css.txt \
   187    189     $(SRCDIR)/../skins/enhanced1/details.txt \
   188    190     $(SRCDIR)/../skins/enhanced1/footer.txt \
................................................................................
   221    223     $(SRCDIR)/sorttable.js \
   222    224     $(SRCDIR)/tree.js \
   223    225     $(SRCDIR)/useredit.js \
   224    226     $(SRCDIR)/wiki.wiki
   225    227   
   226    228   TRANS_SRC = \
   227    229     $(OBJDIR)/add_.c \
          230  +  $(OBJDIR)/alerts_.c \
   228    231     $(OBJDIR)/allrepo_.c \
   229    232     $(OBJDIR)/attach_.c \
   230    233     $(OBJDIR)/backoffice_.c \
   231    234     $(OBJDIR)/bag_.c \
   232    235     $(OBJDIR)/bisect_.c \
   233    236     $(OBJDIR)/blob_.c \
   234    237     $(OBJDIR)/branch_.c \
................................................................................
   251    254     $(OBJDIR)/delta_.c \
   252    255     $(OBJDIR)/deltacmd_.c \
   253    256     $(OBJDIR)/descendants_.c \
   254    257     $(OBJDIR)/diff_.c \
   255    258     $(OBJDIR)/diffcmd_.c \
   256    259     $(OBJDIR)/dispatch_.c \
   257    260     $(OBJDIR)/doc_.c \
   258         -  $(OBJDIR)/email_.c \
   259    261     $(OBJDIR)/encode_.c \
   260    262     $(OBJDIR)/etag_.c \
   261    263     $(OBJDIR)/event_.c \
   262    264     $(OBJDIR)/export_.c \
   263    265     $(OBJDIR)/file_.c \
   264    266     $(OBJDIR)/finfo_.c \
   265    267     $(OBJDIR)/foci_.c \
................................................................................
   358    360     $(OBJDIR)/wysiwyg_.c \
   359    361     $(OBJDIR)/xfer_.c \
   360    362     $(OBJDIR)/xfersetup_.c \
   361    363     $(OBJDIR)/zip_.c
   362    364   
   363    365   OBJ = \
   364    366    $(OBJDIR)/add.o \
          367  + $(OBJDIR)/alerts.o \
   365    368    $(OBJDIR)/allrepo.o \
   366    369    $(OBJDIR)/attach.o \
   367    370    $(OBJDIR)/backoffice.o \
   368    371    $(OBJDIR)/bag.o \
   369    372    $(OBJDIR)/bisect.o \
   370    373    $(OBJDIR)/blob.o \
   371    374    $(OBJDIR)/branch.o \
................................................................................
   388    391    $(OBJDIR)/delta.o \
   389    392    $(OBJDIR)/deltacmd.o \
   390    393    $(OBJDIR)/descendants.o \
   391    394    $(OBJDIR)/diff.o \
   392    395    $(OBJDIR)/diffcmd.o \
   393    396    $(OBJDIR)/dispatch.o \
   394    397    $(OBJDIR)/doc.o \
   395         - $(OBJDIR)/email.o \
   396    398    $(OBJDIR)/encode.o \
   397    399    $(OBJDIR)/etag.o \
   398    400    $(OBJDIR)/event.o \
   399    401    $(OBJDIR)/export.o \
   400    402    $(OBJDIR)/file.o \
   401    403    $(OBJDIR)/finfo.o \
   402    404    $(OBJDIR)/foci.o \
................................................................................
   544    546   #  -quiet    Hide most output from the terminal
   545    547   #  -strict   Treat known bugs as failures
   546    548   #
   547    549   # TESTFLAGS can also include names of specific test files to limit
   548    550   # the run to just those test cases.
   549    551   #
   550    552   test:	$(OBJDIR) $(APPNAME)
   551         -	$(TCLSH) $(SRCDIR)/../test/tester.tcl $(APPNAME) -quiet $(TESTFLAGS)
          553  +	$(TCLSH) $(SRCDIR)/../test/tester.tcl $(APPNAME) $(TESTFLAGS)
   552    554   
   553    555   $(OBJDIR)/VERSION.h:	$(SRCDIR)/../manifest.uuid $(SRCDIR)/../manifest $(SRCDIR)/../VERSION $(OBJDIR)/mkversion
   554    556   	$(OBJDIR)/mkversion $(SRCDIR)/../manifest.uuid  $(SRCDIR)/../manifest  $(SRCDIR)/../VERSION >$(OBJDIR)/VERSION.h
   555    557   
   556    558   $(OBJDIR)/default_css.h:	$(SRCDIR)/default_css.txt $(OBJDIR)/mkcss
   557    559   	$(OBJDIR)/mkcss $(SRCDIR)/default_css.txt $(OBJDIR)/default_css.h
   558    560   
................................................................................
   693    695   	$(OBJDIR)/mkindex $(TRANS_SRC) >$@
   694    696   
   695    697   $(OBJDIR)/builtin_data.h: $(OBJDIR)/mkbuiltin $(EXTRA_FILES)
   696    698   	$(OBJDIR)/mkbuiltin --prefix $(SRCDIR)/ $(EXTRA_FILES) >$@
   697    699   
   698    700   $(OBJDIR)/headers:	$(OBJDIR)/page_index.h $(OBJDIR)/builtin_data.h $(OBJDIR)/default_css.h $(OBJDIR)/makeheaders $(OBJDIR)/VERSION.h
   699    701   	$(OBJDIR)/makeheaders $(OBJDIR)/add_.c:$(OBJDIR)/add.h \
          702  +	$(OBJDIR)/alerts_.c:$(OBJDIR)/alerts.h \
   700    703   	$(OBJDIR)/allrepo_.c:$(OBJDIR)/allrepo.h \
   701    704   	$(OBJDIR)/attach_.c:$(OBJDIR)/attach.h \
   702    705   	$(OBJDIR)/backoffice_.c:$(OBJDIR)/backoffice.h \
   703    706   	$(OBJDIR)/bag_.c:$(OBJDIR)/bag.h \
   704    707   	$(OBJDIR)/bisect_.c:$(OBJDIR)/bisect.h \
   705    708   	$(OBJDIR)/blob_.c:$(OBJDIR)/blob.h \
   706    709   	$(OBJDIR)/branch_.c:$(OBJDIR)/branch.h \
................................................................................
   723    726   	$(OBJDIR)/delta_.c:$(OBJDIR)/delta.h \
   724    727   	$(OBJDIR)/deltacmd_.c:$(OBJDIR)/deltacmd.h \
   725    728   	$(OBJDIR)/descendants_.c:$(OBJDIR)/descendants.h \
   726    729   	$(OBJDIR)/diff_.c:$(OBJDIR)/diff.h \
   727    730   	$(OBJDIR)/diffcmd_.c:$(OBJDIR)/diffcmd.h \
   728    731   	$(OBJDIR)/dispatch_.c:$(OBJDIR)/dispatch.h \
   729    732   	$(OBJDIR)/doc_.c:$(OBJDIR)/doc.h \
   730         -	$(OBJDIR)/email_.c:$(OBJDIR)/email.h \
   731    733   	$(OBJDIR)/encode_.c:$(OBJDIR)/encode.h \
   732    734   	$(OBJDIR)/etag_.c:$(OBJDIR)/etag.h \
   733    735   	$(OBJDIR)/event_.c:$(OBJDIR)/event.h \
   734    736   	$(OBJDIR)/export_.c:$(OBJDIR)/export.h \
   735    737   	$(OBJDIR)/file_.c:$(OBJDIR)/file.h \
   736    738   	$(OBJDIR)/finfo_.c:$(OBJDIR)/finfo.h \
   737    739   	$(OBJDIR)/foci_.c:$(OBJDIR)/foci.h \
................................................................................
   841    843   $(OBJDIR)/add_.c:	$(SRCDIR)/add.c $(OBJDIR)/translate
   842    844   	$(OBJDIR)/translate $(SRCDIR)/add.c >$@
   843    845   
   844    846   $(OBJDIR)/add.o:	$(OBJDIR)/add_.c $(OBJDIR)/add.h $(SRCDIR)/config.h
   845    847   	$(XTCC) -o $(OBJDIR)/add.o -c $(OBJDIR)/add_.c
   846    848   
   847    849   $(OBJDIR)/add.h:	$(OBJDIR)/headers
          850  +
          851  +$(OBJDIR)/alerts_.c:	$(SRCDIR)/alerts.c $(OBJDIR)/translate
          852  +	$(OBJDIR)/translate $(SRCDIR)/alerts.c >$@
          853  +
          854  +$(OBJDIR)/alerts.o:	$(OBJDIR)/alerts_.c $(OBJDIR)/alerts.h $(SRCDIR)/config.h
          855  +	$(XTCC) -o $(OBJDIR)/alerts.o -c $(OBJDIR)/alerts_.c
          856  +
          857  +$(OBJDIR)/alerts.h:	$(OBJDIR)/headers
   848    858   
   849    859   $(OBJDIR)/allrepo_.c:	$(SRCDIR)/allrepo.c $(OBJDIR)/translate
   850    860   	$(OBJDIR)/translate $(SRCDIR)/allrepo.c >$@
   851    861   
   852    862   $(OBJDIR)/allrepo.o:	$(OBJDIR)/allrepo_.c $(OBJDIR)/allrepo.h $(SRCDIR)/config.h
   853    863   	$(XTCC) -o $(OBJDIR)/allrepo.o -c $(OBJDIR)/allrepo_.c
   854    864   
................................................................................
  1082   1092   	$(OBJDIR)/translate $(SRCDIR)/doc.c >$@
  1083   1093   
  1084   1094   $(OBJDIR)/doc.o:	$(OBJDIR)/doc_.c $(OBJDIR)/doc.h $(SRCDIR)/config.h
  1085   1095   	$(XTCC) -o $(OBJDIR)/doc.o -c $(OBJDIR)/doc_.c
  1086   1096   
  1087   1097   $(OBJDIR)/doc.h:	$(OBJDIR)/headers
  1088   1098   
  1089         -$(OBJDIR)/email_.c:	$(SRCDIR)/email.c $(OBJDIR)/translate
  1090         -	$(OBJDIR)/translate $(SRCDIR)/email.c >$@
  1091         -
  1092         -$(OBJDIR)/email.o:	$(OBJDIR)/email_.c $(OBJDIR)/email.h $(SRCDIR)/config.h
  1093         -	$(XTCC) -o $(OBJDIR)/email.o -c $(OBJDIR)/email_.c
  1094         -
  1095         -$(OBJDIR)/email.h:	$(OBJDIR)/headers
  1096         -
  1097   1099   $(OBJDIR)/encode_.c:	$(SRCDIR)/encode.c $(OBJDIR)/translate
  1098   1100   	$(OBJDIR)/translate $(SRCDIR)/encode.c >$@
  1099   1101   
  1100   1102   $(OBJDIR)/encode.o:	$(OBJDIR)/encode_.c $(OBJDIR)/encode.h $(SRCDIR)/config.h
  1101   1103   	$(XTCC) -o $(OBJDIR)/encode.o -c $(OBJDIR)/encode_.c
  1102   1104   
  1103   1105   $(OBJDIR)/encode.h:	$(OBJDIR)/headers

Changes to src/makemake.tcl.

    24     24   # project, simply add the basename to this list and rerun this script.
    25     25   #
    26     26   # Set the separate extra_files variable further down for how to add non-C
    27     27   # files, such as string and BLOB resources.
    28     28   #
    29     29   set src {
    30     30     add
           31  +  alerts
    31     32     allrepo
    32     33     attach
    33     34     backoffice
    34     35     bag
    35     36     bisect
    36     37     blob
    37     38     branch
................................................................................
    54     55     delta
    55     56     deltacmd
    56     57     descendants
    57     58     diff
    58     59     diffcmd
    59     60     dispatch
    60     61     doc
    61         -  email
    62     62     encode
    63     63     etag
    64     64     event
    65     65     export
    66     66     file
    67     67     finfo
    68     68     foci
................................................................................
   284    284   #
   285    285   # This file is included by primary Makefile.
   286    286   #
   287    287   
   288    288   XBCC = $(BCC) $(BCCFLAGS)
   289    289   XTCC = $(TCC) -I. -I$(SRCDIR) -I$(OBJDIR) $(TCCFLAGS)
   290    290   
          291  +TESTFLAGS := -quiet
   291    292   }
   292    293   writeln -nonewline "SRC ="
   293    294   foreach s [lsort $src] {
   294    295     writeln -nonewline " \\\n  \$(SRCDIR)/$s.c"
   295    296   }
   296    297   writeln "\n"
   297    298   writeln -nonewline "EXTRA_FILES ="
................................................................................
   359    360   #  -quiet    Hide most output from the terminal
   360    361   #  -strict   Treat known bugs as failures
   361    362   #
   362    363   # TESTFLAGS can also include names of specific test files to limit
   363    364   # the run to just those test cases.
   364    365   #
   365    366   test:	$(OBJDIR) $(APPNAME)
   366         -	$(TCLSH) $(SRCDIR)/../test/tester.tcl $(APPNAME) -quiet $(TESTFLAGS)
          367  +	$(TCLSH) $(SRCDIR)/../test/tester.tcl $(APPNAME) $(TESTFLAGS)
   367    368   
   368    369   $(OBJDIR)/VERSION.h:	$(SRCDIR)/../manifest.uuid $(SRCDIR)/../manifest $(SRCDIR)/../VERSION $(OBJDIR)/mkversion
   369    370   	$(OBJDIR)/mkversion $(SRCDIR)/../manifest.uuid \
   370    371   		$(SRCDIR)/../manifest \
   371    372   		$(SRCDIR)/../VERSION >$(OBJDIR)/VERSION.h
   372    373   
   373    374   $(OBJDIR)/default_css.h:	$(SRCDIR)/default_css.txt $(OBJDIR)/mkcss
................................................................................
   693    694   ZLIBCONFIG =
   694    695   ZLIBTARGETS =
   695    696   endif
   696    697   
   697    698   #### Disable creation of the OpenSSL shared libraries.  Also, disable support
   698    699   #    for SSLv3 (i.e. thereby forcing the use of TLS).
   699    700   #
   700         -SSLCONFIG += no-ssl3 enable-capieng no-weak-ssl-ciphers no-shared
          701  +SSLCONFIG += no-ssl3 no-weak-ssl-ciphers no-shared
   701    702   
   702    703   #### When using zlib, make sure that OpenSSL is configured to use the zlib
   703    704   #    that Fossil knows about (i.e. the one within the source tree).
   704    705   #
   705    706   ifndef FOSSIL_ENABLE_MINIZ
   706    707   SSLCONFIG +=  --with-zlib-lib=$(PWD)/$(ZLIBDIR) --with-zlib-include=$(PWD)/$(ZLIBDIR) zlib
   707    708   endif
................................................................................
  1575   1576   SSLLIBDIR = $(SSLDIR)\out32
  1576   1577   !endif
  1577   1578   SSLLFLAGS = /nologo /opt:ref /debug
  1578   1579   SSLLIB    = ssleay32.lib libeay32.lib user32.lib gdi32.lib crypt32.lib
  1579   1580   !if "$(PLATFORM)"=="amd64" || "$(PLATFORM)"=="x64"
  1580   1581   !message Using 'x64' platform for OpenSSL...
  1581   1582   # BUGBUG (OpenSSL): Using "no-ssl*" here breaks the build.
  1582         -# SSLCONFIG = VC-WIN64A no-asm no-ssl3 enable-capieng no-weak-ssl-ciphers
         1583  +# SSLCONFIG = VC-WIN64A no-asm no-ssl3 no-weak-ssl-ciphers
  1583   1584   SSLCONFIG = VC-WIN64A no-asm
  1584   1585   !if $(FOSSIL_DYNAMIC_BUILD)!=0
  1585   1586   SSLCONFIG = $(SSLCONFIG) shared
  1586   1587   !else
  1587   1588   SSLCONFIG = $(SSLCONFIG) no-shared
  1588   1589   !endif
  1589   1590   SSLSETUP  = ms\do_win64a.bat
................................................................................
  1590   1591   !if $(FOSSIL_DYNAMIC_BUILD)!=0
  1591   1592   SSLNMAKE  = ms\ntdll.mak all
  1592   1593   !else
  1593   1594   SSLNMAKE  = ms\nt.mak all
  1594   1595   !endif
  1595   1596   # BUGBUG (OpenSSL): Using "OPENSSL_NO_SSL*" here breaks dynamic builds.
  1596   1597   !if $(FOSSIL_DYNAMIC_BUILD)==0
  1597         -SSLCFLAGS = -DOPENSSL_NO_SSL3
         1598  +SSLCFLAGS = -DOPENSSL_NO_SSL3 -DOPENSSL_NO_WEAK_SSL_CIPHERS
  1598   1599   !endif
  1599   1600   !elseif "$(PLATFORM)"=="ia64"
  1600   1601   !message Using 'ia64' platform for OpenSSL...
  1601   1602   # BUGBUG (OpenSSL): Using "no-ssl*" here breaks the build.
  1602         -# SSLCONFIG = VC-WIN64I no-asm no-ssl3 enable-capieng no-weak-ssl-ciphers
         1603  +# SSLCONFIG = VC-WIN64I no-asm no-ssl3 no-weak-ssl-ciphers
  1603   1604   SSLCONFIG = VC-WIN64I no-asm
  1604   1605   !if $(FOSSIL_DYNAMIC_BUILD)!=0
  1605   1606   SSLCONFIG = $(SSLCONFIG) shared
  1606   1607   !else
  1607   1608   SSLCONFIG = $(SSLCONFIG) no-shared
  1608   1609   !endif
  1609   1610   SSLSETUP  = ms\do_win64i.bat
................................................................................
  1610   1611   !if $(FOSSIL_DYNAMIC_BUILD)!=0
  1611   1612   SSLNMAKE  = ms\ntdll.mak all
  1612   1613   !else
  1613   1614   SSLNMAKE  = ms\nt.mak all
  1614   1615   !endif
  1615   1616   # BUGBUG (OpenSSL): Using "OPENSSL_NO_SSL*" here breaks dynamic builds.
  1616   1617   !if $(FOSSIL_DYNAMIC_BUILD)==0
  1617         -SSLCFLAGS = -DOPENSSL_NO_SSL3
         1618  +SSLCFLAGS = -DOPENSSL_NO_SSL3 -DOPENSSL_NO_WEAK_SSL_CIPHERS
  1618   1619   !endif
  1619   1620   !else
  1620   1621   !message Assuming 'x86' platform for OpenSSL...
  1621   1622   # BUGBUG (OpenSSL): Using "no-ssl*" here breaks the build.
  1622         -# SSLCONFIG = VC-WIN32 no-asm no-ssl3 enable-capieng no-weak-ssl-ciphers
         1623  +# SSLCONFIG = VC-WIN32 no-asm no-ssl3 no-weak-ssl-ciphers
  1623   1624   SSLCONFIG = VC-WIN32 no-asm
  1624   1625   !if $(FOSSIL_DYNAMIC_BUILD)!=0
  1625   1626   SSLCONFIG = $(SSLCONFIG) shared
  1626   1627   !else
  1627   1628   SSLCONFIG = $(SSLCONFIG) no-shared
  1628   1629   !endif
  1629   1630   SSLSETUP  = ms\do_ms.bat
................................................................................
  1630   1631   !if $(FOSSIL_DYNAMIC_BUILD)!=0
  1631   1632   SSLNMAKE  = ms\ntdll.mak all
  1632   1633   !else
  1633   1634   SSLNMAKE  = ms\nt.mak all
  1634   1635   !endif
  1635   1636   # BUGBUG (OpenSSL): Using "OPENSSL_NO_SSL*" here breaks dynamic builds.
  1636   1637   !if $(FOSSIL_DYNAMIC_BUILD)==0
  1637         -SSLCFLAGS = -DOPENSSL_NO_SSL3
         1638  +SSLCFLAGS = -DOPENSSL_NO_SSL3 -DOPENSSL_NO_WEAK_SSL_CIPHERS
  1638   1639   !endif
  1639   1640   !endif
  1640   1641   !endif
  1641   1642   
  1642   1643   !if $(FOSSIL_ENABLE_TCL)!=0
  1643   1644   TCLDIR    = $(B)\compat\tcl-8.6
  1644   1645   TCLSRCDIR = $(TCLDIR)

Changes to src/markdown.md.

    91     91   
    92     92   > The first row is a header if followed by a horizontal rule or a blank line.
    93     93   
    94     94   > Placing **:** at the left, both, or right sides of a cell gives left-aligned,
    95     95   > centered, or right-aligned text, respectively.  By default, header cells are
    96     96   > centered, and body cells are left-aligned.
    97     97   
    98         -> The leftmost **\|** is required if the first column contains at least one
    99         -> blank cell.  The rightmost **\|** is optional.
           98  +> The leftmost or rightmost **\|** is required only if the first or last column,
           99  +> respectively, contains at least one blank cell.
   100    100   
   101    101   ## Miscellaneous ##
   102    102   
   103    103   > *   In-line images are made using **\!\[alt-text\]\(image-URL\)**.
   104    104   > *   Use HTML for advanced formatting such as forms.
   105    105   > *   **\<!--** HTML-style comments **-->** are supported.
   106    106   > *   Escape special characters (ex: **\[** **\(** **\|** **\***)

Changes to src/rebuild.c.

   356    356   
   357    357     bag_init(&bagDone);
   358    358     ttyOutput = doOut;
   359    359     processCnt = 0;
   360    360     if (ttyOutput && !g.fQuiet) {
   361    361       percent_complete(0);
   362    362     }
   363         -  email_triggers_disable();
          363  +  alert_triggers_disable();
   364    364     rebuild_update_schema();
   365    365     blob_init(&sql, 0, 0);
   366    366     db_prepare(&q,
   367    367        "SELECT name FROM sqlite_master /*scan*/"
   368    368        " WHERE type='table'"
   369    369        " AND name NOT IN ('admin_log', 'blob','delta','rcvfrom','user','alias',"
   370    370                          "'config','shun','private','reportfmt',"
   371    371                          "'concealed','accesslog','modreq',"
   372    372                          "'purgeevent','purgeitem','unversioned',"
   373         -                       "'subscriber','pending_alert','email_bounce')"
          373  +                       "'subscriber','pending_alert','alert_bounce')"
   374    374        " AND name NOT GLOB 'sqlite_*'"
   375    375        " AND name NOT GLOB 'fx_*'"
   376    376     );
   377    377     while( db_step(&q)==SQLITE_ROW ){
   378    378       blob_appendf(&sql, "DROP TABLE IF EXISTS \"%w\";\n", db_column_text(&q,0));
   379    379     }
   380    380     db_finalize(&q);
................................................................................
   447    447       percent_complete((processCnt*1000)/totalSize);
   448    448     }
   449    449     if( doClustering ) create_cluster();
   450    450     if( ttyOutput && !g.fQuiet && totalSize>0 ){
   451    451       processCnt += incrSize;
   452    452       percent_complete((processCnt*1000)/totalSize);
   453    453     }
   454         -  email_triggers_enable();
          454  +  alert_triggers_enable();
   455    455     if(!g.fQuiet && ttyOutput ){
   456    456       percent_complete(1000);
   457    457       fossil_print("\n");
   458    458     }
   459    459     return errCnt;
   460    460   }
   461    461   

Changes to src/security_audit.c.

   388    388         @ %,lld(file_size(g.zErrlog, ExtFILE)) bytes in size.
   389    389       }
   390    390     }
   391    391   
   392    392     @ <li><p> User capability summary:
   393    393     capability_summary();
   394    394   
   395         -  if( email_enabled() ){
          395  +  if( alert_enabled() ){
   396    396       @ <li><p> Email alert configuration summary:
   397    397       @ <table class="label-value">
   398    398       stats_for_email();
   399    399       @ </table>
   400    400     }else{
   401    401       @ <li><p> Email alerts are disabled
   402    402     }

Changes to src/setup.c.

   884    884     @ to a documentation page (ex: "/doc/tip/index.wiki") or to "/timeline".</p>
   885    885     @
   886    886     @ <p>Note:  To avoid a redirect loop or other problems, this entry must
   887    887     @ begin with "/" and it must specify a valid page.  For example,
   888    888     @ "<b>/home</b>" will work but "<b>home</b>" will not, since it omits the
   889    889     @ leading "/".</p>
   890    890     @ <p>(Property: "index-page")
          891  +  @ <hr>
          892  +  @ <p>Extra links to appear on the <a href="%R/sitemap">/sitemap</a> page.
          893  +  @ Often these are filled in with links like 
          894  +  @ "/doc/trunk/doc/<i>filename</i>.md" so that they refer to 
          895  +  @ embedded documentation, or like "/wiki/<i>pagename</i>" to refer
          896  +  @ to wiki pages.
          897  +  @ Leave blank to omit.
          898  +  @ <p>
          899  +  entry_attribute("Documentation Index", 40, "sitemap-docidx", "smdocidx",
          900  +                  "", 0);
          901  +  @ (Property: sitemap-docidx)<br>
          902  +  entry_attribute("Download", 40, "sitemap-download", "smdownload",
          903  +                  "", 0);
          904  +  @ (Property: sitemap-download)<br>
          905  +  entry_attribute("License", 40, "sitemap-license", "smlicense",
          906  +                  "", 0);
          907  +  @ (Property: sitemap-license)<br>
          908  +  entry_attribute("Contact", 40, "sitemap-contact", "smcontact",
          909  +                  "", 0);
          910  +  @ (Property: sitemap-contact)
   891    911     @ <hr />
   892    912     onoff_attribute("Use HTML as wiki markup language",
   893    913       "wiki-use-html", "wiki-use-html", 0, 0);
   894    914     @ <p>Use HTML as the wiki markup language. Wiki links will still be parsed
   895    915     @ but all other wiki formatting will be ignored. This option is helpful
   896    916     @ if you have chosen to use a rich HTML editor for wiki markup such as
   897    917     @ TinyMCE.</p>
................................................................................
  1341   1361     const char *zQ = P("q");
  1342   1362     int go = P("go")!=0;
  1343   1363     login_check_credentials();
  1344   1364     if( !g.perm.Setup ){
  1345   1365       login_needed(0);
  1346   1366       return;
  1347   1367     }
  1348         -  db_begin_transaction();
  1349   1368     style_header("Raw TH1 Commands");
  1350   1369     @ <p><b>Caution:</b> There are no restrictions on the TH1 that can be
  1351   1370     @ run by this page.  If Tcl integration was enabled at compile-time and
  1352   1371     @ the "tcl" setting is enabled, Tcl commands may be run as well.</p>
  1353   1372     @
  1354   1373     @ <form method="post" action="%s(g.zTop)/admin_th1">
  1355   1374     login_insert_csrf_secret();

Changes to src/setupuser.c.

   197    197     @ Users with privilege <span class="capability">v</span> inherit the combined
   198    198     @ privileges of <span class="usertype">developer</span>,
   199    199     @ <span class="usertype">anonymous</span>, and
   200    200     @ <span class="usertype">nobody</span>.
   201    201     @ </p></li>
   202    202     @
   203    203     @ <li><p>The permission flags are as follows:</p>
   204         -  capabilities_table();
          204  +  capabilities_table(CAPCLASS_ALL);
   205    205     @ </li>
   206    206     @ </ol>
   207    207     style_footer();
   208    208   }
   209    209   
   210    210   /*
   211    211   ** WEBPAGE: setup_ucap_list
   212    212   **
   213    213   ** A documentation page showing the meaning of the various user capabilities
   214    214   ** code letters.
   215    215   */
   216    216   void setup_ucap_list(void){
   217    217     style_header("User Capability Codes");
   218         -  capabilities_table();
          218  +  @ <h1>All capabilities</h1>
          219  +  capabilities_table(CAPCLASS_ALL);
          220  +  @ <h1>Capabilities associated with checked-in content</h1>
          221  +  capabilities_table(CAPCLASS_CODE);
          222  +  @ <h1>Capabilities associated with data transfer and sync</h1>
          223  +  capabilities_table(CAPCLASS_DATA);
          224  +  @ <h1>Capabilities associated with the forum</h1>
          225  +  capabilities_table(CAPCLASS_FORUM);
          226  +  @ <h1>Capabilities associated with tickets</h1>
          227  +  capabilities_table(CAPCLASS_TKT);
          228  +  @ <h1>Capabilities associated with wiki</h1>
          229  +  capabilities_table(CAPCLASS_WIKI);
          230  +  @ <h1>Administrative capabilities</h1>
          231  +  capabilities_table(CAPCLASS_SUPER);
          232  +  @ <h1>Miscellaneous capabilities</h1>
          233  +  capabilities_table(CAPCLASS_OTHER);
   219    234     style_footer();
   220    235   }
   221    236   
   222    237   /*
   223    238   ** Return true if zPw is a valid password string.  A valid
   224    239   ** password string is:
   225    240   **
................................................................................
   239    254   ** Edit information about a user or create a new user.
   240    255   ** Requires Admin privileges.
   241    256   */
   242    257   void user_edit(void){
   243    258     const char *zId, *zLogin, *zInfo, *zCap, *zPw;
   244    259     const char *zGroup;
   245    260     const char *zOldLogin;
   246         -  int doWrite;
   247    261     int uid, i;
          262  +  char *zDeleteVerify = 0;   /* Delete user verification text */
   248    263     int higherUser = 0;  /* True if user being edited is SETUP and the */
   249    264                          /* user doing the editing is ADMIN.  Disallow editing */
   250    265     const char *inherit[128];
   251    266     int a[128];
   252    267     const char *oa[128];
   253    268   
   254    269     /* Must have ADMIN privileges to access this page
................................................................................
   268    283     }
   269    284   
   270    285     if( P("can") ){
   271    286       /* User pressed the cancel button */
   272    287       cgi_redirect(cgi_referer("setup_ulist"));
   273    288       return;
   274    289     }
          290  +
          291  +  /* Check for requests to delete the user */
          292  +  if( P("delete") && cgi_csrf_safe(1) ){
          293  +    int n;
          294  +    if( P("verifydelete") ){
          295  +      /* Verified delete user request */
          296  +      db_multi_exec("DELETE FROM user WHERE uid=%d", uid);
          297  +      cgi_redirect(cgi_referer("setup_ulist"));
          298  +      return;
          299  +    }
          300  +    n = db_int(0, "SELECT count(*) FROM event"
          301  +                  " WHERE user=%Q AND objid NOT IN private",
          302  +                  P("login"));
          303  +    if( n==0 ){
          304  +      zDeleteVerify = mprintf("Check this box and press \"Delete User\" again");
          305  +    }else{
          306  +      zDeleteVerify = mprintf(
          307  +        "User \"%s\" has %d or more artifacts in the block-chain. "
          308  +        "Delete anyhow?",
          309  +        P("login")/*safe-for-%s*/, n);
          310  +    }
          311  +  }
   275    312   
   276    313     /* If we have all the necessary information, write the new or
   277    314     ** modified user record.  After writing the user record, redirect
   278    315     ** to the page that displays a list of users.
   279    316     */
   280         -  doWrite = cgi_all("login","info","pw") && !higherUser && cgi_csrf_safe(1);
   281         -  if( doWrite ){
          317  +  if( !cgi_all("login","info","pw","apply") ){
          318  +    /* need all of the above properties to make a change.  Since one or
          319  +    ** more are missing, no-op */
          320  +  }else if( higherUser ){
          321  +    /* An Admin (a) user cannot edit a Superuser (s) */
          322  +  }else if( zDeleteVerify!=0 ){
          323  +    /* Need to verify a delete request */
          324  +  }else if( !cgi_csrf_safe(1) ){
          325  +    /* This might be a cross-site request forgery, so ignore it */
          326  +  }else{
          327  +    /* We have all the information we need to make the change to the user */
   282    328       char c;
   283    329       char zCap[70], zNm[4];
   284    330       zNm[0] = 'a';
   285    331       zNm[2] = 0;
   286    332       for(i=0, c='a'; c<='z'; c++){
   287    333         zNm[1] = c;
   288    334         a[c&0x7f] = (c!='s' || g.perm.Setup) && P(zNm)!=0;
................................................................................
   558    604     @  Send Announcements%s(B('A'))</label>
   559    605     @  <li><label><input type="checkbox" name="aD"%s(oa['D']) />
   560    606     @  Enable Debug%s(B('D'))</label>
   561    607     @ </ul></div>
   562    608     @   </td>
   563    609     @ </tr>
   564    610     @ <tr>
   565         -  @   <td class="usetupEditLabel">Selected Cap.:</td>
          611  +  @   <td class="usetupEditLabel">Selected Cap:</td>
   566    612     @   <td>
   567    613     @     <span id="usetupEditCapability">(missing JS?)</span>
   568    614     @     <a href="%R/setup_ucap_list">(key)</a>
   569    615     @   </td>
   570    616     @ </tr>
   571    617     if( !login_is_special(zLogin) ){
   572    618       @ <tr>
................................................................................
   588    634       @ <input type="radio" name="all" checked value="0">
   589    635       @ Apply changes to this repository only.<br />
   590    636       @ <input type="radio" name="all" value="1">
   591    637       @ Apply changes to all repositories in the "<b>%h(zGroup)</b>"
   592    638       @ login group.</td></tr>
   593    639     }
   594    640     if( !higherUser ){
          641  +    if( zDeleteVerify ){
          642  +      @ <tr>
          643  +      @   <td valign="top" align="right">Verify:</td>
          644  +      @   <td><label><input type="checkbox" name="verifydelete">\
          645  +      @   Confirm Delete \
          646  +      @   <span class="loginError">&larr; %h(zDeleteVerify)</span>
          647  +      @   </label></td>
          648  +      @ <tr>
          649  +    }
   595    650       @ <tr>
   596    651       @   <td>&nbsp;</td>
   597         -    @   <td><input type="submit" name="submit" value="Apply Changes" /></td>
          652  +    @   <td><input type="submit" name="apply" value="Apply Changes">
          653  +    if( !login_is_special(zLogin) ){
          654  +      @   <input type="submit" name="delete" value="Delete User">
          655  +    }
          656  +    @   <input type="submit" name="can" value="Cancel"></td>
   598    657       @ </tr>
   599    658     }
   600    659     @ </table>
   601    660     @ </div></form>
   602    661     @ </div>
   603    662     style_load_one_js_file("useredit.js");
   604    663     @ <hr>

Changes to src/shell.c.

  2222   2222   ){
  2223   2223     HANDLE hFindFile;
  2224   2224     WIN32_FIND_DATAW fd;
  2225   2225     LPWSTR zUnicodeName;
  2226   2226     extern LPWSTR sqlite3_win32_utf8_to_unicode(const char*);
  2227   2227     zUnicodeName = sqlite3_win32_utf8_to_unicode(zPath);
  2228   2228     if( zUnicodeName ){
  2229         -    memset(&fd, 0, sizeof(WIN32_FIND_DATA));
         2229  +    memset(&fd, 0, sizeof(WIN32_FIND_DATAW));
  2230   2230       hFindFile = FindFirstFileW(zUnicodeName, &fd);
  2231   2231       if( hFindFile!=NULL ){
  2232   2232         pStatBuf->st_ctime = (time_t)fileTimeToUnixTime(&fd.ftCreationTime);
  2233   2233         pStatBuf->st_atime = (time_t)fileTimeToUnixTime(&fd.ftLastAccessTime);
  2234   2234         pStatBuf->st_mtime = (time_t)fileTimeToUnixTime(&fd.ftLastWriteTime);
  2235   2235         FindClose(hFindFile);
  2236   2236       }
................................................................................
  3311   3311     completionCursorReset(pCur);
  3312   3312     if( idxNum & 1 ){
  3313   3313       pCur->nPrefix = sqlite3_value_bytes(argv[iArg]);
  3314   3314       if( pCur->nPrefix>0 ){
  3315   3315         pCur->zPrefix = sqlite3_mprintf("%s", sqlite3_value_text(argv[iArg]));
  3316   3316         if( pCur->zPrefix==0 ) return SQLITE_NOMEM;
  3317   3317       }
  3318         -    iArg++;
         3318  +    iArg = 1;
  3319   3319     }
  3320   3320     if( idxNum & 2 ){
  3321   3321       pCur->nLine = sqlite3_value_bytes(argv[iArg]);
  3322   3322       if( pCur->nLine>0 ){
  3323   3323         pCur->zLine = sqlite3_mprintf("%s", sqlite3_value_text(argv[iArg]));
  3324   3324         if( pCur->zLine==0 ) return SQLITE_NOMEM;
  3325   3325       }
  3326         -    iArg++;
  3327   3326     }
  3328   3327     if( pCur->zLine!=0 && pCur->zPrefix==0 ){
  3329   3328       int i = pCur->nLine;
  3330   3329       while( i>0 && (isalnum(pCur->zLine[i-1]) || pCur->zLine[i-1]=='_') ){
  3331   3330         i--;
  3332   3331       }
  3333   3332       pCur->nPrefix = pCur->nLine - i;

Changes to src/sitemap.c.

    27     27   ** List some of the web pages offered by the Fossil web engine.  This
    28     28   ** page is intended as a supplement to the menu bar on the main screen.
    29     29   ** That is, this page is designed to hold links that are omitted from
    30     30   ** the main menu due to lack of space.
    31     31   */
    32     32   void sitemap_page(void){
    33     33     int srchFlags;
           34  +  int inSublist = 0;
           35  +  int i;
           36  +  int isPopup = 0;         /* This is an XMLHttpRequest() for /sitemap */
           37  +  const struct {
           38  +    const char *zTitle;
           39  +    const char *zProperty;
           40  +  } aExtra[] = {
           41  +    { "Documentation",  "sitemap-docidx" },
           42  +    { "Download",       "sitemap-download" },
           43  +    { "License",        "sitemap-license" },
           44  +    { "Contact",        "sitemap-contact" },
           45  +  };
           46  +
    34     47     login_check_credentials();
           48  +  if( P("popup")!=0 && cgi_csrf_safe(1) ){
           49  +    /* If this is a POST from the same origin with the popup=1 parameter,
           50  +    ** then disable anti-robot defenses */
           51  +    isPopup = 1;
           52  +    g.perm.Hyperlink = 1;
           53  +    g.javascriptHyperlink = 0;
           54  +  }
    35     55     srchFlags = search_restrict(SRCH_ALL);
    36         -  style_header("Site Map");
    37         -  style_adunit_config(ADUNIT_RIGHT_OK);
    38         -#if 0
    39         -  @ <p>
    40         -  @ The following links are just a few of the many web-pages available for
    41         -  @ this Fossil repository:
    42         -  @ </p>
    43         -  @
    44         -#endif
    45         -  @ <ul>
           56  +  if( !isPopup ){
           57  +    style_header("Site Map");
           58  +    style_adunit_config(ADUNIT_RIGHT_OK);
           59  +  }
           60  +  @ <ul id="sitemap" class="columns" style="column-width:20em">
    46     61     @ <li>%z(href("%R/home"))Home Page</a>
           62  +  for(i=0; i<sizeof(aExtra)/sizeof(aExtra[0]); i++){
           63  +    char *z = db_get(aExtra[i].zProperty,0);
           64  +    if( z==0 || z[0]==0 ) continue;
           65  +    if( !inSublist ){
           66  +      @ <ul>
           67  +      inSublist = 1;
           68  +    }
           69  +    if( z[0]=='/' ){
           70  +      @ <li>%z(href("%R%s",z))%s(aExtra[i].zTitle)</a></li>
           71  +    }else{
           72  +      @ <li>%z(href("%s",z))%s(aExtra[i].zTitle)</a></li>
           73  +    }
           74  +  }
    47     75     if( srchFlags & SRCH_DOC ){
    48         -    @   <ul>
    49         -    @   <li>%z(href("%R/docsrch"))Search Project Documentation</a></li>
    50         -    @   </ul>
           76  +    if( !inSublist ){
           77  +      @ <ul>
           78  +      inSublist = 1;
           79  +    }
           80  +    @ <li>%z(href("%R/docsrch"))Documentation Search</a></li>
           81  +  }
           82  +  if( inSublist ){
           83  +    @ </ul>
           84  +    inSublist = 0;    
    51     85     }
    52     86     @ </li>
    53     87     if( g.perm.Read ){
    54         -    @ <li>%z(href("%R/tree"))File Browser</a></li>
           88  +    @ <li>%z(href("%R/tree"))File Browser</a>
    55     89       @   <ul>
    56     90       @   <li>%z(href("%R/tree?type=tree&ci=trunk"))Tree-view,
    57     91       @        Trunk Check-in</a></li>
    58     92       @   <li>%z(href("%R/tree?type=flat"))Flat-view</a></li>
    59     93       @   <li>%z(href("%R/fileage?name=trunk"))File ages for Trunk</a></li>
           94  +    @   <li>%z(href("%R/uvlist"))Unversioned Files</a>
    60     95       @ </ul>
    61     96     }
    62     97     if( g.perm.Read ){
    63         -    @ <li>%z(href("%R/timeline?n=200"))Project Timeline</a></li>
           98  +    @ <li>%z(href("%R/timeline"))Project Timeline</a>
    64     99       @ <ul>
    65    100       @   <li>%z(href("%R/reports"))Activity Reports</a></li>
    66    101       @   <li>%z(href("%R/timeline?n=all&namechng"))File name changes</a></li>
    67    102       @   <li>%z(href("%R/timeline?n=all&forks"))Forks</a></li>
    68    103       @   <li>%z(href("%R/timeline?a=1970-01-01&y=ci&n=10"))First 10
    69    104       @       check-ins</a></li>
    70    105       @ </ul>
          106  +    @ </li>
    71    107     }
    72    108     if( g.perm.Read ){
    73         -    @ <li>%z(href("%R/brlist"))Branches</a></li>
          109  +    @ <li>%z(href("%R/brlist"))Branches</a>
    74    110       @ <ul>
          111  +    @   <li>%z(href("%R/taglist"))Tags</a></li>
    75    112       @   <li>%z(href("%R/leaves"))Leaf Check-ins</a></li>
    76         -    @   <li>%z(href("%R/taglist"))List of Tags</a></li>
    77    113       @ </ul>
    78    114       @ </li>
    79    115     }
    80         -  if( g.perm.RdWiki ){
    81         -    @ <li>%z(href("%R/wikihelp"))Wiki</a>
    82         -    @   <ul>
    83         -    if( srchFlags & SRCH_WIKI ){
    84         -      @     <li>%z(href("%R/wikisrch"))Wiki Search</a></li>
    85         -    }
    86         -    @     <li>%z(href("%R/wcontent"))List of Wiki Pages</a></li>
    87         -    @     <li>%z(href("%R/timeline?y=w"))Recent activity</a></li>
    88         -    @     <li>%z(href("%R/wiki_rules"))Wiki Formatting Rules</a></li>
    89         -    @     <li>%z(href("%R/md_rules"))Markdown Formatting Rules</a></li>
    90         -    @     <li>%z(href("%R/wiki?name=Sandbox"))Sandbox</a></li>
    91         -    @     <li>%z(href("%R/attachlist"))List of Attachments</a></li>
    92         -    @   </ul>
    93         -    @ </li>
          116  +  if( srchFlags ){
          117  +    @ <li>%z(href("%R/search"))Search</a></li>
    94    118     }
    95    119     if( g.perm.RdForum ){
    96         -    @ <li>%z(href("%R/forum"))Forum</a></li>
          120  +    @ <li>%z(href("%R/forum"))Forum</a>
          121  +    @ <ul>
          122  +    @   <li>%z(href("%R/timeline?y=f"))Recent activity</a></li>
          123  +    @ </ul>
          124  +    @ </li>
    97    125     }
    98    126     if( g.perm.RdTkt ){
    99    127       @ <li>%z(href("%R/reportlist"))Tickets</a>
   100    128       @   <ul>
   101    129       if( srchFlags & SRCH_TKT ){
   102    130         @   <li>%z(href("%R/tktsrch"))Ticket Search</a></li>
   103    131       }
   104    132       @   <li>%z(href("%R/timeline?y=t"))Recent activity</a></li>
   105    133       @   <li>%z(href("%R/attachlist"))List of Attachments</a></li>
   106    134       @   </ul>
   107    135       @ </li>
   108    136     }
   109         -  if( g.perm.Read ){
   110         -    @ <li>%z(href("%R/uvlist"))Unversioned Files</a>
          137  +  if( g.perm.RdWiki ){
          138  +    @ <li>%z(href("%R/wikihelp"))Wiki</a>
          139  +    @   <ul>
          140  +    if( srchFlags & SRCH_WIKI ){
          141  +      @     <li>%z(href("%R/wikisrch"))Wiki Search</a></li>
          142  +    }
          143  +    @     <li>%z(href("%R/wcontent"))List of Wiki Pages</a></li>
          144  +    @     <li>%z(href("%R/timeline?y=w"))Recent activity</a></li>
          145  +    @     <li>%z(href("%R/wiki?name=Sandbox"))Sandbox</a></li>
          146  +    @     <li>%z(href("%R/attachlist"))List of Attachments</a></li>
          147  +    @   </ul>
          148  +    @ </li>
          149  +  }
          150  +
          151  +  if( !g.zLogin ){
          152  +    @ <li>%z(href("%R/login"))Login</a>
          153  +    if( login_self_register_available(0) ){
          154  +       @ <ul>
          155  +       @ <li>%z(href("%R/register"))Create a new account</a></li>
          156  +       inSublist = 1;
          157  +    }
          158  +  }else {
          159  +    @ <li>%z(href("%R/logout"))Logout</a>
          160  +    if( g.perm.Password ){
          161  +      @ <ul>
          162  +      @ <li>%z(href("%R/logout"))Change Password</a></li>
          163  +      inSublist = 1;
          164  +    }
          165  +  }
          166  +  if( alert_enabled() && g.perm.EmailAlert ){
          167  +    if( !inSublist ){
          168  +      inSublist = 1;
          169  +      @ <ul>
          170  +    }
          171  +    if( login_is_individual() ){
          172  +      @ <li>%z(href("%R/alerts"))Email Alerts</a></li>
          173  +    }else{
          174  +      @ <li>%z(href("%R/subscribe"))Subscribe to Email Alerts</a></li>
          175  +    }
   111    176     }
   112         -  if( srchFlags ){
   113         -    @ <li>%z(href("%R/search"))Full-Text Search</a></li>
          177  +  if( inSublist ){
          178  +    @ </ul>
          179  +    inSublist = 0;
   114    180     }
   115         -  @ <li>%z(href("%R/login"))Login/Logout/Change Password</a></li>
          181  +  @ </li>
          182  +
   116    183     if( g.perm.Read ){
   117    184       @ <li>%z(href("%R/stat"))Repository Status</a>
   118    185       @   <ul>
   119    186       @   <li>%z(href("%R/hash-collisions"))Collisions on hash prefixes</a></li>
   120    187       if( g.perm.Admin ){
   121    188         @   <li>%z(href("%R/urllist"))List of URLs used to access
   122    189         @       this repository</a></li>
   123    190       }
   124    191       @   <li>%z(href("%R/bloblist"))List of Artifacts</a></li>
   125    192       @   <li>%z(href("%R/timewarps"))List of "Timewarp" Check-ins</a></li>
   126    193       @   </ul>
   127    194       @ </li>
   128    195     }
   129         -  @ <li>On-line Documentation
          196  +  @ <li>Help
   130    197     @   <ul>
          198  +  @   <li>%z(href("%R/wiki_rules"))Wiki Formatting Rules</a></li>
          199  +  @   <li>%z(href("%R/md_rules"))Markdown Formatting Rules</a></li>
   131    200     @   <li>%z(href("%R/help"))List of All Commands and Web Pages</a></li>
   132    201     @   <li>%z(href("%R/test-all-help"))All "help" text on a single page</a></li>
   133    202     @   <li>%z(href("%R/mimetype_list"))Filename suffix to mimetype map</a></li>
   134    203     @   </ul></li>
   135    204     if( g.perm.Admin ){
   136    205       @ <li>%z(href("%R/setup"))Administration Pages</a>
   137    206       @   <ul>
................................................................................
   148    217     if( g.perm.Read ){
   149    218       @   <li>%z(href("%R/test-rename-list"))List of file renames</a></li>
   150    219     }
   151    220     @   <li>%z(href("%R/hash-color-test"))Page to experiment with the automatic
   152    221     @       colors assigned to branch names</a>
   153    222     @   <li>%z(href("%R/test-captcha"))Random ASCII-art Captcha image</a></li>
   154    223     @   </ul></li>
   155         -  @ </ul></li>
   156         -  style_footer();
          224  +  @ </ul>
          225  +  if( !isPopup ){
          226  +    style_footer();
          227  +  }
   157    228   }

Changes to src/skins.c.

    53     53     { "Black & White, Menu on Left",       "black_and_white",   0 },
    54     54     { "Plain Gray, No Logo",               "plain_gray",        0 },
    55     55     { "Khaki, No Logo",                    "khaki",             0 },
    56     56     { "Ardoise",                           "ardoise",           0 },
    57     57   };
    58     58   
    59     59   /*
    60         -** A skin consists of four "files" named here:
           60  +** A skin consists of five "files" named here:
    61     61   */
    62         -static const char *azSkinFile[] = { "css", "header", "footer", "details" };
           62  +static const char *azSkinFile[] = { 
           63  +  "css", "header", "footer", "details", "js"
           64  +};
    63     65   
    64     66   /*
    65     67   ** Alternative skins can be specified in the CGI script or by options
    66     68   ** on the "http", "ui", and "server" commands.  The alternative skin
    67     69   ** name must be one of the aBuiltinSkin[].zLabel names.  If there is
    68     70   ** a match, that alternative is used.
    69     71   **
................................................................................
   147    149     iDraftSkin = i;
   148    150   }
   149    151   
   150    152   /*
   151    153   ** The following routines return the various components of the skin
   152    154   ** that should be used for the current run.
   153    155   **
   154         -** zWhat is one of:  "css", "header", "footer", "details".
          156  +** zWhat is one of:  "css", "header", "footer", "details", "js"
   155    157   */
   156    158   const char *skin_get(const char *zWhat){
   157    159     const char *zOut;
   158    160     char *z;
   159    161     if( iDraftSkin ){
   160    162       z = mprintf("draft%d-%s", iDraftSkin, zWhat);
   161    163       zOut = db_get(z, 0);
................................................................................
   478    480         @ be undone.  Please confirm that this is what you want to do:</p>
   479    481         @ <input type="hidden" name="sn" value="%h(P("sn"))" />
   480    482         @ <input type="submit" name="del2" value="Confirm - Delete The Skin" />
   481    483         @ <input type="submit" name="cancel" value="Cancel - Do Not Delete" />
   482    484         login_insert_csrf_secret();
   483    485         @ </div></form>
   484    486         style_footer();
          487  +      db_end_transaction(1);
   485    488         return;
   486    489       }
   487    490       if( P("del2")!=0 && (zName = skinVarName(P("sn"), 1))!=0 ){
   488    491         db_multi_exec("DELETE FROM config WHERE name=%Q", zName);
   489    492       }
   490    493       if( P("draftdel")!=0 ){
   491    494         const char *zDraft = P("name");
   492    495         if( sqlite3_strglob("draft[1-9]",zDraft)==0 ){
   493    496           db_multi_exec("DELETE FROM config WHERE name GLOB '%q-*'", zDraft);
   494    497         }
   495    498       }
   496         -    if( skinRename() ) return;
   497         -    if( skinSave(zCurrent) ) return;
          499  +    if( skinRename() || skinSave(zCurrent) ){
          500  +      db_end_transaction(0);
          501  +      return;
          502  +    }
   498    503     
   499    504       /* The user pressed one of the "Install" buttons. */
   500    505       if( P("load") && (z = P("sn"))!=0 && z[0] ){
   501    506         int seen = 0;
   502    507     
   503    508         /* Check to see if the current skin is already saved.  If it is, there
   504    509         ** is no need to create a backup */
................................................................................
   686    691   
   687    692   /*
   688    693   ** WEBPAGE: setup_skinedit
   689    694   **
   690    695   ** Edit aspects of a skin determined by the w= query parameter.
   691    696   ** Requires Setup privileges.
   692    697   **
   693         -**    w=NUM     -- 0=CSS, 1=footer, 2=header, 3=details
          698  +**    w=NUM     -- 0=CSS, 1=footer, 2=header, 3=details, 4=js
   694    699   **    sk=NUM    -- the draft skin number
   695    700   */
   696    701   void setup_skinedit(void){
   697    702     static const struct sSkinAddr {
   698    703       const char *zFile;
   699    704       const char *zTitle;
   700    705       const char *zSubmenu;
   701    706     } aSkinAttr[] = {
   702    707       /* 0 */ { "css",     "CSS",             "CSS",     },
   703    708       /* 1 */ { "footer",  "Page Footer",     "Footer",  },
   704    709       /* 2 */ { "header",  "Page Header",     "Header",  },
   705    710       /* 3 */ { "details", "Display Details", "Details", },
          711  +    /* 4 */ { "js",      "JavaScript",      "Script",  },
   706    712     };
   707    713     const char *zBasis;         /* The baseline file */
          714  +  const char *zOrig;          /* Original content prior to editing */
   708    715     const char *zContent;       /* Content after editing */
          716  +  const char *zDflt;          /* Default content */
   709    717     char *zDraft;               /* Which draft:  "draft%d" */
   710    718     char *zKey;                 /* CONFIG table key name: "draft%d-%s" */
   711    719     char *zTitle;               /* Title of this page */
   712    720     const char *zFile;          /* One of "css", "footer", "header", "details" */
   713    721     int iSkin;                  /* draft number.  1..9 */
   714    722     int ii;                     /* Index in aSkinAttr[] of this file */
   715    723     int j;                      /* Loop counter */
          724  +  int isRevert = 0;           /* True if Revert-to-Baseline was pressed */
   716    725   
   717    726     login_check_credentials();
   718    727   
   719    728     /* Figure out which skin we are editing */
   720    729     iSkin = atoi(PD("sk","1"));
   721    730     if( iSkin<1 || iSkin>9 ) iSkin = 1;
   722    731   
................................................................................
   743    752     ii = atoi(PD("w","0"));
   744    753     if( ii<0 || ii>count(aSkinAttr) ) ii = 0;
   745    754     zFile = aSkinAttr[ii].zFile;
   746    755     zDraft = mprintf("draft%d", iSkin);
   747    756     zKey = mprintf("draft%d-%s", iSkin, zFile);
   748    757     zTitle = mprintf("%s for Draft%d", aSkinAttr[ii].zTitle, iSkin);
   749    758     zBasis = PD("basis","current");
          759  +  zDflt = skin_file_content(zBasis, zFile);
          760  +  zOrig = db_get(zKey, zDflt);
          761  +  zContent = PD(zFile,zOrig);
          762  +  if( P("revert")!=0 && cgi_csrf_safe(0) ){
          763  +    zContent = zDflt;
          764  +    isRevert = 1;
          765  +  }
   750    766   
   751    767     db_begin_transaction();
   752    768     style_header("%s", zTitle);
   753    769     for(j=0; j<count(aSkinAttr); j++){
   754         -    if( j==ii ) continue;
   755    770       style_submenu_element(aSkinAttr[j].zSubmenu,
   756    771             "%R/setup_skinedit?w=%d&basis=%h&sk=%d",j,zBasis,iSkin);
   757    772     }
   758    773     @ <form action="%s(g.zTop)/setup_skinedit" method="post"><div>
   759    774     login_insert_csrf_secret();
   760    775     @ <input type='hidden' name='w' value='%d(ii)'>
   761    776     @ <input type='hidden' name='sk' value='%d(iSkin)'>
   762    777     @ <h2>Edit %s(zTitle):</h2>
   763         -  zContent = textarea_attribute(
   764         -        "",                      /* Text label */
   765         -        10, 80,                  /* Height and width of the edit area */
   766         -        zKey,                    /* Name of CONFIG table entry */
   767         -        zFile,                              /* CGI query parameter name */
   768         -        skin_file_content(zBasis, zFile),   /* Default value of the text */
   769         -        0                                   /* Disabled flag */
   770         -  );
          778  +  if( P("submit") && cgi_csrf_safe(0) && strcmp(zOrig,zContent)!=0 ){
          779  +    db_set(zKey, zContent, 0);
          780  +  }
          781  +  @ <textarea name="%s(zFile)" rows="10" cols="80">\
          782  +  @ %h(zContent)</textarea>
   771    783     @ <br />
   772    784     @ <input type="submit" name="submit" value="Apply Changes" />
          785  +  if( isRevert ){
          786  +    @ &larr; Press to complete reversion to "%s(zBasis)"
          787  +  }else if( fossil_strcmp(zContent,zDflt)!=0 ){
          788  +    @ <input type="submit" name="revert" value='Revert To "%s(zBasis)"' />
          789  +  }
   773    790     @ <hr />
   774    791     @ Baseline: \
   775    792     skin_emit_skin_selector("basis", zBasis, zDraft);
   776    793     @ <input type="submit" name="diff" value="Unified Diff" />
   777    794     @ <input type="submit" name="sbsdiff" value="Side-by-Side Diff" />
   778    795     if( P("diff")!=0 || P("sbsdiff")!=0 ){
   779    796       u64 diffFlags = construct_diff_flags(1) | DIFF_STRIP_EOLCR;
................................................................................
  1000   1017       @ <li><a href='%R/setup_skinedit?w=0&sk=%d(iSkin)' target='_blank'>CSS</a>
  1001   1018       @ <li><a href='%R/setup_skinedit?w=2&sk=%d(iSkin)' target='_blank'>\
  1002   1019       @ Header</a>
  1003   1020       @ <li><a href='%R/setup_skinedit?w=1&sk=%d(iSkin)' target='_blank'>\
  1004   1021       @ Footer</a>
  1005   1022       @ <li><a href='%R/setup_skinedit?w=3&sk=%d(iSkin)' target='_blank'>\
  1006   1023       @ Details</a>
         1024  +    @ <li><a href='%R/setup_skinedit?w=4&sk=%d(iSkin)' target='_blank'>\
         1025  +    @ Javascript</a> (optional)
  1007   1026       @ </ul>
  1008   1027     }
  1009   1028     @
  1010   1029     @ <a name='step5'></a>
  1011   1030     @ <h1>Step 5: Verify The Draft Skin</h1>
  1012   1031     @
  1013   1032     @ <p>To test this draft skin, insert text "/draft%d(iSkin)/" just before the

Changes to src/sqlite3.c.

more than 10,000 changes

Changes to src/sqlite3.h.

   121    121   **
   122    122   ** See also: [sqlite3_libversion()],
   123    123   ** [sqlite3_libversion_number()], [sqlite3_sourceid()],
   124    124   ** [sqlite_version()] and [sqlite_source_id()].
   125    125   */
   126    126   #define SQLITE_VERSION        "3.25.0"
   127    127   #define SQLITE_VERSION_NUMBER 3025000
   128         -#define SQLITE_SOURCE_ID      "2018-08-16 16:24:24 456842924bb33c0af8af29402f06e5f25b6791f698a0d12a080258b20b0cfb61"
          128  +#define SQLITE_SOURCE_ID      "2018-09-10 19:34:06 74c381b573817d0212153278b5ee5d2238a27a727dcf7ee769365c47bb9fc40d"
   129    129   
   130    130   /*
   131    131   ** CAPI3REF: Run-Time Library Version Numbers
   132    132   ** KEYWORDS: sqlite3_version sqlite3_sourceid
   133    133   **
   134    134   ** These interfaces provide the same information as the [SQLITE_VERSION],
   135    135   ** [SQLITE_VERSION_NUMBER], and [SQLITE_SOURCE_ID] C preprocessor macros
................................................................................
   468    468   ** on a per database connection basis using the
   469    469   ** [sqlite3_extended_result_codes()] API.  Or, the extended code for
   470    470   ** the most recent error can be obtained using
   471    471   ** [sqlite3_extended_errcode()].
   472    472   */
   473    473   #define SQLITE_ERROR_MISSING_COLLSEQ   (SQLITE_ERROR | (1<<8))
   474    474   #define SQLITE_ERROR_RETRY             (SQLITE_ERROR | (2<<8))
          475  +#define SQLITE_ERROR_SNAPSHOT          (SQLITE_ERROR | (3<<8))
   475    476   #define SQLITE_IOERR_READ              (SQLITE_IOERR | (1<<8))
   476    477   #define SQLITE_IOERR_SHORT_READ        (SQLITE_IOERR | (2<<8))
   477    478   #define SQLITE_IOERR_WRITE             (SQLITE_IOERR | (3<<8))
   478    479   #define SQLITE_IOERR_FSYNC             (SQLITE_IOERR | (4<<8))
   479    480   #define SQLITE_IOERR_DIR_FSYNC         (SQLITE_IOERR | (5<<8))
   480    481   #define SQLITE_IOERR_TRUNCATE          (SQLITE_IOERR | (6<<8))
   481    482   #define SQLITE_IOERR_FSTAT             (SQLITE_IOERR | (7<<8))
................................................................................
  6436   6437   #define SQLITE_INDEX_CONSTRAINT_GLOB      66
  6437   6438   #define SQLITE_INDEX_CONSTRAINT_REGEXP    67
  6438   6439   #define SQLITE_INDEX_CONSTRAINT_NE        68
  6439   6440   #define SQLITE_INDEX_CONSTRAINT_ISNOT     69
  6440   6441   #define SQLITE_INDEX_CONSTRAINT_ISNOTNULL 70
  6441   6442   #define SQLITE_INDEX_CONSTRAINT_ISNULL    71
  6442   6443   #define SQLITE_INDEX_CONSTRAINT_IS        72
         6444  +#define SQLITE_INDEX_CONSTRAINT_FUNCTION 150
  6443   6445   
  6444   6446   /*
  6445   6447   ** CAPI3REF: Register A Virtual Table Implementation
  6446   6448   ** METHOD: sqlite3
  6447   6449   **
  6448   6450   ** ^These routines are used to register a new [virtual table module] name.
  6449   6451   ** ^Module names must be registered before
................................................................................
  9048   9050   ** must have no active statements (SELECT statements that have been passed
  9049   9051   ** to sqlite3_step() but not sqlite3_reset() or sqlite3_finalize()). 
  9050   9052   ** SQLITE_ERROR is returned if either of these conditions is violated, or
  9051   9053   ** if schema S does not exist, or if the snapshot object is invalid.
  9052   9054   **
  9053   9055   ** ^A call to sqlite3_snapshot_open() will fail to open if the specified
  9054   9056   ** snapshot has been overwritten by a [checkpoint]. In this case 
  9055         -** SQLITE_BUSY_SNAPSHOT is returned.
         9057  +** SQLITE_ERROR_SNAPSHOT is returned.
  9056   9058   **
  9057   9059   ** If there is already a read transaction open when this function is 
  9058   9060   ** invoked, then the same read transaction remains open (on the same
  9059         -** database snapshot) if SQLITE_ERROR, SQLITE_BUSY or SQLITE_BUSY_SNAPSHOT
         9061  +** database snapshot) if SQLITE_ERROR, SQLITE_BUSY or SQLITE_ERROR_SNAPSHOT
  9060   9062   ** is returned. If another error code - for example SQLITE_PROTOCOL or an
  9061   9063   ** SQLITE_IOERR error code - is returned, then the final state of the
  9062   9064   ** read transaction is undefined. If SQLITE_OK is returned, then the 
  9063   9065   ** read transaction is now open on database snapshot P.
  9064   9066   **
  9065   9067   ** ^(A call to [sqlite3_snapshot_open(D,S,P)] will fail if the
  9066   9068   ** database connection D does not know that the database file for

Changes to src/stat.c.

   268    268       }
   269    269       @ </td></tr>
   270    270     }
   271    271     if( g.perm.Admin ){
   272    272       @ <tr><th>Backoffice:</th>
   273    273       @ <td>Last run: %z(backoffice_last_run())</td></tr>
   274    274     }
   275         -  if( g.perm.Admin && email_enabled() ){
          275  +  if( g.perm.Admin && alert_enabled() ){
   276    276       stats_for_email();
   277    277     }
   278    278   
   279    279     @ </table>
   280    280     style_footer();
   281    281   }
   282    282   

Changes to src/style.c.

    84     84   /*
    85     85   ** Flags for various javascript files needed prior to </body>
    86     86   */
    87     87   static int needHrefJs = 0;   /* href.js */
    88     88   static int needSortJs = 0;   /* sorttable.js */
    89     89   static int needGraphJs = 0;  /* graph.js */
    90     90   
           91  +/*
           92  +** Extra JS added to the end of the file.
           93  +*/
           94  +static Blob blobOnLoad = BLOB_INITIALIZER;
           95  +
    91     96   /*
    92     97   ** Generate and return a anchor tag like this:
    93     98   **
    94     99   **        <a href="URL">
    95    100   **  or    <a id="ID">
    96    101   **
    97    102   ** The form of the anchor tag is determined by the g.javascriptHyperlink
................................................................................
   358    363   static void image_url_var(const char *zImageName){
   359    364     char *zVarPrefix = mprintf("%s_image", zImageName);
   360    365     char *zConfigName = mprintf("%s-image", zImageName);
   361    366     url_var(zVarPrefix, zConfigName, zImageName);
   362    367     free(zVarPrefix);
   363    368     free(zConfigName);
   364    369   }
          370  +
          371  +/*
          372  +** Return a random nonce that is stored in static space.  For a particular
          373  +** run, the same nonce is always returned.
          374  +*/
          375  +char *style_nonce(void){
          376  +  static char zNonce[52];
          377  +  if( zNonce[0]==0 ){
          378  +    unsigned char zSeed[24];
          379  +    sqlite3_randomness(24, zSeed);
          380  +    encode16(zSeed,(unsigned char*)zNonce,24);
          381  +  }
          382  +  return zNonce;
          383  +}
   365    384   
   366    385   /*
   367    386   ** Default HTML page header text through <body>.  If the repository-specific
   368    387   ** header template lacks a <body> tag, then all of the following is
   369    388   ** prepended.
   370    389   */
   371    390   static char zDfltHeader[] = 
   372    391   @ <html>
   373    392   @ <head>
   374    393   @ <base href="$baseurl/$current_page" />
   375    394   @ <meta http-equiv="Content-Security-Policy" \
   376         -@  content="default-src 'self' data: 'unsafe-inline'" />
          395  +@  content="default-src 'self' data: ; \
          396  +@  script-src 'self' 'nonce-$<nonce>' ;\
          397  +@  style-src 'self' 'unsafe-inline'" />
   377    398   @ <meta name="viewport" content="width=device-width, initial-scale=1.0">
   378    399   @ <title>$<project_name>: $<title></title>
   379    400   @ <link rel="alternate" type="application/rss+xml" title="RSS Feed" \
   380    401   @  href="$home/timeline.rss" />
   381    402   @ <link rel="stylesheet" href="$stylesheet_url" type="text/css" \
   382    403   @  media="screen" />
   383    404   @ </head>
   384    405   @ <body>
   385    406   ;
          407  +
          408  +/*
          409  +** Initialize all the default TH1 variables
          410  +*/
          411  +static void style_init_th1_vars(const char *zTitle){
          412  +  Th_Store("nonce", style_nonce());
          413  +  Th_Store("project_name", db_get("project-name","Unnamed Fossil Project"));
          414  +  Th_Store("project_description", db_get("project-description",""));
          415  +  if( zTitle ) Th_Store("title", zTitle);
          416  +  Th_Store("baseurl", g.zBaseURL);
          417  +  Th_Store("secureurl", login_wants_https_redirect()? g.zHttpsURL: g.zBaseURL);
          418  +  Th_Store("home", g.zTop);
          419  +  Th_Store("index_page", db_get("index-page","/home"));
          420  +  if( local_zCurrentPage==0 ) style_set_current_page("%T", g.zPath);
          421  +  Th_Store("current_page", local_zCurrentPage);
          422  +  Th_Store("csrf_token", g.zCsrfToken);
          423  +  Th_Store("release_version", RELEASE_VERSION);
          424  +  Th_Store("manifest_version", MANIFEST_VERSION);
          425  +  Th_Store("manifest_date", MANIFEST_DATE);
          426  +  Th_Store("compiler_name", COMPILER_NAME);
          427  +  url_var("stylesheet", "css", "style.css");
          428  +  image_url_var("logo");
          429  +  image_url_var("background");
          430  +  if( !login_is_nobody() ){
          431  +    Th_Store("login", g.zLogin);
          432  +  }
          433  +}
   386    434   
   387    435   /*
   388    436   ** Draw the header.
   389    437   */
   390    438   void style_header(const char *zTitleFormat, ...){
   391    439     va_list ap;
   392    440     char *zTitle;
................................................................................
   400    448     cgi_destination(CGI_HEADER);
   401    449   
   402    450     @ <!DOCTYPE html>
   403    451   
   404    452     if( g.thTrace ) Th_Trace("BEGIN_HEADER<br />\n", -1);
   405    453   
   406    454     /* Generate the header up through the main menu */
   407         -  Th_Store("project_name", db_get("project-name","Unnamed Fossil Project"));
   408         -  Th_Store("project_description", db_get("project-description",""));
   409         -  Th_Store("title", zTitle);
   410         -  Th_Store("baseurl", g.zBaseURL);
   411         -  Th_Store("secureurl", login_wants_https_redirect()? g.zHttpsURL: g.zBaseURL);
   412         -  Th_Store("home", g.zTop);
   413         -  Th_Store("index_page", db_get("index-page","/home"));
   414         -  if( local_zCurrentPage==0 ) style_set_current_page("%T", g.zPath);
   415         -  Th_Store("current_page", local_zCurrentPage);
   416         -  Th_Store("csrf_token", g.zCsrfToken);
   417         -  Th_Store("release_version", RELEASE_VERSION);
   418         -  Th_Store("manifest_version", MANIFEST_VERSION);
   419         -  Th_Store("manifest_date", MANIFEST_DATE);
   420         -  Th_Store("compiler_name", COMPILER_NAME);
   421         -  url_var("stylesheet", "css", "style.css");
   422         -  image_url_var("logo");
   423         -  image_url_var("background");
   424         -  if( !login_is_nobody() ){
   425         -    Th_Store("login", g.zLogin);
   426         -  }
          455  +  style_init_th1_vars(zTitle);
   427    456     if( sqlite3_strlike("%<body%", zHeader, 0)!=0 ){
   428    457       Th_Render(zDfltHeader);
   429    458     }
   430    459     if( g.thTrace ) Th_Trace("BEGIN_HEADER_SCRIPT<br />\n", -1);
   431    460     Th_Render(zHeader);
   432    461     if( g.thTrace ) Th_Trace("END_HEADER<br />\n", -1);
   433    462     Th_Unstore("title");   /* Avoid collisions with ticket field names */
................................................................................
   521    550   */
   522    551   void style_load_js(const char *zName){
   523    552     int i;
   524    553     for(i=0; i<nJsToLoad; i++){
   525    554       if( fossil_strcmp(zName, azJsToLoad[i])==0 ) return;
   526    555     }
   527    556     if( nJsToLoad>=sizeof(azJsToLoad)/sizeof(azJsToLoad[0]) ){
   528         -    fossil_panic("too man JS files");
          557  +    fossil_panic("too many JS files");
   529    558     }
   530    559     azJsToLoad[nJsToLoad++] = zName;
   531    560   }
   532    561   
   533    562   /*
   534    563   ** Generate code to load all required javascript files.
   535    564   */
................................................................................
   539    568       int nDelay = db_get_int("auto-hyperlink-delay",0);
   540    569       int bMouseover;
   541    570       /* Load up the page data */
   542    571       bMouseover = (!g.isHuman || db_get_boolean("auto-hyperlink-ishuman",0))
   543    572                    && db_get_boolean("auto-hyperlink-mouseover",0);
   544    573       @ <script id='href-data' type='application/json'>\
   545    574       @ {"delay":%d(nDelay),"mouseover":%d(bMouseover)}</script>
   546         -    style_load_one_js_file("href.js");
          575  +  }
          576  +  @ <script nonce="%h(style_nonce())">
          577  +  if( needHrefJs ){
          578  +    cgi_append_content(builtin_text("href.js"),-1);
   547    579     }
   548    580     if( needSortJs ){
   549         -    style_load_one_js_file("sorttable.js");
          581  +    cgi_append_content(builtin_text("sorttable.js"),-1);
   550    582     }
   551    583     if( needGraphJs ){
   552         -    style_load_one_js_file("graph.js");
          584  +    cgi_append_content(builtin_text("graph.js"),-1);
   553    585     }
   554    586     for(i=0; i<nJsToLoad; i++){
   555         -    style_load_one_js_file(azJsToLoad[i]);
          587  +    cgi_append_content(builtin_text(azJsToLoad[i]),-1);
   556    588     }
          589  +  if( blob_size(&blobOnLoad)>0 ){
          590  +    @ window.onload = function(){
          591  +    cgi_append_content(blob_buffer(&blobOnLoad), blob_size(&blobOnLoad));
          592  +    cgi_append_content("\n}\n", -1);
          593  +  }
          594  +  @ </script>
          595  +}
          596  +
          597  +/*
          598  +** Extra JS to run after all content is loaded.
          599  +*/
          600  +void style_js_onload(const char *zFormat, ...){
          601  +  va_list ap;
          602  +  va_start(ap, zFormat);
          603  +  blob_vappendf(&blobOnLoad, zFormat, ap);
          604  +  va_end(ap);
   557    605   }
   558    606   
   559    607   /*
   560    608   ** Draw the footer at the bottom of the page.
   561    609   */
   562    610   void style_footer(void){
   563    611     const char *zFooter;
................................................................................
   805    853     if( g.argc!=4 ) usage("FILENAME SELECTOR");
   806    854     blob_read_from_file(&css, g.argv[2], ExtFILE);
   807    855     zSelector = g.argv[3];
   808    856     found = containsSelector(blob_str(&css), zSelector);
   809    857     fossil_print("%s %s\n", zSelector, found ? "found" : "not found");
   810    858     blob_reset(&css);
   811    859   }
          860  +
          861  +/*
          862  +** WEBPAGE: script.js
          863  +**
          864  +** Return the "Javascript" content for the current skin (if there is any)
          865  +*/
          866  +void page_script_js(void){
          867  +  const char *zScript = skin_get("js");
          868  +  if( P("test") ){
          869  +    /* Render the script as plain-text for testing purposes, if the "test"
          870  +    ** query parameter is present */
          871  +    cgi_set_content_type("text/plain");
          872  +  }else{
          873  +    /* Default behavior is to return javascript */
          874  +    cgi_set_content_type("application/javascript");
          875  +  }
          876  +  style_init_th1_vars(0);
          877  +  Th_Render(zScript?zScript:"");
          878  +}
   812    879   
   813    880   
   814    881   /*
   815    882   ** WEBPAGE: style.css
   816    883   **
   817    884   ** Return the style sheet.
   818    885   */

Changes to src/th_main.c.

  1298   1298       Th_SetResult(interp, 0, 0);
  1299   1299       return TH_OK;
  1300   1300     }else{
  1301   1301       Th_SetResult(interp, "repository unavailable", -1);
  1302   1302       return TH_ERROR;
  1303   1303     }
  1304   1304   }
         1305  +
         1306  +/*
         1307  +** TH1 command: styleScript
         1308  +**
         1309  +** Render the configured javascript for the selected skin
         1310  +*/
         1311  +static int styleScriptCmd(
         1312  +  Th_Interp *interp,
         1313  +  void *p,
         1314  +  int argc,
         1315  +  const char **argv,
         1316  +  int *argl
         1317  +){
         1318  +  if( argc!=1 ){
         1319  +    return Th_WrongNumArgs(interp, "styleScript");
         1320  +  }
         1321  +  if( Th_IsRepositoryOpen() ){
         1322  +    const char *zScript = skin_get("js");
         1323  +    if( zScript==0 ) zScript = "";
         1324  +    Th_Render(zScript);
         1325  +    Th_SetResult(interp, 0, 0);
         1326  +    return TH_OK;
         1327  +  }else{
         1328  +    Th_SetResult(interp, "repository unavailable", -1);
         1329  +    return TH_ERROR;
         1330  +  }
         1331  +}
         1332  +
  1305   1333   
  1306   1334   /*
  1307   1335   ** TH1 command: artifact ID ?FILENAME?
  1308   1336   **
  1309   1337   ** Attempts to locate the specified artifact and return its contents.  An
  1310   1338   ** error is generated if the repository is not open or the artifact cannot
  1311   1339   ** be found.
................................................................................
  1982   2010       {"regexp",        regexpCmd,            0},
  1983   2011       {"reinitialize",  reinitializeCmd,      0},
  1984   2012       {"render",        renderCmd,            0},
  1985   2013       {"repository",    repositoryCmd,        0},
  1986   2014       {"searchable",    searchableCmd,        0},
  1987   2015       {"setParameter",  setParameterCmd,      0},
  1988   2016       {"setting",       settingCmd,           0},
  1989         -    {"styleHeader",   styleHeaderCmd,       0},
  1990   2017       {"styleFooter",   styleFooterCmd,       0},
         2018  +    {"styleHeader",   styleHeaderCmd,       0},
         2019  +    {"styleScript",   styleScriptCmd,       0},
  1991   2020       {"tclReady",      tclReadyCmd,          0},
  1992   2021       {"trace",         traceCmd,             0},
  1993   2022       {"stime",         stimeCmd,             0},
  1994   2023       {"unversioned",   unversionedCmd,       0},
  1995   2024       {"utime",         utimeCmd,             0},
  1996   2025       {"verifyCsrf",    verifyCsrfCmd,        0},
  1997   2026       {"wiki",          wikiCmd,              (void*)&aFlags[0]},

Changes to test/commit-warning.test.

   159    159   # TODO: Change to a collection of test-case crafted files
   160    160   #       rather than depend on this list of files that will
   161    161   #       be fragile as development progresses.
   162    162   #
   163    163   # Unless the real goal of this test is to document a collection
   164    164   # of source files that MUST NEVER BE TEXT.
   165    165   #
   166         -run_in_checkout {
          166  +test_block_in_checkout pre-commit-warnings-fossil-1 {
   167    167     fossil test-commit-warning --no-settings
   168         -}
   169         -
   170         -test pre-commit-warnings-fossil-1 {[normalize_result] eq \
   171         -    [subst -nocommands -novariables [string trim {
   172         -1\tart/branching.odp\tbinary data
   173         -1\tart/concept1.dia\tbinary data
   174         -1\tart/concept2.dia\tbinary data
   175         -1\tcompat/zlib/contrib/blast/test.pk\tbinary data
   176         -1\tcompat/zlib/contrib/dotzlib/DotZLib.build\tCR/LF line endings
   177         -1\tcompat/zlib/contrib/dotzlib/DotZLib.chm\tbinary data
   178         -1\tcompat/zlib/contrib/dotzlib/DotZLib.sln\tCR/LF line endings
   179         -1\tcompat/zlib/contrib/dotzlib/DotZLib/AssemblyInfo.cs\tCR/LF line endings
   180         -1\tcompat/zlib/contrib/dotzlib/DotZLib/ChecksumImpl.cs\tinvalid UTF-8
   181         -1\tcompat/zlib/contrib/dotzlib/DotZLib/CircularBuffer.cs\tinvalid UTF-8
   182         -1\tcompat/zlib/contrib/dotzlib/DotZLib/CodecBase.cs\tinvalid UTF-8
   183         -1\tcompat/zlib/contrib/dotzlib/DotZLib/Deflater.cs\tinvalid UTF-8
   184         -1\tcompat/zlib/contrib/dotzlib/DotZLib/DotZLib.cs\tinvalid UTF-8
   185         -1\tcompat/zlib/contrib/dotzlib/DotZLib/DotZLib.csproj\tCR/LF line endings
   186         -1\tcompat/zlib/contrib/dotzlib/DotZLib/GZipStream.cs\tinvalid UTF-8
   187         -1\tcompat/zlib/contrib/dotzlib/DotZLib/Inflater.cs\tinvalid UTF-8
   188         -1\tcompat/zlib/contrib/dotzlib/DotZLib/UnitTests.cs\tCR/LF line endings
   189         -1\tcompat/zlib/contrib/dotzlib/LICENSE_1_0.txt\tCR/LF line endings
   190         -1\tcompat/zlib/contrib/dotzlib/readme.txt\tCR/LF line endings
   191         -1\tcompat/zlib/contrib/gcc_gvmat64/gvmat64.S\tCR/LF line endings
   192         -1\tcompat/zlib/contrib/masmx64/bld_ml64.bat\tCR/LF line endings
   193         -1\tcompat/zlib/contrib/masmx64/gvmat64.asm\tCR/LF line endings
   194         -1\tcompat/zlib/contrib/masmx64/inffas8664.c\tCR/LF line endings
   195         -1\tcompat/zlib/contrib/masmx64/inffasx64.asm\tCR/LF line endings
   196         -1\tcompat/zlib/contrib/masmx64/readme.txt\tCR/LF line endings
   197         -1\tcompat/zlib/contrib/masmx86/bld_ml32.bat\tCR/LF line endings
   198         -1\tcompat/zlib/contrib/masmx86/inffas32.asm\tCR/LF line endings
   199         -1\tcompat/zlib/contrib/masmx86/match686.asm\tCR/LF line endings
   200         -1\tcompat/zlib/contrib/masmx86/readme.txt\tCR/LF line endings
   201         -1\tcompat/zlib/contrib/puff/zeros.raw\tbinary data
   202         -1\tcompat/zlib/contrib/testzlib/testzlib.c\tCR/LF line endings
   203         -1\tcompat/zlib/contrib/testzlib/testzlib.txt\tCR/LF line endings
   204         -1\tcompat/zlib/contrib/vstudio/readme.txt\tCR/LF line endings
   205         -1\tcompat/zlib/contrib/vstudio/vc10/miniunz.vcxproj\tCR/LF line endings
   206         -1\tcompat/zlib/contrib/vstudio/vc10/miniunz.vcxproj.filters\tCR/LF line endings
   207         -1\tcompat/zlib/contrib/vstudio/vc10/minizip.vcxproj\tCR/LF line endings
   208         -1\tcompat/zlib/contrib/vstudio/vc10/minizip.vcxproj.filters\tCR/LF line endings
   209         -1\tcompat/zlib/contrib/vstudio/vc10/testzlib.vcxproj\tCR/LF line endings
   210         -1\tcompat/zlib/contrib/vstudio/vc10/testzlib.vcxproj.filters\tCR/LF line endings
   211         -1\tcompat/zlib/contrib/vstudio/vc10/testzlibdll.vcxproj\tCR/LF line endings
   212         -1\tcompat/zlib/contrib/vstudio/vc10/testzlibdll.vcxproj.filters\tCR/LF line endings
   213         -1\tcompat/zlib/contrib/vstudio/vc10/zlib.rc\tCR/LF line endings
   214         -1\tcompat/zlib/contrib/vstudio/vc10/zlibstat.vcxproj\tCR/LF line endings
   215         -1\tcompat/zlib/contrib/vstudio/vc10/zlibstat.vcxproj.filters\tCR/LF line endings
   216         -1\tcompat/zlib/contrib/vstudio/vc10/zlibvc.def\tCR/LF line endings
   217         -1\tcompat/zlib/contrib/vstudio/vc10/zlibvc.sln\tCR/LF line endings
   218         -1\tcompat/zlib/contrib/vstudio/vc10/zlibvc.vcxproj\tCR/LF line endings
   219         -1\tcompat/zlib/contrib/vstudio/vc10/zlibvc.vcxproj.filters\tCR/LF line endings
   220         -1\tcompat/zlib/contrib/vstudio/vc11/miniunz.vcxproj\tCR/LF line endings
   221         -1\tcompat/zlib/contrib/vstudio/vc11/minizip.vcxproj\tCR/LF line endings
   222         -1\tcompat/zlib/contrib/vstudio/vc11/testzlib.vcxproj\tCR/LF line endings
   223         -1\tcompat/zlib/contrib/vstudio/vc11/testzlibdll.vcxproj\tCR/LF line endings
   224         -1\tcompat/zlib/contrib/vstudio/vc11/zlib.rc\tCR/LF line endings
   225         -1\tcompat/zlib/contrib/vstudio/vc11/zlibstat.vcxproj\tCR/LF line endings
   226         -1\tcompat/zlib/contrib/vstudio/vc11/zlibvc.def\tCR/LF line endings
   227         -1\tcompat/zlib/contrib/vstudio/vc11/zlibvc.sln\tCR/LF line endings
   228         -1\tcompat/zlib/contrib/vstudio/vc11/zlibvc.vcxproj\tCR/LF line endings
   229         -1\tcompat/zlib/contrib/vstudio/vc12/zlibvc.def\tCR/LF line endings
   230         -1\tcompat/zlib/contrib/vstudio/vc14/zlibvc.def\tCR/LF line endings
   231         -1\tcompat/zlib/contrib/vstudio/vc9/miniunz.vcproj\tCR/LF line endings
   232         -1\tcompat/zlib/contrib/vstudio/vc9/minizip.vcproj\tCR/LF line endings
   233         -1\tcompat/zlib/contrib/vstudio/vc9/testzlib.vcproj\tCR/LF line endings
   234         -1\tcompat/zlib/contrib/vstudio/vc9/testzlibdll.vcproj\tCR/LF line endings
   235         -1\tcompat/zlib/contrib/vstudio/vc9/zlib.rc\tCR/LF line endings
   236         -1\tcompat/zlib/contrib/vstudio/vc9/zlibstat.vcproj\tCR/LF line endings
   237         -1\tcompat/zlib/contrib/vstudio/vc9/zlibvc.def\tCR/LF line endings
   238         -1\tcompat/zlib/contrib/vstudio/vc9/zlibvc.sln\tCR/LF line endings
   239         -1\tcompat/zlib/contrib/vstudio/vc9/zlibvc.vcproj\tCR/LF line endings
   240         -1\tcompat/zlib/win32/zlib.def\tCR/LF line endings
   241         -1\tcompat/zlib/zlib.3.pdf\tbinary data
   242         -1\tcompat/zlib/zlib.map\tCR/LF line endings
   243         -1\tsetup/fossil.iss\tCR/LF line endings
   244         -1\tskins/blitz/arrow_project.png\tbinary data
   245         -1\tskins/blitz/dir.png\tbinary data
   246         -1\tskins/blitz/file.png\tbinary data
   247         -1\tskins/blitz/fossil_100.png\tbinary data
   248         -1\tskins/blitz/fossil_80_reversed_darkcyan.png\tbinary data
   249         -1\tskins/blitz/fossil_80_reversed_darkcyan_text.png\tbinary data
   250         -1\tskins/blitz/rss_20.png\tbinary data
   251         -1\tskins/bootstrap/css.txt\tlong lines
   252         -1\ttest/th1-docs-input.txt\tCR/LF line endings
   253         -1\ttest/th1-hooks-input.txt\tCR/LF line endings
   254         -1\ttest/utf16be.txt\tUnicode
   255         -1\ttest/utf16le.txt\tUnicode
   256         -1\twin/buildmsvc.bat\tCR/LF line endings
   257         -1\twin/fossil.ico\tbinary data
   258         -1\twin/fossil.rc\tinvalid UTF-8
   259         -1\twww/CollRev1.gif\tbinary data
   260         -1\twww/CollRev2.gif\tbinary data
   261         -1\twww/CollRev3.gif\tbinary data
   262         -1\twww/CollRev4.gif\tbinary data
   263         -1\twww/apple-touch-icon.png\tbinary data
   264         -1\twww/background.jpg\tbinary data
   265         -1\twww/branch01.gif\tbinary data
   266         -1\twww/branch02.gif\tbinary data
   267         -1\twww/branch03.gif\tbinary data
   268         -1\twww/branch04.gif\tbinary data
   269         -1\twww/branch05.gif\tbinary data
   270         -1\twww/build-icons/linux.gif\tbinary data
   271         -1\twww/build-icons/linux64.gif\tbinary data
   272         -1\twww/build-icons/mac.gif\tbinary data
   273         -1\twww/build-icons/openbsd.gif\tbinary data
   274         -1\twww/build-icons/src.gif\tbinary data
   275         -1\twww/build-icons/win32.gif\tbinary data
   276         -1\twww/concept1.gif\tbinary data
   277         -1\twww/concept2.gif\tbinary data
   278         -1\twww/copyright-release.pdf\tbinary data
   279         -1\twww/delta1.gif\tbinary data
   280         -1\twww/delta2.gif\tbinary data
   281         -1\twww/delta3.gif\tbinary data
   282         -1\twww/delta4.gif\tbinary data
   283         -1\twww/delta5.gif\tbinary data
   284         -1\twww/delta6.gif\tbinary data
   285         -1\twww/encode1.gif\tbinary data
   286         -1\twww/encode10.gif\tbinary data
   287         -1\twww/encode2.gif\tbinary data
   288         -1\twww/encode3.gif\tbinary data
   289         -1\twww/encode4.gif\tbinary data
   290         -1\twww/encode5.gif\tbinary data
   291         -1\twww/encode6.gif\tbinary data
   292         -1\twww/encode7.gif\tbinary data
   293         -1\twww/encode8.gif\tbinary data
   294         -1\twww/encode9.gif\tbinary data
   295         -1\twww/fossil.gif\tbinary data
   296         -1\twww/fossil2.gif\tbinary data
   297         -1\twww/fossil3.gif\tbinary data
   298         -1\twww/fossil_logo_small.gif\tbinary data
   299         -1\twww/fossil_logo_small2.gif\tbinary data
   300         -1\twww/fossil_logo_small3.gif\tbinary data
   301         -1\twww/xkcd-git.gif\tbinary data
   302         -1}]]}
          168  +} {
          169  +  test pre-commit-warnings-fossil-1 {[normalize_result] eq \
          170  +      [subst -nocommands -novariables [string trim {
          171  +  1\tart/branching.odp\tbinary data
          172  +  1\tart/concept1.dia\tbinary data
          173  +  1\tart/concept2.dia\tbinary data
          174  +  1\tcompat/zlib/contrib/blast/test.pk\tbinary data
          175  +  1\tcompat/zlib/contrib/dotzlib/DotZLib.build\tCR/LF line endings
          176  +  1\tcompat/zlib/contrib/dotzlib/DotZLib.chm\tbinary data
          177  +  1\tcompat/zlib/contrib/dotzlib/DotZLib.sln\tCR/LF line endings
          178  +  1\tcompat/zlib/contrib/dotzlib/DotZLib/AssemblyInfo.cs\tCR/LF line endings
          179  +  1\tcompat/zlib/contrib/dotzlib/DotZLib/ChecksumImpl.cs\tinvalid UTF-8
          180  +  1\tcompat/zlib/contrib/dotzlib/DotZLib/CircularBuffer.cs\tinvalid UTF-8
          181  +  1\tcompat/zlib/contrib/dotzlib/DotZLib/CodecBase.cs\tinvalid UTF-8
          182  +  1\tcompat/zlib/contrib/dotzlib/DotZLib/Deflater.cs\tinvalid UTF-8
          183  +  1\tcompat/zlib/contrib/dotzlib/DotZLib/DotZLib.cs\tinvalid UTF-8
          184  +  1\tcompat/zlib/contrib/dotzlib/DotZLib/DotZLib.csproj\tCR/LF line endings
          185  +  1\tcompat/zlib/contrib/dotzlib/DotZLib/GZipStream.cs\tinvalid UTF-8
          186  +  1\tcompat/zlib/contrib/dotzlib/DotZLib/Inflater.cs\tinvalid UTF-8
          187  +  1\tcompat/zlib/contrib/dotzlib/DotZLib/UnitTests.cs\tCR/LF line endings
          188  +  1\tcompat/zlib/contrib/dotzlib/LICENSE_1_0.txt\tCR/LF line endings
          189  +  1\tcompat/zlib/contrib/dotzlib/readme.txt\tCR/LF line endings
          190  +  1\tcompat/zlib/contrib/gcc_gvmat64/gvmat64.S\tCR/LF line endings
          191  +  1\tcompat/zlib/contrib/masmx64/bld_ml64.bat\tCR/LF line endings
          192  +  1\tcompat/zlib/contrib/masmx64/gvmat64.asm\tCR/LF line endings
          193  +  1\tcompat/zlib/contrib/masmx64/inffas8664.c\tCR/LF line endings
          194  +  1\tcompat/zlib/contrib/masmx64/inffasx64.asm\tCR/LF line endings
          195  +  1\tcompat/zlib/contrib/masmx64/readme.txt\tCR/LF line endings
          196  +  1\tcompat/zlib/contrib/masmx86/bld_ml32.bat\tCR/LF line endings
          197  +  1\tcompat/zlib/contrib/masmx86/inffas32.asm\tCR/LF line endings
          198  +  1\tcompat/zlib/contrib/masmx86/match686.asm\tCR/LF line endings
          199  +  1\tcompat/zlib/contrib/masmx86/readme.txt\tCR/LF line endings
          200  +  1\tcompat/zlib/contrib/puff/zeros.raw\tbinary data
          201  +  1\tcompat/zlib/contrib/testzlib/testzlib.c\tCR/LF line endings
          202  +  1\tcompat/zlib/contrib/testzlib/testzlib.txt\tCR/LF line endings
          203  +  1\tcompat/zlib/contrib/vstudio/readme.txt\tCR/LF line endings
          204  +  1\tcompat/zlib/contrib/vstudio/vc10/miniunz.vcxproj\tCR/LF line endings
          205  +  1\tcompat/zlib/contrib/vstudio/vc10/miniunz.vcxproj.filters\tCR/LF line endings
          206  +  1\tcompat/zlib/contrib/vstudio/vc10/minizip.vcxproj\tCR/LF line endings
          207  +  1\tcompat/zlib/contrib/vstudio/vc10/minizip.vcxproj.filters\tCR/LF line endings
          208  +  1\tcompat/zlib/contrib/vstudio/vc10/testzlib.vcxproj\tCR/LF line endings
          209  +  1\tcompat/zlib/contrib/vstudio/vc10/testzlib.vcxproj.filters\tCR/LF line endings
          210  +  1\tcompat/zlib/contrib/vstudio/vc10/testzlibdll.vcxproj\tCR/LF line endings
          211  +  1\tcompat/zlib/contrib/vstudio/vc10/testzlibdll.vcxproj.filters\tCR/LF line endings
          212  +  1\tcompat/zlib/contrib/vstudio/vc10/zlib.rc\tCR/LF line endings
          213  +  1\tcompat/zlib/contrib/vstudio/vc10/zlibstat.vcxproj\tCR/LF line endings
          214  +  1\tcompat/zlib/contrib/vstudio/vc10/zlibstat.vcxproj.filters\tCR/LF line endings
          215  +  1\tcompat/zlib/contrib/vstudio/vc10/zlibvc.def\tCR/LF line endings
          216  +  1\tcompat/zlib/contrib/vstudio/vc10/zlibvc.sln\tCR/LF line endings
          217  +  1\tcompat/zlib/contrib/vstudio/vc10/zlibvc.vcxproj\tCR/LF line endings
          218  +  1\tcompat/zlib/contrib/vstudio/vc10/zlibvc.vcxproj.filters\tCR/LF line endings
          219  +  1\tcompat/zlib/contrib/vstudio/vc11/miniunz.vcxproj\tCR/LF line endings
          220  +  1\tcompat/zlib/contrib/vstudio/vc11/minizip.vcxproj\tCR/LF line endings
          221  +  1\tcompat/zlib/contrib/vstudio/vc11/testzlib.vcxproj\tCR/LF line endings
          222  +  1\tcompat/zlib/contrib/vstudio/vc11/testzlibdll.vcxproj\tCR/LF line endings
          223  +  1\tcompat/zlib/contrib/vstudio/vc11/zlib.rc\tCR/LF line endings
          224  +  1\tcompat/zlib/contrib/vstudio/vc11/zlibstat.vcxproj\tCR/LF line endings
          225  +  1\tcompat/zlib/contrib/vstudio/vc11/zlibvc.def\tCR/LF line endings
          226  +  1\tcompat/zlib/contrib/vstudio/vc11/zlibvc.sln\tCR/LF line endings
          227  +  1\tcompat/zlib/contrib/vstudio/vc11/zlibvc.vcxproj\tCR/LF line endings
          228  +  1\tcompat/zlib/contrib/vstudio/vc12/zlibvc.def\tCR/LF line endings
          229  +  1\tcompat/zlib/contrib/vstudio/vc14/zlibvc.def\tCR/LF line endings
          230  +  1\tcompat/zlib/contrib/vstudio/vc9/miniunz.vcproj\tCR/LF line endings
          231  +  1\tcompat/zlib/contrib/vstudio/vc9/minizip.vcproj\tCR/LF line endings
          232  +  1\tcompat/zlib/contrib/vstudio/vc9/testzlib.vcproj\tCR/LF line endings
          233  +  1\tcompat/zlib/contrib/vstudio/vc9/testzlibdll.vcproj\tCR/LF line endings
          234  +  1\tcompat/zlib/contrib/vstudio/vc9/zlib.rc\tCR/LF line endings
          235  +  1\tcompat/zlib/contrib/vstudio/vc9/zlibstat.vcproj\tCR/LF line endings
          236  +  1\tcompat/zlib/contrib/vstudio/vc9/zlibvc.def\tCR/LF line endings
          237  +  1\tcompat/zlib/contrib/vstudio/vc9/zlibvc.sln\tCR/LF line endings
          238  +  1\tcompat/zlib/contrib/vstudio/vc9/zlibvc.vcproj\tCR/LF line endings
          239  +  1\tcompat/zlib/win32/zlib.def\tCR/LF line endings
          240  +  1\tcompat/zlib/zlib.3.pdf\tbinary data
          241  +  1\tcompat/zlib/zlib.map\tCR/LF line endings
          242  +  1\tsetup/fossil.iss\tCR/LF line endings
          243  +  1\tskins/blitz/arrow_project.png\tbinary data
          244  +  1\tskins/blitz/dir.png\tbinary data
          245  +  1\tskins/blitz/file.png\tbinary data
          246  +  1\tskins/blitz/fossil_100.png\tbinary data
          247  +  1\tskins/blitz/fossil_80_reversed_darkcyan.png\tbinary data
          248  +  1\tskins/blitz/fossil_80_reversed_darkcyan_text.png\tbinary data
          249  +  1\tskins/blitz/rss_20.png\tbinary data
          250  +  1\tskins/bootstrap/css.txt\tlong lines
          251  +  1\ttest/th1-docs-input.txt\tCR/LF line endings
          252  +  1\ttest/th1-hooks-input.txt\tCR/LF line endings
          253  +  1\ttest/utf16be.txt\tUnicode
          254  +  1\ttest/utf16le.txt\tUnicode
          255  +  1\twin/buildmsvc.bat\tCR/LF line endings
          256  +  1\twin/fossil.ico\tbinary data
          257  +  1\twin/fossil.rc\tinvalid UTF-8
          258  +  1\twww/CollRev1.gif\tbinary data
          259  +  1\twww/CollRev2.gif\tbinary data
          260  +  1\twww/CollRev3.gif\tbinary data
          261  +  1\twww/CollRev4.gif\tbinary data
          262  +  1\twww/apple-touch-icon.png\tbinary data
          263  +  1\twww/background.jpg\tbinary data
          264  +  1\twww/branch01.gif\tbinary data
          265  +  1\twww/branch02.gif\tbinary data
          266  +  1\twww/branch03.gif\tbinary data
          267  +  1\twww/branch04.gif\tbinary data
          268  +  1\twww/branch05.gif\tbinary data
          269  +  1\twww/build-icons/linux.gif\tbinary data
          270  +  1\twww/build-icons/linux64.gif\tbinary data
          271  +  1\twww/build-icons/mac.gif\tbinary data
          272  +  1\twww/build-icons/openbsd.gif\tbinary data
          273  +  1\twww/build-icons/src.gif\tbinary data
          274  +  1\twww/build-icons/win32.gif\tbinary data
          275  +  1\twww/concept1.gif\tbinary data
          276  +  1\twww/concept2.gif\tbinary data
          277  +  1\twww/copyright-release.pdf\tbinary data
          278  +  1\twww/delta1.gif\tbinary data
          279  +  1\twww/delta2.gif\tbinary data
          280  +  1\twww/delta3.gif\tbinary data
          281  +  1\twww/delta4.gif\tbinary data
          282  +  1\twww/delta5.gif\tbinary data
          283  +  1\twww/delta6.gif\tbinary data
          284  +  1\twww/encode1.gif\tbinary data
          285  +  1\twww/encode10.gif\tbinary data
          286  +  1\twww/encode2.gif\tbinary data
          287  +  1\twww/encode3.gif\tbinary data
          288  +  1\twww/encode4.gif\tbinary data
          289  +  1\twww/encode5.gif\tbinary data
          290  +  1\twww/encode6.gif\tbinary data
          291  +  1\twww/encode7.gif\tbinary data
          292  +  1\twww/encode8.gif\tbinary data
          293  +  1\twww/encode9.gif\tbinary data
          294  +  1\twww/fossil.gif\tbinary data
          295  +  1\twww/fossil2.gif\tbinary data
          296  +  1\twww/fossil3.gif\tbinary data
          297  +  1\twww/fossil_logo_small.gif\tbinary data
          298  +  1\twww/fossil_logo_small2.gif\tbinary data
          299  +  1\twww/fossil_logo_small3.gif\tbinary data
          300  +  1\twww/xkcd-git.gif\tbinary data
          301  +  1}]]}
          302  +}
   303    303   
   304    304   ###############################################################################
   305    305   
   306    306   test_cleanup

Changes to test/tester.tcl.

    18     18   # This is the main test script.  To run a regression test, do this:
    19     19   #
    20     20   #     tclsh ../test/tester.tcl ../bld/fossil
    21     21   #
    22     22   # Where ../test/tester.tcl is the name of this file and ../bld/fossil
    23     23   # is the name of the executable to be tested.
    24     24   #
           25  +
           26  +# We use some things introduced in 8.6 such as lmap.  auto.def should
           27  +# have found us a suitable Tcl installation.
           28  +package require Tcl 8.6
    25     29   
    26     30   set testfiledir [file normalize [file dirname [info script]]]
    27     31   set testrundir [pwd]
    28     32   set testdir [file normalize [file dirname $argv0]]
    29     33   set fossilexe [file normalize [lindex $argv 0]]
           34  +set is_windows [expr {$::tcl_platform(platform) eq "windows"}]
    30     35   
    31         -if {$tcl_platform(platform) eq "windows" && \
    32         -    [string length [file extension $fossilexe]] == 0} {
    33         -  append fossilexe .exe
           36  +if {$::is_windows} {
           37  +  if {[string length [file extension $fossilexe]] == 0} {
           38  +    append fossilexe .exe
           39  +  }
           40  +  set outside_fossil_repo [expr ![file exists "$::testfiledir\\..\\_FOSSIL_"]]
           41  +} else {
           42  +  set outside_fossil_repo [expr ![file exists "$::testfiledir/../.fslckout"]]
    34     43   }
           44  +
           45  +catch {exec $::fossilexe changes --changed} res
           46  +set dirty_ckout [string length $res]
    35     47   
    36     48   set argv [lrange $argv 1 end]
    37     49   
    38     50   set i [lsearch $argv -keep]
    39     51   if {$i>=0} {
    40     52     set KEEP 1
    41     53     set argv [lreplace $argv $i $i]
................................................................................
   437    449     # Finally, attempt to gracefully delete the temporary home directory,
   438    450     # unless forbidden by external forces.
   439    451     if {![info exists ::tempKeepHome]} {delete_temporary_home}
   440    452   }
   441    453   
   442    454   proc delete_temporary_home {} {
   443    455     if {$::KEEP} {return}; # All cleanup disabled?
   444         -  if {$::tcl_platform(platform) eq "windows"} {
          456  +  if {$::is_windows} {
   445    457       robust_delete [file join $::tempHomePath _fossil]
   446    458     } else {
   447    459       robust_delete [file join $::tempHomePath .fossil]
   448    460     }
   449    461     robust_delete $::tempHomePath
   450    462   }
   451    463   
................................................................................
   510    522     fossil test-th-eval "setting th1-hooks"
   511    523     if {[normalize_result] eq "1"} {return 1}
   512    524     fossil test-th-eval --open-config "setting th1-hooks"
   513    525     if {[normalize_result] eq "1"} {return 1}
   514    526     return [info exists ::env(TH1_ENABLE_HOOKS)]
   515    527   }
   516    528   
   517         -# This (rarely used) procedure is designed to run a test within the Fossil
   518         -# source checkout (e.g. one that does NOT modify any state), while saving
   519         -# and restoring the current directory (e.g. one used when running a test
   520         -# file outside of the Fossil source checkout).  Please do NOT use this
   521         -# procedure unless you are absolutely sure it does not modify the state of
   522         -# the repository or source checkout in any way.
          529  +# Run the given command script inside the Fossil source repo checkout.
   523    530   #
          531  +# Callers of this function must ensure two things:
          532  +#
          533  +# 1. This test run is in fact being done from within a Fossil repo
          534  +#    checkout directory.  If you are unsure, test $::outside_fossil_repo
          535  +#    or call one of the test_* wrappers below which do that for you.
          536  +#
          537  +#    As a rule, you should not be calling this function directly!
          538  +#
          539  +# 2. This test run is being done from a repo checkout directory that
          540  +#    doesn't have any uncommitted changes.  If it does, that affects the
          541  +#    output of any test based on the output of "fossil status",
          542  +#    "... diff", etc., which is likely to make the test appear to fail.
          543  +#    If you must call this function directly, test $::dirty_ckout and
          544  +#    skip the call if it's true.  The test_* wrappers do this for you.
          545  +#
          546  +# 3. The test does NOT modify the Fossil checkout tree in any way.
   524    547   proc run_in_checkout { script {dir ""} } {
   525    548     if {[string length $dir] == 0} {set dir $::testfiledir}
   526    549     set savedPwd [pwd]; cd $dir
   527    550     set code [catch {
   528    551       uplevel 1 $script
   529    552     } result]
   530    553     cd $savedPwd; unset savedPwd
   531    554     return -code $code $result
   532    555   }
          556  +
          557  +# Wrapper for the above function pair.  The tscript parameter is an
          558  +# optional post-run test script.  Some callers choose instead to put
          559  +# the tests inline with the rscript commands.
          560  +#
          561  +# Be sure to adhere to the requirements of run_in_checkout!
          562  +proc test_block_in_checkout { name rscript {tscript ""} } {
          563  +  if {$::outside_fossil_repo || $::dirty_ckout} {
          564  +    set $::CODE 0
          565  +    set $::RESULT ""
          566  +  } else {
          567  +    run_in_checkout $rscript
          568  +    if {[string length $tscript] == 0} {
          569  +      return ""
          570  +    } else {
          571  +      set code [catch {
          572  +        uplevel 1 $tscript
          573  +      } result]
          574  +      return -code $code $result
          575  +    }
          576  +  }
          577  +}
          578  +
          579  +# Single-test wrapper for the above.
          580  +proc test_in_checkout { name rscript tscript } {
          581  +  return test_block_in_checkout name rscript {
          582  +    test $name $tscript
          583  +  }
          584  +}
   533    585   
   534    586   # Normalize file status lists (like those returned by 'fossil changes')
   535    587   # so they can be compared using simple string comparison
   536    588   #
   537    589   proc normalize_status_list {list} {
   538    590     set normalized [list]
   539    591     set matches [regexp -all -inline -line {^\s*([A-Z_]+:?)\x20+(\S.*)$} $list]
................................................................................
   622    674         }
   623    675       }
   624    676     }
   625    677   
   626    678     #
   627    679     # NOTE: On non-Windows systems, fallback to /tmp if it is usable.
   628    680     #
   629         -  if {$::tcl_platform(platform) ne "windows"} {
          681  +  if {!$::is_windows} {
   630    682       set value /tmp
   631    683   
   632    684       if {[file exists $value] && [file isdirectory $value]} {
   633    685         return $value
   634    686       }
   635    687     }
   636    688   
................................................................................
   794    846   # passed to the [test_stop_server] procedure.
   795    847   proc test_start_server { repository {varName ""} } {
   796    848     global fossilexe tempPath
   797    849     set command [list exec $fossilexe server --localhost]
   798    850     if {[string length $varName] > 0} {
   799    851       upvar 1 $varName stopArg
   800    852     }
   801         -  if {$::tcl_platform(platform) eq "windows"} {
          853  +  if {$::is_windows} {
   802    854       set stopArg [file join [getTemporaryPath] [appendArgs \
   803    855           [string trim [clock seconds] -] _ [getSeqNo] .stopper]]
   804    856       lappend command --stopper $stopArg
   805    857     }
   806    858     set outFileName [file join $tempPath [appendArgs \
   807    859         fossil_server_ [string trim [clock seconds] -] _ \
   808    860         [getSeqNo]]].out
   809    861     lappend command $repository >&$outFileName &
   810    862     set pid [eval $command]
   811         -  if {$::tcl_platform(platform) ne "windows"} {
          863  +  if {!$::is_windows} {
   812    864       set stopArg $pid
   813    865     }
   814    866     after 1000; # output might not be there yet
   815    867     set output [read_file $outFileName]
   816    868     if {![regexp {Listening.*TCP port (\d+)} $output dummy port]} {
   817    869       puts stdout "Could not detect Fossil server port, using default..."
   818    870       set port 8080; # return the default port just in case
................................................................................
   821    873   }
   822    874   
   823    875   # This procedure stops a Fossil server instance that was previously started
   824    876   # by the [test_start_server] procedure.  The value of the "stop argument"
   825    877   # will vary by platform as will the exact method used to stop the server.
   826    878   # The fileName argument is the name of a temporary output file to delete.
   827    879   proc test_stop_server { stopArg pid fileName } {
   828         -  if {$::tcl_platform(platform) eq "windows"} {
          880  +  if {$::is_windows} {
   829    881       #
   830    882       # NOTE: On Windows, the "stop argument" must be the name of a file
   831    883       #       that does NOT already exist.
   832    884       #
   833    885       if {[string length $stopArg] > 0 && \
   834    886           ![file exists $stopArg] && \
   835    887           [catch {write_file $stopArg [clock seconds]}] == 0} {
................................................................................
   948   1000   # returns the third to last line of the normalized result.
   949   1001   proc third_to_last_data_line {} {
   950   1002     return [lindex [split [normalize_result] \n] end-2]
   951   1003   }
   952   1004   
   953   1005   set tempPath [getTemporaryPath]
   954   1006   
   955         -if {$tcl_platform(platform) eq "windows"} {
         1007  +if {$is_windows} {
   956   1008     set tempPath [string map [list \\ /] $tempPath]
   957   1009   }
   958   1010   
   959   1011   if {[catch {
   960   1012     set tempFile [file join $tempPath temporary.txt]
   961   1013     write_file $tempFile [clock seconds]; file delete $tempFile
   962   1014   } error] != 0} {

Changes to test/th1-docs.test.

    27     27   
    28     28   fossil test-th-eval "hasfeature tcl"
    29     29   
    30     30   if {[normalize_result] ne "1"} {
    31     31     puts "Fossil was not compiled with Tcl support."
    32     32     test_cleanup_then_return
    33     33   }
           34  +
           35  +if {$::outside_fossil_repo} {
           36  +  puts "Skipping th1-docs-* tests: not in Fossil repo checkout."
           37  +  test_cleanup_then_return
           38  +} elseif ($::dirty_ckout) {
           39  +  puts "Skipping th1-docs-* tests: uncommitted changes in Fossil checkout."
           40  +  test_cleanup_then_return
           41  +}
    34     42   
    35     43   ###############################################################################
    36     44   
    37     45   test_setup ""
    38     46   
    39     47   ###############################################################################
    40     48   

Changes to test/th1.test.

   551    551   
   552    552   ###############################################################################
   553    553   
   554    554   fossil test-th-eval "lindex list -0x"
   555    555   test th1-expr-49 {$RESULT eq {TH_ERROR: expected integer, got: "-0x"}}
   556    556   
   557    557   ###############################################################################
          558  +
          559  +set skip_anycap 1
          560  +if {$::outside_fossil_repo} {
          561  +  puts "Skipping th1-anycap-*-1 perm tests: not in Fossil repo checkout."
          562  +} elseif ($::dirty_ckout) {
          563  +  puts "Skipping th1-anycap-*-1 perm tests: uncommitted changes in Fossil checkout."
          564  +} else {
          565  +  set skip_anycap 0
          566  +}
   558    567   
   559    568   foreach perm [list a b c d e f g h i j k l m n o p q r s t u v w x y z] {
   560    569     if {$perm eq "u"} continue; # NOTE: Skip "reader" meta-permission.
   561    570     if {$perm eq "v"} continue; # NOTE: Skip "developer" meta-permission.
   562    571   
   563    572     fossil test-th-eval "anycap $perm"
   564    573     test th1-anycap-no-$perm-1 {$RESULT eq {0}}
   565    574   
   566    575     fossil test-th-eval "hascap $perm"
   567    576     test th1-hascap-no-$perm-1 {$RESULT eq {0}}
   568    577   
   569    578     fossil test-th-eval "anoncap $perm"
   570    579     test th1-anoncap-no-$perm-1 {$RESULT eq {0}}
          580  +
          581  +  if {$skip_anycap} { continue }
   571    582   
   572    583     run_in_checkout {
   573    584       fossil test-th-eval --set-user-caps "anycap $perm"
   574    585       test th1-anycap-yes-$perm-1 {$RESULT eq {1}}
   575    586   
   576    587       set ::env(TH1_TEST_USER_CAPS) 1; # NOTE: Bad permission.
   577    588       fossil test-th-eval --set-user-caps "anycap $perm"
................................................................................
   619    630   ###############################################################################
   620    631   
   621    632   fossil test-th-eval "anoncap o h"
   622    633   test th1-anoncap-no-multiple-2 {$RESULT eq {0}}
   623    634   
   624    635   ###############################################################################
   625    636   
   626         -run_in_checkout {
          637  +test_block_in_checkout "test-anoncap-*" {
   627    638     fossil test-th-eval --set-user-caps "anycap oh"
   628    639     test th1-anycap-yes-multiple-1 {$RESULT eq {1}}
   629    640   
   630    641     set ::env(TH1_TEST_USER_CAPS) o
   631    642     fossil test-th-eval --set-user-caps "anycap oh"
   632    643     test th1-anycap-yes-multiple-2 {$RESULT eq {1}}
   633    644     unset ::env(TH1_TEST_USER_CAPS)
................................................................................
   663    674     fossil test-th-eval --set-anon-caps "anoncap o h"
   664    675     test th1-anoncap-no-multiple-4 {$RESULT eq {0}}
   665    676     unset ::env(TH1_TEST_ANON_CAPS)
   666    677   }
   667    678   
   668    679   ###############################################################################
   669    680   
   670         -run_in_checkout {
          681  +test_in_checkout th1-checkout-1 {
   671    682     # NOTE: The "1" here forces the checkout to be opened.
   672    683     fossil test-th-eval "checkout 1"
   673         -}
   674         -
   675         -test th1-checkout-1 {[string length $RESULT] > 0}
          684  +} {[string length $RESULT] > 0}
   676    685   
   677    686   ###############################################################################
   678    687   
   679         -run_in_checkout {
          688  +test_in_checkout th1-checkout-2 {
   680    689     if {$th1Hooks} {
   681    690       fossil test-th-eval "checkout"
   682    691     } else {
   683    692       # NOTE: No TH1 hooks, force checkout to be populated.
   684    693       fossil test-th-eval --open-config "checkout"
   685    694     }
   686         -}
   687         -
   688         -test th1-checkout-2 {[string length $RESULT] > 0}
          695  +} {[string length $RESULT] > 0}
   689    696   
   690    697   ###############################################################################
   691    698   
   692    699   set savedPwd [pwd]; cd /
   693    700   fossil test-th-eval "checkout 1"
   694    701   cd $savedPwd; unset savedPwd
   695    702   test th1-checkout-3 {[string length $RESULT] == 0}
................................................................................
   760    767   ###############################################################################
   761    768   
   762    769   fossil test-th-eval "styleHeader {Page Title Here}"
   763    770   test th1-header-1 {$RESULT eq {TH_ERROR: repository unavailable}}
   764    771   
   765    772   ###############################################################################
   766    773   
   767         -run_in_checkout {
          774  +test_in_checkout th1-header-2 {
   768    775     fossil test-th-eval --open-config "styleHeader {Page Title Here}"
   769         -}
   770         -
   771         -test th1-header-2 {[regexp -- {<title>Fossil: Page Title Here</title>} $RESULT]}
          776  +} {[regexp -- {<title>Fossil: Page Title Here</title>} $RESULT]}
   772    777   
   773    778   ###############################################################################
   774    779   
   775    780   fossil test-th-eval "styleFooter"
   776    781   test th1-footer-1 {$RESULT eq {TH_ERROR: repository unavailable}}
   777    782   
   778    783   ###############################################################################
................................................................................
   846    851   ###############################################################################
   847    852   
   848    853   fossil test-th-eval "artifact tip"
   849    854   test th1-artifact-2 {$RESULT eq {TH_ERROR: repository unavailable}}
   850    855   
   851    856   ###############################################################################
   852    857   
   853         -run_in_checkout {
          858  +test_in_checkout th1-artifact-3 {
   854    859     fossil test-th-eval --open-config "artifact tip"
   855         -}
   856         -
   857         -test th1-artifact-3 {[regexp -- {F test/th1\.test [0-9a-f]{40,64}} $RESULT]}
          860  +} {[regexp -- {F test/th1\.test [0-9a-f]{40,64}} $RESULT]}
   858    861   
   859    862   ###############################################################################
   860    863   
   861    864   fossil test-th-eval "artifact 0000000000"
   862    865   test th1-artifact-4 {$RESULT eq {TH_ERROR: repository unavailable}}
   863    866   
   864    867   ###############################################################################
................................................................................
   869    872   ###############################################################################
   870    873   
   871    874   fossil test-th-eval "artifact tip test/th1.test"
   872    875   test th1-artifact-6 {$RESULT eq {TH_ERROR: repository unavailable}}
   873    876   
   874    877   ###############################################################################
   875    878   
   876         -run_in_checkout {
          879  +test_in_checkout th1-artifact-7 {
   877    880     fossil test-th-eval --open-config "artifact tip test/th1.test"
   878         -}
   879         -
   880         -test th1-artifact-7 {[regexp -- {th1-artifact-7} $RESULT]}
          881  +} {[regexp -- {th1-artifact-7} $RESULT]}
   881    882   
   882    883   ###############################################################################
   883    884   
   884    885   fossil test-th-eval "artifact 0000000000 test/th1.test"
   885    886   test th1-artifact-8 {$RESULT eq {TH_ERROR: repository unavailable}}
   886    887   
   887    888   ###############################################################################
   888    889   
   889    890   fossil test-th-eval --open-config "artifact 0000000000 test/th1.test"
   890    891   test th1-artifact-9 {$RESULT eq {TH_ERROR: manifest not found}}
   891    892   
   892    893   ###############################################################################
   893    894   
   894         -run_in_checkout {
          895  +test_in_checkout th1-globalState-1 {
   895    896     if {$th1Hooks} {
   896    897       fossil test-th-eval "globalState checkout"
   897    898     } else {
   898    899       # NOTE: No TH1 hooks, force checkout to be populated.
   899    900       fossil test-th-eval --open-config "globalState checkout"
   900    901     }
   901         -}
   902         -
   903         -test th1-globalState-1 {[string length $RESULT] > 0}
          902  +} {[string length $RESULT] > 0}
   904    903   
   905    904   ###############################################################################
   906    905   
   907         -run_in_checkout {
          906  +test_block_in_checkout th1-globalState-2 {
   908    907     if {$th1Hooks} {
   909    908       fossil test-th-eval "globalState checkout"
   910    909       test th1-globalState-2 {$RESULT eq [fossil test-th-eval checkout]}
   911    910     } else {
   912    911       # NOTE: No TH1 hooks, force checkout to be populated.
   913    912       fossil test-th-eval --open-config "globalState checkout"
   914    913   
................................................................................
   940    939   ###############################################################################
   941    940   
   942    941   fossil test-th-eval --errorlog foserrors.log "globalState log"
   943    942   test th1-globalState-7 {$RESULT eq "foserrors.log"}
   944    943   
   945    944   ###############################################################################
   946    945   
   947         -run_in_checkout {
          946  +test_in_checkout th1-globalState-8 {
   948    947     if {$th1Hooks} {
   949    948       fossil test-th-eval "globalState repository"
   950    949     } else {
   951    950       # NOTE: No TH1 hooks, force repository to be populated.
   952    951       fossil test-th-eval --open-config "globalState repository"
   953    952     }
   954         -}
   955         -
   956         -test th1-globalState-8 {[string length $RESULT] > 0}
          953  +} {[string length $RESULT] > 0}
   957    954   
   958    955   ###############################################################################
   959    956   
   960         -run_in_checkout {
          957  +test_block_in_checkout th1-globalState-9 {
   961    958     if {$th1Hooks} {
   962    959       fossil test-th-eval "globalState repository"
   963    960       test th1-globalState-9 {$RESULT eq [fossil test-th-eval repository]}
   964    961     } else {
   965    962       # NOTE: No TH1 hooks, force repository to be populated.
   966    963       fossil test-th-eval --open-config "globalState repository"
   967    964   
................................................................................
  1601   1598   
  1602   1599   fossil test-th-eval {encode64 test\x00}
  1603   1600   test th1-encode64-2 {$RESULT eq "dGVzdAA="}
  1604   1601   
  1605   1602   ###############################################################################
  1606   1603   
  1607   1604   #
  1608         -# TODO: Modify the result of this test if the source file (i.e.
  1609         -#       "ajax/cgi-bin/fossil-json.cgi.example") changes.
         1605  +# This test will fail if the Fossil source file named below changes.  Update
         1606  +# the expected result string below if that happens.
  1610   1607   #
  1611         -run_in_checkout {
         1608  +test_in_checkout th1-encode64-3 {
  1612   1609     fossil test-th-eval --open-config \
  1613   1610         {encode64 [artifact trunk ajax/cgi-bin/fossil-json.cgi.example]}
         1611  +} {
         1612  +  $RESULT eq "IyEvcGF0aC90by9mb3NzaWwvYmluYXJ5CnJlcG9zaXRvcnk6IC9wYXRoL3RvL3JlcG8uZnNsCg=="
  1614   1613   }
  1615   1614   
  1616         -test th1-encode64-3 {$RESULT eq \
  1617         -"IyEvcGF0aC90by9mb3NzaWwvYmluYXJ5CnJlcG9zaXRvcnk6IC9wYXRoL3RvL3JlcG8uZnNsCg=="}
  1618         -
  1619   1615   ###############################################################################
  1620   1616   
  1621   1617   fossil test-th-eval {array exists tcl_platform}
  1622   1618   test th1-platform-1 {$RESULT eq "1"}
  1623   1619   
  1624   1620   ###############################################################################
  1625   1621   

Changes to win/Makefile.dmc.

    26     26   TCC    = $(DMDIR)\bin\dmc $(CFLAGS) $(DMCDEF) $(SSL) $(INCL)
    27     27   LIBS   = $(DMDIR)\extra\lib\ zlib wsock32 advapi32 dnsapi
    28     28   
    29     29   SQLITE_OPTIONS = -DNDEBUG=1 -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_GET_TABLE -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_FTS3_PARENTHESIS -DSQLITE_ENABLE_DBSTAT_VTAB -DSQLITE_ENABLE_JSON1 -DSQLITE_ENABLE_FTS5 -DSQLITE_ENABLE_STMTVTAB -DSQLITE_HAVE_ZLIB -DSQLITE_INTROSPECTION_PRAGMAS -DSQLITE_ENABLE_DBPAGE_VTAB
    30     30   
    31     31   SHELL_OPTIONS = -DNDEBUG=1 -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_GET_TABLE -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_FTS3_PARENTHESIS -DSQLITE_ENABLE_DBSTAT_VTAB -DSQLITE_ENABLE_JSON1 -DSQLITE_ENABLE_FTS5 -DSQLITE_ENABLE_STMTVTAB -DSQLITE_HAVE_ZLIB -DSQLITE_INTROSPECTION_PRAGMAS -DSQLITE_ENABLE_DBPAGE_VTAB -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     32   
    33         -SRC   = add_.c allrepo_.c attach_.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 checkin_.c checkout_.c clearsign_.c clone_.c comformat_.c configure_.c content_.c cookies_.c db_.c delta_.c deltacmd_.c descendants_.c diff_.c diffcmd_.c dispatch_.c doc_.c email_.c encode_.c etag_.c event_.c export_.c file_.c finfo_.c foci_.c forum_.c fshell_.c fusefs_.c glob_.c graph_.c gzip_.c hname_.c http_.c http_socket_.c http_ssl_.c http_transport_.c import_.c info_.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 pivot_.c popen_.c pqueue_.c printf_.c publish_.c purge_.c rebuild_.c regexp_.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 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 wysiwyg_.c xfer_.c xfersetup_.c zip_.c
           33  +SRC   = add_.c alerts_.c allrepo_.c attach_.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 checkin_.c checkout_.c clearsign_.c clone_.c comformat_.c configure_.c content_.c cookies_.c db_.c delta_.c deltacmd_.c descendants_.c diff_.c diffcmd_.c dispatch_.c doc_.c encode_.c etag_.c event_.c export_.c file_.c finfo_.c foci_.c forum_.c fshell_.c fusefs_.c glob_.c graph_.c gzip_.c hname_.c http_.c http_socket_.c http_ssl_.c http_transport_.c import_.c info_.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 pivot_.c popen_.c pqueue_.c printf_.c publish_.c purge_.c rebuild_.c regexp_.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 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 wysiwyg_.c xfer_.c xfersetup_.c zip_.c
    34     34   
    35         -OBJ   = $(OBJDIR)\add$O $(OBJDIR)\allrepo$O $(OBJDIR)\attach$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)\checkin$O $(OBJDIR)\checkout$O $(OBJDIR)\clearsign$O $(OBJDIR)\clone$O $(OBJDIR)\comformat$O $(OBJDIR)\configure$O $(OBJDIR)\content$O $(OBJDIR)\cookies$O $(OBJDIR)\db$O $(OBJDIR)\delta$O $(OBJDIR)\deltacmd$O $(OBJDIR)\descendants$O $(OBJDIR)\diff$O $(OBJDIR)\diffcmd$O $(OBJDIR)\dispatch$O $(OBJDIR)\doc$O $(OBJDIR)\email$O $(OBJDIR)\encode$O $(OBJDIR)\etag$O $(OBJDIR)\event$O $(OBJDIR)\export$O $(OBJDIR)\file$O $(OBJDIR)\finfo$O $(OBJDIR)\foci$O $(OBJDIR)\forum$O $(OBJDIR)\fshell$O $(OBJDIR)\fusefs$O $(OBJDIR)\glob$O $(OBJDIR)\graph$O $(OBJDIR)\gzip$O $(OBJDIR)\hname$O $(OBJDIR)\http$O $(OBJDIR)\http_socket$O $(OBJDIR)\http_ssl$O $(OBJDIR)\http_transport$O $(OBJDIR)\import$O $(OBJDIR)\info$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)\pivot$O $(OBJDIR)\popen$O $(OBJDIR)\pqueue$O $(OBJDIR)\printf$O $(OBJDIR)\publish$O $(OBJDIR)\purge$O $(OBJDIR)\rebuild$O $(OBJDIR)\regexp$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)\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)\wysiwyg$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)\alerts$O $(OBJDIR)\allrepo$O $(OBJDIR)\attach$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)\checkin$O $(OBJDIR)\checkout$O $(OBJDIR)\clearsign$O $(OBJDIR)\clone$O $(OBJDIR)\comformat$O $(OBJDIR)\configure$O $(OBJDIR)\content$O $(OBJDIR)\cookies$O $(OBJDIR)\db$O $(OBJDIR)\delta$O $(OBJDIR)\deltacmd$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)\file$O $(OBJDIR)\finfo$O $(OBJDIR)\foci$O $(OBJDIR)\forum$O $(OBJDIR)\fshell$O $(OBJDIR)\fusefs$O $(OBJDIR)\glob$O $(OBJDIR)\graph$O $(OBJDIR)\gzip$O $(OBJDIR)\hname$O $(OBJDIR)\http$O $(OBJDIR)\http_socket$O $(OBJDIR)\http_ssl$O $(OBJDIR)\http_transport$O $(OBJDIR)\import$O $(OBJDIR)\info$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)\pivot$O $(OBJDIR)\popen$O $(OBJDIR)\pqueue$O $(OBJDIR)\printf$O $(OBJDIR)\publish$O $(OBJDIR)\purge$O $(OBJDIR)\rebuild$O $(OBJDIR)\regexp$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)\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)\wysiwyg$O $(OBJDIR)\xfer$O $(OBJDIR)\xfersetup$O $(OBJDIR)\zip$O $(OBJDIR)\shell$O $(OBJDIR)\sqlite3$O $(OBJDIR)\th$O $(OBJDIR)\th_lang$O
    36     36   
    37     37   
    38     38   RC=$(DMDIR)\bin\rcc
    39     39   RCFLAGS=-32 -w1 -I$(SRCDIR) /D__DMC__
    40     40   
    41     41   APPNAME = $(OBJDIR)\fossil$(E)
    42     42   
................................................................................
    47     47   	codecheck1$E $(SRC)
    48     48   	$(DMDIR)\bin\link @link
    49     49   
    50     50   $(OBJDIR)\fossil.res:	$B\win\fossil.rc
    51     51   	$(RC) $(RCFLAGS) -o$@ $**
    52     52   
    53     53   $(OBJDIR)\link: $B\win\Makefile.dmc $(OBJDIR)\fossil.res
    54         -	+echo add allrepo attach backoffice bag bisect blob branch browse builtin bundle cache capabilities captcha cgi checkin checkout clearsign clone comformat configure content cookies db delta deltacmd descendants diff diffcmd dispatch doc email encode etag event export file finfo foci forum fshell fusefs glob graph gzip hname http http_socket http_ssl http_transport import info 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 pivot popen pqueue printf publish purge rebuild regexp report rss schema search security_audit setup setupuser sha1 sha1hard sha3 shun sitemap skins smtp sqlcmd stash stat statrep style sync tag tar th_main timeline tkt tktsetup undo unicode unversioned update url user utf8 util verify vfile webmail wiki wikiformat winfile winhttp wysiwyg xfer xfersetup zip shell sqlite3 th th_lang > $@
           54  +	+echo add alerts allrepo attach backoffice bag bisect blob branch browse builtin bundle cache capabilities captcha cgi checkin checkout clearsign clone comformat configure content cookies db delta deltacmd descendants diff diffcmd dispatch doc encode etag event export file finfo foci forum fshell fusefs glob graph gzip hname http http_socket http_ssl http_transport import info 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 pivot popen pqueue printf publish purge rebuild regexp report rss schema search security_audit setup setupuser sha1 sha1hard sha3 shun sitemap skins smtp sqlcmd stash stat statrep style sync tag tar th_main timeline tkt tktsetup undo unicode unversioned update url user utf8 util verify vfile webmail wiki wikiformat winfile winhttp wysiwyg xfer xfersetup zip shell sqlite3 th th_lang > $@
    55     55   	+echo fossil >> $@
    56     56   	+echo fossil >> $@
    57     57   	+echo $(LIBS) >> $@
    58     58   	+echo. >> $@
    59     59   	+echo fossil >> $@
    60     60   
    61     61   translate$E: $(SRCDIR)\translate.c
................................................................................
   132    132   
   133    133   
   134    134   $(OBJDIR)\add$O : add_.c add.h
   135    135   	$(TCC) -o$@ -c add_.c
   136    136   
   137    137   add_.c : $(SRCDIR)\add.c
   138    138   	+translate$E $** > $@
          139  +
          140  +$(OBJDIR)\alerts$O : alerts_.c alerts.h
          141  +	$(TCC) -o$@ -c alerts_.c
          142  +
          143  +alerts_.c : $(SRCDIR)\alerts.c
          144  +	+translate$E $** > $@
   139    145   
   140    146   $(OBJDIR)\allrepo$O : allrepo_.c allrepo.h
   141    147   	$(TCC) -o$@ -c allrepo_.c
   142    148   
   143    149   allrepo_.c : $(SRCDIR)\allrepo.c
   144    150   	+translate$E $** > $@
   145    151   
................................................................................
   313    319   
   314    320   $(OBJDIR)\doc$O : doc_.c doc.h
   315    321   	$(TCC) -o$@ -c doc_.c
   316    322   
   317    323   doc_.c : $(SRCDIR)\doc.c
   318    324   	+translate$E $** > $@
   319    325   
   320         -$(OBJDIR)\email$O : email_.c email.h
   321         -	$(TCC) -o$@ -c email_.c
   322         -
   323         -email_.c : $(SRCDIR)\email.c
   324         -	+translate$E $** > $@
   325         -
   326    326   $(OBJDIR)\encode$O : encode_.c encode.h
   327    327   	$(TCC) -o$@ -c encode_.c
   328    328   
   329    329   encode_.c : $(SRCDIR)\encode.c
   330    330   	+translate$E $** > $@
   331    331   
   332    332   $(OBJDIR)\etag$O : etag_.c etag.h
................................................................................
   938    938   $(OBJDIR)\zip$O : zip_.c zip.h
   939    939   	$(TCC) -o$@ -c zip_.c
   940    940   
   941    941   zip_.c : $(SRCDIR)\zip.c
   942    942   	+translate$E $** > $@
   943    943   
   944    944   headers: makeheaders$E page_index.h builtin_data.h default_css.h VERSION.h
   945         -	 +makeheaders$E add_.c:add.h allrepo_.c:allrepo.h attach_.c:attach.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 checkin_.c:checkin.h checkout_.c:checkout.h clearsign_.c:clearsign.h clone_.c:clone.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 descendants_.c:descendants.h diff_.c:diff.h diffcmd_.c:diffcmd.h dispatch_.c:dispatch.h doc_.c:doc.h email_.c:email.h encode_.c:encode.h etag_.c:etag.h event_.c:event.h export_.c:export.h file_.c:file.h finfo_.c:finfo.h foci_.c:foci.h forum_.c:forum.h fshell_.c:fshell.h fusefs_.c:fusefs.h glob_.c:glob.h graph_.c:graph.h gzip_.c:gzip.h hname_.c:hname.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 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 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 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 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 wysiwyg_.c:wysiwyg.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
          945  +	 +makeheaders$E add_.c:add.h alerts_.c:alerts.h allrepo_.c:allrepo.h attach_.c:attach.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 checkin_.c:checkin.h checkout_.c:checkout.h clearsign_.c:clearsign.h clone_.c:clone.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 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 file_.c:file.h finfo_.c:finfo.h foci_.c:foci.h forum_.c:forum.h fshell_.c:fshell.h fusefs_.c:fusefs.h glob_.c:glob.h graph_.c:graph.h gzip_.c:gzip.h hname_.c:hname.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 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 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 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 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 wysiwyg_.c:wysiwyg.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
   946    946   	@copy /Y nul: headers

Changes to win/Makefile.mingw.

   158    158   ZLIBCONFIG =
   159    159   ZLIBTARGETS =
   160    160   endif
   161    161   
   162    162   #### Disable creation of the OpenSSL shared libraries.  Also, disable support
   163    163   #    for SSLv3 (i.e. thereby forcing the use of TLS).
   164    164   #
   165         -SSLCONFIG += no-ssl3 enable-capieng no-weak-ssl-ciphers no-shared
          165  +SSLCONFIG += no-ssl3 no-weak-ssl-ciphers no-shared
   166    166   
   167    167   #### When using zlib, make sure that OpenSSL is configured to use the zlib
   168    168   #    that Fossil knows about (i.e. the one within the source tree).
   169    169   #
   170    170   ifndef FOSSIL_ENABLE_MINIZ
   171    171   SSLCONFIG +=  --with-zlib-lib=$(PWD)/$(ZLIBDIR) --with-zlib-include=$(PWD)/$(ZLIBDIR) zlib
   172    172   endif
................................................................................
   435    435   # You should not need to change anything below this line
   436    436   #--------------------------------------------------------
   437    437   XBCC = $(BCC) $(CFLAGS)
   438    438   XTCC = $(TCC) $(CFLAGS) -I. -I$(SRCDIR)
   439    439   
   440    440   SRC = \
   441    441     $(SRCDIR)/add.c \
          442  +  $(SRCDIR)/alerts.c \
   442    443     $(SRCDIR)/allrepo.c \
   443    444     $(SRCDIR)/attach.c \
   444    445     $(SRCDIR)/backoffice.c \
   445    446     $(SRCDIR)/bag.c \
   446    447     $(SRCDIR)/bisect.c \
   447    448     $(SRCDIR)/blob.c \
   448    449     $(SRCDIR)/branch.c \
................................................................................
   465    466     $(SRCDIR)/delta.c \
   466    467     $(SRCDIR)/deltacmd.c \
   467    468     $(SRCDIR)/descendants.c \
   468    469     $(SRCDIR)/diff.c \
   469    470     $(SRCDIR)/diffcmd.c \
   470    471     $(SRCDIR)/dispatch.c \
   471    472     $(SRCDIR)/doc.c \
   472         -  $(SRCDIR)/email.c \
   473    473     $(SRCDIR)/encode.c \
   474    474     $(SRCDIR)/etag.c \
   475    475     $(SRCDIR)/event.c \
   476    476     $(SRCDIR)/export.c \
   477    477     $(SRCDIR)/file.c \
   478    478     $(SRCDIR)/finfo.c \
   479    479     $(SRCDIR)/foci.c \
................................................................................
   598    598     $(SRCDIR)/../skins/bootstrap/details.txt \
   599    599     $(SRCDIR)/../skins/bootstrap/footer.txt \
   600    600     $(SRCDIR)/../skins/bootstrap/header.txt \
   601    601     $(SRCDIR)/../skins/default/css.txt \
   602    602     $(SRCDIR)/../skins/default/details.txt \
   603    603     $(SRCDIR)/../skins/default/footer.txt \
   604    604     $(SRCDIR)/../skins/default/header.txt \
          605  +  $(SRCDIR)/../skins/default/js.txt \
   605    606     $(SRCDIR)/../skins/eagle/css.txt \
   606    607     $(SRCDIR)/../skins/eagle/details.txt \
   607    608     $(SRCDIR)/../skins/eagle/footer.txt \
   608    609     $(SRCDIR)/../skins/eagle/header.txt \
   609    610     $(SRCDIR)/../skins/enhanced1/css.txt \
   610    611     $(SRCDIR)/../skins/enhanced1/details.txt \
   611    612     $(SRCDIR)/../skins/enhanced1/footer.txt \
................................................................................
   644    645     $(SRCDIR)/sorttable.js \
   645    646     $(SRCDIR)/tree.js \
   646    647     $(SRCDIR)/useredit.js \
   647    648     $(SRCDIR)/wiki.wiki
   648    649   
   649    650   TRANS_SRC = \
   650    651     $(OBJDIR)/add_.c \
          652  +  $(OBJDIR)/alerts_.c \
   651    653     $(OBJDIR)/allrepo_.c \
   652    654     $(OBJDIR)/attach_.c \
   653    655     $(OBJDIR)/backoffice_.c \
   654    656     $(OBJDIR)/bag_.c \
   655    657     $(OBJDIR)/bisect_.c \
   656    658     $(OBJDIR)/blob_.c \
   657    659     $(OBJDIR)/branch_.c \
................................................................................
   674    676     $(OBJDIR)/delta_.c \
   675    677     $(OBJDIR)/deltacmd_.c \
   676    678     $(OBJDIR)/descendants_.c \
   677    679     $(OBJDIR)/diff_.c \
   678    680     $(OBJDIR)/diffcmd_.c \
   679    681     $(OBJDIR)/dispatch_.c \
   680    682     $(OBJDIR)/doc_.c \
   681         -  $(OBJDIR)/email_.c \
   682    683     $(OBJDIR)/encode_.c \
   683    684     $(OBJDIR)/etag_.c \
   684    685     $(OBJDIR)/event_.c \
   685    686     $(OBJDIR)/export_.c \
   686    687     $(OBJDIR)/file_.c \
   687    688     $(OBJDIR)/finfo_.c \
   688    689     $(OBJDIR)/foci_.c \
................................................................................
   781    782     $(OBJDIR)/wysiwyg_.c \
   782    783     $(OBJDIR)/xfer_.c \
   783    784     $(OBJDIR)/xfersetup_.c \
   784    785     $(OBJDIR)/zip_.c
   785    786   
   786    787   OBJ = \
   787    788    $(OBJDIR)/add.o \
          789  + $(OBJDIR)/alerts.o \
   788    790    $(OBJDIR)/allrepo.o \
   789    791    $(OBJDIR)/attach.o \
   790    792    $(OBJDIR)/backoffice.o \
   791    793    $(OBJDIR)/bag.o \
   792    794    $(OBJDIR)/bisect.o \
   793    795    $(OBJDIR)/blob.o \
   794    796    $(OBJDIR)/branch.o \
................................................................................
   811    813    $(OBJDIR)/delta.o \
   812    814    $(OBJDIR)/deltacmd.o \
   813    815    $(OBJDIR)/descendants.o \
   814    816    $(OBJDIR)/diff.o \
   815    817    $(OBJDIR)/diffcmd.o \
   816    818    $(OBJDIR)/dispatch.o \
   817    819    $(OBJDIR)/doc.o \
   818         - $(OBJDIR)/email.o \
   819    820    $(OBJDIR)/encode.o \
   820    821    $(OBJDIR)/etag.o \
   821    822    $(OBJDIR)/event.o \
   822    823    $(OBJDIR)/export.o \
   823    824    $(OBJDIR)/file.o \
   824    825    $(OBJDIR)/finfo.o \
   825    826    $(OBJDIR)/foci.o \
................................................................................
  1137   1138   	$(MKINDEX) $(TRANS_SRC) >$@
  1138   1139   
  1139   1140   $(OBJDIR)/builtin_data.h:	$(MKBUILTIN) $(EXTRA_FILES)
  1140   1141   	$(MKBUILTIN) --prefix $(SRCDIR)/ $(EXTRA_FILES) >$@
  1141   1142   
  1142   1143   $(OBJDIR)/headers:	$(OBJDIR)/page_index.h $(OBJDIR)/builtin_data.h $(OBJDIR)/default_css.h $(MAKEHEADERS) $(OBJDIR)/VERSION.h
  1143   1144   	$(MAKEHEADERS) $(OBJDIR)/add_.c:$(OBJDIR)/add.h \
         1145  +		$(OBJDIR)/alerts_.c:$(OBJDIR)/alerts.h \
  1144   1146   		$(OBJDIR)/allrepo_.c:$(OBJDIR)/allrepo.h \
  1145   1147   		$(OBJDIR)/attach_.c:$(OBJDIR)/attach.h \
  1146   1148   		$(OBJDIR)/backoffice_.c:$(OBJDIR)/backoffice.h \
  1147   1149   		$(OBJDIR)/bag_.c:$(OBJDIR)/bag.h \
  1148   1150   		$(OBJDIR)/bisect_.c:$(OBJDIR)/bisect.h \
  1149   1151   		$(OBJDIR)/blob_.c:$(OBJDIR)/blob.h \
  1150   1152   		$(OBJDIR)/branch_.c:$(OBJDIR)/branch.h \
................................................................................
  1167   1169   		$(OBJDIR)/delta_.c:$(OBJDIR)/delta.h \
  1168   1170   		$(OBJDIR)/deltacmd_.c:$(OBJDIR)/deltacmd.h \
  1169   1171   		$(OBJDIR)/descendants_.c:$(OBJDIR)/descendants.h \
  1170   1172   		$(OBJDIR)/diff_.c:$(OBJDIR)/diff.h \
  1171   1173   		$(OBJDIR)/diffcmd_.c:$(OBJDIR)/diffcmd.h \
  1172   1174   		$(OBJDIR)/dispatch_.c:$(OBJDIR)/dispatch.h \
  1173   1175   		$(OBJDIR)/doc_.c:$(OBJDIR)/doc.h \
  1174         -		$(OBJDIR)/email_.c:$(OBJDIR)/email.h \
  1175   1176   		$(OBJDIR)/encode_.c:$(OBJDIR)/encode.h \
  1176   1177   		$(OBJDIR)/etag_.c:$(OBJDIR)/etag.h \
  1177   1178   		$(OBJDIR)/event_.c:$(OBJDIR)/event.h \
  1178   1179   		$(OBJDIR)/export_.c:$(OBJDIR)/export.h \
  1179   1180   		$(OBJDIR)/file_.c:$(OBJDIR)/file.h \
  1180   1181   		$(OBJDIR)/finfo_.c:$(OBJDIR)/finfo.h \
  1181   1182   		$(OBJDIR)/foci_.c:$(OBJDIR)/foci.h \
................................................................................
  1287   1288   $(OBJDIR)/add_.c:	$(SRCDIR)/add.c $(TRANSLATE)
  1288   1289   	$(TRANSLATE) $(SRCDIR)/add.c >$@
  1289   1290   
  1290   1291   $(OBJDIR)/add.o:	$(OBJDIR)/add_.c $(OBJDIR)/add.h $(SRCDIR)/config.h
  1291   1292   	$(XTCC) -o $(OBJDIR)/add.o -c $(OBJDIR)/add_.c
  1292   1293   
  1293   1294   $(OBJDIR)/add.h:	$(OBJDIR)/headers
         1295  +
         1296  +$(OBJDIR)/alerts_.c:	$(SRCDIR)/alerts.c $(TRANSLATE)
         1297  +	$(TRANSLATE) $(SRCDIR)/alerts.c >$@
         1298  +
         1299  +$(OBJDIR)/alerts.o:	$(OBJDIR)/alerts_.c $(OBJDIR)/alerts.h $(SRCDIR)/config.h
         1300  +	$(XTCC) -o $(OBJDIR)/alerts.o -c $(OBJDIR)/alerts_.c
         1301  +
         1302  +$(OBJDIR)/alerts.h:	$(OBJDIR)/headers
  1294   1303   
  1295   1304   $(OBJDIR)/allrepo_.c:	$(SRCDIR)/allrepo.c $(TRANSLATE)
  1296   1305   	$(TRANSLATE) $(SRCDIR)/allrepo.c >$@
  1297   1306   
  1298   1307   $(OBJDIR)/allrepo.o:	$(OBJDIR)/allrepo_.c $(OBJDIR)/allrepo.h $(SRCDIR)/config.h
  1299   1308   	$(XTCC) -o $(OBJDIR)/allrepo.o -c $(OBJDIR)/allrepo_.c
  1300   1309   
................................................................................
  1528   1537   	$(TRANSLATE) $(SRCDIR)/doc.c >$@
  1529   1538   
  1530   1539   $(OBJDIR)/doc.o:	$(OBJDIR)/doc_.c $(OBJDIR)/doc.h $(SRCDIR)/config.h
  1531   1540   	$(XTCC) -o $(OBJDIR)/doc.o -c $(OBJDIR)/doc_.c
  1532   1541   
  1533   1542   $(OBJDIR)/doc.h:	$(OBJDIR)/headers
  1534   1543   
  1535         -$(OBJDIR)/email_.c:	$(SRCDIR)/email.c $(TRANSLATE)
  1536         -	$(TRANSLATE) $(SRCDIR)/email.c >$@
  1537         -
  1538         -$(OBJDIR)/email.o:	$(OBJDIR)/email_.c $(OBJDIR)/email.h $(SRCDIR)/config.h
  1539         -	$(XTCC) -o $(OBJDIR)/email.o -c $(OBJDIR)/email_.c
  1540         -
  1541         -$(OBJDIR)/email.h:	$(OBJDIR)/headers
  1542         -
  1543   1544   $(OBJDIR)/encode_.c:	$(SRCDIR)/encode.c $(TRANSLATE)
  1544   1545   	$(TRANSLATE) $(SRCDIR)/encode.c >$@
  1545   1546   
  1546   1547   $(OBJDIR)/encode.o:	$(OBJDIR)/encode_.c $(OBJDIR)/encode.h $(SRCDIR)/config.h
  1547   1548   	$(XTCC) -o $(OBJDIR)/encode.o -c $(OBJDIR)/encode_.c
  1548   1549   
  1549   1550   $(OBJDIR)/encode.h:	$(OBJDIR)/headers

Changes to win/Makefile.mingw.mistachkin.

   158    158   ZLIBCONFIG =
   159    159   ZLIBTARGETS =
   160    160   endif
   161    161   
   162    162   #### Disable creation of the OpenSSL shared libraries.  Also, disable support
   163    163   #    for SSLv3 (i.e. thereby forcing the use of TLS).
   164    164   #
   165         -SSLCONFIG += no-ssl3 enable-capieng no-weak-ssl-ciphers no-shared
          165  +SSLCONFIG += no-ssl3 no-weak-ssl-ciphers no-shared
   166    166   
   167    167   #### When using zlib, make sure that OpenSSL is configured to use the zlib
   168    168   #    that Fossil knows about (i.e. the one within the source tree).
   169    169   #
   170    170   ifndef FOSSIL_ENABLE_MINIZ
   171    171   SSLCONFIG +=  --with-zlib-lib=$(PWD)/$(ZLIBDIR) --with-zlib-include=$(PWD)/$(ZLIBDIR) zlib
   172    172   endif
................................................................................
   435    435   # You should not need to change anything below this line
   436    436   #--------------------------------------------------------
   437    437   XBCC = $(BCC) $(CFLAGS)
   438    438   XTCC = $(TCC) $(CFLAGS) -I. -I$(SRCDIR)
   439    439   
   440    440   SRC = \
   441    441     $(SRCDIR)/add.c \
          442  +  $(SRCDIR)/alerts.c \
   442    443     $(SRCDIR)/allrepo.c \
   443    444     $(SRCDIR)/attach.c \
   444    445     $(SRCDIR)/backoffice.c \
   445    446     $(SRCDIR)/bag.c \
   446    447     $(SRCDIR)/bisect.c \
   447    448     $(SRCDIR)/blob.c \
   448    449     $(SRCDIR)/branch.c \
................................................................................
   465    466     $(SRCDIR)/delta.c \
   466    467     $(SRCDIR)/deltacmd.c \
   467    468     $(SRCDIR)/descendants.c \
   468    469     $(SRCDIR)/diff.c \
   469    470     $(SRCDIR)/diffcmd.c \
   470    471     $(SRCDIR)/dispatch.c \
   471    472     $(SRCDIR)/doc.c \
   472         -  $(SRCDIR)/email.c \
   473    473     $(SRCDIR)/encode.c \
   474    474     $(SRCDIR)/etag.c \
   475    475     $(SRCDIR)/event.c \
   476    476     $(SRCDIR)/export.c \
   477    477     $(SRCDIR)/file.c \
   478    478     $(SRCDIR)/finfo.c \
   479    479     $(SRCDIR)/foci.c \
................................................................................
   530    530     $(SRCDIR)/regexp.c \
   531    531     $(SRCDIR)/report.c \
   532    532     $(SRCDIR)/rss.c \
   533    533     $(SRCDIR)/schema.c \
   534    534     $(SRCDIR)/search.c \
   535    535     $(SRCDIR)/security_audit.c \
   536    536     $(SRCDIR)/setup.c \
          537  +  $(SRCDIR)/setupuser.c \
   537    538     $(SRCDIR)/sha1.c \
   538    539     $(SRCDIR)/sha1hard.c \
   539    540     $(SRCDIR)/sha3.c \
   540    541     $(SRCDIR)/shun.c \
   541    542     $(SRCDIR)/sitemap.c \
   542    543     $(SRCDIR)/skins.c \
   543    544     $(SRCDIR)/smtp.c \
................................................................................
   597    598     $(SRCDIR)/../skins/bootstrap/details.txt \
   598    599     $(SRCDIR)/../skins/bootstrap/footer.txt \
   599    600     $(SRCDIR)/../skins/bootstrap/header.txt \
   600    601     $(SRCDIR)/../skins/default/css.txt \
   601    602     $(SRCDIR)/../skins/default/details.txt \
   602    603     $(SRCDIR)/../skins/default/footer.txt \
   603    604     $(SRCDIR)/../skins/default/header.txt \
          605  +  $(SRCDIR)/../skins/default/js.txt \
   604    606     $(SRCDIR)/../skins/eagle/css.txt \
   605    607     $(SRCDIR)/../skins/eagle/details.txt \
   606    608     $(SRCDIR)/../skins/eagle/footer.txt \
   607    609     $(SRCDIR)/../skins/eagle/header.txt \
   608    610     $(SRCDIR)/../skins/enhanced1/css.txt \
   609    611     $(SRCDIR)/../skins/enhanced1/details.txt \
   610    612     $(SRCDIR)/../skins/enhanced1/footer.txt \
................................................................................
   643    645     $(SRCDIR)/sorttable.js \
   644    646     $(SRCDIR)/tree.js \
   645    647     $(SRCDIR)/useredit.js \
   646    648     $(SRCDIR)/wiki.wiki
   647    649   
   648    650   TRANS_SRC = \
   649    651     $(OBJDIR)/add_.c \
          652  +  $(OBJDIR)/alerts_.c \
   650    653     $(OBJDIR)/allrepo_.c \
   651    654     $(OBJDIR)/attach_.c \
   652    655     $(OBJDIR)/backoffice_.c \
   653    656     $(OBJDIR)/bag_.c \
   654    657     $(OBJDIR)/bisect_.c \
   655    658     $(OBJDIR)/blob_.c \
   656    659     $(OBJDIR)/branch_.c \
................................................................................
   673    676     $(OBJDIR)/delta_.c \
   674    677     $(OBJDIR)/deltacmd_.c \
   675    678     $(OBJDIR)/descendants_.c \
   676    679     $(OBJDIR)/diff_.c \
   677    680     $(OBJDIR)/diffcmd_.c \
   678    681     $(OBJDIR)/dispatch_.c \
   679    682     $(OBJDIR)/doc_.c \
   680         -  $(OBJDIR)/email_.c \
   681    683     $(OBJDIR)/encode_.c \
   682    684     $(OBJDIR)/etag_.c \
   683    685     $(OBJDIR)/event_.c \
   684    686     $(OBJDIR)/export_.c \
   685    687     $(OBJDIR)/file_.c \
   686    688     $(OBJDIR)/finfo_.c \
   687    689     $(OBJDIR)/foci_.c \
................................................................................
   738    740     $(OBJDIR)/regexp_.c \
   739    741     $(OBJDIR)/report_.c \
   740    742     $(OBJDIR)/rss_.c \
   741    743     $(OBJDIR)/schema_.c \
   742    744     $(OBJDIR)/search_.c \
   743    745     $(OBJDIR)/security_audit_.c \
   744    746     $(OBJDIR)/setup_.c \
          747  +  $(OBJDIR)/setupuser_.c \
   745    748     $(OBJDIR)/sha1_.c \
   746    749     $(OBJDIR)/sha1hard_.c \
   747    750     $(OBJDIR)/sha3_.c \
   748    751     $(OBJDIR)/shun_.c \
   749    752     $(OBJDIR)/sitemap_.c \
   750    753     $(OBJDIR)/skins_.c \
   751    754     $(OBJDIR)/smtp_.c \
................................................................................
   779    782     $(OBJDIR)/wysiwyg_.c \
   780    783     $(OBJDIR)/xfer_.c \
   781    784     $(OBJDIR)/xfersetup_.c \
   782    785     $(OBJDIR)/zip_.c
   783    786   
   784    787   OBJ = \
   785    788    $(OBJDIR)/add.o \
          789  + $(OBJDIR)/alerts.o \
   786    790    $(OBJDIR)/allrepo.o \
   787    791    $(OBJDIR)/attach.o \
   788    792    $(OBJDIR)/backoffice.o \
   789    793    $(OBJDIR)/bag.o \
   790    794    $(OBJDIR)/bisect.o \
   791    795    $(OBJDIR)/blob.o \
   792    796    $(OBJDIR)/branch.o \
................................................................................
   809    813    $(OBJDIR)/delta.o \
   810    814    $(OBJDIR)/deltacmd.o \
   811    815    $(OBJDIR)/descendants.o \
   812    816    $(OBJDIR)/diff.o \
   813    817    $(OBJDIR)/diffcmd.o \
   814    818    $(OBJDIR)/dispatch.o \
   815    819    $(OBJDIR)/doc.o \
   816         - $(OBJDIR)/email.o \
   817    820    $(OBJDIR)/encode.o \
   818    821    $(OBJDIR)/etag.o \
   819    822    $(OBJDIR)/event.o \
   820    823    $(OBJDIR)/export.o \
   821    824    $(OBJDIR)/file.o \
   822    825    $(OBJDIR)/finfo.o \
   823    826    $(OBJDIR)/foci.o \
................................................................................
   874    877    $(OBJDIR)/regexp.o \
   875    878    $(OBJDIR)/report.o \
   876    879    $(OBJDIR)/rss.o \
   877    880    $(OBJDIR)/schema.o \
   878    881    $(OBJDIR)/search.o \
   879    882    $(OBJDIR)/security_audit.o \
   880    883    $(OBJDIR)/setup.o \
          884  + $(OBJDIR)/setupuser.o \
   881    885    $(OBJDIR)/sha1.o \
   882    886    $(OBJDIR)/sha1hard.o \
   883    887    $(OBJDIR)/sha3.o \
   884    888    $(OBJDIR)/shun.o \
   885    889    $(OBJDIR)/sitemap.o \
   886    890    $(OBJDIR)/skins.o \
   887    891    $(OBJDIR)/smtp.o \
................................................................................
  1134   1138   	$(MKINDEX) $(TRANS_SRC) >$@
  1135   1139   
  1136   1140   $(OBJDIR)/builtin_data.h:	$(MKBUILTIN) $(EXTRA_FILES)
  1137   1141   	$(MKBUILTIN) --prefix $(SRCDIR)/ $(EXTRA_FILES) >$@
  1138   1142   
  1139   1143   $(OBJDIR)/headers:	$(OBJDIR)/page_index.h $(OBJDIR)/builtin_data.h $(OBJDIR)/default_css.h $(MAKEHEADERS) $(OBJDIR)/VERSION.h
  1140   1144   	$(MAKEHEADERS) $(OBJDIR)/add_.c:$(OBJDIR)/add.h \
         1145  +		$(OBJDIR)/alerts_.c:$(OBJDIR)/alerts.h \
  1141   1146   		$(OBJDIR)/allrepo_.c:$(OBJDIR)/allrepo.h \
  1142   1147   		$(OBJDIR)/attach_.c:$(OBJDIR)/attach.h \
  1143   1148   		$(OBJDIR)/backoffice_.c:$(OBJDIR)/backoffice.h \
  1144   1149   		$(OBJDIR)/bag_.c:$(OBJDIR)/bag.h \
  1145   1150   		$(OBJDIR)/bisect_.c:$(OBJDIR)/bisect.h \
  1146   1151   		$(OBJDIR)/blob_.c:$(OBJDIR)/blob.h \
  1147   1152   		$(OBJDIR)/branch_.c:$(OBJDIR)/branch.h \
................................................................................
  1164   1169   		$(OBJDIR)/delta_.c:$(OBJDIR)/delta.h \
  1165   1170   		$(OBJDIR)/deltacmd_.c:$(OBJDIR)/deltacmd.h \
  1166   1171   		$(OBJDIR)/descendants_.c:$(OBJDIR)/descendants.h \
  1167   1172   		$(OBJDIR)/diff_.c:$(OBJDIR)/diff.h \
  1168   1173   		$(OBJDIR)/diffcmd_.c:$(OBJDIR)/diffcmd.h \
  1169   1174   		$(OBJDIR)/dispatch_.c:$(OBJDIR)/dispatch.h \
  1170   1175   		$(OBJDIR)/doc_.c:$(OBJDIR)/doc.h \
  1171         -		$(OBJDIR)/email_.c:$(OBJDIR)/email.h \
  1172   1176   		$(OBJDIR)/encode_.c:$(OBJDIR)/encode.h \
  1173   1177   		$(OBJDIR)/etag_.c:$(OBJDIR)/etag.h \
  1174   1178   		$(OBJDIR)/event_.c:$(OBJDIR)/event.h \
  1175   1179   		$(OBJDIR)/export_.c:$(OBJDIR)/export.h \
  1176   1180   		$(OBJDIR)/file_.c:$(OBJDIR)/file.h \
  1177   1181   		$(OBJDIR)/finfo_.c:$(OBJDIR)/finfo.h \
  1178   1182   		$(OBJDIR)/foci_.c:$(OBJDIR)/foci.h \
................................................................................
  1229   1233   		$(OBJDIR)/regexp_.c:$(OBJDIR)/regexp.h \
  1230   1234   		$(OBJDIR)/report_.c:$(OBJDIR)/report.h \
  1231   1235   		$(OBJDIR)/rss_.c:$(OBJDIR)/rss.h \
  1232   1236   		$(OBJDIR)/schema_.c:$(OBJDIR)/schema.h \
  1233   1237   		$(OBJDIR)/search_.c:$(OBJDIR)/search.h \
  1234   1238   		$(OBJDIR)/security_audit_.c:$(OBJDIR)/security_audit.h \
  1235   1239   		$(OBJDIR)/setup_.c:$(OBJDIR)/setup.h \
         1240  +		$(OBJDIR)/setupuser_.c:$(OBJDIR)/setupuser.h \
  1236   1241   		$(OBJDIR)/sha1_.c:$(OBJDIR)/sha1.h \
  1237   1242   		$(OBJDIR)/sha1hard_.c:$(OBJDIR)/sha1hard.h \
  1238   1243   		$(OBJDIR)/sha3_.c:$(OBJDIR)/sha3.h \
  1239   1244   		$(OBJDIR)/shun_.c:$(OBJDIR)/shun.h \
  1240   1245   		$(OBJDIR)/sitemap_.c:$(OBJDIR)/sitemap.h \
  1241   1246   		$(OBJDIR)/skins_.c:$(OBJDIR)/skins.h \
  1242   1247   		$(OBJDIR)/smtp_.c:$(OBJDIR)/smtp.h \
................................................................................
  1283   1288   $(OBJDIR)/add_.c:	$(SRCDIR)/add.c $(TRANSLATE)
  1284   1289   	$(TRANSLATE) $(SRCDIR)/add.c >$@
  1285   1290   
  1286   1291   $(OBJDIR)/add.o:	$(OBJDIR)/add_.c $(OBJDIR)/add.h $(SRCDIR)/config.h
  1287   1292   	$(XTCC) -o $(OBJDIR)/add.o -c $(OBJDIR)/add_.c
  1288   1293   
  1289   1294   $(OBJDIR)/add.h:	$(OBJDIR)/headers
         1295  +
         1296  +$(OBJDIR)/alerts_.c:	$(SRCDIR)/alerts.c $(TRANSLATE)
         1297  +	$(TRANSLATE) $(SRCDIR)/alerts.c >$@
         1298  +
         1299  +$(OBJDIR)/alerts.o:	$(OBJDIR)/alerts_.c $(OBJDIR)/alerts.h $(SRCDIR)/config.h
         1300  +	$(XTCC) -o $(OBJDIR)/alerts.o -c $(OBJDIR)/alerts_.c
         1301  +
         1302  +$(OBJDIR)/alerts.h:	$(OBJDIR)/headers
  1290   1303   
  1291   1304   $(OBJDIR)/allrepo_.c:	$(SRCDIR)/allrepo.c $(TRANSLATE)
  1292   1305   	$(TRANSLATE) $(SRCDIR)/allrepo.c >$@
  1293   1306   
  1294   1307   $(OBJDIR)/allrepo.o:	$(OBJDIR)/allrepo_.c $(OBJDIR)/allrepo.h $(SRCDIR)/config.h
  1295   1308   	$(XTCC) -o $(OBJDIR)/allrepo.o -c $(OBJDIR)/allrepo_.c
  1296   1309   
................................................................................
  1524   1537   	$(TRANSLATE) $(SRCDIR)/doc.c >$@
  1525   1538   
  1526   1539   $(OBJDIR)/doc.o:	$(OBJDIR)/doc_.c $(OBJDIR)/doc.h $(SRCDIR)/config.h
  1527   1540   	$(XTCC) -o $(OBJDIR)/doc.o -c $(OBJDIR)/doc_.c
  1528   1541   
  1529   1542   $(OBJDIR)/doc.h:	$(OBJDIR)/headers
  1530   1543   
  1531         -$(OBJDIR)/email_.c:	$(SRCDIR)/email.c $(TRANSLATE)
  1532         -	$(TRANSLATE) $(SRCDIR)/email.c >$@
  1533         -
  1534         -$(OBJDIR)/email.o:	$(OBJDIR)/email_.c $(OBJDIR)/email.h $(SRCDIR)/config.h
  1535         -	$(XTCC) -o $(OBJDIR)/email.o -c $(OBJDIR)/email_.c
  1536         -
  1537         -$(OBJDIR)/email.h:	$(OBJDIR)/headers
  1538         -
  1539   1544   $(OBJDIR)/encode_.c:	$(SRCDIR)/encode.c $(TRANSLATE)
  1540   1545   	$(TRANSLATE) $(SRCDIR)/encode.c >$@
  1541   1546   
  1542   1547   $(OBJDIR)/encode.o:	$(OBJDIR)/encode_.c $(OBJDIR)/encode.h $(SRCDIR)/config.h
  1543   1548   	$(XTCC) -o $(OBJDIR)/encode.o -c $(OBJDIR)/encode_.c
  1544   1549   
  1545   1550   $(OBJDIR)/encode.h:	$(OBJDIR)/headers
................................................................................
  2043   2048   $(OBJDIR)/setup_.c:	$(SRCDIR)/setup.c $(TRANSLATE)
  2044   2049   	$(TRANSLATE) $(SRCDIR)/setup.c >$@
  2045   2050   
  2046   2051   $(OBJDIR)/setup.o:	$(OBJDIR)/setup_.c $(OBJDIR)/setup.h $(SRCDIR)/config.h
  2047   2052   	$(XTCC) -o $(OBJDIR)/setup.o -c $(OBJDIR)/setup_.c
  2048   2053   
  2049   2054   $(OBJDIR)/setup.h:	$(OBJDIR)/headers
         2055  +
         2056  +$(OBJDIR)/setupuser_.c:	$(SRCDIR)/setupuser.c $(TRANSLATE)
         2057  +	$(TRANSLATE) $(SRCDIR)/setupuser.c >$@
         2058  +
         2059  +$(OBJDIR)/setupuser.o:	$(OBJDIR)/setupuser_.c $(OBJDIR)/setupuser.h $(SRCDIR)/config.h
         2060  +	$(XTCC) -o $(OBJDIR)/setupuser.o -c $(OBJDIR)/setupuser_.c
         2061  +
         2062  +$(OBJDIR)/setupuser.h:	$(OBJDIR)/headers
  2050   2063   
  2051   2064   $(OBJDIR)/sha1_.c:	$(SRCDIR)/sha1.c $(TRANSLATE)
  2052   2065   	$(TRANSLATE) $(SRCDIR)/sha1.c >$@
  2053   2066   
  2054   2067   $(OBJDIR)/sha1.o:	$(OBJDIR)/sha1_.c $(OBJDIR)/sha1.h $(SRCDIR)/config.h
  2055   2068   	$(XTCC) -o $(OBJDIR)/sha1.o -c $(OBJDIR)/sha1_.c
  2056   2069   

Changes to win/Makefile.msc.

   108    108   SSLLIBDIR = $(SSLDIR)\out32
   109    109   !endif
   110    110   SSLLFLAGS = /nologo /opt:ref /debug
   111    111   SSLLIB    = ssleay32.lib libeay32.lib user32.lib gdi32.lib crypt32.lib
   112    112   !if "$(PLATFORM)"=="amd64" || "$(PLATFORM)"=="x64"
   113    113   !message Using 'x64' platform for OpenSSL...
   114    114   # BUGBUG (OpenSSL): Using "no-ssl*" here breaks the build.
   115         -# SSLCONFIG = VC-WIN64A no-asm no-ssl3 enable-capieng no-weak-ssl-ciphers
          115  +# SSLCONFIG = VC-WIN64A no-asm no-ssl3 no-weak-ssl-ciphers
   116    116   SSLCONFIG = VC-WIN64A no-asm
   117    117   !if $(FOSSIL_DYNAMIC_BUILD)!=0
   118    118   SSLCONFIG = $(SSLCONFIG) shared
   119    119   !else
   120    120   SSLCONFIG = $(SSLCONFIG) no-shared
   121    121   !endif
   122    122   SSLSETUP  = ms\do_win64a.bat
................................................................................
   123    123   !if $(FOSSIL_DYNAMIC_BUILD)!=0
   124    124   SSLNMAKE  = ms\ntdll.mak all
   125    125   !else
   126    126   SSLNMAKE  = ms\nt.mak all
   127    127   !endif
   128    128   # BUGBUG (OpenSSL): Using "OPENSSL_NO_SSL*" here breaks dynamic builds.
   129    129   !if $(FOSSIL_DYNAMIC_BUILD)==0
   130         -SSLCFLAGS = -DOPENSSL_NO_SSL3
          130  +SSLCFLAGS = -DOPENSSL_NO_SSL3 -DOPENSSL_NO_WEAK_SSL_CIPHERS
   131    131   !endif
   132    132   !elseif "$(PLATFORM)"=="ia64"
   133    133   !message Using 'ia64' platform for OpenSSL...
   134    134   # BUGBUG (OpenSSL): Using "no-ssl*" here breaks the build.
   135         -# SSLCONFIG = VC-WIN64I no-asm no-ssl3 enable-capieng no-weak-ssl-ciphers
          135  +# SSLCONFIG = VC-WIN64I no-asm no-ssl3 no-weak-ssl-ciphers
   136    136   SSLCONFIG = VC-WIN64I no-asm
   137    137   !if $(FOSSIL_DYNAMIC_BUILD)!=0
   138    138   SSLCONFIG = $(SSLCONFIG) shared
   139    139   !else
   140    140   SSLCONFIG = $(SSLCONFIG) no-shared
   141    141   !endif
   142    142   SSLSETUP  = ms\do_win64i.bat
................................................................................
   143    143   !if $(FOSSIL_DYNAMIC_BUILD)!=0
   144    144   SSLNMAKE  = ms\ntdll.mak all
   145    145   !else
   146    146   SSLNMAKE  = ms\nt.mak all
   147    147   !endif
   148    148   # BUGBUG (OpenSSL): Using "OPENSSL_NO_SSL*" here breaks dynamic builds.
   149    149   !if $(FOSSIL_DYNAMIC_BUILD)==0
   150         -SSLCFLAGS = -DOPENSSL_NO_SSL3
          150  +SSLCFLAGS = -DOPENSSL_NO_SSL3 -DOPENSSL_NO_WEAK_SSL_CIPHERS
   151    151   !endif
   152    152   !else
   153    153   !message Assuming 'x86' platform for OpenSSL...
   154    154   # BUGBUG (OpenSSL): Using "no-ssl*" here breaks the build.
   155         -# SSLCONFIG = VC-WIN32 no-asm no-ssl3 enable-capieng no-weak-ssl-ciphers
          155  +# SSLCONFIG = VC-WIN32 no-asm no-ssl3 no-weak-ssl-ciphers
   156    156   SSLCONFIG = VC-WIN32 no-asm
   157    157   !if $(FOSSIL_DYNAMIC_BUILD)!=0
   158    158   SSLCONFIG = $(SSLCONFIG) shared
   159    159   !else
   160    160   SSLCONFIG = $(SSLCONFIG) no-shared
   161    161   !endif
   162    162   SSLSETUP  = ms\do_ms.bat
................................................................................
   163    163   !if $(FOSSIL_DYNAMIC_BUILD)!=0
   164    164   SSLNMAKE  = ms\ntdll.mak all
   165    165   !else
   166    166   SSLNMAKE  = ms\nt.mak all
   167    167   !endif
   168    168   # BUGBUG (OpenSSL): Using "OPENSSL_NO_SSL*" here breaks dynamic builds.
   169    169   !if $(FOSSIL_DYNAMIC_BUILD)==0
   170         -SSLCFLAGS = -DOPENSSL_NO_SSL3
          170  +SSLCFLAGS = -DOPENSSL_NO_SSL3 -DOPENSSL_NO_WEAK_SSL_CIPHERS
   171    171   !endif
   172    172   !endif
   173    173   !endif
   174    174   
   175    175   !if $(FOSSIL_ENABLE_TCL)!=0
   176    176   TCLDIR    = $(B)\compat\tcl-8.6
   177    177   TCLSRCDIR = $(TCLDIR)
................................................................................
   377    377                   /Dfopen=fossil_fopen
   378    378   
   379    379   MINIZ_OPTIONS = /DMINIZ_NO_STDIO \
   380    380                   /DMINIZ_NO_TIME \
   381    381                   /DMINIZ_NO_ARCHIVE_APIS
   382    382   
   383    383   SRC   = add_.c \
          384  +        alerts_.c \
   384    385           allrepo_.c \
   385    386           attach_.c \
   386    387           backoffice_.c \
   387    388           bag_.c \
   388    389           bisect_.c \
   389    390           blob_.c \
   390    391           branch_.c \
................................................................................
   407    408           delta_.c \
   408    409           deltacmd_.c \
   409    410           descendants_.c \
   410    411           diff_.c \
   411    412           diffcmd_.c \
   412    413           dispatch_.c \
   413    414           doc_.c \
   414         -        email_.c \
   415    415           encode_.c \
   416    416           etag_.c \
   417    417           event_.c \
   418    418           export_.c \
   419    419           file_.c \
   420    420           finfo_.c \
   421    421           foci_.c \
................................................................................
   539    539           $(SRCDIR)\..\skins\bootstrap\details.txt \
   540    540           $(SRCDIR)\..\skins\bootstrap\footer.txt \
   541    541           $(SRCDIR)\..\skins\bootstrap\header.txt \
   542    542           $(SRCDIR)\..\skins\default\css.txt \
   543    543           $(SRCDIR)\..\skins\default\details.txt \
   544    544           $(SRCDIR)\..\skins\default\footer.txt \
   545    545           $(SRCDIR)\..\skins\default\header.txt \
          546  +        $(SRCDIR)\..\skins\default\js.txt \
   546    547           $(SRCDIR)\..\skins\eagle\css.txt \
   547    548           $(SRCDIR)\..\skins\eagle\details.txt \
   548    549           $(SRCDIR)\..\skins\eagle\footer.txt \
   549    550           $(SRCDIR)\..\skins\eagle\header.txt \
   550    551           $(SRCDIR)\..\skins\enhanced1\css.txt \
   551    552           $(SRCDIR)\..\skins\enhanced1\details.txt \
   552    553           $(SRCDIR)\..\skins\enhanced1\footer.txt \
................................................................................
   584    585           $(SRCDIR)\skin.js \
   585    586           $(SRCDIR)\sorttable.js \
   586    587           $(SRCDIR)\tree.js \
   587    588           $(SRCDIR)\useredit.js \
   588    589           $(SRCDIR)\wiki.wiki
   589    590   
   590    591   OBJ   = $(OX)\add$O \
          592  +        $(OX)\alerts$O \
   591    593           $(OX)\allrepo$O \
   592    594           $(OX)\attach$O \
   593    595           $(OX)\backoffice$O \
   594    596           $(OX)\bag$O \
   595    597           $(OX)\bisect$O \
   596    598           $(OX)\blob$O \
   597    599           $(OX)\branch$O \
................................................................................
   615    617           $(OX)\delta$O \
   616    618           $(OX)\deltacmd$O \
   617    619           $(OX)\descendants$O \
   618    620           $(OX)\diff$O \
   619    621           $(OX)\diffcmd$O \
   620    622           $(OX)\dispatch$O \
   621    623           $(OX)\doc$O \
   622         -        $(OX)\email$O \
   623    624           $(OX)\encode$O \
   624    625           $(OX)\etag$O \
   625    626           $(OX)\event$O \
   626    627           $(OX)\export$O \
   627    628           $(OX)\file$O \
   628    629           $(OX)\finfo$O \
   629    630           $(OX)\foci$O \
................................................................................
   780    781   	codecheck1$E $(SRC)
   781    782   	link $(LDFLAGS) /OUT:$@ $(LIBDIR) Wsetargv.obj fossil.res @linkopts
   782    783   	if exist $@.manifest \
   783    784   		$(MTC) -nologo -manifest $@.manifest -outputresource:$@;1
   784    785   
   785    786   $(OX)\linkopts: $B\win\Makefile.msc
   786    787   	echo $(OX)\add.obj > $@
          788  +	echo $(OX)\alerts.obj >> $@
   787    789   	echo $(OX)\allrepo.obj >> $@
   788    790   	echo $(OX)\attach.obj >> $@
   789    791   	echo $(OX)\backoffice.obj >> $@
   790    792   	echo $(OX)\bag.obj >> $@
   791    793   	echo $(OX)\bisect.obj >> $@
   792    794   	echo $(OX)\blob.obj >> $@
   793    795   	echo $(OX)\branch.obj >> $@
................................................................................
   811    813   	echo $(OX)\delta.obj >> $@
   812    814   	echo $(OX)\deltacmd.obj >> $@
   813    815   	echo $(OX)\descendants.obj >> $@
   814    816   	echo $(OX)\diff.obj >> $@
   815    817   	echo $(OX)\diffcmd.obj >> $@
   816    818   	echo $(OX)\dispatch.obj >> $@
   817    819   	echo $(OX)\doc.obj >> $@
   818         -	echo $(OX)\email.obj >> $@
   819    820   	echo $(OX)\encode.obj >> $@
   820    821   	echo $(OX)\etag.obj >> $@
   821    822   	echo $(OX)\event.obj >> $@
   822    823   	echo $(OX)\export.obj >> $@
   823    824   	echo $(OX)\file.obj >> $@
   824    825   	echo $(OX)\finfo.obj >> $@
   825    826   	echo $(OX)\foci.obj >> $@
................................................................................
  1043   1044   $(OBJDIR)\json_wiki$O : $(SRCDIR)\json_detail.h
  1044   1045   
  1045   1046   $(OX)\add$O : add_.c add.h
  1046   1047   	$(TCC) /Fo$@ -c add_.c
  1047   1048   
  1048   1049   add_.c : $(SRCDIR)\add.c
  1049   1050   	translate$E $** > $@
         1051  +
         1052  +$(OX)\alerts$O : alerts_.c alerts.h
         1053  +	$(TCC) /Fo$@ -c alerts_.c
         1054  +
         1055  +alerts_.c : $(SRCDIR)\alerts.c
         1056  +	translate$E $** > $@
  1050   1057   
  1051   1058   $(OX)\allrepo$O : allrepo_.c allrepo.h
  1052   1059   	$(TCC) /Fo$@ -c allrepo_.c
  1053   1060   
  1054   1061   allrepo_.c : $(SRCDIR)\allrepo.c
  1055   1062   	translate$E $** > $@
  1056   1063   
................................................................................
  1224   1231   
  1225   1232   $(OX)\doc$O : doc_.c doc.h
  1226   1233   	$(TCC) /Fo$@ -c doc_.c
  1227   1234   
  1228   1235   doc_.c : $(SRCDIR)\doc.c
  1229   1236   	translate$E $** > $@
  1230   1237   
  1231         -$(OX)\email$O : email_.c email.h
  1232         -	$(TCC) /Fo$@ -c email_.c
  1233         -
  1234         -email_.c : $(SRCDIR)\email.c
  1235         -	translate$E $** > $@
  1236         -
  1237   1238   $(OX)\encode$O : encode_.c encode.h
  1238   1239   	$(TCC) /Fo$@ -c encode_.c
  1239   1240   
  1240   1241   encode_.c : $(SRCDIR)\encode.c
  1241   1242   	translate$E $** > $@
  1242   1243   
  1243   1244   $(OX)\etag$O : etag_.c etag.h
................................................................................
  1853   1854   	translate$E $** > $@
  1854   1855   
  1855   1856   fossil.res : $B\win\fossil.rc
  1856   1857   	$(RCC)  /fo $@ $**
  1857   1858   
  1858   1859   headers: makeheaders$E page_index.h builtin_data.h default_css.h VERSION.h
  1859   1860   	makeheaders$E add_.c:add.h \
         1861  +			alerts_.c:alerts.h \
  1860   1862   			allrepo_.c:allrepo.h \
  1861   1863   			attach_.c:attach.h \
  1862   1864   			backoffice_.c:backoffice.h \
  1863   1865   			bag_.c:bag.h \
  1864   1866   			bisect_.c:bisect.h \
  1865   1867   			blob_.c:blob.h \
  1866   1868   			branch_.c:branch.h \
................................................................................
  1883   1885   			delta_.c:delta.h \
  1884   1886   			deltacmd_.c:deltacmd.h \
  1885   1887   			descendants_.c:descendants.h \
  1886   1888   			diff_.c:diff.h \
  1887   1889   			diffcmd_.c:diffcmd.h \
  1888   1890   			dispatch_.c:dispatch.h \
  1889   1891   			doc_.c:doc.h \
  1890         -			email_.c:email.h \
  1891   1892   			encode_.c:encode.h \
  1892   1893   			etag_.c:etag.h \
  1893   1894   			event_.c:event.h \
  1894   1895   			export_.c:export.h \
  1895   1896   			file_.c:file.h \
  1896   1897   			finfo_.c:finfo.h \
  1897   1898   			foci_.c:foci.h \

Changes to www/aboutdownload.wiki.

     4      4   <h2>1.0 Overview</h2>
     5      5   
     6      6   The [/uv/download.html|Download] page for the Fossil self-hosting
     7      7   repository is implemented using [./unvers.wiki|unversioned files].
     8      8   The "download.html" screen itself, and the various build products
     9      9   are all stored as unversioned content.  The download.html page
    10     10   uses AJAX to retrieve the [/help?cmd=/juvlist|/juvlist] webpage
    11         -for a list of all unversioned files.  Javascript within the
    12         -download.html page then figures out which unversioned files are
           11  +for a list of all unversioned files.  Javascript in the
           12  +[/uv/download.js?mimetype=text/plain|download.js] file (which is
           13  +sourced by "download.html") then figures out which unversioned files are
    13     14   build products and paints appropriate icons on the displayed
    14     15   download page.
    15     16   
    16     17   When a new version is generated, the developers use the
    17     18   [/help?cmd=uv|fossil uv edit] command to make minor changes
    18         -to the "[/uv/download.html?mimetype=text/plain|download.html]"
           19  +to the "[/uv/download.js?mimetype=text/plain|download.js]"
    19     20   file so that it knows about the
    20     21   new version number.  Then the developers run
    21     22   the [/help?cmd=uv|fossil uv add] command for each
    22     23   build product.  Finally, the
    23     24   [/help?cmd=uv|fossil uv sync] command is run to push all
    24     25   the content up to servers.  All
    25     26   [./selfhost.wiki|three self-hosting repositories] for Fossil
    26     27   are updated automatically.
    27     28   
    28     29   <h2>2.0 Details</h2>
    29     30   
    30         -The current text of the "download.html" file can be seen
    31         -[/uv/download.html?mimetype=text/plain|here].  (Mouse-over that
    32         -hyperlink to see how the "mimetype=text/plain" query parameter
    33         -is added in order to display the file as plain text instead of
    34         -the usual HTML.)  The default mimetype for "download.html" is
    35         -text/html.  But because the entire page is enclosed within
           31  +The current text of the "download.html" and "download.js" files can
           32  +be seen at:
           33  +
           34  +   *   [/uv/download.html?mimetype=text/plain]
           35  +   *   [/uv/download.js?mimetype=text/plain]
           36  +
           37  +Notice how the hyperlinks above use the "mimetype=text/plain"
           38  +query parameter in order to display the file as plain text
           39  +instead of the usual HTML or Javascript.
    36     40   
           41  +The default mimetype for "download.html" is
           42  +text/html.  But because the entire page is enclosed within
    37     43   
    38     44       <b>&lt;div class='fossil-doc' data-title='Download Page'&gt;...&lt;/div&gt;</b>
    39     45   
    40     46   Fossil knows to add its standard header and footer information to the
    41     47   document, making it look just like any other page.  See
    42     48   "[./embeddeddoc.wiki|embedded documentation]" for further details on
    43         -how this works.
           49  +how &lt;div class='fossil-doc'&gt; this works.
    44     50   
    45     51   With each new release, the "releases" variable in the javascript on
    46         -the [/uv/download.html?mimetype=text/plain|download.html] page is
    47         -edited (using "[/help?cmd=uv|fossil uv edit download.html]") to add
           52  +the [/uv/download.js?mimetype=text/plain|download.js] page is
           53  +edited (using "[/help?cmd=uv|fossil uv edit download.js]") to add
    48     54   details of the release.
    49     55   
    50         -When the javascript on the "download.html" page runs, it requests
           56  +When the javascript in the "download.js" file runs, it requests
    51     57   a listing of all unversioned content using the /juvlist URL.
    52     58   ([/juvlist|sample /juvlist output]).  The content of the download page is
    53     59   constructed by matching unversioned files against regular expressions
    54     60   in the "releases" variable.
    55     61   
    56     62   Build products need to be constructed on different machines.  The precompiled
    57     63   binary for Linux is compiled on Linux, the precompiled binary for Windows
................................................................................
    60     66   out the release and compiles it, then runs
    61     67   [/help?cmd=uv|fossil uv add] for the build product followed by
    62     68   [/help?cmd=uv|fossil uv sync] to push the new build product to the
    63     69   [./selfhost.wiki|various servers].  This process is repeated for
    64     70   each build product.
    65     71   
    66     72   When older builds are retired from the download page, the
    67         -[/uv/download.html?mimetype=text/plain|download.html] page is again
           73  +[/uv/download.js?mimetype=text/plain|download.js] page is again
    68     74   edited to remove the corresponding entry from the "release" variable
    69     75   and the edit is synced using
    70     76   [/help?cmd=uv|fossil uv sync].  This causes the build products to
    71     77   disappear from the download page immediately.  But those build products
    72     78   are still taking up space in the unversioned content table of the
    73     79   server repository.  To purge the obsolete build products, one or
    74     80   more [/help?cmd=uv|fossil uv rm] commands are run, followed by

Changes to www/changes.wiki.

     1      1   <title>Change Log</title>
     2      2   
     3      3   <a name='v2_7'></a>
     4      4   <h2>Changes for Version 2.7 (2018-??-??)</h2>
     5      5   
     6         -  *  Add support for [./alerts.md|email alerts].
     7         -  *  Add support for forums.
     8         -  *  Added new user capabilities letters needed to support alerts and forum.
            6  +  *  Add the [./alerts.md|email alerts] feature for commits, ticket
            7  +     changes, wiki changes, forum posts, and announcements.
            8  +  *  Add the discussion forum feature.
            9  +  *  Add new user capabilities letters needed to support alerts and forum.
     9     10        Formerly, user capabilities were letters from &#91;a-z&#93;, but with the
    10     11        enhancements, the supply of lower case letters was exhausted.
    11     12        User capabilities are now letters in &#91;a-zA-Z0-9&#93;.
    12         -  *  Added the [./backoffice.md|backoffice].
           13  +  *  The default skin is now responsive, providing better layout on
           14  +     small screens, including mobile devices.
           15  +  *  Add the [./backoffice.md|backoffice].
    13     16     *  Update internal Unicode character tables, used in regular expression
    14     17        handling, from version 10.0 to 11.0.
    15     18     *  Improvements to the "Security Audit" administration page
    16     19   
    17     20   <a name='v2_6'></a>
    18     21   <h2>Changes for Version 2.6 (2018-05-04)</h2>
    19     22   

Changes to www/forum.wiki.

   171    171   
   172    172   For either type of repository, you are likely to want to give at least
   173    173   the WriteTrusted capability (4) to users in the <tt>developer</tt>
   174    174   category. If you did not give the Read Forum capability (2) to
   175    175   <tt>anonymous</tt> above, you should give <tt>developer</tt> that
   176    176   capability here if you choose to give it capability 3 or 4.
   177    177   
   178         -You must give at least one user or user category the Email Alerts
   179         -capability (7), else the only people able to sign themselves up for
   180         -email notifications are those with the Setup or Admin capability. Those
   181         -users could sign others up via Admin &rarr; Notification, but you
   182         -probably want to give this capability to one of the user categories.
   183         -Give it to <tt>nobody</tt> if you want anyone to sign up without any
   184         -restrictions.  Give it to <tt>anonymous</tt> if you want the user to
   185         -solve a simple CAPTCHA before signing up. Give it to <tt>reader</tt> or
   186         -<tt>developer</tt> if you want only users with Fossil logins to have
   187         -this ability. (That's assuming you give one or both of these
   188         -capabilities to every user on your Fossil repository.)
          178  +If you want to use the email notification feature, by default only those
          179  +users in the Setup and Admin user categories can make use of it. Grant
          180  +the Email Alerts capability (7) to give others access to this feature.
          181  +Alternately, you can handle notification signups outside of Fossil, with
          182  +a Setup or Admin users manually signing users up via Admin &rarr;
          183  +Notification. You'll want to grant this capability to the
          184  +<tt>nobody</tt> user category if you want anyone to sign up without any
          185  +restrictions.  Give it to <tt>anonymous</tt> instead if you want the
          186  +user to solve a simple CAPTCHA before signing up. Or, give it to
          187  +<tt>reader</tt> or <tt>developer</tt> if you want only users with Fossil
          188  +logins to have this ability. (That's assuming you give one or both of
          189  +these capabilities to every user on your Fossil repository.)
   189    190   
   190    191   By following this advice, you should not need to tediously add
   191    192   capabilities to individual accounts except in atypical cases, such as
   192    193   to grant the Moderate Forum capability (5) to an uncommonly
   193    194   highly-trusted user.
   194    195   
   195    196   
   196    197   <h3 id="skin">Skin Setup</h3>
   197    198   
   198    199   If you create a new Fossil repository with version 2.7 or newer, its
   199    200   default skin is already set up correctly for typical forum
   200    201   configurations.
   201    202   
   202         -Those upgrading existing repositories will need to edit the Header part
   203         -of their existing Fossil skin in Admin &rarr; Skins, adding something
   204         -like this to create the navbar link:
          203  +If you have an existing repository, you have two choices if you want its
          204  +skin to be upgraded to support forums:
          205  +
          206  +<ol>
          207  +  <li>Go into Admin &rarr; Skins and switch from your current skin to
          208  +  one of the stock skins.  If you were on a stock skin, just switch away
          209  +  from your current one to the actual stock skin, since they will be
          210  +  different after the upgrade.</li>
          211  +
          212  +  <li>If you have local customization that you do not want to throw
          213  +  away, you can use the diff feature of Fossil's skin editor to show how
          214  +  the skins differ.</li>
          215  +</ol>
          216  +
          217  +The remainder of this section summarizes the differences you're expected
          218  +to see when taking option #2.
          219  +
          220  +The first thing is that you'll need to add something like the following
          221  +to the Header part of the skin to create the navbar link:
   205    222   
   206    223   <verbatim>
   207         -  if {[anycap 23456] || [anoncap 2] || [anoncap 3]} {
          224  +  if {[anycap 234567] || [anoncap 2] || [anoncap 3]} {
   208    225       menulink /forum Forum
   209    226     }
   210    227   </verbatim>
   211    228   
   212    229   These rules say that any logged-in user with any forum-related
   213         -capability (2-6 inclusive, as of this writing) or an anonymous user with
          230  +capability (2-7 inclusive, as of this writing) or an anonymous user with
   214    231   read or write capability on the forum (2, 3) will see the "Forum" navbar
   215    232   link, which just takes you to <tt>/forum</tt>.
   216    233   
   217    234   The exact code you need here varies depending on which skin you're
   218    235   using. Follow the style you see for the other navbar links.
   219    236   
   220    237   The new forum feature also brings many new CSS styles to the table. If
................................................................................
   238    255   <verbatim>
   239    256     div.forumSel {
   240    257       background-color: rgba(255, 255, 255, 0.05);
   241    258     }
   242    259   </verbatim>
   243    260   
   244    261   That overlays the background with 5% white to lighten it slightly.
          262  +
          263  +Another new forum-related CSS style you might want to reflect into your
          264  +existing skin is:
          265  +
          266  +<verbatim>
          267  +  div.forumPosts a:visited {
          268  +    color: #6A7F94;
          269  +  }
          270  +</verbatim>
          271  +
          272  +This changes the clicked-hyperlink color for the forum post links on the
          273  +main <tt>/forum</tt> page only, which allows your browser's history
          274  +mechanism to show which threads a user has read and which not. The link
          275  +color will change back to the normal link color — indicating "unread" —
          276  +when a reply is added to an existing thread because that changes where
          277  +the link from the <tt>/forum</tt> page points, taking you to the newest
          278  +post in the thread.
          279  +
          280  +The color given above is suitable for the stock skin.
          281  +
          282  +Beware that when changing this example, there are some
          283  +[https://hacks.mozilla.org/2010/03/privacy-related-changes-coming-to-css-vistited/
          284  +| stringent restrictions] in modern browsers to prevent snoopy web sites
          285  +from brute-forcing your browsing history. (See the link for the method,
          286  +which explains the restrictions.)
   245    287   
   246    288   
   247    289   <h3 id="search">Enable Forum Search</h3>
   248    290   
   249    291   One of the underlying assumptions of the forum feature is that you will
   250    292   want to be able to search the forum archives, so the <tt>/forum</tt>
   251    293   page always includes a search box. Since that depends on search being

Changes to www/th1.md.

    31     31   operations are accomplished by converting from string to numeric, performing
    32     32   the computation, then converting the result back into a string.  (This might
    33     33   seem inefficient, but it is faster than people imagine, and numeric
    34     34   computations do not come up very often for the kinds of work that TH1 does,
    35     35   so it has never been a factor.)
    36     36   
    37     37   A TH1 script consist of a sequence of commands.
    38         -Each command is terminated by the first (unescaped) newline or ";" character.
           38  +Each command is terminated by the first *unescaped* newline or ";" character.
    39     39   The text of the command (excluding the newline or semicolon terminator)
    40     40   is broken into space-separated tokens.  The first token is the command
    41         -name and subsequent tokens are the arguments.  In this since, TH1 syntax
           41  +name and subsequent tokens are the arguments.  In this sense, TH1 syntax
    42     42   is similar to the familiar command-line shell syntax.
    43     43   
    44     44   A token is any sequence of characters other than whitespace and semicolons.
    45     45   Or, all text without double-quotes is a single token even if it includes
    46     46   whitespace and semicolons.  Or, all text without nested {...} pairs is a
    47     47   single token.
    48     48   
    49     49   The nested {...} form of tokens is important because it allows TH1 commands
    50     50   to have an appearance similar to C/C++.  It is important to remember, though,
    51     51   that a TH1 script is really just a list of text commands, not a context-free
    52     52   language with a grammar like C/C++.  This can be confusing to long-time
    53         -C/C++ programmers because TH1 does look a lot like C/C++.  But the semantics
           53  +C/C++ programmers because TH1 does look a lot like C/C++, but the semantics
    54     54   of TH1 are closer to FORTH or Lisp than they are to C.
    55     55   
    56         -Consider the "if" command in TH1.
           56  +Consider the `if` command in TH1.
    57     57   
    58     58           if {$current eq "dev"} {
    59     59             puts "hello"
    60     60           } else {
    61     61             puts "world"
    62     62           }
    63     63   
    64     64   The example above is a single command.  The first token, and the name
    65         -of the command, is "if".
    66         -The second token is '$current eq "dev"' - an expression.  (The outer {...}
           65  +of the command, is `if`.
           66  +The second token is `$current eq "dev"` - an expression.  (The outer {...}
    67     67   are removed from each token by the command parser.)  The third token
    68         -is the 'puts "hello"', with its whitespace and newlines.  The fourth token
    69         -is "else".  And the fifth and last token is 'puts "world"'.
           68  +is the `puts "hello"`, with its whitespace and newlines.  The fourth token
           69  +is `else"`  And the fifth and last token is `puts "world"`.
    70     70   
    71         -The "if" command word by evaluating its first argument (the second token)
    72         -as an expression, and if that expression is true, evaluating its
           71  +The `if` command evaluates its first argument (the second token)
           72  +as an expression, and if that expression is true, evaluates its
    73     73   second argument (the third token) as a TH1 script.
    74         -If the expression is false and the third argument is "else" then
           74  +If the expression is false and the third argument is `else`, then
    75     75   the fourth argument is evaluated as a TH1 expression.
    76     76   
    77     77   So, you see, even though the example above spans five lines, it is really
    78     78   just a single command.
           79  +
           80  +All of this also explains the emphasis on *unescaped* characters above:
           81  +the curly braces `{ }` are string quoting characters in Tcl/TH1, not
           82  +block delimiters as in C. This is how we can have a command that extends
           83  +over multiple lines. It is also why the `else` keyword must be cuddled
           84  +up with the closing brace for the `if` clause's scriptlet. The following
           85  +is invalid Tcl/TH1:
           86  +
           87  +        if {$current eq "dev"} {
           88  +          puts "hello"
           89  +        }
           90  +        else {
           91  +          puts "world"
           92  +        }
           93  +
           94  +If you try to run this under either Tcl or TH1, the interpreter will
           95  +tell you that there is no `else` command, because with the newline on
           96  +the third line, you terminated the `if` command.
           97  +
           98  +Occasionally in Tcl/TH1 scripts, you may need to use a backslash at the
           99  +end of a line to allow a command to extend over multiple lines without
          100  +being considered two separate commands. Here's an example from one of
          101  +Fossil's test scripts:
          102  +
          103  +        return [lindex [regexp -line -inline -nocase -- \
          104  +            {^uuid:\s+([0-9A-F]{40}) } [eval [getFossilCommand \
          105  +            $repository "" info trunk]]] end]
          106  +
          107  +Those backslashes allow the command to wrap nicely within a standard
          108  +terminal width while telling the interpreter to consider those three
          109  +lines as a single command.
          110  +
    79    111   
    80    112   Summary of Core TH1 Commands
    81    113   ----------------------------
    82    114   
    83    115   The original Tcl language after when TH1 is modeled has a very rich
    84    116   repertoire of commands.  TH1, as it is designed to be minimalist and
    85    117   embedded has a greatly reduced command set.  The following bullets