Fossil

Check-in [2269309f]
Login

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

Overview
Comment:Merge updates from trunk.
Downloads: Tarball | ZIP archive
Timelines: family | ancestors | descendants | both | cmake-ide
Files: files | file ages | folders
SHA3-256: 2269309f92d928ca9fcf3d57874c39926b4c4b941b72e3a02fb5f344936b8441
User & Date: ashepilko 2018-09-19 20:19:45.382
Context
2018-09-19
21:00
Add docs on using IDE for Fossil development. ... (check-in: 7d2be6f8 user: ashepilko tags: cmake-ide)
20:19
Merge updates from trunk. ... (check-in: 2269309f user: ashepilko tags: cmake-ide)
19:55
Ignore errors in the clean target commands. ... (check-in: 27c7a491 user: ashepilko tags: trunk)
2018-09-14
21:15
Describe the debugging options. ... (check-in: d364933a user: ashepilko tags: cmake-ide)
Changes
Unified Diff Ignore Whitespace Patch
Changes to Dockerfile.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
###
#   Dockerfile for Fossil
###
FROM fedora:24

### Now install some additional parts we will need for the build
RUN dnf update -y && dnf install -y gcc make zlib-devel openssl-devel tar && dnf clean all && groupadd -r fossil -g 433 && useradd -u 431 -r -g fossil -d /opt/fossil -s /sbin/nologin -c "Fossil user" fossil

### If you want to build "trunk", change the next line accordingly.
ENV FOSSIL_INSTALL_VERSION release

RUN curl "http://core.tcl.tk/tcl/tarball/tcl-src.tar.gz?name=tcl-src&uuid=release" | tar zx
RUN cd tcl-src/unix && ./configure --prefix=/usr --disable-load && make && make install
RUN curl "http://www.fossil-scm.org/index.html/tarball/fossil-src.tar.gz?name=fossil-src&uuid=${FOSSIL_INSTALL_VERSION}" | tar zx
RUN cd fossil-src && ./configure --disable-fusefs --json --with-th1-docs --with-th1-hooks --with-tcl --with-tcl-stubs --with-tcl-private-stubs
RUN cd fossil-src/src && mv main.c main.c.orig && sed s/\"now\"/0/ <main.c.orig >main.c
RUN cd fossil-src && make && strip fossil && cp fossil /usr/bin && cd .. && rm -rf fossil-src && chmod a+rx /usr/bin/fossil && mkdir -p /opt/fossil && chown fossil:fossil /opt/fossil

### Build is done, remove modules no longer needed
RUN dnf remove -y gcc make zlib-devel openssl-devel tar && dnf clean all

USER fossil

ENV HOME /opt/fossil

EXPOSE 8080




|


|




<
<
|
|




|







1
2
3
4
5
6
7
8
9
10
11


12
13
14
15
16
17
18
19
20
21
22
23
24
25
###
#   Dockerfile for Fossil
###
FROM fedora:28

### Now install some additional parts we will need for the build
RUN dnf update -y && dnf install -y gcc make tcl tcl-devel zlib-devel openssl-devel tar && dnf clean all && groupadd -r fossil -g 433 && useradd -u 431 -r -g fossil -d /opt/fossil -s /sbin/nologin -c "Fossil user" fossil

### If you want to build "trunk", change the next line accordingly.
ENV FOSSIL_INSTALL_VERSION release



RUN curl "https://www.fossil-scm.org/index.html/tarball/fossil-src.tar.gz?name=fossil-src&uuid=${FOSSIL_INSTALL_VERSION}" | tar zx
RUN cd fossil-src && ./configure --disable-fusefs --json --with-th1-docs --with-th1-hooks --with-tcl=1 --with-tcl-stubs --with-tcl-private-stubs
RUN cd fossil-src/src && mv main.c main.c.orig && sed s/\"now\"/0/ <main.c.orig >main.c
RUN cd fossil-src && make && strip fossil && cp fossil /usr/bin && cd .. && rm -rf fossil-src && chmod a+rx /usr/bin/fossil && mkdir -p /opt/fossil && chown fossil:fossil /opt/fossil

### Build is done, remove modules no longer needed
RUN dnf remove -y gcc make zlib-devel tcl-devel openssl-devel tar && dnf clean all

USER fossil

ENV HOME /opt/fossil

EXPOSE 8080

Changes to Makefile.in.
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
USE_MMAN_H = @USE_MMAN_H@
USE_SEE = @USE_SEE@
FOSSIL_ENABLE_MINIZ = @FOSSIL_ENABLE_MINIZ@

include $(SRCDIR)/main.mk

distclean: clean
	rm -f autoconfig.h config.log Makefile

reconfig:
	@AUTOREMAKE@

# Automatically reconfigure whenever an autosetup file or one of the
# make source files change.
#







|







48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
USE_MMAN_H = @USE_MMAN_H@
USE_SEE = @USE_SEE@
FOSSIL_ENABLE_MINIZ = @FOSSIL_ENABLE_MINIZ@

include $(SRCDIR)/main.mk

distclean: clean
	-rm -f autoconfig.h config.log Makefile

reconfig:
	@AUTOREMAKE@

# Automatically reconfigure whenever an autosetup file or one of the
# make source files change.
#
Changes to VERSION.
1
2.6
|
1
2.7
Changes to auto.def.
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
    # search for the system SQLite once with -ldl, and once without. If
    # the library can only be found with $extralibs set to -ldl, then
    # the code below will append -ldl to LIBS.
    #
    foreach extralibs {{} {-ldl}} {

      # Locate the system SQLite by searching for sqlite3_open(). Then check
      # if sqlite3_keyword_check() can be found as well. If we can find open() but
      # not keyword_check(), then the system SQLite is too old to link against
      # fossil.
      #
      if {[check-function-in-lib sqlite3_open sqlite3 $extralibs]} {
        if {![check-function-in-lib sqlite3_keyword_check sqlite3 $extralibs]} {
          user-error "system sqlite3 too old (require >= 3.24.0)"
        }

        # Success. Update symbols and return.
        #
        define USE_SYSTEM_SQLITE 1
        define-append LIBS -lsqlite3
        define-append LIBS $extralibs







|
|



|
|







124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
    # search for the system SQLite once with -ldl, and once without. If
    # the library can only be found with $extralibs set to -ldl, then
    # the code below will append -ldl to LIBS.
    #
    foreach extralibs {{} {-ldl}} {

      # Locate the system SQLite by searching for sqlite3_open(). Then check
      # if sqlite3_create_window_function can be found as well. If we can find open() but
      # not create_window_function(), then the system SQLite is too old to link against
      # fossil.
      #
      if {[check-function-in-lib sqlite3_open sqlite3 $extralibs]} {
        if {![check-function-in-lib sqlite3_create_window_function sqlite3 $extralibs]} {
          user-error "system sqlite3 too old (require >= 3.25.0)"
        }

        # Success. Update symbols and return.
        #
        define USE_SYSTEM_SQLITE 1
        define-append LIBS -lsqlite3
        define-append LIBS $extralibs
Changes to skins/default/css.txt.
141
142
143
144
145
146
147


148

149
150
151
152
153
154
155
.udiff, .sbsdiff {
    font-size: .85em !important;
    overflow: auto;
    border: 1px solid #ccc;
    border-radius: 5px;
}
.content blockquote {


    padding: 0 15px;

}

table.report {
    cursor: auto;
    border-radius: 5px;
    border: 1px solid #ccc;
    margin: 1em 0;







>
>
|
>







141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
.udiff, .sbsdiff {
    font-size: .85em !important;
    overflow: auto;
    border: 1px solid #ccc;
    border-radius: 5px;
}
.content blockquote {
    background-color: rgba(65, 131, 196, 0.1);
    border-left: 3px solid #254769;
    padding: 0.1em;
    padding-left: 1em;
}

table.report {
    cursor: auto;
    border-radius: 5px;
    border: 1px solid #ccc;
    margin: 1em 0;
Changes to src/branch.c.
266
267
268
269
270
271
272














273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294

295
296
297
298
299
300
301
302
303
304
305
306
307
308













309










310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
** COMMAND: branch
**
** Usage: %fossil branch SUBCOMMAND ... ?OPTIONS?
**
** Run various subcommands to manage branches of the open repository or
** of the repository identified by the -R or --repository option.
**














**    fossil branch new BRANCH-NAME BASIS ?OPTIONS?
**
**        Create a new branch BRANCH-NAME off of check-in BASIS.
**        Supported options for this subcommand include:
**        --private             branch is private (i.e., remains local)
**        --bgcolor COLOR       use COLOR instead of automatic background
**        --nosign              do not sign contents on this branch
**        --date-override DATE  DATE to use instead of 'now'
**        --user-override USER  USER to use instead of the current default
**
**        DATE may be "now" or "YYYY-MM-DDTHH:MM:SS.SSS". If in
**        year-month-day form, it may be truncated, the "T" may be
**        replaced by a space, and it may also name a timezone offset
**        from UTC as "-HH:MM" (westward) or "+HH:MM" (eastward).
**        Either no timezone suffix or "Z" means UTC.
**
**    fossil branch list|ls ?-a|--all|-c|--closed?
**
**        List all branches.  Use -a or --all to list all branches and
**        -c or --closed to list all closed branches.  The default is to
**        show only open branches.
**

**    fossil branch info BRANCH-NAME
**
**        Print information about a branch
**
** Options:
**    -R|--repository FILE       Run commands on repository FILE
*/
void branch_cmd(void){
  int n;
  const char *zCmd = "list";
  db_find_and_open_repository(0, 0);
  if( g.argc>=3 ) zCmd = g.argv[2];
  n = strlen(zCmd);
  if( strncmp(zCmd,"new",n)==0 ){













    branch_new();










  }else if( (strncmp(zCmd,"list",n)==0)||(strncmp(zCmd, "ls", n)==0) ){
    Stmt q;
    int vid;
    char *zCurrent = 0;
    int brFlags = BRL_OPEN_ONLY;
    if( find_option("all","a",0)!=0 ) brFlags = BRL_BOTH;
    if( find_option("closed","c",0)!=0 ) brFlags = BRL_CLOSED_ONLY;

    if( g.localOpen ){
      vid = db_lget_int("checkout", 0);
      zCurrent = db_text(0, "SELECT value FROM tagxref"
                            " WHERE rid=%d AND tagid=%d", vid, TAG_BRANCH);
    }
    branch_prepare_list_query(&q, brFlags);
    while( db_step(&q)==SQLITE_ROW ){
      const char *zBr = db_column_text(&q, 0);
      int isCur = zCurrent!=0 && fossil_strcmp(zCurrent,zBr)==0;
      fossil_print("%s%s\n", (isCur ? "* " : "  "), zBr);
    }
    db_finalize(&q);
  }else if( strncmp(zCmd,"info",n)==0 ){
    int i;
    for(i=3; i<g.argc; i++){
      const char *zBrName = g.argv[i];
      int rid = branch_is_open(zBrName);
      if( rid==0 ){
        fossil_print("%s: not an open branch\n", zBrName);
      }else{
        const char *zUuid = db_text(0,"SELECT uuid FROM blob WHERE rid=%d",rid);
        const char *zDate = db_text(0,
          "SELECT datetime(mtime,toLocal()) FROM event"
          " WHERE objid=%d", rid);
        fossil_print("%s: open as of %s on %.16s\n", zBrName, zDate, zUuid);
      }
    }
  }else{
    fossil_fatal("branch subcommand should be one of: "
                 "info list ls new");
  }
}

static const char brlistQuery[] =
@ SELECT
@   tagxref.value,
@   max(event.mtime),







>
>
>
>
>
>
>
>
>
>
>
>
>
>
















<
|
<
<
|

>
|
|
|
<
|
<







|
>
>
>
>
>
>
>
>
>
>
>
>
>
|
>
>
>
>
>
>
>
>
>
>




















|
<
<
<
|
<
<
|
<
<
<
<
<
<
<
<

|







266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302

303


304
305
306
307
308
309

310

311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363



364


365








366
367
368
369
370
371
372
373
374
** COMMAND: branch
**
** Usage: %fossil branch SUBCOMMAND ... ?OPTIONS?
**
** Run various subcommands to manage branches of the open repository or
** of the repository identified by the -R or --repository option.
**
**    fossil branch current
**
**        Print the name of the branch for the current check-out
**
**    fossil branch info BRANCH-NAME
**
**        Print information about a branch
**
**    fossil branch list|ls ?-a|--all|-c|--closed?
**
**        List all branches.  Use -a or --all to list all branches and
**        -c or --closed to list all closed branches.  The default is to
**        show only open branches.
**
**    fossil branch new BRANCH-NAME BASIS ?OPTIONS?
**
**        Create a new branch BRANCH-NAME off of check-in BASIS.
**        Supported options for this subcommand include:
**        --private             branch is private (i.e., remains local)
**        --bgcolor COLOR       use COLOR instead of automatic background
**        --nosign              do not sign contents on this branch
**        --date-override DATE  DATE to use instead of 'now'
**        --user-override USER  USER to use instead of the current default
**
**        DATE may be "now" or "YYYY-MM-DDTHH:MM:SS.SSS". If in
**        year-month-day form, it may be truncated, the "T" may be
**        replaced by a space, and it may also name a timezone offset
**        from UTC as "-HH:MM" (westward) or "+HH:MM" (eastward).
**        Either no timezone suffix or "Z" means UTC.
**

** Options:


**    -R|--repository FILE       Run commands on repository FILE
**
** Summary:
**    fossil branch current
**    fossil branch info BRANCHNAME
**    fossil branch [list|ls]

**    fossil branch new

*/
void branch_cmd(void){
  int n;
  const char *zCmd = "list";
  db_find_and_open_repository(0, 0);
  if( g.argc>=3 ) zCmd = g.argv[2];
  n = strlen(zCmd);
  if( strncmp(zCmd,"current",n)==0 ){
    if( !g.localOpen ){
      fossil_fatal("not within an open checkout");
    }else{
      int vid = db_lget_int("checkout", 0);
      char *zCurrent = db_text(0, "SELECT value FROM tagxref"
                            " WHERE rid=%d AND tagid=%d", vid, TAG_BRANCH);
      fossil_print("%s\n", zCurrent);
      fossil_free(zCurrent);
    }
  }else if( strncmp(zCmd,"info",n)==0 ){
    int i;
    for(i=3; i<g.argc; i++){
      const char *zBrName = g.argv[i];
      int rid = branch_is_open(zBrName);
      if( rid==0 ){
        fossil_print("%s: not an open branch\n", zBrName);
      }else{
        const char *zUuid = db_text(0,"SELECT uuid FROM blob WHERE rid=%d",rid);
        const char *zDate = db_text(0,
          "SELECT datetime(mtime,toLocal()) FROM event"
          " WHERE objid=%d", rid);
        fossil_print("%s: open as of %s on %.16s\n", zBrName, zDate, zUuid);
      }
    }
  }else if( (strncmp(zCmd,"list",n)==0)||(strncmp(zCmd, "ls", n)==0) ){
    Stmt q;
    int vid;
    char *zCurrent = 0;
    int brFlags = BRL_OPEN_ONLY;
    if( find_option("all","a",0)!=0 ) brFlags = BRL_BOTH;
    if( find_option("closed","c",0)!=0 ) brFlags = BRL_CLOSED_ONLY;

    if( g.localOpen ){
      vid = db_lget_int("checkout", 0);
      zCurrent = db_text(0, "SELECT value FROM tagxref"
                            " WHERE rid=%d AND tagid=%d", vid, TAG_BRANCH);
    }
    branch_prepare_list_query(&q, brFlags);
    while( db_step(&q)==SQLITE_ROW ){
      const char *zBr = db_column_text(&q, 0);
      int isCur = zCurrent!=0 && fossil_strcmp(zCurrent,zBr)==0;
      fossil_print("%s%s\n", (isCur ? "* " : "  "), zBr);
    }
    db_finalize(&q);
  }else if( strncmp(zCmd,"new",n)==0 ){



    branch_new();


  }else{








    fossil_fatal("branch subcommand should be one of: "
                 "current info list ls new");
  }
}

static const char brlistQuery[] =
@ SELECT
@   tagxref.value,
@   max(event.mtime),
Changes to src/json_branch.c.
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
  cson_value * payV;
  cson_object * pay;
  cson_value * listV;
  cson_array * list;
  char const * range = NULL;
  int branchListFlags = BRL_OPEN_ONLY;
  char * sawConversionError = NULL;
  Stmt q;
  if( !g.perm.Read ){
    json_set_err(FSL_JSON_E_DENIED,
                 "Requires 'o' permissions.");
    return NULL;
  }
  payV = cson_value_new_object();
  pay = cson_value_get_object(payV);







|







66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
  cson_value * payV;
  cson_object * pay;
  cson_value * listV;
  cson_array * list;
  char const * range = NULL;
  int branchListFlags = BRL_OPEN_ONLY;
  char * sawConversionError = NULL;
  Stmt q = empty_Stmt;
  if( !g.perm.Read ){
    json_set_err(FSL_JSON_E_DENIED,
                 "Requires 'o' permissions.");
    return NULL;
  }
  payV = cson_value_new_object();
  pay = cson_value_get_object(payV);
141
142
143
144
145
146
147

148
149
150
151
152
153
154
                                   __FILE__,__LINE__);
    }
  }
  if( sawConversionError ){
    json_warn(FSL_JSON_W_COL_TO_JSON_FAILED,"%s",sawConversionError);
    free(sawConversionError);
  }

  return payV;
}

/*
** Parameters for the create-branch operation.
*/
typedef struct BranchCreateOptions{







>







141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
                                   __FILE__,__LINE__);
    }
  }
  if( sawConversionError ){
    json_warn(FSL_JSON_W_COL_TO_JSON_FAILED,"%s",sawConversionError);
    free(sawConversionError);
  }
  db_finalize(&q);
  return payV;
}

/*
** Parameters for the create-branch operation.
*/
typedef struct BranchCreateOptions{
Changes to src/main.mk.
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
# This rule prevents make from using its default rules to try build
# an executable named "manifest" out of the file named "manifest.c"
#
$(SRCDIR)/../manifest:
	# noop

clean:
	rm -rf $(OBJDIR)/* $(APPNAME)


$(OBJDIR)/page_index.h: $(TRANS_SRC) $(OBJDIR)/mkindex
	$(OBJDIR)/mkindex $(TRANS_SRC) >$@

$(OBJDIR)/builtin_data.h: $(OBJDIR)/mkbuiltin $(EXTRA_FILES)
	$(OBJDIR)/mkbuiltin --prefix $(SRCDIR)/ $(EXTRA_FILES) >$@







|







684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
# This rule prevents make from using its default rules to try build
# an executable named "manifest" out of the file named "manifest.c"
#
$(SRCDIR)/../manifest:
	# noop

clean:
	-rm -rf $(OBJDIR)/* $(APPNAME)


$(OBJDIR)/page_index.h: $(TRANS_SRC) $(OBJDIR)/mkindex
	$(OBJDIR)/mkindex $(TRANS_SRC) >$@

$(OBJDIR)/builtin_data.h: $(OBJDIR)/mkbuiltin $(EXTRA_FILES)
	$(OBJDIR)/mkbuiltin --prefix $(SRCDIR)/ $(EXTRA_FILES) >$@
Changes to src/makemake.tcl.
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
# This rule prevents make from using its default rules to try build
# an executable named "manifest" out of the file named "manifest.c"
#
$(SRCDIR)/../manifest:
	# noop

clean:
	rm -rf $(OBJDIR)/* $(APPNAME)

}

set mhargs {}
foreach s [lsort $src] {
  append mhargs "\$(OBJDIR)/${s}_.c:\$(OBJDIR)/$s.h <<<NEXT_LINE>>>"
  set extra_h($s) { }







|







446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
# This rule prevents make from using its default rules to try build
# an executable named "manifest" out of the file named "manifest.c"
#
$(SRCDIR)/../manifest:
	# noop

clean:
	-rm -rf $(OBJDIR)/* $(APPNAME)

}

set mhargs {}
foreach s [lsort $src] {
  append mhargs "\$(OBJDIR)/${s}_.c:\$(OBJDIR)/$s.h <<<NEXT_LINE>>>"
  set extra_h($s) { }
1983
1984
1985
1986
1987
1988
1989
1990
1991
1992
1993
1994
1995
1996
1997
1998
1999
2000
2001
2002
2003
2004
2005
2006
2007
2008
2009
2010
2011
2012
2013
2014
2015
2016
2017
2018
2019
2020
2021
2022
2023
2024
2025
2026
"$(OX)\page_index.h": "$(OBJDIR)\mkindex$E" $(SRC)
	$** > $@

"$(OX)\builtin_data.h":	"$(OBJDIR)\mkbuiltin$E" "$(OX)\builtin_data.reslist"
	"$(OBJDIR)\mkbuiltin$E" --prefix "$(SRCDIR)/" --reslist "$(OX)\builtin_data.reslist" > $@

cleanx:
	del "$(OX)\*.obj" 2>NUL
	del "$(OBJDIR)\*.obj" 2>NUL
	del "$(OX)\*_.c" 2>NUL
	del "$(OX)\*.h" 2>NUL
	del "$(OX)\*.ilk" 2>NUL
	del "$(OX)\*.map" 2>NUL
	del "$(OX)\*.res" 2>NUL
	del "$(OX)\*.reslist" 2>NUL
	del "$(OX)\headers" 2>NUL
	del "$(OX)\linkopts" 2>NUL
	del "$(OX)\vc*.pdb" 2>NUL

clean: cleanx
	del "$(APPNAME)" 2>NUL
	del "$(PDBNAME)" 2>NUL
	del "$(APPMANIFEST)" 2>NUL
	del "$(OBJDIR)\translate$E" 2>NUL
	del "$(OBJDIR)\translate$P" 2>NUL
	del "$(OBJDIR)\mkindex$E" 2>NUL
	del "$(OBJDIR)\mkindex$P" 2>NUL
	del "$(OBJDIR)\makeheaders$E" 2>NUL
	del "$(OBJDIR)\makeheaders$P" 2>NUL
	del "$(OBJDIR)\mkversion$E" 2>NUL
	del "$(OBJDIR)\mkversion$P" 2>NUL
	del "$(OBJDIR)\mkcss$E" 2>NUL
	del "$(OBJDIR)\mkcss$P" 2>NUL
	del "$(OBJDIR)\codecheck1$E" 2>NUL
	del "$(OBJDIR)\codecheck1$P" 2>NUL
	del "$(OBJDIR)\mkbuiltin$E" 2>NUL
	del "$(OBJDIR)\mkbuiltin$P" 2>NUL

realclean: clean

"$(OBJDIR)\json$O" : "$(SRCDIR)\json_detail.h"
"$(OBJDIR)\json_artifact$O" : "$(SRCDIR)\json_detail.h"
"$(OBJDIR)\json_branch$O" : "$(SRCDIR)\json_detail.h"
"$(OBJDIR)\json_config$O" : "$(SRCDIR)\json_detail.h"







|
|
|
|
|
|
|
|
|
|
|


|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|







1983
1984
1985
1986
1987
1988
1989
1990
1991
1992
1993
1994
1995
1996
1997
1998
1999
2000
2001
2002
2003
2004
2005
2006
2007
2008
2009
2010
2011
2012
2013
2014
2015
2016
2017
2018
2019
2020
2021
2022
2023
2024
2025
2026
"$(OX)\page_index.h": "$(OBJDIR)\mkindex$E" $(SRC)
	$** > $@

"$(OX)\builtin_data.h":	"$(OBJDIR)\mkbuiltin$E" "$(OX)\builtin_data.reslist"
	"$(OBJDIR)\mkbuiltin$E" --prefix "$(SRCDIR)/" --reslist "$(OX)\builtin_data.reslist" > $@

cleanx:
	-del "$(OX)\*.obj" 2>NUL
	-del "$(OBJDIR)\*.obj" 2>NUL
	-del "$(OX)\*_.c" 2>NUL
	-del "$(OX)\*.h" 2>NUL
	-del "$(OX)\*.ilk" 2>NUL
	-del "$(OX)\*.map" 2>NUL
	-del "$(OX)\*.res" 2>NUL
	-del "$(OX)\*.reslist" 2>NUL
	-del "$(OX)\headers" 2>NUL
	-del "$(OX)\linkopts" 2>NUL
	-del "$(OX)\vc*.pdb" 2>NUL

clean: cleanx
	-del "$(APPNAME)" 2>NUL
	-del "$(PDBNAME)" 2>NUL
	-del "$(APPMANIFEST)" 2>NUL
	-del "$(OBJDIR)\translate$E" 2>NUL
	-del "$(OBJDIR)\translate$P" 2>NUL
	-del "$(OBJDIR)\mkindex$E" 2>NUL
	-del "$(OBJDIR)\mkindex$P" 2>NUL
	-del "$(OBJDIR)\makeheaders$E" 2>NUL
	-del "$(OBJDIR)\makeheaders$P" 2>NUL
	-del "$(OBJDIR)\mkversion$E" 2>NUL
	-del "$(OBJDIR)\mkversion$P" 2>NUL
	-del "$(OBJDIR)\mkcss$E" 2>NUL
	-del "$(OBJDIR)\mkcss$P" 2>NUL
	-del "$(OBJDIR)\codecheck1$E" 2>NUL
	-del "$(OBJDIR)\codecheck1$P" 2>NUL
	-del "$(OBJDIR)\mkbuiltin$E" 2>NUL
	-del "$(OBJDIR)\mkbuiltin$P" 2>NUL

realclean: clean

"$(OBJDIR)\json$O" : "$(SRCDIR)\json_detail.h"
"$(OBJDIR)\json_artifact$O" : "$(SRCDIR)\json_detail.h"
"$(OBJDIR)\json_branch$O" : "$(SRCDIR)\json_detail.h"
"$(OBJDIR)\json_config$O" : "$(SRCDIR)\json_detail.h"
2273
2274
2275
2276
2277
2278
2279
2280
2281
2282
2283
2284
2285
2286
2287
2288
$(APPLICATION):	$(TRANSLATEDOBJ) $(SQLITEOBJ) $(SQLITESHELLOBJ) $(THOBJ) $(ZLIBOBJ) headers $(RESOURCE)
	$(LINK) $(LINKFLAGS) -out:"$@" $(TRANSLATEDOBJ) $(SQLITEOBJ) $(SQLITESHELLOBJ) $(THOBJ) $(ZLIBOBJ) $(RESOURCE)

# cleanup

.PHONY: clean
clean:
	del /F $(TRANSLATEDOBJ) $(SQLITEOBJ) $(THOBJ) $(ZLIBOBJ) $(UTILS_OBJ) version.obj
	del /F $(TRANSLATEDSRC)
	del /F *.h headers
	del /F $(RESOURCE)

.PHONY: clobber
clobber: clean
	del /F *.exe
}]







|
|
|
|



|

2273
2274
2275
2276
2277
2278
2279
2280
2281
2282
2283
2284
2285
2286
2287
2288
$(APPLICATION):	$(TRANSLATEDOBJ) $(SQLITEOBJ) $(SQLITESHELLOBJ) $(THOBJ) $(ZLIBOBJ) headers $(RESOURCE)
	$(LINK) $(LINKFLAGS) -out:"$@" $(TRANSLATEDOBJ) $(SQLITEOBJ) $(SQLITESHELLOBJ) $(THOBJ) $(ZLIBOBJ) $(RESOURCE)

# cleanup

.PHONY: clean
clean:
	-del /F $(TRANSLATEDOBJ) $(SQLITEOBJ) $(THOBJ) $(ZLIBOBJ) $(UTILS_OBJ) version.obj
	-del /F $(TRANSLATEDSRC)
	-del /F *.h headers
	-del /F $(RESOURCE)

.PHONY: clobber
clobber: clean
	-del /F *.exe
}]
Changes to src/sqlite3.c.
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
**
** See also: [sqlite3_libversion()],
** [sqlite3_libversion_number()], [sqlite3_sourceid()],
** [sqlite_version()] and [sqlite_source_id()].
*/
#define SQLITE_VERSION        "3.25.0"
#define SQLITE_VERSION_NUMBER 3025000
#define SQLITE_SOURCE_ID      "2018-09-10 19:34:06 74c381b573817d0212153278b5ee5d2238a27a727dcf7ee769365c47bb9fc40d"

/*
** CAPI3REF: Run-Time Library Version Numbers
** KEYWORDS: sqlite3_version sqlite3_sourceid
**
** These interfaces provide the same information as the [SQLITE_VERSION],
** [SQLITE_VERSION_NUMBER], and [SQLITE_SOURCE_ID] C preprocessor macros







|







1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
**
** See also: [sqlite3_libversion()],
** [sqlite3_libversion_number()], [sqlite3_sourceid()],
** [sqlite_version()] and [sqlite_source_id()].
*/
#define SQLITE_VERSION        "3.25.0"
#define SQLITE_VERSION_NUMBER 3025000
#define SQLITE_SOURCE_ID      "2018-09-15 04:01:47 b63af6c3bd33152742648d5d2e8dc5d5fcbcdd27df409272b6aea00a6f761760"

/*
** CAPI3REF: Run-Time Library Version Numbers
** KEYWORDS: sqlite3_version sqlite3_sourceid
**
** These interfaces provide the same information as the [SQLITE_VERSION],
** [SQLITE_VERSION_NUMBER], and [SQLITE_SOURCE_ID] C preprocessor macros
2111
2112
2113
2114
2115
2116
2117
2118
2119
2120
2121
2122
2123
2124
2125
2126
2127
2128
2129

2130
2131
2132
2133
2134
2135
2136
**
** <li>[[SQLITE_FCNTL_DATA_VERSION]]
** The [SQLITE_FCNTL_DATA_VERSION] opcode is used to detect changes to
** a database file.  The argument is a pointer to a 32-bit unsigned integer.
** The "data version" for the pager is written into the pointer.  The
** "data version" changes whenever any change occurs to the corresponding
** database file, either through SQL statements on the same database
** connection, or through transactions committed by separate database
** connections possibly in other processes. The [sqlite3_total_changes()]
** interface can be used to find if any database on the connection has changed,
** but that interface response to changes on TEMP as well as MAIN and does
** not provide a mechanism to detect changes to MAIN only.  Also, the
** [sqlite3_total_changes()] interface response to internal changes only and
** omits changes made by other database connections.  The
** [PRAGMA data_version] command provide a mechanism to detect changes to
** a single attached database that occur due to other database connections,
** but omits changes implemented by the database connection for which it is
** called.  This file control is the only mechanism to detect changes that
** happen either internally or externally on a single database.

** </ul>
*/
#define SQLITE_FCNTL_LOCKSTATE               1
#define SQLITE_FCNTL_GET_LOCKPROXYFILE       2
#define SQLITE_FCNTL_SET_LOCKPROXYFILE       3
#define SQLITE_FCNTL_LAST_ERRNO              4
#define SQLITE_FCNTL_SIZE_HINT               5







|


|

|



|

|
>







2111
2112
2113
2114
2115
2116
2117
2118
2119
2120
2121
2122
2123
2124
2125
2126
2127
2128
2129
2130
2131
2132
2133
2134
2135
2136
2137
**
** <li>[[SQLITE_FCNTL_DATA_VERSION]]
** The [SQLITE_FCNTL_DATA_VERSION] opcode is used to detect changes to
** a database file.  The argument is a pointer to a 32-bit unsigned integer.
** The "data version" for the pager is written into the pointer.  The
** "data version" changes whenever any change occurs to the corresponding
** database file, either through SQL statements on the same database
** connection or through transactions committed by separate database
** connections possibly in other processes. The [sqlite3_total_changes()]
** interface can be used to find if any database on the connection has changed,
** but that interface responds to changes on TEMP as well as MAIN and does
** not provide a mechanism to detect changes to MAIN only.  Also, the
** [sqlite3_total_changes()] interface responds to internal changes only and
** omits changes made by other database connections.  The
** [PRAGMA data_version] command provide a mechanism to detect changes to
** a single attached database that occur due to other database connections,
** but omits changes implemented by the database connection on which it is
** called.  This file control is the only mechanism to detect changes that
** happen either internally or externally and that are associated with
** a particular attached database.
** </ul>
*/
#define SQLITE_FCNTL_LOCKSTATE               1
#define SQLITE_FCNTL_GET_LOCKPROXYFILE       2
#define SQLITE_FCNTL_SET_LOCKPROXYFILE       3
#define SQLITE_FCNTL_LAST_ERRNO              4
#define SQLITE_FCNTL_SIZE_HINT               5
5760
5761
5762
5763
5764
5765
5766
5767
5768
5769
5770
5771
5772
5773
5774
** parameters. ^An aggregate SQL function requires an implementation of xStep
** and xFinal and NULL pointer must be passed for xFunc. ^To delete an existing
** SQL function or aggregate, pass NULL pointers for all three function
** callbacks.
**
** ^The sixth, seventh, eighth and ninth parameters (xStep, xFinal, xValue 
** and xInverse) passed to sqlite3_create_window_function are pointers to
** C-lanugage callbacks that implement the new function. xStep and xFinal
** must both be non-NULL. xValue and xInverse may either both be NULL, in
** which case a regular aggregate function is created, or must both be 
** non-NULL, in which case the new function may be used as either an aggregate
** or aggregate window function. More details regarding the implementation
** of aggregate window functions are 
** [user-defined window functions|available here].
**







|







5761
5762
5763
5764
5765
5766
5767
5768
5769
5770
5771
5772
5773
5774
5775
** parameters. ^An aggregate SQL function requires an implementation of xStep
** and xFinal and NULL pointer must be passed for xFunc. ^To delete an existing
** SQL function or aggregate, pass NULL pointers for all three function
** callbacks.
**
** ^The sixth, seventh, eighth and ninth parameters (xStep, xFinal, xValue 
** and xInverse) passed to sqlite3_create_window_function are pointers to
** C-language callbacks that implement the new function. xStep and xFinal
** must both be non-NULL. xValue and xInverse may either both be NULL, in
** which case a regular aggregate function is created, or must both be 
** non-NULL, in which case the new function may be used as either an aggregate
** or aggregate window function. More details regarding the implementation
** of aggregate window functions are 
** [user-defined window functions|available here].
**
10286
10287
10288
10289
10290
10291
10292
10293
10294
10295
10296
10297
10298
10299
10300
** The following are allowed values for 6th argument (the F argument) to
** the [sqlite3_deserialize(D,S,P,N,M,F)] interface.
**
** The SQLITE_DESERIALIZE_FREEONCLOSE means that the database serialization
** in the P argument is held in memory obtained from [sqlite3_malloc64()]
** and that SQLite should take ownership of this memory and automatically
** free it when it has finished using it.  Without this flag, the caller
** is resposible for freeing any dynamically allocated memory.
**
** The SQLITE_DESERIALIZE_RESIZEABLE flag means that SQLite is allowed to
** grow the size of the database using calls to [sqlite3_realloc64()].  This
** flag should only be used if SQLITE_DESERIALIZE_FREEONCLOSE is also used.
** Without this flag, the deserialized database cannot increase in size beyond
** the number of bytes specified by the M parameter.
**







|







10287
10288
10289
10290
10291
10292
10293
10294
10295
10296
10297
10298
10299
10300
10301
** The following are allowed values for 6th argument (the F argument) to
** the [sqlite3_deserialize(D,S,P,N,M,F)] interface.
**
** The SQLITE_DESERIALIZE_FREEONCLOSE means that the database serialization
** in the P argument is held in memory obtained from [sqlite3_malloc64()]
** and that SQLite should take ownership of this memory and automatically
** free it when it has finished using it.  Without this flag, the caller
** is responsible for freeing any dynamically allocated memory.
**
** The SQLITE_DESERIALIZE_RESIZEABLE flag means that SQLite is allowed to
** grow the size of the database using calls to [sqlite3_realloc64()].  This
** flag should only be used if SQLITE_DESERIALIZE_FREEONCLOSE is also used.
** Without this flag, the deserialized database cannot increase in size beyond
** the number of bytes specified by the M parameter.
**
12464
12465
12466
12467
12468
12469
12470
12471
12472
12473
12474
12475
12476
12477
12478
**            document such as "I won first place" is tokenized, entries are
**            added to the FTS index for "i", "won", "first", "1st" and
**            "place".
**
**            This way, even if the tokenizer does not provide synonyms
**            when tokenizing query text (it should not - to do would be
**            inefficient), it doesn't matter if the user queries for 
**            'first + place' or '1st + place', as there are entires in the
**            FTS index corresponding to both forms of the first token.
**   </ol>
**
**   Whether it is parsing document or query text, any call to xToken that
**   specifies a <i>tflags</i> argument with the FTS5_TOKEN_COLOCATED bit
**   is considered to supply a synonym for the previous token. For example,
**   when parsing the document "I won first place", a tokenizer that supports







|







12465
12466
12467
12468
12469
12470
12471
12472
12473
12474
12475
12476
12477
12478
12479
**            document such as "I won first place" is tokenized, entries are
**            added to the FTS index for "i", "won", "first", "1st" and
**            "place".
**
**            This way, even if the tokenizer does not provide synonyms
**            when tokenizing query text (it should not - to do would be
**            inefficient), it doesn't matter if the user queries for 
**            'first + place' or '1st + place', as there are entries in the
**            FTS index corresponding to both forms of the first token.
**   </ol>
**
**   Whether it is parsing document or query text, any call to xToken that
**   specifies a <i>tflags</i> argument with the FTS5_TOKEN_COLOCATED bit
**   is considered to supply a synonym for the previous token. For example,
**   when parsing the document "I won first place", a tokenizer that supports
12492
12493
12494
12495
12496
12497
12498
12499
12500
12501
12502
12503
12504
12505
12506
**   There is no limit to the number of synonyms that may be provided for a
**   single token.
**
**   In many cases, method (1) above is the best approach. It does not add 
**   extra data to the FTS index or require FTS5 to query for multiple terms,
**   so it is efficient in terms of disk space and query speed. However, it
**   does not support prefix queries very well. If, as suggested above, the
**   token "first" is subsituted for "1st" by the tokenizer, then the query:
**
**   <codeblock>
**     ... MATCH '1s*'</codeblock>
**
**   will not match documents that contain the token "1st" (as the tokenizer
**   will probably not map "1s" to any prefix of "first").
**







|







12493
12494
12495
12496
12497
12498
12499
12500
12501
12502
12503
12504
12505
12506
12507
**   There is no limit to the number of synonyms that may be provided for a
**   single token.
**
**   In many cases, method (1) above is the best approach. It does not add 
**   extra data to the FTS index or require FTS5 to query for multiple terms,
**   so it is efficient in terms of disk space and query speed. However, it
**   does not support prefix queries very well. If, as suggested above, the
**   token "first" is substituted for "1st" by the tokenizer, then the query:
**
**   <codeblock>
**     ... MATCH '1s*'</codeblock>
**
**   will not match documents that contain the token "1st" (as the tokenizer
**   will probably not map "1s" to any prefix of "first").
**
18970
18971
18972
18973
18974
18975
18976

18977
18978
18979
18980
18981
18982
18983
SQLITE_PRIVATE int sqlite3ResolveOrderGroupBy(Parse*, Select*, ExprList*, const char*);
SQLITE_PRIVATE void sqlite3ColumnDefault(Vdbe *, Table *, int, int);
SQLITE_PRIVATE void sqlite3AlterFinishAddColumn(Parse *, Token *);
SQLITE_PRIVATE void sqlite3AlterBeginAddColumn(Parse *, SrcList *);
SQLITE_PRIVATE void *sqlite3RenameTokenMap(Parse*, void*, Token*);
SQLITE_PRIVATE void sqlite3RenameTokenRemap(Parse*, void *pTo, void *pFrom);
SQLITE_PRIVATE void sqlite3RenameExprUnmap(Parse*, Expr*);

SQLITE_PRIVATE CollSeq *sqlite3GetCollSeq(Parse*, u8, CollSeq *, const char*);
SQLITE_PRIVATE char sqlite3AffinityType(const char*, Column*);
SQLITE_PRIVATE void sqlite3Analyze(Parse*, Token*, Token*);
SQLITE_PRIVATE int sqlite3InvokeBusyHandler(BusyHandler*, sqlite3_file*);
SQLITE_PRIVATE int sqlite3FindDb(sqlite3*, Token*);
SQLITE_PRIVATE int sqlite3FindDbName(sqlite3 *, const char *);
SQLITE_PRIVATE int sqlite3AnalysisLoad(sqlite3*,int iDB);







>







18971
18972
18973
18974
18975
18976
18977
18978
18979
18980
18981
18982
18983
18984
18985
SQLITE_PRIVATE int sqlite3ResolveOrderGroupBy(Parse*, Select*, ExprList*, const char*);
SQLITE_PRIVATE void sqlite3ColumnDefault(Vdbe *, Table *, int, int);
SQLITE_PRIVATE void sqlite3AlterFinishAddColumn(Parse *, Token *);
SQLITE_PRIVATE void sqlite3AlterBeginAddColumn(Parse *, SrcList *);
SQLITE_PRIVATE void *sqlite3RenameTokenMap(Parse*, void*, Token*);
SQLITE_PRIVATE void sqlite3RenameTokenRemap(Parse*, void *pTo, void *pFrom);
SQLITE_PRIVATE void sqlite3RenameExprUnmap(Parse*, Expr*);
SQLITE_PRIVATE void sqlite3RenameExprlistUnmap(Parse*, ExprList*);
SQLITE_PRIVATE CollSeq *sqlite3GetCollSeq(Parse*, u8, CollSeq *, const char*);
SQLITE_PRIVATE char sqlite3AffinityType(const char*, Column*);
SQLITE_PRIVATE void sqlite3Analyze(Parse*, Token*, Token*);
SQLITE_PRIVATE int sqlite3InvokeBusyHandler(BusyHandler*, sqlite3_file*);
SQLITE_PRIVATE int sqlite3FindDb(sqlite3*, Token*);
SQLITE_PRIVATE int sqlite3FindDbName(sqlite3 *, const char *);
SQLITE_PRIVATE int sqlite3AnalysisLoad(sqlite3*,int iDB);
27604
27605
27606
27607
27608
27609
27610
27611





27612
27613
27614
27615
27616
27617
27618
          xtype = etSTRING;
        }else{
          bufpt = va_arg(ap,char*);
        }
        if( bufpt==0 ){
          bufpt = "";
        }else if( xtype==etDYNSTRING ){
          if( pAccum->nChar==0 && pAccum->mxAlloc && width==0 && precision<0 ){





            /* Special optimization for sqlite3_mprintf("%z..."):
            ** Extend an existing memory allocation rather than creating
            ** a new one. */
            assert( (pAccum->printfFlags&SQLITE_PRINTF_MALLOCED)==0 );
            pAccum->zText = bufpt;
            pAccum->nAlloc = sqlite3DbMallocSize(pAccum->db, bufpt);
            pAccum->nChar = 0x7fffffff & (int)strlen(bufpt);







|
>
>
>
>
>







27606
27607
27608
27609
27610
27611
27612
27613
27614
27615
27616
27617
27618
27619
27620
27621
27622
27623
27624
27625
          xtype = etSTRING;
        }else{
          bufpt = va_arg(ap,char*);
        }
        if( bufpt==0 ){
          bufpt = "";
        }else if( xtype==etDYNSTRING ){
          if( pAccum->nChar==0
           && pAccum->mxAlloc
           && width==0
           && precision<0
           && pAccum->accError==0
          ){
            /* Special optimization for sqlite3_mprintf("%z..."):
            ** Extend an existing memory allocation rather than creating
            ** a new one. */
            assert( (pAccum->printfFlags&SQLITE_PRINTF_MALLOCED)==0 );
            pAccum->zText = bufpt;
            pAccum->nAlloc = sqlite3DbMallocSize(pAccum->db, bufpt);
            pAccum->nChar = 0x7fffffff & (int)strlen(bufpt);
36966
36967
36968
36969
36970
36971
36972

36973

36974
36975
36976
36977
36978
36979
36980
** any load or store begun after the barrier.
*/
static void unixShmBarrier(
  sqlite3_file *fd                /* Database file holding the shared memory */
){
  UNUSED_PARAMETER(fd);
  sqlite3MemoryBarrier();         /* compiler-defined memory barrier */

  assert( unixFileMutexNotheld((unixFile*)fd) );

  unixEnterMutex();               /* Also mutex, for redundancy */
  unixLeaveMutex();
}

/*
** Close a connection to shared-memory.  Delete the underlying 
** storage if deleteFlag is true.







>
|
>







36973
36974
36975
36976
36977
36978
36979
36980
36981
36982
36983
36984
36985
36986
36987
36988
36989
** any load or store begun after the barrier.
*/
static void unixShmBarrier(
  sqlite3_file *fd                /* Database file holding the shared memory */
){
  UNUSED_PARAMETER(fd);
  sqlite3MemoryBarrier();         /* compiler-defined memory barrier */
  assert( fd->pMethods->xLock==nolockLock 
       || unixFileMutexNotheld((unixFile*)fd) 
  );
  unixEnterMutex();               /* Also mutex, for redundancy */
  unixLeaveMutex();
}

/*
** Close a connection to shared-memory.  Delete the underlying 
** storage if deleteFlag is true.
46283
46284
46285
46286
46287
46288
46289
46290
46291

46292
46293
46294
46295
46296
46297
46298
**
** This file implements an in-memory VFS. A database is held as a contiguous
** block of memory.
**
** This file also implements interface sqlite3_serialize() and
** sqlite3_deserialize().
*/
#ifdef SQLITE_ENABLE_DESERIALIZE
/* #include "sqliteInt.h" */


/*
** Forward declaration of objects used by this utility
*/
typedef struct sqlite3_vfs MemVfs;
typedef struct MemFile MemFile;








<

>







46292
46293
46294
46295
46296
46297
46298

46299
46300
46301
46302
46303
46304
46305
46306
46307
**
** This file implements an in-memory VFS. A database is held as a contiguous
** block of memory.
**
** This file also implements interface sqlite3_serialize() and
** sqlite3_deserialize().
*/

/* #include "sqliteInt.h" */
#ifdef SQLITE_ENABLE_DESERIALIZE

/*
** Forward declaration of objects used by this utility
*/
typedef struct sqlite3_vfs MemVfs;
typedef struct MemFile MemFile;

60505
60506
60507
60508
60509
60510
60511
60512
60513
60514
60515
60516
60517
60518
60519
** it takes a snapshot of the state of the WAL and wal-index for the current
** instant in time.  The current thread will continue to use this snapshot.
** Other threads might append new content to the WAL and wal-index but
** that extra content is ignored by the current thread.
**
** If the database contents have changes since the previous read
** transaction, then *pChanged is set to 1 before returning.  The
** Pager layer will use this to know that is cache is stale and
** needs to be flushed.
*/
SQLITE_PRIVATE int sqlite3WalBeginReadTransaction(Wal *pWal, int *pChanged){
  int rc;                         /* Return code */
  int cnt = 0;                    /* Number of TryBeginRead attempts */

#ifdef SQLITE_ENABLE_SNAPSHOT







|







60514
60515
60516
60517
60518
60519
60520
60521
60522
60523
60524
60525
60526
60527
60528
** it takes a snapshot of the state of the WAL and wal-index for the current
** instant in time.  The current thread will continue to use this snapshot.
** Other threads might append new content to the WAL and wal-index but
** that extra content is ignored by the current thread.
**
** If the database contents have changes since the previous read
** transaction, then *pChanged is set to 1 before returning.  The
** Pager layer will use this to know that its cache is stale and
** needs to be flushed.
*/
SQLITE_PRIVATE int sqlite3WalBeginReadTransaction(Wal *pWal, int *pChanged){
  int rc;                         /* Return code */
  int cnt = 0;                    /* Number of TryBeginRead attempts */

#ifdef SQLITE_ENABLE_SNAPSHOT
78536
78537
78538
78539
78540
78541
78542
78543
78544
78545
78546
78547
78548
78549
78550

  /* If the VM did not run to completion or if it encountered an
  ** error, then it might not have been halted properly.  So halt
  ** it now.
  */
  sqlite3VdbeHalt(p);

  /* If the VDBE has be run even partially, then transfer the error code
  ** and error message from the VDBE into the main database structure.  But
  ** if the VDBE has just been set to run but has not actually executed any
  ** instructions yet, leave the main database error information unchanged.
  */
  if( p->pc>=0 ){
    vdbeInvokeSqllog(p);
    sqlite3VdbeTransferError(p);







|







78545
78546
78547
78548
78549
78550
78551
78552
78553
78554
78555
78556
78557
78558
78559

  /* If the VM did not run to completion or if it encountered an
  ** error, then it might not have been halted properly.  So halt
  ** it now.
  */
  sqlite3VdbeHalt(p);

  /* If the VDBE has been run even partially, then transfer the error code
  ** and error message from the VDBE into the main database structure.  But
  ** if the VDBE has just been set to run but has not actually executed any
  ** instructions yet, leave the main database error information unchanged.
  */
  if( p->pc>=0 ){
    vdbeInvokeSqllog(p);
    sqlite3VdbeTransferError(p);
101159
101160
101161
101162
101163
101164
101165
101166
101167
101168
101169
101170
101171
101172
101173
/*
** Generate code to verify that the schemas of database zDb and, if
** bTemp is not true, database "temp", can still be parsed. This is
** called at the end of the generation of an ALTER TABLE ... RENAME ...
** statement to ensure that the operation has not rendered any schema
** objects unusable.
*/
void renameTestSchema(Parse *pParse, const char *zDb, int bTemp){
  sqlite3NestedParse(pParse, 
      "SELECT 1 "
      "FROM \"%w\".%s "
      "WHERE name NOT LIKE 'sqlite_%%'"
      " AND sql NOT LIKE 'create virtual%%'"
      " AND sqlite_rename_test(%Q, sql, type, name, %d)=NULL ",
      zDb, MASTER_NAME, 







|







101168
101169
101170
101171
101172
101173
101174
101175
101176
101177
101178
101179
101180
101181
101182
/*
** Generate code to verify that the schemas of database zDb and, if
** bTemp is not true, database "temp", can still be parsed. This is
** called at the end of the generation of an ALTER TABLE ... RENAME ...
** statement to ensure that the operation has not rendered any schema
** objects unusable.
*/
static void renameTestSchema(Parse *pParse, const char *zDb, int bTemp){
  sqlite3NestedParse(pParse, 
      "SELECT 1 "
      "FROM \"%w\".%s "
      "WHERE name NOT LIKE 'sqlite_%%'"
      " AND sql NOT LIKE 'create virtual%%'"
      " AND sqlite_rename_test(%Q, sql, type, name, %d)=NULL ",
      zDb, MASTER_NAME, 
101186
101187
101188
101189
101190
101191
101192
101193
101194
101195
101196
101197
101198
101199
101200
  }
}

/*
** Generate code to reload the schema for database iDb. And, if iDb!=1, for
** the temp database as well.
*/
void renameReloadSchema(Parse *pParse, int iDb){
  Vdbe *v = pParse->pVdbe;
  if( v ){
    sqlite3ChangeCookie(pParse, iDb);
    sqlite3VdbeAddParseSchemaOp(pParse->pVdbe, iDb, 0);
    if( iDb!=1 ) sqlite3VdbeAddParseSchemaOp(pParse->pVdbe, 1, 0);
  }
}







|







101195
101196
101197
101198
101199
101200
101201
101202
101203
101204
101205
101206
101207
101208
101209
  }
}

/*
** Generate code to reload the schema for database iDb. And, if iDb!=1, for
** the temp database as well.
*/
static void renameReloadSchema(Parse *pParse, int iDb){
  Vdbe *v = pParse->pVdbe;
  if( v ){
    sqlite3ChangeCookie(pParse, iDb);
    sqlite3VdbeAddParseSchemaOp(pParse->pVdbe, iDb, 0);
    if( iDb!=1 ) sqlite3VdbeAddParseSchemaOp(pParse->pVdbe, 1, 0);
  }
}
101766
101767
101768
101769
101770
101771
101772
101773
101774
101775
101776
101777
101778
101779
101780
**
**     sqlite3_free(x);
**     if( x==y ) ...
**
** Technically, as x no longer points into a valid object or to the byte
** following a valid object, it may not be used in comparison operations.
*/
void renameTokenCheckAll(Parse *pParse, void *pPtr){
  if( pParse->nErr==0 && pParse->db->mallocFailed==0 ){
    RenameToken *p;
    u8 i = 0;
    for(p=pParse->pRename; p; p=p->pNext){
      if( p->p ){
        assert( p->p!=pPtr );
        i += *(u8*)(p->p);







|







101775
101776
101777
101778
101779
101780
101781
101782
101783
101784
101785
101786
101787
101788
101789
**
**     sqlite3_free(x);
**     if( x==y ) ...
**
** Technically, as x no longer points into a valid object or to the byte
** following a valid object, it may not be used in comparison operations.
*/
static void renameTokenCheckAll(Parse *pParse, void *pPtr){
  if( pParse->nErr==0 && pParse->db->mallocFailed==0 ){
    RenameToken *p;
    u8 i = 0;
    for(p=pParse->pRename; p; p=p->pNext){
      if( p->p ){
        assert( p->p!=pPtr );
        i += *(u8*)(p->p);
101838
101839
101840
101841
101842
101843
101844


















101845
101846
101847
101848
101849
101850
101851
SQLITE_PRIVATE void sqlite3RenameExprUnmap(Parse *pParse, Expr *pExpr){
  Walker sWalker;
  memset(&sWalker, 0, sizeof(Walker));
  sWalker.pParse = pParse;
  sWalker.xExprCallback = renameUnmapExprCb;
  sqlite3WalkExpr(&sWalker, pExpr);
}



















/*
** Free the list of RenameToken objects given in the second argument
*/
static void renameTokenFree(sqlite3 *db, RenameToken *pToken){
  RenameToken *pNext;
  RenameToken *p;







>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>







101847
101848
101849
101850
101851
101852
101853
101854
101855
101856
101857
101858
101859
101860
101861
101862
101863
101864
101865
101866
101867
101868
101869
101870
101871
101872
101873
101874
101875
101876
101877
101878
SQLITE_PRIVATE void sqlite3RenameExprUnmap(Parse *pParse, Expr *pExpr){
  Walker sWalker;
  memset(&sWalker, 0, sizeof(Walker));
  sWalker.pParse = pParse;
  sWalker.xExprCallback = renameUnmapExprCb;
  sqlite3WalkExpr(&sWalker, pExpr);
}

/*
** Remove all nodes that are part of expression-list pEList from the 
** rename list.
*/
SQLITE_PRIVATE void sqlite3RenameExprlistUnmap(Parse *pParse, ExprList *pEList){
  if( pEList ){
    int i;
    Walker sWalker;
    memset(&sWalker, 0, sizeof(Walker));
    sWalker.pParse = pParse;
    sWalker.xExprCallback = renameUnmapExprCb;
    sqlite3WalkExprList(&sWalker, pEList);
    for(i=0; i<pEList->nExpr; i++){
      sqlite3RenameTokenRemap(pParse, 0, (void*)pEList->a[i].zName);
    }
  }
}

/*
** Free the list of RenameToken objects given in the second argument
*/
static void renameTokenFree(sqlite3 *db, RenameToken *pToken){
  RenameToken *pNext;
  RenameToken *p;
107744
107745
107746
107747
107748
107749
107750



107751
107752
107753
107754
107755
107756
107757
  sEnd.n = 1;

  /* Use sqlite3EndTable() to add the view to the SQLITE_MASTER table */
  sqlite3EndTable(pParse, 0, &sEnd, 0, 0);

create_view_fail:
  sqlite3SelectDelete(db, pSelect);



  sqlite3ExprListDelete(db, pCNames);
  return;
}
#endif /* SQLITE_OMIT_VIEW */

#if !defined(SQLITE_OMIT_VIEW) || !defined(SQLITE_OMIT_VIRTUALTABLE)
/*







>
>
>







107771
107772
107773
107774
107775
107776
107777
107778
107779
107780
107781
107782
107783
107784
107785
107786
107787
  sEnd.n = 1;

  /* Use sqlite3EndTable() to add the view to the SQLITE_MASTER table */
  sqlite3EndTable(pParse, 0, &sEnd, 0, 0);

create_view_fail:
  sqlite3SelectDelete(db, pSelect);
  if( IN_RENAME_OBJECT ){
    sqlite3RenameExprlistUnmap(pParse, pCNames);
  }
  sqlite3ExprListDelete(db, pCNames);
  return;
}
#endif /* SQLITE_OMIT_VIEW */

#if !defined(SQLITE_OMIT_VIEW) || !defined(SQLITE_OMIT_VIRTUALTABLE)
/*
135327
135328
135329
135330
135331
135332
135333
135334
135335
135336
135337
135338
135339
135340
135341
135342
135343
135344
135345
** an access of the index rather than the original table.
*/
static int codeCursorHintFixExpr(Walker *pWalker, Expr *pExpr){
  int rc = WRC_Continue;
  struct CCurHint *pHint = pWalker->u.pCCurHint;
  if( pExpr->op==TK_COLUMN ){
    if( pExpr->iTable!=pHint->iTabCur ){
      Vdbe *v = pWalker->pParse->pVdbe;
      int reg = ++pWalker->pParse->nMem;   /* Register for column value */
      sqlite3ExprCodeGetColumnOfTable(
          v, pExpr->pTab, pExpr->iTable, pExpr->iColumn, reg
      );
      pExpr->op = TK_REGISTER;
      pExpr->iTable = reg;
    }else if( pHint->pIdx!=0 ){
      pExpr->iTable = pHint->iIdxCur;
      pExpr->iColumn = sqlite3ColumnOfIndex(pHint->pIdx, pExpr->iColumn);
      assert( pExpr->iColumn>=0 );
    }







<

|
<
<







135357
135358
135359
135360
135361
135362
135363

135364
135365


135366
135367
135368
135369
135370
135371
135372
** an access of the index rather than the original table.
*/
static int codeCursorHintFixExpr(Walker *pWalker, Expr *pExpr){
  int rc = WRC_Continue;
  struct CCurHint *pHint = pWalker->u.pCCurHint;
  if( pExpr->op==TK_COLUMN ){
    if( pExpr->iTable!=pHint->iTabCur ){

      int reg = ++pWalker->pParse->nMem;   /* Register for column value */
      sqlite3ExprCode(pWalker->pParse, pExpr, reg);


      pExpr->op = TK_REGISTER;
      pExpr->iTable = reg;
    }else if( pHint->pIdx!=0 ){
      pExpr->iTable = pHint->iIdxCur;
      pExpr->iColumn = sqlite3ColumnOfIndex(pHint->pIdx, pExpr->iColumn);
      assert( pExpr->iColumn>=0 );
    }
177351
177352
177353
177354
177355
177356
177357
177358
177359
177360
177361
177362
177363
177364
177365
177366
177367
177368
177369
  if( pStr ){
    pStr->pCtx = ctx;
    jsonAppendChar(pStr, ']');
    if( pStr->bErr ){
      if( pStr->bErr==1 ) sqlite3_result_error_nomem(ctx);
      assert( pStr->bStatic );
    }else if( isFinal ){
      sqlite3_result_text(ctx, pStr->zBuf, pStr->nUsed,
                          pStr->bStatic ? SQLITE_TRANSIENT : sqlite3_free);
      pStr->bStatic = 1;
    }else{
      sqlite3_result_text(ctx, pStr->zBuf, pStr->nUsed, SQLITE_TRANSIENT);
      pStr->nUsed--;
    }
  }else{
    sqlite3_result_text(ctx, "[]", 2, SQLITE_STATIC);
  }
  sqlite3_result_subtype(ctx, JSON_SUBTYPE);
}







|



|







177378
177379
177380
177381
177382
177383
177384
177385
177386
177387
177388
177389
177390
177391
177392
177393
177394
177395
177396
  if( pStr ){
    pStr->pCtx = ctx;
    jsonAppendChar(pStr, ']');
    if( pStr->bErr ){
      if( pStr->bErr==1 ) sqlite3_result_error_nomem(ctx);
      assert( pStr->bStatic );
    }else if( isFinal ){
      sqlite3_result_text(ctx, pStr->zBuf, (int)pStr->nUsed,
                          pStr->bStatic ? SQLITE_TRANSIENT : sqlite3_free);
      pStr->bStatic = 1;
    }else{
      sqlite3_result_text(ctx, pStr->zBuf, (int)pStr->nUsed, SQLITE_TRANSIENT);
      pStr->nUsed--;
    }
  }else{
    sqlite3_result_text(ctx, "[]", 2, SQLITE_STATIC);
  }
  sqlite3_result_subtype(ctx, JSON_SUBTYPE);
}
177404
177405
177406
177407
177408
177409
177410
177411
177412
177413
177414
177415
177416
177417
177418
    if( z[i]=='"' ){
      inStr = !inStr;
    }else if( z[i]=='\\' ){
      i++;
    }
  }
  pStr->nUsed -= i;      
  memmove(&z[1], &z[i+1], pStr->nUsed-1);
}
#else
# define jsonGroupInverse 0
#endif


/*







|







177431
177432
177433
177434
177435
177436
177437
177438
177439
177440
177441
177442
177443
177444
177445
    if( z[i]=='"' ){
      inStr = !inStr;
    }else if( z[i]=='\\' ){
      i++;
    }
  }
  pStr->nUsed -= i;      
  memmove(&z[1], &z[i+1], (size_t)pStr->nUsed-1);
}
#else
# define jsonGroupInverse 0
#endif


/*
177450
177451
177452
177453
177454
177455
177456
177457
177458
177459
177460
177461
177462
177463
177464
177465
177466
177467
177468
  pStr = (JsonString*)sqlite3_aggregate_context(ctx, 0);
  if( pStr ){
    jsonAppendChar(pStr, '}');
    if( pStr->bErr ){
      if( pStr->bErr==1 ) sqlite3_result_error_nomem(ctx);
      assert( pStr->bStatic );
    }else if( isFinal ){
      sqlite3_result_text(ctx, pStr->zBuf, pStr->nUsed,
                          pStr->bStatic ? SQLITE_TRANSIENT : sqlite3_free);
      pStr->bStatic = 1;
    }else{
      sqlite3_result_text(ctx, pStr->zBuf, pStr->nUsed, SQLITE_TRANSIENT);
      pStr->nUsed--;
    }
  }else{
    sqlite3_result_text(ctx, "{}", 2, SQLITE_STATIC);
  }
  sqlite3_result_subtype(ctx, JSON_SUBTYPE);
}







|



|







177477
177478
177479
177480
177481
177482
177483
177484
177485
177486
177487
177488
177489
177490
177491
177492
177493
177494
177495
  pStr = (JsonString*)sqlite3_aggregate_context(ctx, 0);
  if( pStr ){
    jsonAppendChar(pStr, '}');
    if( pStr->bErr ){
      if( pStr->bErr==1 ) sqlite3_result_error_nomem(ctx);
      assert( pStr->bStatic );
    }else if( isFinal ){
      sqlite3_result_text(ctx, pStr->zBuf, (int)pStr->nUsed,
                          pStr->bStatic ? SQLITE_TRANSIENT : sqlite3_free);
      pStr->bStatic = 1;
    }else{
      sqlite3_result_text(ctx, pStr->zBuf, (int)pStr->nUsed, SQLITE_TRANSIENT);
      pStr->nUsed--;
    }
  }else{
    sqlite3_result_text(ctx, "{}", 2, SQLITE_STATIC);
  }
  sqlite3_result_subtype(ctx, JSON_SUBTYPE);
}
181308
181309
181310
181311
181312
181313
181314
181315
181316
181317
181318
181319
181320
181321
181322
**   BEGIN;
**     INSERT INTO rtree...
**     DROP TABLE <tablename>;    -- Would fail with SQLITE_LOCKED
**   COMMIT;
*/
static int rtreeSavepoint(sqlite3_vtab *pVtab, int iSavepoint){
  Rtree *pRtree = (Rtree *)pVtab;
  int iwt = pRtree->inWrTrans;
  UNUSED_PARAMETER(iSavepoint);
  pRtree->inWrTrans = 0;
  nodeBlobReset(pRtree);
  pRtree->inWrTrans = iwt;
  return SQLITE_OK;
}








|







181335
181336
181337
181338
181339
181340
181341
181342
181343
181344
181345
181346
181347
181348
181349
**   BEGIN;
**     INSERT INTO rtree...
**     DROP TABLE <tablename>;    -- Would fail with SQLITE_LOCKED
**   COMMIT;
*/
static int rtreeSavepoint(sqlite3_vtab *pVtab, int iSavepoint){
  Rtree *pRtree = (Rtree *)pVtab;
  u8 iwt = pRtree->inWrTrans;
  UNUSED_PARAMETER(iSavepoint);
  pRtree->inWrTrans = 0;
  nodeBlobReset(pRtree);
  pRtree->inWrTrans = iwt;
  return SQLITE_OK;
}

182448
182449
182450
182451
182452
182453
182454
182455
182456
182457
182458
182459
182460
182461
182462
      }
      if( c<'0' || c>'9' ) return 0;
      continue;
    }
    break;
  }
  if( z[j-1]<'0' ) return 0;
  if( pVal ) *pVal = atof((const char*)p->z);
  p->z += j;
  return 1;
}

/*
** If the input is a well-formed JSON array of coordinates with at least
** four coordinates and where each coordinate is itself a two-value array,







|







182475
182476
182477
182478
182479
182480
182481
182482
182483
182484
182485
182486
182487
182488
182489
      }
      if( c<'0' || c>'9' ) return 0;
      continue;
    }
    break;
  }
  if( z[j-1]<'0' ) return 0;
  if( pVal ) *pVal = (GeoCoord)atof((const char*)p->z);
  p->z += j;
  return 1;
}

/*
** If the input is a well-formed JSON array of coordinates with at least
** four coordinates and where each coordinate is itself a two-value array,
182703
182704
182705
182706
182707
182708
182709
182710
182711
182712
182713
182714
182715
182716
182717
182718
  double F = sqlite3_value_double(argv[6]);
  GeoCoord x1, y1, x0, y0;
  int ii;
  if( p ){
    for(ii=0; ii<p->nVertex; ii++){
      x0 = p->a[ii*2];
      y0 = p->a[ii*2+1];
      x1 = A*x0 + B*y0 + E;
      y1 = C*x0 + D*y0 + F;
      p->a[ii*2] = x1;
      p->a[ii*2+1] = y1;
    }
    sqlite3_result_blob(context, p->hdr, 
       4+8*p->nVertex, SQLITE_TRANSIENT);
    sqlite3_free(p);
  }







|
|







182730
182731
182732
182733
182734
182735
182736
182737
182738
182739
182740
182741
182742
182743
182744
182745
  double F = sqlite3_value_double(argv[6]);
  GeoCoord x1, y1, x0, y0;
  int ii;
  if( p ){
    for(ii=0; ii<p->nVertex; ii++){
      x0 = p->a[ii*2];
      y0 = p->a[ii*2+1];
      x1 = (GeoCoord)(A*x0 + B*y0 + E);
      y1 = (GeoCoord)(C*x0 + D*y0 + F);
      p->a[ii*2] = x1;
      p->a[ii*2+1] = y1;
    }
    sqlite3_result_blob(context, p->hdr, 
       4+8*p->nVertex, SQLITE_TRANSIENT);
    sqlite3_free(p);
  }
182779
182780
182781
182782
182783
182784
182785
182786
182787
182788
182789
182790
182791
182792
182793
182794
182795
182796
182797
  }
  if( p ){
    int ii;
    mnX = mxX = p->a[0];
    mnY = mxY = p->a[1];
    for(ii=1; ii<p->nVertex; ii++){
      double r = p->a[ii*2];
      if( r<mnX ) mnX = r;
      else if( r>mxX ) mxX = r;
      r = p->a[ii*2+1];
      if( r<mnY ) mnY = r;
      else if( r>mxY ) mxY = r;
    }
    if( pRc ) *pRc = SQLITE_OK;
    if( aCoord==0 ){
      geopolyBboxFill:
      pOut = sqlite3_realloc(p, sizeof(GeoPoly)+sizeof(GeoCoord)*6);
      if( pOut==0 ){
        sqlite3_free(p);







|
|

|
|







182806
182807
182808
182809
182810
182811
182812
182813
182814
182815
182816
182817
182818
182819
182820
182821
182822
182823
182824
  }
  if( p ){
    int ii;
    mnX = mxX = p->a[0];
    mnY = mxY = p->a[1];
    for(ii=1; ii<p->nVertex; ii++){
      double r = p->a[ii*2];
      if( r<mnX ) mnX = (float)r;
      else if( r>mxX ) mxX = (float)r;
      r = p->a[ii*2+1];
      if( r<mnY ) mnY = (float)r;
      else if( r>mxY ) mxY = (float)r;
    }
    if( pRc ) *pRc = SQLITE_OK;
    if( aCoord==0 ){
      geopolyBboxFill:
      pOut = sqlite3_realloc(p, sizeof(GeoPoly)+sizeof(GeoCoord)*6);
      if( pOut==0 ){
        sqlite3_free(p);
197383
197384
197385
197386
197387
197388
197389
197390
197391
197392
197393
197394
197395
197396
197397
**            document such as "I won first place" is tokenized, entries are
**            added to the FTS index for "i", "won", "first", "1st" and
**            "place".
**
**            This way, even if the tokenizer does not provide synonyms
**            when tokenizing query text (it should not - to do would be
**            inefficient), it doesn't matter if the user queries for 
**            'first + place' or '1st + place', as there are entires in the
**            FTS index corresponding to both forms of the first token.
**   </ol>
**
**   Whether it is parsing document or query text, any call to xToken that
**   specifies a <i>tflags</i> argument with the FTS5_TOKEN_COLOCATED bit
**   is considered to supply a synonym for the previous token. For example,
**   when parsing the document "I won first place", a tokenizer that supports







|







197410
197411
197412
197413
197414
197415
197416
197417
197418
197419
197420
197421
197422
197423
197424
**            document such as "I won first place" is tokenized, entries are
**            added to the FTS index for "i", "won", "first", "1st" and
**            "place".
**
**            This way, even if the tokenizer does not provide synonyms
**            when tokenizing query text (it should not - to do would be
**            inefficient), it doesn't matter if the user queries for 
**            'first + place' or '1st + place', as there are entries in the
**            FTS index corresponding to both forms of the first token.
**   </ol>
**
**   Whether it is parsing document or query text, any call to xToken that
**   specifies a <i>tflags</i> argument with the FTS5_TOKEN_COLOCATED bit
**   is considered to supply a synonym for the previous token. For example,
**   when parsing the document "I won first place", a tokenizer that supports
197411
197412
197413
197414
197415
197416
197417
197418
197419
197420
197421
197422
197423
197424
197425
**   There is no limit to the number of synonyms that may be provided for a
**   single token.
**
**   In many cases, method (1) above is the best approach. It does not add 
**   extra data to the FTS index or require FTS5 to query for multiple terms,
**   so it is efficient in terms of disk space and query speed. However, it
**   does not support prefix queries very well. If, as suggested above, the
**   token "first" is subsituted for "1st" by the tokenizer, then the query:
**
**   <codeblock>
**     ... MATCH '1s*'</codeblock>
**
**   will not match documents that contain the token "1st" (as the tokenizer
**   will probably not map "1s" to any prefix of "first").
**







|







197438
197439
197440
197441
197442
197443
197444
197445
197446
197447
197448
197449
197450
197451
197452
**   There is no limit to the number of synonyms that may be provided for a
**   single token.
**
**   In many cases, method (1) above is the best approach. It does not add 
**   extra data to the FTS index or require FTS5 to query for multiple terms,
**   so it is efficient in terms of disk space and query speed. However, it
**   does not support prefix queries very well. If, as suggested above, the
**   token "first" is substituted for "1st" by the tokenizer, then the query:
**
**   <codeblock>
**     ... MATCH '1s*'</codeblock>
**
**   will not match documents that contain the token "1st" (as the tokenizer
**   will probably not map "1s" to any prefix of "first").
**
214385
214386
214387
214388
214389
214390
214391
214392
214393
214394
214395
214396
214397
214398
214399
static void fts5SourceIdFunc(
  sqlite3_context *pCtx,          /* Function call context */
  int nArg,                       /* Number of args */
  sqlite3_value **apUnused        /* Function arguments */
){
  assert( nArg==0 );
  UNUSED_PARAM2(nArg, apUnused);
  sqlite3_result_text(pCtx, "fts5: 2018-09-10 16:38:25 564fa75195c5179d0bc86431aaff06136fc65ec63aa9839cd1114b1fbfac928b", -1, SQLITE_TRANSIENT);
}

static int fts5Init(sqlite3 *db){
  static const sqlite3_module fts5Mod = {
    /* iVersion      */ 2,
    /* xCreate       */ fts5CreateMethod,
    /* xConnect      */ fts5ConnectMethod,







|







214412
214413
214414
214415
214416
214417
214418
214419
214420
214421
214422
214423
214424
214425
214426
static void fts5SourceIdFunc(
  sqlite3_context *pCtx,          /* Function call context */
  int nArg,                       /* Number of args */
  sqlite3_value **apUnused        /* Function arguments */
){
  assert( nArg==0 );
  UNUSED_PARAM2(nArg, apUnused);
  sqlite3_result_text(pCtx, "fts5: 2018-09-15 04:01:47 b63af6c3bd33152742648d5d2e8dc5d5fcbcdd27df409272b6aea00a6f761760", -1, SQLITE_TRANSIENT);
}

static int fts5Init(sqlite3 *db){
  static const sqlite3_module fts5Mod = {
    /* iVersion      */ 2,
    /* xCreate       */ fts5CreateMethod,
    /* xConnect      */ fts5ConnectMethod,
219095
219096
219097
219098
219099
219100
219101
219102
219103
219104
219105
219106
219107
219108
#endif
  return rc;
}
#endif /* SQLITE_CORE */
#endif /* !defined(SQLITE_CORE) || defined(SQLITE_ENABLE_STMTVTAB) */

/************** End of stmt.c ************************************************/
#if __LINE__!=219102
#undef SQLITE_SOURCE_ID
#define SQLITE_SOURCE_ID      "2018-09-10 19:34:06 74c381b573817d0212153278b5ee5d2238a27a727dcf7ee769365c47bb9falt2"
#endif
/* Return the source-id for this library */
SQLITE_API const char *sqlite3_sourceid(void){ return SQLITE_SOURCE_ID; }
/************************** End of sqlite3.c ******************************/







|

|




219122
219123
219124
219125
219126
219127
219128
219129
219130
219131
219132
219133
219134
219135
#endif
  return rc;
}
#endif /* SQLITE_CORE */
#endif /* !defined(SQLITE_CORE) || defined(SQLITE_ENABLE_STMTVTAB) */

/************** End of stmt.c ************************************************/
#if __LINE__!=219129
#undef SQLITE_SOURCE_ID
#define SQLITE_SOURCE_ID      "2018-09-15 04:01:47 b63af6c3bd33152742648d5d2e8dc5d5fcbcdd27df409272b6aea00a6f76alt2"
#endif
/* Return the source-id for this library */
SQLITE_API const char *sqlite3_sourceid(void){ return SQLITE_SOURCE_ID; }
/************************** End of sqlite3.c ******************************/
Changes to src/sqlite3.h.
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
**
** See also: [sqlite3_libversion()],
** [sqlite3_libversion_number()], [sqlite3_sourceid()],
** [sqlite_version()] and [sqlite_source_id()].
*/
#define SQLITE_VERSION        "3.25.0"
#define SQLITE_VERSION_NUMBER 3025000
#define SQLITE_SOURCE_ID      "2018-09-10 19:34:06 74c381b573817d0212153278b5ee5d2238a27a727dcf7ee769365c47bb9fc40d"

/*
** CAPI3REF: Run-Time Library Version Numbers
** KEYWORDS: sqlite3_version sqlite3_sourceid
**
** These interfaces provide the same information as the [SQLITE_VERSION],
** [SQLITE_VERSION_NUMBER], and [SQLITE_SOURCE_ID] C preprocessor macros







|







121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
**
** See also: [sqlite3_libversion()],
** [sqlite3_libversion_number()], [sqlite3_sourceid()],
** [sqlite_version()] and [sqlite_source_id()].
*/
#define SQLITE_VERSION        "3.25.0"
#define SQLITE_VERSION_NUMBER 3025000
#define SQLITE_SOURCE_ID      "2018-09-15 04:01:47 b63af6c3bd33152742648d5d2e8dc5d5fcbcdd27df409272b6aea00a6f761760"

/*
** CAPI3REF: Run-Time Library Version Numbers
** KEYWORDS: sqlite3_version sqlite3_sourceid
**
** These interfaces provide the same information as the [SQLITE_VERSION],
** [SQLITE_VERSION_NUMBER], and [SQLITE_SOURCE_ID] C preprocessor macros
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096

1097
1098
1099
1100
1101
1102
1103
**
** <li>[[SQLITE_FCNTL_DATA_VERSION]]
** The [SQLITE_FCNTL_DATA_VERSION] opcode is used to detect changes to
** a database file.  The argument is a pointer to a 32-bit unsigned integer.
** The "data version" for the pager is written into the pointer.  The
** "data version" changes whenever any change occurs to the corresponding
** database file, either through SQL statements on the same database
** connection, or through transactions committed by separate database
** connections possibly in other processes. The [sqlite3_total_changes()]
** interface can be used to find if any database on the connection has changed,
** but that interface response to changes on TEMP as well as MAIN and does
** not provide a mechanism to detect changes to MAIN only.  Also, the
** [sqlite3_total_changes()] interface response to internal changes only and
** omits changes made by other database connections.  The
** [PRAGMA data_version] command provide a mechanism to detect changes to
** a single attached database that occur due to other database connections,
** but omits changes implemented by the database connection for which it is
** called.  This file control is the only mechanism to detect changes that
** happen either internally or externally on a single database.

** </ul>
*/
#define SQLITE_FCNTL_LOCKSTATE               1
#define SQLITE_FCNTL_GET_LOCKPROXYFILE       2
#define SQLITE_FCNTL_SET_LOCKPROXYFILE       3
#define SQLITE_FCNTL_LAST_ERRNO              4
#define SQLITE_FCNTL_SIZE_HINT               5







|


|

|



|

|
>







1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
**
** <li>[[SQLITE_FCNTL_DATA_VERSION]]
** The [SQLITE_FCNTL_DATA_VERSION] opcode is used to detect changes to
** a database file.  The argument is a pointer to a 32-bit unsigned integer.
** The "data version" for the pager is written into the pointer.  The
** "data version" changes whenever any change occurs to the corresponding
** database file, either through SQL statements on the same database
** connection or through transactions committed by separate database
** connections possibly in other processes. The [sqlite3_total_changes()]
** interface can be used to find if any database on the connection has changed,
** but that interface responds to changes on TEMP as well as MAIN and does
** not provide a mechanism to detect changes to MAIN only.  Also, the
** [sqlite3_total_changes()] interface responds to internal changes only and
** omits changes made by other database connections.  The
** [PRAGMA data_version] command provide a mechanism to detect changes to
** a single attached database that occur due to other database connections,
** but omits changes implemented by the database connection on which it is
** called.  This file control is the only mechanism to detect changes that
** happen either internally or externally and that are associated with
** a particular attached database.
** </ul>
*/
#define SQLITE_FCNTL_LOCKSTATE               1
#define SQLITE_FCNTL_GET_LOCKPROXYFILE       2
#define SQLITE_FCNTL_SET_LOCKPROXYFILE       3
#define SQLITE_FCNTL_LAST_ERRNO              4
#define SQLITE_FCNTL_SIZE_HINT               5
4727
4728
4729
4730
4731
4732
4733
4734
4735
4736
4737
4738
4739
4740
4741
** parameters. ^An aggregate SQL function requires an implementation of xStep
** and xFinal and NULL pointer must be passed for xFunc. ^To delete an existing
** SQL function or aggregate, pass NULL pointers for all three function
** callbacks.
**
** ^The sixth, seventh, eighth and ninth parameters (xStep, xFinal, xValue 
** and xInverse) passed to sqlite3_create_window_function are pointers to
** C-lanugage callbacks that implement the new function. xStep and xFinal
** must both be non-NULL. xValue and xInverse may either both be NULL, in
** which case a regular aggregate function is created, or must both be 
** non-NULL, in which case the new function may be used as either an aggregate
** or aggregate window function. More details regarding the implementation
** of aggregate window functions are 
** [user-defined window functions|available here].
**







|







4728
4729
4730
4731
4732
4733
4734
4735
4736
4737
4738
4739
4740
4741
4742
** parameters. ^An aggregate SQL function requires an implementation of xStep
** and xFinal and NULL pointer must be passed for xFunc. ^To delete an existing
** SQL function or aggregate, pass NULL pointers for all three function
** callbacks.
**
** ^The sixth, seventh, eighth and ninth parameters (xStep, xFinal, xValue 
** and xInverse) passed to sqlite3_create_window_function are pointers to
** C-language callbacks that implement the new function. xStep and xFinal
** must both be non-NULL. xValue and xInverse may either both be NULL, in
** which case a regular aggregate function is created, or must both be 
** non-NULL, in which case the new function may be used as either an aggregate
** or aggregate window function. More details regarding the implementation
** of aggregate window functions are 
** [user-defined window functions|available here].
**
9253
9254
9255
9256
9257
9258
9259
9260
9261
9262
9263
9264
9265
9266
9267
** The following are allowed values for 6th argument (the F argument) to
** the [sqlite3_deserialize(D,S,P,N,M,F)] interface.
**
** The SQLITE_DESERIALIZE_FREEONCLOSE means that the database serialization
** in the P argument is held in memory obtained from [sqlite3_malloc64()]
** and that SQLite should take ownership of this memory and automatically
** free it when it has finished using it.  Without this flag, the caller
** is resposible for freeing any dynamically allocated memory.
**
** The SQLITE_DESERIALIZE_RESIZEABLE flag means that SQLite is allowed to
** grow the size of the database using calls to [sqlite3_realloc64()].  This
** flag should only be used if SQLITE_DESERIALIZE_FREEONCLOSE is also used.
** Without this flag, the deserialized database cannot increase in size beyond
** the number of bytes specified by the M parameter.
**







|







9254
9255
9256
9257
9258
9259
9260
9261
9262
9263
9264
9265
9266
9267
9268
** The following are allowed values for 6th argument (the F argument) to
** the [sqlite3_deserialize(D,S,P,N,M,F)] interface.
**
** The SQLITE_DESERIALIZE_FREEONCLOSE means that the database serialization
** in the P argument is held in memory obtained from [sqlite3_malloc64()]
** and that SQLite should take ownership of this memory and automatically
** free it when it has finished using it.  Without this flag, the caller
** is responsible for freeing any dynamically allocated memory.
**
** The SQLITE_DESERIALIZE_RESIZEABLE flag means that SQLite is allowed to
** grow the size of the database using calls to [sqlite3_realloc64()].  This
** flag should only be used if SQLITE_DESERIALIZE_FREEONCLOSE is also used.
** Without this flag, the deserialized database cannot increase in size beyond
** the number of bytes specified by the M parameter.
**
11431
11432
11433
11434
11435
11436
11437
11438
11439
11440
11441
11442
11443
11444
11445
**            document such as "I won first place" is tokenized, entries are
**            added to the FTS index for "i", "won", "first", "1st" and
**            "place".
**
**            This way, even if the tokenizer does not provide synonyms
**            when tokenizing query text (it should not - to do would be
**            inefficient), it doesn't matter if the user queries for 
**            'first + place' or '1st + place', as there are entires in the
**            FTS index corresponding to both forms of the first token.
**   </ol>
**
**   Whether it is parsing document or query text, any call to xToken that
**   specifies a <i>tflags</i> argument with the FTS5_TOKEN_COLOCATED bit
**   is considered to supply a synonym for the previous token. For example,
**   when parsing the document "I won first place", a tokenizer that supports







|







11432
11433
11434
11435
11436
11437
11438
11439
11440
11441
11442
11443
11444
11445
11446
**            document such as "I won first place" is tokenized, entries are
**            added to the FTS index for "i", "won", "first", "1st" and
**            "place".
**
**            This way, even if the tokenizer does not provide synonyms
**            when tokenizing query text (it should not - to do would be
**            inefficient), it doesn't matter if the user queries for 
**            'first + place' or '1st + place', as there are entries in the
**            FTS index corresponding to both forms of the first token.
**   </ol>
**
**   Whether it is parsing document or query text, any call to xToken that
**   specifies a <i>tflags</i> argument with the FTS5_TOKEN_COLOCATED bit
**   is considered to supply a synonym for the previous token. For example,
**   when parsing the document "I won first place", a tokenizer that supports
11459
11460
11461
11462
11463
11464
11465
11466
11467
11468
11469
11470
11471
11472
11473
**   There is no limit to the number of synonyms that may be provided for a
**   single token.
**
**   In many cases, method (1) above is the best approach. It does not add 
**   extra data to the FTS index or require FTS5 to query for multiple terms,
**   so it is efficient in terms of disk space and query speed. However, it
**   does not support prefix queries very well. If, as suggested above, the
**   token "first" is subsituted for "1st" by the tokenizer, then the query:
**
**   <codeblock>
**     ... MATCH '1s*'</codeblock>
**
**   will not match documents that contain the token "1st" (as the tokenizer
**   will probably not map "1s" to any prefix of "first").
**







|







11460
11461
11462
11463
11464
11465
11466
11467
11468
11469
11470
11471
11472
11473
11474
**   There is no limit to the number of synonyms that may be provided for a
**   single token.
**
**   In many cases, method (1) above is the best approach. It does not add 
**   extra data to the FTS index or require FTS5 to query for multiple terms,
**   so it is efficient in terms of disk space and query speed. However, it
**   does not support prefix queries very well. If, as suggested above, the
**   token "first" is substituted for "1st" by the tokenizer, then the query:
**
**   <codeblock>
**     ... MATCH '1s*'</codeblock>
**
**   will not match documents that contain the token "1st" (as the tokenizer
**   will probably not map "1s" to any prefix of "first").
**
Changes to win/Makefile.PellesCGMake.
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
$(APPLICATION):	$(TRANSLATEDOBJ) $(SQLITEOBJ) $(SQLITESHELLOBJ) $(THOBJ) $(ZLIBOBJ) headers $(RESOURCE)
	$(LINK) $(LINKFLAGS) -out:"$@" $(TRANSLATEDOBJ) $(SQLITEOBJ) $(SQLITESHELLOBJ) $(THOBJ) $(ZLIBOBJ) $(RESOURCE)

# cleanup

.PHONY: clean
clean:
	del /F $(TRANSLATEDOBJ) $(SQLITEOBJ) $(THOBJ) $(ZLIBOBJ) $(UTILS_OBJ) version.obj
	del /F $(TRANSLATEDSRC)
	del /F *.h headers
	del /F $(RESOURCE)

.PHONY: clobber
clobber: clean
	del /F *.exe








|
|
|
|



|

182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
$(APPLICATION):	$(TRANSLATEDOBJ) $(SQLITEOBJ) $(SQLITESHELLOBJ) $(THOBJ) $(ZLIBOBJ) headers $(RESOURCE)
	$(LINK) $(LINKFLAGS) -out:"$@" $(TRANSLATEDOBJ) $(SQLITEOBJ) $(SQLITESHELLOBJ) $(THOBJ) $(ZLIBOBJ) $(RESOURCE)

# cleanup

.PHONY: clean
clean:
	-del /F $(TRANSLATEDOBJ) $(SQLITEOBJ) $(THOBJ) $(ZLIBOBJ) $(UTILS_OBJ) version.obj
	-del /F $(TRANSLATEDSRC)
	-del /F *.h headers
	-del /F $(RESOURCE)

.PHONY: clobber
clobber: clean
	-del /F *.exe

Changes to win/Makefile.msc.
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
"$(OX)\page_index.h": "$(OBJDIR)\mkindex$E" $(SRC)
	$** > $@

"$(OX)\builtin_data.h":	"$(OBJDIR)\mkbuiltin$E" "$(OX)\builtin_data.reslist"
	"$(OBJDIR)\mkbuiltin$E" --prefix "$(SRCDIR)/" --reslist "$(OX)\builtin_data.reslist" > $@

cleanx:
	del "$(OX)\*.obj" 2>NUL
	del "$(OBJDIR)\*.obj" 2>NUL
	del "$(OX)\*_.c" 2>NUL
	del "$(OX)\*.h" 2>NUL
	del "$(OX)\*.ilk" 2>NUL
	del "$(OX)\*.map" 2>NUL
	del "$(OX)\*.res" 2>NUL
	del "$(OX)\*.reslist" 2>NUL
	del "$(OX)\headers" 2>NUL
	del "$(OX)\linkopts" 2>NUL
	del "$(OX)\vc*.pdb" 2>NUL

clean: cleanx
	del "$(APPNAME)" 2>NUL
	del "$(PDBNAME)" 2>NUL
	del "$(APPMANIFEST)" 2>NUL
	del "$(OBJDIR)\translate$E" 2>NUL
	del "$(OBJDIR)\translate$P" 2>NUL
	del "$(OBJDIR)\mkindex$E" 2>NUL
	del "$(OBJDIR)\mkindex$P" 2>NUL
	del "$(OBJDIR)\makeheaders$E" 2>NUL
	del "$(OBJDIR)\makeheaders$P" 2>NUL
	del "$(OBJDIR)\mkversion$E" 2>NUL
	del "$(OBJDIR)\mkversion$P" 2>NUL
	del "$(OBJDIR)\mkcss$E" 2>NUL
	del "$(OBJDIR)\mkcss$P" 2>NUL
	del "$(OBJDIR)\codecheck1$E" 2>NUL
	del "$(OBJDIR)\codecheck1$P" 2>NUL
	del "$(OBJDIR)\mkbuiltin$E" 2>NUL
	del "$(OBJDIR)\mkbuiltin$P" 2>NUL

realclean: clean

"$(OBJDIR)\json$O" : "$(SRCDIR)\json_detail.h"
"$(OBJDIR)\json_artifact$O" : "$(SRCDIR)\json_detail.h"
"$(OBJDIR)\json_branch$O" : "$(SRCDIR)\json_detail.h"
"$(OBJDIR)\json_config$O" : "$(SRCDIR)\json_detail.h"







|
|
|
|
|
|
|
|
|
|
|


|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|







1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
"$(OX)\page_index.h": "$(OBJDIR)\mkindex$E" $(SRC)
	$** > $@

"$(OX)\builtin_data.h":	"$(OBJDIR)\mkbuiltin$E" "$(OX)\builtin_data.reslist"
	"$(OBJDIR)\mkbuiltin$E" --prefix "$(SRCDIR)/" --reslist "$(OX)\builtin_data.reslist" > $@

cleanx:
	-del "$(OX)\*.obj" 2>NUL
	-del "$(OBJDIR)\*.obj" 2>NUL
	-del "$(OX)\*_.c" 2>NUL
	-del "$(OX)\*.h" 2>NUL
	-del "$(OX)\*.ilk" 2>NUL
	-del "$(OX)\*.map" 2>NUL
	-del "$(OX)\*.res" 2>NUL
	-del "$(OX)\*.reslist" 2>NUL
	-del "$(OX)\headers" 2>NUL
	-del "$(OX)\linkopts" 2>NUL
	-del "$(OX)\vc*.pdb" 2>NUL

clean: cleanx
	-del "$(APPNAME)" 2>NUL
	-del "$(PDBNAME)" 2>NUL
	-del "$(APPMANIFEST)" 2>NUL
	-del "$(OBJDIR)\translate$E" 2>NUL
	-del "$(OBJDIR)\translate$P" 2>NUL
	-del "$(OBJDIR)\mkindex$E" 2>NUL
	-del "$(OBJDIR)\mkindex$P" 2>NUL
	-del "$(OBJDIR)\makeheaders$E" 2>NUL
	-del "$(OBJDIR)\makeheaders$P" 2>NUL
	-del "$(OBJDIR)\mkversion$E" 2>NUL
	-del "$(OBJDIR)\mkversion$P" 2>NUL
	-del "$(OBJDIR)\mkcss$E" 2>NUL
	-del "$(OBJDIR)\mkcss$P" 2>NUL
	-del "$(OBJDIR)\codecheck1$E" 2>NUL
	-del "$(OBJDIR)\codecheck1$P" 2>NUL
	-del "$(OBJDIR)\mkbuiltin$E" 2>NUL
	-del "$(OBJDIR)\mkbuiltin$P" 2>NUL

realclean: clean

"$(OBJDIR)\json$O" : "$(SRCDIR)\json_detail.h"
"$(OBJDIR)\json_artifact$O" : "$(SRCDIR)\json_detail.h"
"$(OBJDIR)\json_branch$O" : "$(SRCDIR)\json_detail.h"
"$(OBJDIR)\json_config$O" : "$(SRCDIR)\json_detail.h"
Changes to www/alerts.md.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25

26
27
28
29



30
31
32

33
34



35
36
37
38


39
40
41




42
43
44
45






46
47
48


49
50


51



52
53
54


55



56
57

58
59

60
61

62
63

64

65

66
67
68
69
70
71
72



73
74
75

76
77
78
79
80
81
82
83
84
85
86
87






88
89



90



91



92
93
94
95
96

97
98
99
100
101
102
103
104
105

106
107
108


109






110
111
112
113
114

115



116
117
118
119
120
121
122
123
124
125
126

127
128
129
130
131
132
133
134
135
136
137

138
139
140
















































































141





142

143





144

145


146

147


148

































































149








150






151



152





153
154


155



























































156


















































































157
158
159
160

161
162
163
164
165
166
167
168










































































































































































































Email Alerts
============

The email alert system is a work-in-progress.
This documentation was last updated on 2018-08-12.
Check back later for updates.

Email Alerts And Notifications
------------------------------

Beginning with version 2.7, Fossil supports the ability to send
email messages to subscribers alerting them to changes in the repository.
Subscribers can request an email notification of the following kinds
of changes:

  *  New check-ins
  *  Changes to any ticket
  *  Changes to any wiki page
  *  New forum posts
  *  Announcements

Subscribers can either elect to receive emails as soon as these events happen,
or they can receive a daily digest of the events instead.

Email alerts are sent by a [Fossil server](./server.wiki).  You must

have a server [set up to make use of email alerts](#setup).  Email
alerts do not currently work if you are only using Fossil from the
command line.




Users and Subscribers
---------------------


Fossil makes a distinction between "users" and "subscribers".  A
user is someone with a username and password — someone who can



log in.  A subscriber is someone who receives email alerts.  Users
can also be subscribers and subscribers can be users, but that does
not have to be the case.  It is possible to be a user without being
a subscriber and to be a subscriber without being a user.



In the repository database file, users are tracked with the USER table
and subscribers are tracked via the SUBSCRIBER table.





<a id="setup"></a>
Activating Email Alerts
-----------------------







Email alerts are turned off by default.  To activate them, log into
the Fossil server as an administrator and visit the 


[Admin/Notification](/setup_notification)
setup page. ([`/setup_notification`](/setup_notification))






Important:  Email alerts are configured using Admin/Notification, not
Admin/Email-Server.  The Email-Server setup screen is used to configure
a different subsystem within Fossil.






The Admin/Notification setup screen lets you configure how Fossil should
send email for alerts.  There are some required fields at the top of the

screen for elements such as the "From:" address for outgoing emails,
the URL for the Fossil server, and a nickname for the repository that

will appear in the "Subject:" line of outgoing emails.  But the key
setup parameter is the "Email Send Method".


Fossil supports multiple methods for sending email alerts:



  1.  Pipe the email message text into a command, such as `sendmail`.

  2.  Store email messages as individual files in a directory and let
      some other process set up by the administrator take care of
      reading and forwarding those files.
  3.  Store email messages as entries in an SQLite database where
      some external process and read and forward the emails.
  4.  Send emails to an SMTP Relay.
  5.  Send emails directly to the recipients via SMTP.




As of 2018-08-08, method (5) is not yet supported, but there are plans
to add support soon.


The self-hosting Fossil repository at <https://www.fossil-scm.org/> currently
uses method (3).  Outgoing email messages are added to an SQLite database
file.  A separate daemon process continously monitors that database file,
extracts email messages as they are added, and hands them off to 
"procmail" to be sent on to the final recipient.  The self-hosting
Fossil repository uses this technique rather than method (1) because
it is running inside of a restrictive chroot jail which is unable to
hand off messages to "procmail" directly.  The daemon that monitors the
email database is a [short TCL script](/file/tools/email-sender.tcl).
That daemon is started automatically by adding this line:







      /usr/bin/tclsh /home/www/fossil/email-sender.tcl &




To the `/etc/rc.local` file of the Ubuntu server that hosts the



repository.




After making necessary changes to the Admin/Notification page, test
those changes by clicking the "[Send Announcement](/announce)" link
at the top of the page.  Fill in your email address in the "To:"
line and a test message below, and press "Send Message" to verify that

outgoing email is working.

<a id="cap7"></a>
Once email notification is working, one must also adjust user permissions
to allow users to subscribe to email notification.  On the 
Setup/User page, under the permissions for each user, is a new capability
called "Email Alerts".  The corresponding capability letter is "7".
That new "7" capability must be enabled in order for
users to be able to become subscribers.  To allow anonymous passers-by

on the internet to subscribe, simply enable "Email Alerts" for the
"nobody" user category. To require that the user solve a simple CAPTCHA
first, add it to the "anonymous" user category instead.









Signing Up For Email Notification
---------------------------------

Users and/or anonymous passers-by can visit the 
[`/subscribe`](/subscribe) page to sign

up for email notification.




If your users are getting the following complaint from Fossil:

<blockquote>
  Use a different login with greater privilege than FOO to access
  /subscribe
</blockquote>

...then you forgot to [give capability 7](#cap7) to that user or to a
user category that the user is a member of.


After signing up, a single verification email
is sent.  The new subscriber must click a link on that email in order to
activate the subscription.

Subscription verification emails are only sent once.  This is a defense
against malicious robots that try to harass innocent internet users
by having subscription pages send multiple verification emails.
If the initial subscription verification does not go through correctly,
an administrator must intervene to reset the subscription.

Every subscriber has a long random hexadecimal security code that serves

as their password.  All email notifications contain a link back to the
Fossil server, incorporating this security code, which allows the 
subscriber to adjust their subscription options.






















































































Administrator Activities

------------------------







The repository administrator has unlimited control over individual


subscriptions.  The "[List Subscribers](/subscribers)" button at the top

of the Setup/Notification screen gives a list of subscribers.  Clicking on


any subscriber link allows the administrator to adjust the subscription.










































































To unsubscribe, first select the "unsubscribe" checkbox, then press the






"Unsubscribe" button.









The "verified" checkbox determines whether or not an email address has
been verified.  This can be enabled or disabled manually by the


administrator.














































































































































Cloning, Syncing, and Backups
-----------------------------

The Setup/Notification settings are not replicated using clone or sync.

In a network of peer repositories, you only want one repository sending
email notifications.  If you were to replicate the email notification
settings to a separate repository, then subscribers would get multiple
notifications for each event, which would be bad.

However, the subscriber list can be synced for backup purposes.  Use the
[`fossil config pull subscriber`](/help?cmd=configuration) command to
pull the latest subscriber list from a server into a backup repository.










































































































































































































|
<

<
<
<
|
<
<

|
|
<
<

|
|
|
|


|


|
>
|
|


>
>
>
|
|

>
|
<
>
>
>
|
<
|
<
>
>

<
<
>
>
>
>

|
<
<
>
>
>
>
>
>

<
|
>
>
|
|
>
>

>
>
>
|
|
<
>
>

>
>
>
|
|
>
|
<
>
|
|
>

|
>

>
|
>
|
|
<
<
<
<
<
>
>
>

|
|
>

|
|
<
|
|
|
|
|
|
|

>
>
>
>
>
>
|

>
>
>
|
>
>
>
|
>
>
>

|
|
|
|
>
|

|
<
|
<
|
|
|
>
|
<
|
>
>

>
>
>
>
>
>
|
<

<
<
>
|
>
>
>

|






|
|

>
|
|
|


|
|
|
|

|
>
|
|
|
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>

>
>
>
>
>
|
>
|
>
>
>
>
>

>
|
>
>
|
>
|
>
>
|
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>

>
>
>
>
>
>
>
>
|
>
>
>
>
>
>
|
>
>
>

>
>
>
>
>
|
|
>
>
|
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>

>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
|
<

|
>
|
|
|
|




>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1

2



3


4
5
6


7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30

31
32
33
34

35

36
37
38


39
40
41
42
43
44


45
46
47
48
49
50
51

52
53
54
55
56
57
58
59
60
61
62
63
64

65
66
67
68
69
70
71
72
73
74

75
76
77
78
79
80
81
82
83
84
85
86
87





88
89
90
91
92
93
94
95
96
97

98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133

134

135
136
137
138
139

140
141
142
143
144
145
146
147
148
149
150

151


152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527

528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
# Email Alerts





## Overview



Beginning with version 2.7, Fossil can send email messages to
subscribers to alert them to changes in the repository:



  *  New [checkins](/help?cmd=ci)
  *  [Ticket](./tickets.wiki) changes
  *  [Wiki](./wikitheory.wiki) page changes
  *  New and edited [forum](./forum.wiki) posts
  *  Announcements

Subscribers can elect to receive emails as soon as these events happen,
or they can receive a daily digest of the events instead.

Email alerts are sent by a [Fossil server](./server.wiki), which must be
[set up](#quick) by the Fossil administrator to send email.

Email alerts do not currently work if you are only using Fossil from the
command line.

A bit of terminology: Fossil uses the terms "email alerts" and
"notifications" interchangeably. We stick to the former term in this
document except when referring to parts of the Fossil UI still using the
latter term.


## Setup Prerequisites


Much of this document describes how to set up Fossil's email alert
system. To follow this guide, you will need a Fossil UI browser window
open to the [Admin → Notification](/setup_notification) Fossil UI screen
on the the Fossil server that will be sending these email alerts, logged

in as a user with Admin capability. It is not possible to work on a

clone of the server's repository and push the configuration changes up
to that repo as an Admin user, [on purpose](#backup).



**Important:** Do not confuse that screen with Admin → Email-Server,
which sets up a different subsystem within Fossil. That feature is
related to this document's topic, but it is currently incomplete, so we
do not cover it at this time.

<a id="cd"></a>


You will also need a CLI window open with its working directory changed
to a checkout directory of the Fossil repository you are setting up to
send email.  If you don't `cd` to such a checkout directory first,
you'll need to add `-R /path/to/repo.fossil` to each `fossil` command
below to tell Fossil which repository you mean it to apply the command
to.


There are other prerequisites for email service, but since they vary
depending on the configuration you choose, we'll cover these inline
below.


<a id="quick"></a>
## Quick Email Service Setup

If you've already got a working Postfix, Exim, or Sendmail server on the
machine running your Fossil instance(s), and you aren't using Fossil's
`chroot` feature to wall Fossil off from the rest of the machine, it's
fairly simple to set up email alerts.


(Otherwise, skip [ahead](#advanced) to the sections on advanced email
service setup.)

This is our "quick setup" option even though setting up an SMTP mail
server is not trival, because there are many other reasons to have such
a server set up already: internal project email service, `cron`
notifications, server status monitoring notifications...

With that out of the way, the Fossil-specific steps are easy:


1.  Go to [Admin → Notification](/setup_notification) and fill out all
    of the **Required** fields:

    * **Canonical server URL** — Use the suggested URL

    * **"From" email address** — `forum-bounces@example.com` is
      traditional, but suit yourself

    * **Repository nickname** — See the suggested examples on the web page.

2.  Set "Email Send Method" to "Pipe to a command"

3.  Set the "Administrator email address" to a suitable valid email





    address on that machine.  It could be the same value you used for
    the "From" address above, or it could be a different value like
    `admin@example.com`.

Save your changes.

At the command line, say

      $ fossil set email-send-command


If that gives a blank value instead of `sendmail -ti`, say

      $ fossil set email-send-command "sendmail -ti"

to force the setting. That works around a [known
bug](https://fossil-scm.org/forum/forumpost/840b676410) which may be
squished by the time you read this.

If you're running Postfix or Exim, you might think that command is
wrong, since you aren't running Sendmail. These mail servers provide a
`sendmail` command for compatibility with software like Fossil that has
no good reason to care exactly which SMTP server implementation is
running at a given site. There may be other SMTP servers that also
provide a compatible `sendmail` command, in which case they may work
with Fossil using the same steps as above.

<a id="status"></a>
If you reload the Admin → Notification page, the Status section at the
top should show:

      Outgoing Email: Piped to command "sendmail -ti"
      Pending Alerts: 0 normal, 0 digest
      Subscribers:    0 active, 0 total

Before you move on to the next section, you might like to read up on
[some subtleties](#pipe) with the "pipe to a command" method that we did
not cover above.


<a id="usage"></a>
## Usage and Testing

Now that email service from Fossil is set up, you can test it and begin
using it.



<a id="sub" name="subscribe"></a>

### Subscribing to Alerts

In the Status output above, we saw that there are no subscribers, so the
next step is to add the first one.


Go to the `/subscribe` page on your Fossil instance to sign up for email
alerts.  At the very least, you will need to sign up for "Forum Posts"
and "Announcements" to complete the testing steps below.

If you're logged in with a Fossil repository user account and put the
same user name and email address into this forum as you used for your
user information under Admin → Users, Fossil will simply tie your alert
preferences to your login record, and the email address in your user's
Contact Info field will be considered already-verified.  Otherwise,
Fossil will create an alert-only record, and you will have to verify the
email address before Fossil will send alerts to it.




This shows a key aspect of the way Fossil's email alerts system works,
by the way: a user can be signed up for email alerts without having a
full-fledged Fossil user account. Only when both user names are the same
are the two records tied together under the hood. For more on this, see
[Users vs Subscribers below](#uvs).

If you are seeing the following complaint from Fossil:

<blockquote>
  Use a different login with greater privilege than FOO to access
  /subscribe
</blockquote>

...then the repository's administrator forgot to [give the Alerts capability](#cap7)
to that user or to a user category that the user is a member of.

After a subscriber signs up for alerts for the first time, a single
verification email is sent to that subscriber's given email address.
The new subscriber must click a link in that email in order to activate
the subscription.

Subscription verification emails are only sent once.  This is a defense
against malicious robots that try to harass innocent Internet users by
having subscription pages send multiple verification emails.  If the
initial subscription verification does not go through correctly, an
administrator must [intervene](#admin) to reset the subscription.

Every subscriber-only email address has a [long random hexadecimal
security code](#scode) that serves in place of a password.  All email
alerts contain a link in their footer back to the Fossil server,
incorporating this security code, which allows the subscriber to adjust
their subscription options.  If a user doesn't have any of those emails,
they can request a link via email by visiting the `/alerts` or
`/unsubscribe` page on the repository.

Those with Fossil repository logins can adjust their email alert
settings by visiting the `/alerts` page on the repository.  With the
default skin, you can get there by clicking the "Logout" link in the
upper right corner of any Fossil UI page then clicking the "Email
Alerts" link.  That link is also available via the Sitemap (`/sitemap`)
and via the default skin's hamburger menu (&#9776;).


<a id="unsub" name="unsubscribe"></a>
### Unsubscribing

To unsubscribe from alerts, visit the `/alerts` page on the repository,
click the "Unsubscribe" button, then check the "Unsbuscribe" checkbox to
verify your action and press the "Unsubscribe" button a second time.

This interlock is intended to prevent accidental unsubscription.


<a id="test"></a>
### Test Email Service

The easiest way to test email sending from Fossil is via the "[Send
Announcement](/announce)" link at the top of the "Email Notification
Setup" page.  Put your email address in the "To:" line and a test
message below, then press "Send Message" to verify that outgoing email
is working.

Another method is from the command line:

      $ fossil alerts test-message you@example.com --body README.md --subject Test

That should send you an email with "Test" in the subject line and the
contents of your project's `README.md` file in the body.

That command assumes that your project contains a "readme" file, but of
course it does, because you have followed the [Programming Style Guide
Checklist][cl], right? Right.

[cl]: https://sendgrid.com/blog/programming-style-guide-checklist/


<a id="cap7"></a>
### User Capabilities

Once email alerts are working, one must also adjust user permissions to
allow users to subscribe to email alerts.  In the capability list for
each user on the Admin → Users page is a new capability called "Email
Alerts".  The corresponding capability letter is "7", which you must
give to any user that needs to use the subscription setup pages,
`/subscribe` and `/alerts`.

To allow any passer-by on the Internet to subscribe, give the "Email
Alerts" capability to the "nobody" user category.  To require that a
person solve a simple CAPTCHA first, give that capability to the
"anonymous" user category instead.


<a id="first" name="frist"></a>
### First Post

I suggest taking the time to compose a suitable introductory message
especially for your project's forum, one which a new user would find
helpful.

Wait a few seconds, and you should receive an email alert with the
post's subject and body text in the email.


<a id="trouble"></a>
### Troubleshooting

If email alerts aren't working, there are several useful commands you
can give to figure out why.

(Be sure to [`cd` into a repo checkout directory](#cd) first!)

      $ fossil alerts status

This should give much the same information as you saw [above](#status).
One difference is that, since you've created a forum post, the
`pending-alerts` value should only be zero if you did in fact get the
requested email alert. If it's zero, check your mailer's spam folder. If
it's nonzero, continue with these troubleshooting steps.

      $ fossil backoffice

That forces Fossil to run its ["back office" process](./backoffice.md).
Its only purpose at the time of this writing is to push out alert
emails, but it might do other things later. Sometimes it can get stuck
and needs to be kicked. For that reason, you might want to set up a
crontab entry to make sure it runs occasionally.

      $ fossil alerts send

This should also kick off the backoffice processing, if there are any
pending alerts to send out.

      $ fossil alert pending

Show any pending alerts. The number of lines output here should equal
the [status output above](#status).

      $ fossil test-add-alerts f5900
      $ fossil alert send

Manually create an email alert and push it out immediately.

The `f` in the first command's final parameter means you're scheduling a
"forum" alert. The integer is the ID of a forum post, which you can find
by visiting `/timeline?showid` on your Fossil instance.

The second command above is necessary because the `test-add-alerts`
command doesn't kick off a backoffice run.

      $ fossil ale send

This only does the same thing as the final command above, rather than
send you an ale, as you might be hoping. Sorry.


<a id="advanced"></a>
## Advanced Email Setups

Fossil offers several methods of sending email:

  1.  Pipe the email message text into a command.
  2.  Store email messages as entries in a SQLite database.
  3.  Store email messages as individual files in a directory.
  4.  Send emails to an SMTP relay.
  5.  Send emails directly to the recipients via SMTP.

This wide range of options allows Fossil to talk to pretty much any
SMTP setup.

The first four options let Fossil delegate email handling to an existing
[MTA][mta] so that Fossil does not need to implement the [roughly two
dozen][mprotos] separate [RFCs][rfcs] required in order to properly
support SMTP email in this complex world we've built.  As well, this
design choice means you do not need to do duplicate configuration, such
as to point Fossil at your server's TLS certificate in order to support
users behind mail servers that require STARTTLS encryption.

[mprotos]: http://sqlite.1065341.n5.nabble.com/Many-ML-emails-going-to-GMail-s-SPAM-tp98685p98722.html
[rfcs]:    https://en.wikipedia.org/wiki/Request_for_Comments


<a id="pipe"></a>
### Method 1: Pipe to a Command

This is our ["quick setup" option](#quick) above, but there are some
details we ignored which we'll cover now.

Fossil pipes the email message in [RFC 822 format][rfc822] to the
standard input of the command you gave as the "Email Send Method",
defaulting to `sendmail -ti`. This constitutes a protocol between Fossil
and the SMTP [message transfer agent (MTA)][mta]. Any other MTA which
speaks the same protocol can be used in place of the most common
options: Sendmail, Exim, and Postfix.

The `-t` option tells the command to expect the list of email recipients
in a `To` header in the RFC 822 message presented on its standard input.
Without this option, the `sendmail` command expects to receive the
recipient list on the command line, but that's not possible with the
current design of this email sending method. Therefore, if you're
attempting to use a less common MTA which cannot parse the recipient
list from the `To` header in the email message, you might need to look
for a different MTA.

The `-i` option is only needed for MTAs that take a dot/period at the
beginning of a line of standard input text as "end of message." Fossil
doesn't attempt to escape such dots, so if the line wrapping happens to
occur such that a dot or period in an alert message is at the beginning
of a line, you'll get a truncated email message without this option.
Statistically, this will happen about once every 70 or so messages, so
it is important to give this option if your MTA treats leading dots on a
line this way.

<a id="msmtp"></a>
We believe the [`msmtp`][msmtp] SMTP client is compatible with this
protocol if you give it the `-t` option. To our knowledge, this remains
untested, but if it works, this would be a useful option on a server
hosting a Fossil repository which doesn't otherwise require a separate
SMTP server for other purposes.

It is probably also possible to configure [`procmail`][pmdoc] to work
with this protocol. If you know how to do it, a patch to this document
or a how-to on [the Fossil forum][ff] would be appreciated.

[ff]:     https://fossil-scm.org/forum/
[msmtp]:  https://marlam.de/msmtp/
[mta]:    https://en.wikipedia.org/wiki/Message_transfer_agent
[pmdoc]:  http://pm-doc.sourceforge.net/doc/
[rfc822]: https://www.w3.org/Protocols/rfc822/


<a id="db"></a>
### Method 2: Store in a Database

The self-hosting Fossil repository at <https://www.fossil-scm.org/>
currently uses this method rather than [the pipe method](#pipe) because
it is running inside of a restrictive [chroot jail][cj] which is unable
to hand off messages to the local MTA directly.

When you configure a Fossil server this way, it adds outgoing email
messages to a SQLite database file.  A separate daemon process can then
extract those messages for further disposition.

Fossil includes a copy of [the daemon](/file/tools/email-sender.tcl)
used on `fossil-scm.org`: it is just a short Tcl script that
continuously monitors this database for new messages and hands any that
it finds off to a local MTA using the same [pipe to MTA protocol](#pipe)
as above.

In this way, outbound email alerts escape the chroot jail without
requiring that we insert a separate MTA configuration inside that jail.
We only need to arrange that the same SQLite DB file be visible both
inside and outside the chroot jail, which we do by naming the database
file in the "Store Emails In This Database" setting under Admin →
Notification.  The Tcl script has this path hard-coded as
`/home/www/fossil/emailqueue.db`, but you will probably need to adjust
that for your local purposes.

This method may work with other similar technologies besides `chroot`:
Docker containers, LXC containers, BSD jails, Solaris zones, etc.

With suitable file share mappings, this method may even work with
virtual machine or distributed computing setups where the MTA and Fossil
servers are not on the same machine, though beware the [risk of DB
corruption][rdbc] if used with a file sharing technology that doesn't
use proper file locking.

You can start this Tcl script as a daemon automatically on most Unix and
Unix-like systems by adding the following line to the `/etc/rc.local`
file of the server that hosts the repository sending email alerts:

      /usr/bin/tclsh /home/www/fossil/email-sender.tcl &

[cj]:   https://en.wikipedia.org/wiki/Chroot
[rdbc]: https://www.sqlite.org/howtocorrupt.html#_filesystems_with_broken_or_missing_lock_implementations


<a id="dir"></a>
### Method 3: Store in a Directory

This method is functionally very similar to [the DB method](#db),
differing only in that messages are written to a directory in the
filesystem.  You should therefore read that section and make the minor
adjustments required by the storage method.

This method may work over a file sharing mechanism that doesn't do file
locking properly, as long as the reading process is somehow restricted
from reading a message file as it's being written.

It might be useful in testing and debugging to temporarily switch to
this method, since you can easily read the generated email messages
without needing to involve [an MTA][mta].


<a id="relay"></a>
### Method 4: SMTP Relay

In this configuration, the Fossil server contacts an open SMTP relay and
sends the messages to it. This method is only appropriate when:

1. You have a local MTA that doesn't accept [the pipe
   protocol](#pipe).

2. The MTA is willing to accept anonymous submissions, since Fossil
   currently has no way to authenticate itself to the MTA.  This is [an
   unsafe configuration][omr] in most cases, but some SMTP servers make
   an exception for connections coming from a `localhost` or LAN
   address, choosing to accept such submissions as inherently safe.

If you have a local MTA meeting criterion #1 but not #2, we'd suggest
using a more powerful SMTP client such as [msmtp](#msmtp) along with one
of the other methods above.

[omr]: https://en.wikipedia.org/wiki/Open_mail_relay


<a id="direct"></a>
### Method 5: Direct SMTP Send

As of Fossil 2.7, the code to support this method is incomplete, so you
cannot currently select it as an option in Admin → Notification.


<a id="uvs"></a>
## Users vs Subscribers

Fossil makes a distinction between "users" and "subscribers".  A user is
someone with a username and password: that is, someone who can log into
the Fossi repository.  A subscriber is someone who receives email
alerts.  Users can also be subscribers and subscribers can be users, but
that does not have to be the case.  It is possible to be a user without
being a subscriber and to be a subscriber without being a user.

In the repository database file, users are tracked with the `user` table
and subscribers are tracked via the `subscriber` table.


<a id="admin"></a>
## Administrator Activities

The "[List Subscribers](/subscribers)" button at the top of the Admin →
Notification screen gives a list of subscribers, which gives a Fossil
server administrator a lot of power over those subscriptions.

Clicking an email address in this subscriber list opens the same
`/alerts` page that the user can see for their own subscription, but
with more information and functionality than normal users get:

*  Subscription creation and modification timestamps.

*  The IP address the user had when they last made a change via either
   `/subscribe` or `/alert`.

*  The user's login name, if they are not [a mere subscriber](#uvs).  A
   Fossil Admin user is allowed to modify this, either to tie a
   subscription-only record to an existing Fossil user account or to
   break that tie.

*  The "Do not call" checkbox allows a Fossil Admin user to mark a given
   email address so that Fossil never sends email to that address.  This
   is distinct from unsubscribing that email address because it prevents
   Fossil from accepting a new subscription for that address.

*  The Verified checkbox is initially unchecked for subscriber-only
   email addresses until the user clicks the link in the verification
   email. This checkbox lets the Fossil Admin user manually verify the
   user, such as in the case where the verification email message got
   lost.  Unchecking this box does not cause another verification email
   to be sent.

This screen also allows a Fossil Admin user to perform other activities
on behalf of a subscriber which they could do themselves, such as to
[unsubscribe](#unsub) them.


<a id="backup"></a>
## Cloning, Syncing, and Backups


The Admin → Notification settings are not replicated using clone or
sync, and it is not possible to push such settings from one repository
to another.  In a network of peer repositories, you only want one
repository sending email alerts.  If you were to replicate the email
alert settings to a separate repository, then subscribers would get
multiple alerts for each event, which would be bad.

However, the subscriber list can be synced for backup purposes.  Use the
[`fossil config pull subscriber`](/help?cmd=configuration) command to
pull the latest subscriber list from a server into a backup repository.

The `push`, `export`, and `import` commands all work similarly.


<a id="pages" name="commands"></a>
## Controlling the Email Alert System

This section collects the list of Fossil UI pages and CLI commands that
control the email alert system, some of which have not been mentioned so
far:

Commands:

   *  The [`alerts`](/help?cmd=alerts) command
   *  The [`test-alert`](/help?cmd=test-alert) command
   *  The [`test-add-alerts`](/help?cmd=test-add-alerts) command

Web pages available to users and subscribers:

   *  The [`/subscribe`](/help?cmd=/subscribe) page
   *  The [`/alerts`](/help?cmd=/alerts) page
   *  The [`/unsubscribe`](/help?cmd=/unsubscribe) page
   *  The [`/contact_admin`](/help?cmd=/contact_admin) page

Administrator-only web pages:

   *  The [`/setup_notification`](/help?cmd=/setup_notification) page
   *  The [`/subscribers`](/help?cmd=/subscribers) page


<a id="design"></a>
## Design of Email Alerts

This section describes the low-level design of the email alert system in
Fossil.  This expands on the high-level administion focused material
above with minimal repetition.

This section assumes expert-level systems knowledge. If the material
above sufficed for your purposes, feel free to skip this section, which
runs to the end of this document.


<a id="datades"></a>
### Data Design

There are three new tables in the repository database, starting with
Fossil 2.7.  These tables are not created in new repositories by
default.  The tables only come into existence as needed when email
alerts are configured and used.


  *  <b>SUBSCRIBER</b> →
     The subscriber table records the email address for people who
     want to receive email notifications.  Each subscriber has a
     `subscriberCode` which is a random 32-byte blob that uniquely
     identifies the subscriber.  There are also fields to indicate
     what kinds of notifications the subscriber wishes to receive,
     whether or not the email address of the subscriber has been
     verified, etc.

  *  <b>PENDING\_ALERT</b> →
     The PENDING\_ALERT table contains records that define events
     about which alert emails might need to be sent.
     A pending\_alert always refers to an entry in the
     EVENT table.  The EVENT table is part of the standard schema
     and records timeline entries.  In other words, there is one
     row in the EVENT table for each possible timeline entry.  The
     PENDING\_ALERT table refers to EVENT table entries for which
     we might need to send alert emails.

  *  <b>EMAIL\_BOUNCE</b> →
     This table is intended to record email bounce history so that
     subscribers with excessive bounces can be turned off.  That
     logic has not yet been implemented so the EMAIL\_BOUNCE table
     is currently unused.

As pointed out above, ["subscribers" are distinct from "users"](#uvs).
The SUBSCRIBER.SUNAME field is the optional linkage between users and
subscribers.


<a id="stdout"></a>
### The "stdout" Method

The [list of mail sending methods](#advanced) above left out an
internal-only method called "stdout" which simply writes the text of the
email message on standard output.  The "stdout" method is used for
testing and debugging.  If you need something similar and can't modify
your local Fossil instance to use this method, you might temporarily
switch to [the "dir" method](#dir) instead.


<a id="msgfmt"></a>
### Message Format

The email messages generated by Fossil have a [well-formed
header][rfc822].  The downstream processing is expected to extract the
"To:", "From:", "Subject:" and whatever other attributes it needs from
the email header text.

These emails use the `text/plain` MIME type with the UTF-8 character
set.  We currently use a transfer encoding of `quoted-printable`, but
there is commented-out code in Fossil to switch to `base64` encoding,
which Fossil used in the early days leading up to the 2.7 release.

If you switch Fossil back to `base64` mode, you may want to build a
utility program that ships in the Fossil source tree named
["tools/decode-email.c"](/file/tools/decode-email.c) which can decode
these messages into a human-readable format.


<a id="inbound" name="bounces"></a>
### Dealing with Inbound Email

Inbound email messages — for example, bounces from failed alert emails —
should be relayed to the `fossil email inbound` command.  That command
is currently a no-op place-holder.  At some point, we will need to
design and write a bounce-message processing system for Fossil.


<a id="password" name="scode" name="verification"></a>
### Passwords vs Subscriber Codes

When anonymous passers-by on the Internet sign up for email alerts,
their email address must first be verified.  An email message is sent to
the address supplied inviting the user to click on a link.  The link
includes a pseudorandom 128-bit blob encoded as 32 hexadecimal digits,
which serves in place of a password for that email address.  (This is
stored in the database as `subscriber.subscriberCode`.) If anyone visits
the link, the email address is verified.

Knowledge of the `subscriberCode` is sufficient to control a
subscription.

Because this code is included in plain text in email alert messages, it
is not as secure as a separate password, but it has several virtues:

*   It is easier for the average subscriber to deal with in that they
    don't have to come up with yet another password and store it safely.

*   If the `subscriberCode` is stolen, the worst that can happen is that
    the thief can change that email address's subscription settings.
    Contrast a password which may be shared with other services, which
    then compromises those other services.

*   No PII other than the subscriber's email address is available to an
    attacker with the `subscriberCode`.  Nor can knowledge of the
    `subscriberCode` lead to a email flood or other annoyance attack, as
    far as I can see.

If the `subscriberCodes` for a Fossil repository are ever compromised,
new ones can be generated as follows:

        UPDATE subscriber SET subscriberCode=randomblob(32);

Since this then affects all new email alerts going out from Fossil, your
end users may never even realize that they're getting new codes, as long
as they don't click on the URLs in the footer of old alert messages.

With that in mind, a Fossil server administrator could choose to
randomize the `subscriberCodes` periodically, such as just before the
daily digest emails are sent out each day.

**Important:** All of the above is distinct from the passwords for users
with a Fossil repository login. Such users also have subscriber codes,
but those codes can only be used to modify the user's email alert
settings. That code cannot allow a user to log into the user's Fossil
repository account.


<a id="processing"></a>
### Internal Processing Flow

Almost all of the email alert code is found in the
[`src/alerts.c`](/file/src/alerts.c) source file.

When email alerts are enabled, a trigger is created in the schema
(`email_trigger1`) that adds a new entry to the `PENDING_ALERT` table
every time a row is added to the `EVENT` table.  During a `fossil
rebuild`, the `EVENT` table is rebuilt from scratch; since we do not
want users to get alerts for every historical check-in, the trigger is
disabled during `rebuild`.

Email alerts are sent out by the `alert_send_alerts()` function, which
is normally called automatically due to the `email-autoexec` setting,
which defaults to enabled. If that setting is disabled or if the user
simply wants to force email alerts to be sent immediately, they can give
a `fossil alert send` command, such as via a `cron` script.  Each time
this function is called, the alert messages are moved further down the
chain, so you cannot cause duplicate alerts by calling it too often.

Digests are handled by recording the time of the last digest in the
`email-last-digest` setting, and only sending a new digest if the
current time is one day or later after the last digest.

Individual emails are sent to each subscriber.  I (drh) ran tests and
found that I could send about 1200 emails/second, which is fast enough
that I do not need to resort to trying to notify multiple subscribers
with a single email.  Because each subscriber gets a separate email, the
system can include information in the email that is unique to the
subscriber, such as a link to the page to edit their subscription.  That
link includes the `subscriberCode`.
Changes to www/changes.wiki.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27

28

29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
<title>Change Log</title>

<a name='v2_7'></a>
<h2>Changes for Version 2.7 (2018-09-??)</h2>

  *  Add the [./alerts.md|email alerts] feature for commits, ticket
     changes, wiki changes, forum posts, and announcements.  This is
     still a work in progress.  It works, but it is not as easy to 
     setup and use as it ought to be.
  *  Add the [./forum.wiki|discussion forum] feature.
  *  Add new user capabilities letters needed to support alerts and forum.
     Formerly, user capabilities were letters from &#91;a-z&#93;, but with the
     enhancements, the supply of lower case letters was exhausted.
     User capabilities are now letters in &#91;a-zA-Z0-9&#93;.
  *  The default skin is now responsive, providing better layout on
     small screens, including mobile devices.
  *  The default skin now includes a hamburger menu that is generated
     by the [/sitemap] page.
  *  The [/sitemap] and other list pages show as multiple columns if
     the viewing window is wide enough.
  *  There is an optional "js" file for each skin that can be used to
     hold javascript.  This file can be loaded by reference or can be
     included in the header or footer.
  *  Add the [./backoffice.md|backoffice].
  *  Update internal Unicode character tables, used in regular expression
     handling, from version 10.0 to 11.0.
  *  Improvements to the "Security Audit" administration page

  *  Update the built-in SQLite to version 3.25.0.

  *  Some code and interfaces are in place to support sending and
     receiving email directly via SMTP, but this feature is not yet
     complete or ready for production use.

<a name='v2_6'></a>
<h2>Changes for Version 2.6 (2018-05-04)</h2>

  *  Fix a bug that was causing crashes while trying to clone the TCL
     repository.  This fix is the main reason for the current release.
  *  Added the new "Classic" timeline viewing mode.  "Classic" is the
     same as "Verbose" in the previous release.  The "Verbose" mode is
     now like "Compact" except the extra check-in details are shown by
     default.
  *  Add support for ETags:, Last-Modified:, and If-Modified-Since:
     cache control mechanisms.
  *  Enhance the [/help?cmd=/tarball|/tarball], 
     [/help?cmd=/zip|/zip], and 
     [/help?cmd=/sqlar|/sqlar] pages so that the checkin
     name to be downloaded can be expressed as part of the URI,
     and without the need for query parameters.
  *  On the [/help?cmd=/timeline|/timeline] webpage, add the days=N
     query parameter and enhance the ymd=DATE and yw=DATE query parameters
     to accept 'now' as an argument to show the latest day or week.
  *  In the web page that comes up in response to the
     [/help?cmd=all|fossil all ui] command, show the last modification
     time for each repository, and allow click-to-sort on the modification
     time column.
  *  In the tarball cache replacement algorithm, give extra weight to
     tarballs that have been accessed more than once.
  *  Additional defenses against web-based attacks.  There have not been
     any known vulnerabilities.  We are just being paranoid.
  *  Update the built-in SQLite to an alpha version of 3.24.0.

<a name='v2_5'></a>
<h2>Changes for Version 2.5 (2018-02-07)</h2>

  *  Numerous enhancements to the look and feel of the web interface.
     Especially:  Added separate "Modern", "Compact", "Verbose", and 
     "Columnar" view options on timelines.
  *  Common display settings (such as the "view" option and the number
     of rows in a timeline) are held in a cookie and thus persist
     across multiple pages.
  *  Rework the skin editing process so that changes are implemented
     on one of nine /draft pages, evaluated, then merged back to the
     default.







|



















>

>















|
|




















|







1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
<title>Change Log</title>

<a name='v2_7'></a>
<h2>Changes for Version 2.7 (2018-09-??)</h2>

  *  Add the [./alerts.md|email alerts] feature for commits, ticket
     changes, wiki changes, forum posts, and announcements.  This is
     still a work in progress.  It works, but it is not as easy to
     setup and use as it ought to be.
  *  Add the [./forum.wiki|discussion forum] feature.
  *  Add new user capabilities letters needed to support alerts and forum.
     Formerly, user capabilities were letters from &#91;a-z&#93;, but with the
     enhancements, the supply of lower case letters was exhausted.
     User capabilities are now letters in &#91;a-zA-Z0-9&#93;.
  *  The default skin is now responsive, providing better layout on
     small screens, including mobile devices.
  *  The default skin now includes a hamburger menu that is generated
     by the [/sitemap] page.
  *  The [/sitemap] and other list pages show as multiple columns if
     the viewing window is wide enough.
  *  There is an optional "js" file for each skin that can be used to
     hold javascript.  This file can be loaded by reference or can be
     included in the header or footer.
  *  Add the [./backoffice.md|backoffice].
  *  Update internal Unicode character tables, used in regular expression
     handling, from version 10.0 to 11.0.
  *  Improvements to the "Security Audit" administration page
  *  Added the [/help?cmd=branch|fossil branch current] command.
  *  Update the built-in SQLite to version 3.25.0.
  *  Add support for TLS 1.3, when compiled with OpenSSL 1.1.1.
  *  Some code and interfaces are in place to support sending and
     receiving email directly via SMTP, but this feature is not yet
     complete or ready for production use.

<a name='v2_6'></a>
<h2>Changes for Version 2.6 (2018-05-04)</h2>

  *  Fix a bug that was causing crashes while trying to clone the TCL
     repository.  This fix is the main reason for the current release.
  *  Added the new "Classic" timeline viewing mode.  "Classic" is the
     same as "Verbose" in the previous release.  The "Verbose" mode is
     now like "Compact" except the extra check-in details are shown by
     default.
  *  Add support for ETags:, Last-Modified:, and If-Modified-Since:
     cache control mechanisms.
  *  Enhance the [/help?cmd=/tarball|/tarball],
     [/help?cmd=/zip|/zip], and
     [/help?cmd=/sqlar|/sqlar] pages so that the checkin
     name to be downloaded can be expressed as part of the URI,
     and without the need for query parameters.
  *  On the [/help?cmd=/timeline|/timeline] webpage, add the days=N
     query parameter and enhance the ymd=DATE and yw=DATE query parameters
     to accept 'now' as an argument to show the latest day or week.
  *  In the web page that comes up in response to the
     [/help?cmd=all|fossil all ui] command, show the last modification
     time for each repository, and allow click-to-sort on the modification
     time column.
  *  In the tarball cache replacement algorithm, give extra weight to
     tarballs that have been accessed more than once.
  *  Additional defenses against web-based attacks.  There have not been
     any known vulnerabilities.  We are just being paranoid.
  *  Update the built-in SQLite to an alpha version of 3.24.0.

<a name='v2_5'></a>
<h2>Changes for Version 2.5 (2018-02-07)</h2>

  *  Numerous enhancements to the look and feel of the web interface.
     Especially:  Added separate "Modern", "Compact", "Verbose", and
     "Columnar" view options on timelines.
  *  Common display settings (such as the "view" option and the number
     of rows in a timeline) are held in a cookie and thus persist
     across multiple pages.
  *  Rework the skin editing process so that changes are implemented
     on one of nine /draft pages, evaluated, then merged back to the
     default.
Deleted www/emaildesign.md.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
Design of Email Notification
============================

This document contains high-level design notes for the email
notification system in Fossil.  Use this document to get a better
understanding of how Fossil handles email notification, to help
with doing custom configurations, or to help contribute features.

This document assumes expert-level systems knowledge.  A separate
tutorial for setting up email notification by non-experts will be
generated once the email notification system stabilizes.

Email notification is under active development as of this writing
(2018-06-25).  Check back frequently for updates.

Data Design
-----------

There are three new tables in the repository database.  These tables
are not created in new repositories by default.  The tables only
come into existance if email notification is configured and used.


  *  <b>SUBSCRIBER</b> &rarr;
     The subscriber table records the email address for people who
     want to receive email notifications.  Each subscriber has a
     `subscriberCode` which is a random 32-byte blob that uniquely
     identifies the subscriber.  There are also fields to indicate
     what kinds of notifications the subscriber wishes to receive,
     whether or not the email address of the subscriber has been
     verified, etc.

  *  <b>PENDING\_ALERT</b> &rarr;
     The PENDING\_ALERT table contains records that define events
     about which notification emails might need to be sent.
     A pending\_alert always refers to an entry in the
     EVENT table.  The EVENT table is part of the standard schema
     and records timeline entries.  In other words, there is one
     row in the EVENT table for each possible timeline entry.  The
     PENDING\_ALERT table refers to EVENT table entries for which
     we might need to send notification emails.

  *  <b>EMAIL\_BOUNCE</b> &rarr;
     This table is intended to record email bounce history so that
     subscribers with excessive bounces can be turned off.  That
     logic has not yet been implemented so the EMAIL\_BOUNCE table
     is currently unused.

Note that "subscribers" are distinct from "users" in the USER table.
A "user" is someone who has a login and password.  A "subscriber" is
an email address that receives notification events.  Users can be
subscribers, and there is a SUBSCRIBER.SUNAME field that records
the linkage between users and subscribers.  But it is also possible
to be a user without being a subscriber, or to be a subscriber without
being a user.

Sending Email Messages
----------------------

Fossil expects to interact with an external [mail transfer agent][MTA].
There are currently three different methods for sending outbound
email messages from Fossil to the external mail agent:

  1.  <b>"pipe"</b> &rarr; Invoke an external command that accepts
      the email message on standard input.  This is useful if the
      host computer has a command like /usr/sbin/sendmail that will
      accept well-formed email messages from standard input and forward
      them to the appropriate destination.

  2.  <b>"db"</b> &rarr; Write outgoing email messages into an
      SQLite database file.  The self-hosting Fossil website uses
      this technique because Fossil runs inside a reduced-privilege
      chroot jail and cannot invoke commands like /usr/sbin/sendmail.
      A separate TCL script running outside of the jail monitors
      the email queue database and forwards email messages to the
      Postfix mail transfer agent.  There is an example TCL script in the
      [tools/email-sender.tcl](/file/tools/email-sender.tcl) file
      of the source tree that shows how this is done.

  3.  <b>"dir"</b> &rarr; Write outgoing email messages as individual
      files in a designated directory.  This might be useful for
      testing and debugging.

Internally, there is a fourth email sending method named "stdout"
which simply writes the text of the email message on standard output.
The "stdout" method is used for testing and debugging.

Perhaps we will add an "smtp" sending method in the future.  The
main problem with an "smtp" delivery method is that front-line Fossil
running inside the privilege jail would need to deal with all kinds
of errors from SMTP, such as unable to connect, or connection resets,
etc.  SMTP expects the sender to have the ability to retry, does it
not?

The emails transmitted have a well-formed header.  The downstream
processing is expected to extract the "To:", "From:", "Subject:" and
whatever other attributes it needs from the email header text.

All emails are text/plain and use a transfer-encoding of base64.

There is a utility command-line program named 
["tools/decode-email.c"](/file/tools/decode-email.c) in
the Fossil source tree.  If you compile this program, you can use it
to convert the base64 transfer-encoding into human-readable output for
testing and debugging.

[MTA]: https://en.wikipedia.org/wiki/Message_transfer_agent


Receiving Email Messages
------------------------

Inbound email messages (for example bounces from failed notification
emails) should be relayed to the "fossil email inbound" command.  That
command is currently a no-op place-holder.  At some point, we will need
to design and write a bounce-message processing system for Fossil.

Controlling The Setup
---------------------

Commands:

   *  The [email](/help?cmd=email) command

Web pages:

   *  The [/subscribe](/help?cmd=/subscribe) page
   *  The [/alerts](/help?cmd=/alerts) page
   *  The [/unsubscribe](/help?cmd=/unsubscribe) page
   *  The [/msgtoadmin](/help?cmd=/msgtoadmin) page

Web pages for administrators only:

   *  The [/setup_email](/help?cmd=/setup_email) page
   *  The [/subscribers](/help?cmd=/subscribers) page

Test command:

   *  The [test-alert](/help?cmd=test-alert) command
   *  The [test-add-alerts](/help?cmd=test-add-alerts) command

Email Address Verification
--------------------------

When anonymous passers-by on the internet sign up for email notifications,
their email address must first be verified.  An email message is sent to
the address supplied inviting the user to click on a link.  The link includes
the random 32-byte `subscriberCode` in hex.  If anyone visits the link, the
email address is verified.

There is no password.  Knowledge of the `subscriberCode` is sufficient to
control the subscription.  This is not a secure as a separate password,
but on the other hand it is easier for the average subscriber to deal
with in that they don't have to come up with yet another password.  Also,
even if the `subscriberCode` is stolen, the worst that can happens is that
the thief can change your subscription settings.  No PII (other than
the subscriber's email address) is available to an attacker with the
`subscriberCode`.  Nor can knowledge of the `subscriberCode` lead to a
email flood or other annoyance attack, as far as I can see.

If subscriberCodes are ever compromised, new ones can be generated
as follows:

        UPDATE subscriber SET subscriberCode=randomblob(32);

Perhaps the system be enhanced to randomize the
`subscriberCodes` periodically - say just before each daily digest
is sent out?

User Control Of Their Subscription
----------------------------------

If a user has a separate account with a login and password for
the repository, then their subscription is linked to their account.
On the /login page is a link to a page to control their subscription.

For users without logins, they can request a link to a page for
controling their subscription on the `/alerts` or `/unsubscribe` page.
The link is sent via email, and includes the `subscriberCode`.

Internal Processing Flow
------------------------

Almost all of the email notification code is found in the `src/email.c`
source file.

When email notifications are enabled, a trigger is created in the schema
(the `email_trigger1` trigger) that adds a new entry to the
`PENDING_ALERT` table every time a row is added to the `EVENT` table.
During a `fossil rebuild`, the `EVENT` table is rebuilt from scratch; since we
do not want users to get notifications for every historical check-in,
the trigger is disabled during `rebuild`.

Email notifications are sent out by the `email_send_alerts()` function.
This function is can be called by having a cron job invoke the
`fossil email exec` command.  Or, if the email-autoexec setting is
enabled, then `email_send_alerts()` is invoked automatically after each
successful webpage is generated.  The latter approach is used on the
Fossil self-hosting repository.  The `email_send_alerts()` function is
a no-op (obviously) if there are no pending events to be sent.

Digests are handled by recording the time of the last digest in the
`email-last-digest` setting, and only sending a new digest if the
current time is one day or later after the last digest.

Individual emails are sent to each subscriber.  I ran tests and found
that I could send about 1200 emails/second, which is fast enough that
I do not need to resort to trying to notify multiple subscribers with
a single email.  Because each subscriber gets a separate email, the
system can include information in the email that is unique to the
subscriber, such as a link to the page to edit their subscription.  That
link includes the `subscriberCode`., 

Other Notes
-----------

The `fossil configuration pull subscriber` command pulls down the content
of the `SUBSCRIBER` table.  This is intended to as a backup-only.  It
is not desirable to have two or more systems sending emails to the
same people for the same repository, as that would mean users would
receive duplicate emails.  Hence, the settings that control email 
notifications are not transmitted with the pull.  The `push`, `export`,
and `import` commands all work similarly.
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<






























































































































































































































































































































































































































































Changes to www/forum.wiki.
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
     restrict posts to plain text only or to allow wild-west
     HTML-formatted MIME email. Fossil's lightweight markup language
     formatting features give you a middle path, providing your users
     enough formatting power to communicate complex ideas well without
     providing so much power as to risk
     [https://wonko.com/post/html-escaping | security problems].

  *  <b>Easy Notification Emails:</b> It is easy to configure Fossil to
     send email notifications of new posts to interested forum users via
     your Fossil server's existing
     [https://en.wikipedia.org/wiki/Message_transfer_agent | message
     transfer agent] (MTA): Postfix, Exim, Sendmail... Notification
     emails include the complete message content for the benefit of
     those that prefer to visit the forum only when they need to post
     something. Notifications are optional, and each user gets the
     choice of immediate or daily digest delivery.

  *  <b>Talks to Everyone:</b> Because Fossil delegates email handling
     to your existing MTA, it does not need to implement the
     [http://sqlite.1065341.n5.nabble.com/Many-ML-emails-going-to-GMail-s-SPAM-tp98685p98722.html
     | roughly two dozen]
     [https://en.wikipedia.org/wiki/Request_for_Comments | RFCs]
     required in order to properly support SMTP email in this complex
     world we've built.  As well, this design choice means you do not
     need to do duplicate configuration, such as to point Fossil at your
     server's TLS certificate private key in order to support users
     behind mail servers that require STARTTLS encryption.


<h2 id="setup">Setting up a Fossil Forum</h2>

<h3 id="caps">Capabilities</h3>

Fossil forums use the same role-based access control mechanism as







|
|
<
<
<
|
|
|
|
<
<
<
<
<
<
<
<
<
<
<







95
96
97
98
99
100
101
102
103



104
105
106
107











108
109
110
111
112
113
114
     restrict posts to plain text only or to allow wild-west
     HTML-formatted MIME email. Fossil's lightweight markup language
     formatting features give you a middle path, providing your users
     enough formatting power to communicate complex ideas well without
     providing so much power as to risk
     [https://wonko.com/post/html-escaping | security problems].

  *  <b>Easy Email Alerts:</b> You can configure Fossil to
     [./alerts.md | send email alerts]. Forum post emails include the



     complete message content for the benefit of those that prefer to
     visit the forum only when they need to post something.  Alerts are
     optional, and each user gets the choice of immediate or daily
     digest delivery.













<h2 id="setup">Setting up a Fossil Forum</h2>

<h3 id="caps">Capabilities</h3>

Fossil forums use the same role-based access control mechanism as
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
     showing the list of pending moderation tasks.

  *  <b>Supervise Forum</b> (<tt>6</tt>): User can grant or revoke
     <b>WriteTrusted</b> capability for other users. (Currently
     unimplemented.)

  *  <b>Email Alerts</b> (<tt>7</tt>): User can sign themselves up for
     email notifications, a.k.a. alerts.

By default, no Fossil user has permission to use the forums except for
users with Setup and Admin capabilities, which get these as part of the
large package of other capabilities they get.

For public Fossil repositories that wish to accept new users without
involving a human, go into Admin &rarr; Access and enable the "Allow







|







134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
     showing the list of pending moderation tasks.

  *  <b>Supervise Forum</b> (<tt>6</tt>): User can grant or revoke
     <b>WriteTrusted</b> capability for other users. (Currently
     unimplemented.)

  *  <b>Email Alerts</b> (<tt>7</tt>): User can sign themselves up for
     email alerts, a.k.a. notifications.

By default, no Fossil user has permission to use the forums except for
users with Setup and Admin capabilities, which get these as part of the
large package of other capabilities they get.

For public Fossil repositories that wish to accept new users without
involving a human, go into Admin &rarr; Access and enable the "Allow
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188

For either type of repository, you are likely to want to give at least
the WriteTrusted capability (4) to users in the <tt>developer</tt>
category. If you did not give the Read Forum capability (2) to
<tt>anonymous</tt> above, you should give <tt>developer</tt> that
capability here if you choose to give it capability 3 or 4.

If you want to use the email notification feature, by default only those
users in the Setup and Admin user categories can make use of it. Grant
the Email Alerts capability (7) to give others access to this feature.
Alternately, you can handle notification signups outside of Fossil, with
a Setup or Admin users manually signing users up via Admin &rarr;
Notification. You'll want to grant this capability to the
<tt>nobody</tt> user category if you want anyone to sign up without any
restrictions.  Give it to <tt>anonymous</tt> instead if you want the
user to solve a simple CAPTCHA before signing up. Or, give it to
<tt>reader</tt> or <tt>developer</tt> if you want only users with Fossil
logins to have this ability. (That's assuming you give one or both of







|


|







157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174

For either type of repository, you are likely to want to give at least
the WriteTrusted capability (4) to users in the <tt>developer</tt>
category. If you did not give the Read Forum capability (2) to
<tt>anonymous</tt> above, you should give <tt>developer</tt> that
capability here if you choose to give it capability 3 or 4.

If you want to use the email alert feature, by default only those
users in the Setup and Admin user categories can make use of it. Grant
the Email Alerts capability (7) to give others access to this feature.
Alternately, you can handle alert signups outside of Fossil, with
a Setup or Admin users manually signing users up via Admin &rarr;
Notification. You'll want to grant this capability to the
<tt>nobody</tt> user category if you want anyone to sign up without any
restrictions.  Give it to <tt>anonymous</tt> instead if you want the
user to solve a simple CAPTCHA before signing up. Or, give it to
<tt>reader</tt> or <tt>developer</tt> if you want only users with Fossil
logins to have this ability. (That's assuming you give one or both of
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
The remainder of this section summarizes the differences you're expected
to see when taking option #2.

The first thing is that you'll need to add something like the following
to the Header part of the skin to create the navbar link:

<verbatim>
  if {[anycap 234567] || [anoncap 2] || [anoncap 3]} {
    menulink /forum Forum
  }
</verbatim>

These rules say that any logged-in user with any forum-related
capability (2-7 inclusive, as of this writing) or an anonymous user with
read or write capability on the forum (2, 3) will see the "Forum" navbar
link, which just takes you to <tt>/forum</tt>.

The exact code you need here varies depending on which skin you're
using. Follow the style you see for the other navbar links.

The new forum feature also brings many new CSS styles to the table. If







|





|







203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
The remainder of this section summarizes the differences you're expected
to see when taking option #2.

The first thing is that you'll need to add something like the following
to the Header part of the skin to create the navbar link:

<verbatim>
  if {[anycap 23456] || [anoncap 2] || [anoncap 3]} {
    menulink /forum Forum
  }
</verbatim>

These rules say that any logged-in user with any forum-related
capability (2-6 inclusive, as of this writing) or an anonymous user with
read or write capability on the forum (2, 3) will see the "Forum" navbar
link, which just takes you to <tt>/forum</tt>.

The exact code you need here varies depending on which skin you're
using. Follow the style you see for the other navbar links.

The new forum feature also brings many new CSS styles to the table. If
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425

426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
project's Fossil-hosted forum. Fossil has a feature to solve this
problem which is probably less well known than it should be, and which
has been a feature of Fossil since April of 2011: Admin &rarr;
Login-Group. This allows one Fossil repository to recognize users
authorized on a different Fossil repository.


<h3 id="alerts">Email Notifications (Alerts)</h3>

The detailed information for this feature of Fossil is in two other
documents:

  *  [./alerts.md | Email Alerts]
  *  [./emaildesign.md | Email Notification Design]

You may want to study those in detail, since email is complicated. Some
of that complexity necessarily leaks out to the configuration of email
notification in Fossil.


<h4 id="quick">Quick Setup</h4>

However, if you've already got a working Postfix, Exim, or Sendmail
server on the machine running your Fossil instance(s), and you aren't
using Fossil's <tt>chroot</tt> feature to wall it off from the rest of
the machine, it's fairly simple to set up email notifications:

<ol>
    <li>Go to Admin &rarr; Notification and fill out all of the
    <b>Required</b> fields:</li>

    <ul>
        <li><b>Canonical server URL</b> — Use the suggested URL</li>
        <li><b>"From" email address</b> — <tt>forum-bounces@example.com</tt>
        is traditional, but do what you like</li>
        <li><b>Repository nickname</b> — See the suggested examples on
        the web page.</li>
    </ul>

    <li>Set "Email Send Method" to "Pipe to a command"</li>

    <li>Set the "Administrator email address" to a suitable email address.
    It could be the same value you used for the "From" address above,
    or it could be a different value like
    <tt>admin@example.com</tt>.</li>
</ol>

Save your changes.

<a id="cd"></a>For all of the following <tt>fossil</tt> CLI commands,
we're going to assume that you've <tt>cd</tt>'d into a checkout
directory for your repository on the central server.  Otherwise, we'd
need to add <tt>-R /path/to/repo.fossil</tt> to each of them.

Give this command:

<verbatim>
    $ fossil set email-send-command
</verbatim>

If that gives a blank value instead of <tt>sendmail -ti</tt>, say

<verbatim>
    $ fossil set email-send-command "sendmail -ti"
</verbatim>

to force the setting. That's a
[https://fossil-scm.org/forum/forumpost/840b676410 | known bug] which
should be squished soon.

This is the correct command even if you're running Postfix or Exim
rather than Sendmail. These mail servers (and perhaps others) provide a
<tt>sendmail</tt> command for this very sort of reason: it lets programs
send email without having to care exactly which server implementation is
running at a given site.

<a id="status"></a>If you reload the Email Notification Setup page, the
Status section at the top should show:

<verbatim>
    Outgoing Email: Piped to command "sendmail -ti"
    Pending Alerts: 0 normal, 0 digest
    Subscribers:    0 active, 0 total
</verbatim>


<h4 id="subscribe">Subscribe to Alerts</h4>

Above, we see that there are no subscribers, so the next step is to add
one.

Go to the <tt>/alerts</tt> page on your Fossil instance and sign your
regular Fossil user up for email alerts. (Alternate path: click the user
name or login/logout link in the upper right corner, depending on the
skin you're using, then go to "Email Alerts".) You will need "Forum
Posts" checked at the least for the testing steps below.

If you use the same user name and email address as you used for your
normal user login, Fossil will simply tie your alert preferences to your
login record, and the email address in your user's Contact Info field
will be considered already-verified. Otherwise, Fossil will create an
alert-only record, and you will have to verify the email address before
Fossil will send notifications to it.

This shows a key aspect of the way Fossil's email alerts system works,

by the way: a user can be signed up for email alerts without having a
full-fledged Fossil user account. Only when both user names are the same
are the two records tied together under the hood.


<h4 id="alert-test">Test the Email Subsystem</h4>

If you'd rather not create an inane "testing" post in your Fossil
instance just to force out an email alert, you can test the email
subsystem separately from the rest of the Fossil email alerts system
with the following command:

<verbatim>
    $ fossil alerts test-message you@example.com --body README.md --subject Test
</verbatim>

That should send you an email with "Test" in the subject line and the
contents of your project's <tt>README.md</tt> file in the body. If it
works, the file's contents may be truncated. I'm uncertain whether
that's due to an intentionally-small buffer size or if it's a bug.

That command assumes that your project [./foss-cklist.wiki | contains a
readme file]. Of course it does. Why would it not?


<h4 id="frist" name="first">First Post</h4>

Since you've already edited the skin per [#skin | the instructions
above], you can click the "Forum" link in the navbar and create a new
post. I suggest taking the time to compose a suitable introductory
message especially for your project's forum, one which a new user would
find helpful.

Wait a few seconds, and you should receive a notification email with the
post's subject and body text in the email.


<h4 id="trouble">Troubleshooting</h4>

If email alerts aren't working, there are several useful commands you
can give to figure out why.

(Be sure to [#cd | <tt>cd</tt> into a repo checkout directory] first!)

<verbatim>
    $ fossil alerts status
</verbatim>

This should give much the same information as you saw [#status | above].
One difference is that, since you've created a forum post, the
<tt>pending-alerts</tt> value should only be zero if you did in fact get
the requested email alert. If it's zero, check your mailer's spam
folder. If it's nonzero, continue with these troubleshooting steps.

<verbatim>
    $ fossil backoffice
</verbatim>

That forces Fossil to run its [./backoffice.md | "back office" process].
Its only purpose at the time of this writing is to push out alert
emails, but it might do other things later. Sometimes it can get stuck
and needs to be kicked. For that reason, you might want to set up a
crontab entry to make sure it runs occasionally.

<verbatim>
    $ fossil alerts send
</verbatim>

This should also kick off the backoffice processing, if there are any
pending alerts to send out.

<verbatim>
    $ fossil alert pending
</verbatim>

Show any pending alerts. The number of lines output here should equal
the [#status | status output above].

<verbatim>
    $ fossil test-add-alerts f5900
    $ fossil alert send
</verbatim>

Manually create an email alert and push it out immediately.

The <tt>f</tt> in the first command's final parameter means you're
scheduling a "forum" alert. The integer is the ID of a forum post, which
you can find by visiting <tt>/timeline?showid</tt> on your Fossil
instance.

The second command above is necessary because the
<tt>test-add-alerts</tt> command doesn't kick off a backoffice run.

<verbatim>
    $ fossil ale send
</verbatim>

This only does the same thing as the final command above, rather than
send you an ale, as you might be hoping. Sorry.


<h2 id="moderation">How Moderation Works</h2>

In this section, we're going to call all of the following a "forum
update:"








|

<
<
|
<
<
|
<
<
<
|
|
<

<
<
<
<
|
<
<
<
|
<
<
<
<
<
<
<
|
<

<
<
<
<
<

<
|
<
<
<
<
<
<
<
<
<
<

<
|
<
<
<

<
<
|
|
|
<
<
<
<
|
<
<
|
<
<
<
<
<
|
|
<
|
<
<
|
<
<
<
<
<
|
<
<
<
<
<
|
|
<
>
<
<
<

|
<
|
<
<
<
<
|
<
<
<
|
<
<
<
<

<
<
|
|
<
|
<
<
<
<
<
|
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<

<
<
<
|
<
<
<
<
<
|
<
<
<
|
<
<

<
<
<
|
<
<
|
<
<
<
<
|
<
|
<
<
<
<
<
<
<
<
<
<
<
<
<
<







307
308
309
310
311
312
313
314
315


316


317



318
319

320




321



322







323

324





325

326










327

328



329


330
331
332




333


334





335
336

337


338





339





340
341

342



343
344

345




346



347




348


349
350

351





352




















353



354





355



356


357



358


359




360

361














362
363
364
365
366
367
368
project's Fossil-hosted forum. Fossil has a feature to solve this
problem which is probably less well known than it should be, and which
has been a feature of Fossil since April of 2011: Admin &rarr;
Login-Group. This allows one Fossil repository to recognize users
authorized on a different Fossil repository.


<h3 id="alerts">Email Alerts (a.k.a. Notifications)</h3>



Internet email service has become rather complicated since its initial


simple and insecure implementation decades ago. Fossil's role in all of



this is rather small at the moment, but the details of the integration
are complex enough to justify [./alerts.md | a separate document].






(The other reason that document is separate is that Fossil's email



alerts system also gets used by features of Fossil other than the







forum.)









<h2 id="access">Accessing the Forum</h2>












There are many paths to a repository's Fossil forum:






<ul>
  <li>
    <p>If you're using the default Fossil skin as shipped with Fossil




    2.7 or one updated to include the changes since 2.6 or prior, there


    is a Forum button in the navbar which appears for users with any of





    the forum-related user capabilities: 2 through 6 inclusive for those
    with repository logins, or caps 2 and 3 for users without a user

    account but who have solved the Anonymous user CAPTCHA.</p>


    <p>This button will not appear in the default skin for such users if





    their browser window is not greater than 1200 pixels wide.  The





    Fossil admin can adjust this limit in the skin's CSS section, down
    near the bottom in the definition of the `wideonly` style.</p>

  </li>




  <li>The other stock skins have this button in them as of 2.7 as well,

  without the screen width restriction, since the navbar in those skins




  wraps on narrow screens more gracefully than the default skin



  does.</li>







  <li>Users who set up their Fossil repository under prior versions and
  who now have local skin changes they don't want to overwrite by

  reverting to the stock 2.7 version of the skin they chose to start





  with can easily [#skin | edit their skin] to include these links.</li>
























  <li>A "Forum" link appears in the drop-down panel when you click the





  default skin's hamburger menu (&#9776;) while logged in as any user



  with one or more of the [#caps | user capabilities listed above].</li>






  <li>That same link also appears on the repository's <tt>/sitemap</tt>


  page, since it provides the content for the hamburger menu's




  panel.</li>

</ul>
















<h2 id="moderation">How Moderation Works</h2>

In this section, we're going to call all of the following a "forum
update:"

Changes to www/makefile.wiki.
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
  11.  [/file/src/mkversion.c | mkversion.c]

The builtin_data.h header file contains the definitions of C-language
byte-array constants that contain various resources such as scripts and
images.  The builtin_data.h header file is generate from the original
resource files using a small program called:

  12   [/file/src/mkbuiltin.c | mkbuildin.c]

Examples of built-in resources include the [/file/src/diff.tcl | diff.tcl]
script used to implement the --tk option to [/help?cmd=diff| fossil diff],
the [/file/src/markdown.md | markdown documentation], and the various
CSS scripts, headers, and footers used to implement built-in skins.  New
resources files are added to the "extra_files" variable in
[/file/src/makemake.tcl | makemake.tcl].







|







68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
  11.  [/file/src/mkversion.c | mkversion.c]

The builtin_data.h header file contains the definitions of C-language
byte-array constants that contain various resources such as scripts and
images.  The builtin_data.h header file is generate from the original
resource files using a small program called:

  12   [/file/src/mkbuiltin.c | mkbuiltin.c]

Examples of built-in resources include the [/file/src/diff.tcl | diff.tcl]
script used to implement the --tk option to [/help?cmd=diff| fossil diff],
the [/file/src/markdown.md | markdown documentation], and the various
CSS scripts, headers, and footers used to implement built-in skins.  New
resources files are added to the "extra_files" variable in
[/file/src/makemake.tcl | makemake.tcl].
Changes to www/mkindex.tcl.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
#!/usr/bin/env tclsh
#
# Run this TCL script to generate a WIKI page that contains a
# permuted index of the various documentation files.
#
#    tclsh mkindex.tcl
#

set doclist {
  aboutcgi.wiki {How CGI Works In Fossil}
  aboutdownload.wiki {How The Download Page Works}
  adding_code.wiki {Adding New Features To Fossil}
  adding_code.wiki {Hacking Fossil}
  alerts.md {Email Alerts And Notification}
  antibot.wiki {Defense against Spiders and Bots}
  backoffice.md {The "Backoffice" mechanism of Fossil}
  blame.wiki {The Annotate/Blame Algorithm Of Fossil}
  branching.wiki {Branching, Forking, Merging, and Tagging}
  bugtheory.wiki {Bug Tracking In Fossil}
  build.wiki {Compiling and Installing Fossil}
  changes.wiki {Fossil Changelog}













|







1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
#!/usr/bin/env tclsh
#
# Run this TCL script to generate a WIKI page that contains a
# permuted index of the various documentation files.
#
#    tclsh mkindex.tcl
#

set doclist {
  aboutcgi.wiki {How CGI Works In Fossil}
  aboutdownload.wiki {How The Download Page Works}
  adding_code.wiki {Adding New Features To Fossil}
  adding_code.wiki {Hacking Fossil}
  alerts.md {Email Alerts And Notifications}
  antibot.wiki {Defense against Spiders and Bots}
  backoffice.md {The "Backoffice" mechanism of Fossil}
  blame.wiki {The Annotate/Blame Algorithm Of Fossil}
  branching.wiki {Branching, Forking, Merging, and Tagging}
  bugtheory.wiki {Bug Tracking In Fossil}
  build.wiki {Compiling and Installing Fossil}
  changes.wiki {Fossil Changelog}
33
34
35
36
37
38
39

40
41
42
43
44
45
46
  embeddeddoc.wiki {Embedded Project Documentation}
  encryptedrepos.wiki {How To Use Encrypted Repositories}
  env-opts.md {Environment Variables and Global Options}
  event.wiki {Events}
  faq.wiki {Frequently Asked Questions}
  fileformat.wiki {Fossil File Format}
  fiveminutes.wiki {Up and Running in 5 Minutes as a Single User}

  foss-cklist.wiki {Checklist For Successful Open-Source Projects}
  fossil-from-msvc.wiki {Integrating Fossil in the Microsoft Express 2010 IDE}
  fossil-v-git.wiki {Fossil Versus Git}
  globs.md {File Name Glob Patterns}
  hacker-howto.wiki {Hacker How-To}
  hashpolicy.wiki {Hash Policy: Choosing Between SHA1 and SHA3-256}
  /help {Lists of Commands and Webpages}







>







33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
  embeddeddoc.wiki {Embedded Project Documentation}
  encryptedrepos.wiki {How To Use Encrypted Repositories}
  env-opts.md {Environment Variables and Global Options}
  event.wiki {Events}
  faq.wiki {Frequently Asked Questions}
  fileformat.wiki {Fossil File Format}
  fiveminutes.wiki {Up and Running in 5 Minutes as a Single User}
  forum.wiki {Fossil Forums}
  foss-cklist.wiki {Checklist For Successful Open-Source Projects}
  fossil-from-msvc.wiki {Integrating Fossil in the Microsoft Express 2010 IDE}
  fossil-v-git.wiki {Fossil Versus Git}
  globs.md {File Name Glob Patterns}
  hacker-howto.wiki {Hacker How-To}
  hashpolicy.wiki {Hash Policy: Choosing Between SHA1 and SHA3-256}
  /help {Lists of Commands and Webpages}
Changes to www/permutedindex.html.
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
<h2>Permuted Index:</h2>
<ul>
<li><a href="fiveminutes.wiki">5 Minutes as a Single User &mdash; Up and Running in</a></li>
<li><a href="fossil-from-msvc.wiki">2010 IDE &mdash; Integrating Fossil in the Microsoft Express</a></li>
<li><a href="tech_overview.wiki"><b>A Technical Overview Of The Design And Implementation Of Fossil</b></a></li>
<li><a href="adding_code.wiki"><b>Adding New Features To Fossil</b></a></li>
<li><a href="copyright-release.html">Agreement &mdash; Contributor License</a></li>
<li><a href="alerts.md">Alerts And Notification &mdash; Email</a></li>
<li><a href="delta_encoder_algorithm.wiki">Algorithm &mdash; Fossil Delta Encoding</a></li>
<li><a href="blame.wiki">Algorithm Of Fossil &mdash; The Annotate/Blame</a></li>
<li><a href="blame.wiki">Annotate/Blame Algorithm Of Fossil &mdash; The</a></li>
<li><a href="customskin.md">Appearance of Web Pages &mdash; Theming: Customizing The</a></li>
<li><a href="faq.wiki">Asked Questions &mdash; Frequently</a></li>
<li><a href="password.wiki">Authentication &mdash; Password Management And</a></li>
<li><a href="backoffice.md">Backoffice mechanism of Fossil &mdash; The</a></li>







|







21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
<h2>Permuted Index:</h2>
<ul>
<li><a href="fiveminutes.wiki">5 Minutes as a Single User &mdash; Up and Running in</a></li>
<li><a href="fossil-from-msvc.wiki">2010 IDE &mdash; Integrating Fossil in the Microsoft Express</a></li>
<li><a href="tech_overview.wiki"><b>A Technical Overview Of The Design And Implementation Of Fossil</b></a></li>
<li><a href="adding_code.wiki"><b>Adding New Features To Fossil</b></a></li>
<li><a href="copyright-release.html">Agreement &mdash; Contributor License</a></li>
<li><a href="alerts.md">Alerts And Notifications &mdash; Email</a></li>
<li><a href="delta_encoder_algorithm.wiki">Algorithm &mdash; Fossil Delta Encoding</a></li>
<li><a href="blame.wiki">Algorithm Of Fossil &mdash; The Annotate/Blame</a></li>
<li><a href="blame.wiki">Annotate/Blame Algorithm Of Fossil &mdash; The</a></li>
<li><a href="customskin.md">Appearance of Web Pages &mdash; Theming: Customizing The</a></li>
<li><a href="faq.wiki">Asked Questions &mdash; Frequently</a></li>
<li><a href="password.wiki">Authentication &mdash; Password Management And</a></li>
<li><a href="backoffice.md">Backoffice mechanism of Fossil &mdash; The</a></li>
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100

101
102
103
104
105

106
107
108
109
110
111
112
<li><a href="tech_overview.wiki">Design And Implementation Of Fossil &mdash; A Technical Overview Of The</a></li>
<li><a href="theory1.wiki">Design Of The Fossil DVCS &mdash; Thoughts On The</a></li>
<li><a href="embeddeddoc.wiki">Documentation &mdash; Embedded Project</a></li>
<li><a href="contribute.wiki">Documentation To The Fossil Project &mdash; Contributing Code or</a></li>
<li><a href="aboutdownload.wiki">Download Page Works &mdash; How The</a></li>
<li><a href="theory1.wiki">DVCS &mdash; Thoughts On The Design Of The Fossil</a></li>
<li><a href="quotes.wiki">DVCSes in General &mdash; Quotes: What People Are Saying About Fossil, Git, and</a></li>
<li><a href="alerts.md"><b>Email Alerts And Notification</b></a></li>
<li><a href="embeddeddoc.wiki"><b>Embedded Project Documentation</b></a></li>
<li><a href="delta_encoder_algorithm.wiki">Encoding Algorithm &mdash; Fossil Delta</a></li>
<li><a href="encryptedrepos.wiki">Encrypted Repositories &mdash; How To Use</a></li>
<li><a href="env-opts.md"><b>Environment Variables and Global Options</b></a></li>
<li><a href="event.wiki"><b>Events</b></a></li>
<li><a href="webpage-ex.md">Examples &mdash; Webpage</a></li>
<li><a href="inout.wiki">Export To And From Git &mdash; Import And</a></li>
<li><a href="fossil-from-msvc.wiki">Express 2010 IDE &mdash; Integrating Fossil in the Microsoft</a></li>
<li><a href="adding_code.wiki">Features To Fossil &mdash; Adding New</a></li>
<li><a href="fileformat.wiki">File Format &mdash; Fossil</a></li>
<li><a href="globs.md"><b>File Name Glob Patterns</b></a></li>
<li><a href="unvers.wiki">Files &mdash; Unversioned</a></li>
<li><a href="branching.wiki">Forking, Merging, and Tagging &mdash; Branching,</a></li>
<li><a href="delta_format.wiki">Format &mdash; Fossil Delta</a></li>
<li><a href="fileformat.wiki">Format &mdash; Fossil File</a></li>
<li><a href="../../../md_rules">Formatting Rules &mdash; Markdown</a></li>
<li><a href="../../../wiki_rules">Formatting Rules &mdash; Wiki</a></li>

<li><a href="changes.wiki"><b>Fossil Changelog</b></a></li>
<li><a href="concepts.wiki"><b>Fossil Core Concepts</b></a></li>
<li><a href="delta_encoder_algorithm.wiki"><b>Fossil Delta Encoding Algorithm</b></a></li>
<li><a href="delta_format.wiki"><b>Fossil Delta Format</b></a></li>
<li><a href="fileformat.wiki"><b>Fossil File Format</b></a></li>

<li><a href="quickstart.wiki"><b>Fossil Quick Start Guide</b></a></li>
<li><a href="selfcheck.wiki"><b>Fossil Repository Integrity Self Checks</b></a></li>
<li><a href="selfhost.wiki"><b>Fossil Self Hosting Repositories</b></a></li>
<li><a href="settings.wiki"><b>Fossil Settings</b></a></li>
<li><a href="hints.wiki"><b>Fossil Tips And Usage Hints</b></a></li>
<li><a href="fossil-v-git.wiki"><b>Fossil Versus Git</b></a></li>
<li><a href="quotes.wiki">Fossil, Git, and DVCSes in General &mdash; Quotes: What People Are Saying About</a></li>







|

















>





>







76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
<li><a href="tech_overview.wiki">Design And Implementation Of Fossil &mdash; A Technical Overview Of The</a></li>
<li><a href="theory1.wiki">Design Of The Fossil DVCS &mdash; Thoughts On The</a></li>
<li><a href="embeddeddoc.wiki">Documentation &mdash; Embedded Project</a></li>
<li><a href="contribute.wiki">Documentation To The Fossil Project &mdash; Contributing Code or</a></li>
<li><a href="aboutdownload.wiki">Download Page Works &mdash; How The</a></li>
<li><a href="theory1.wiki">DVCS &mdash; Thoughts On The Design Of The Fossil</a></li>
<li><a href="quotes.wiki">DVCSes in General &mdash; Quotes: What People Are Saying About Fossil, Git, and</a></li>
<li><a href="alerts.md"><b>Email Alerts And Notifications</b></a></li>
<li><a href="embeddeddoc.wiki"><b>Embedded Project Documentation</b></a></li>
<li><a href="delta_encoder_algorithm.wiki">Encoding Algorithm &mdash; Fossil Delta</a></li>
<li><a href="encryptedrepos.wiki">Encrypted Repositories &mdash; How To Use</a></li>
<li><a href="env-opts.md"><b>Environment Variables and Global Options</b></a></li>
<li><a href="event.wiki"><b>Events</b></a></li>
<li><a href="webpage-ex.md">Examples &mdash; Webpage</a></li>
<li><a href="inout.wiki">Export To And From Git &mdash; Import And</a></li>
<li><a href="fossil-from-msvc.wiki">Express 2010 IDE &mdash; Integrating Fossil in the Microsoft</a></li>
<li><a href="adding_code.wiki">Features To Fossil &mdash; Adding New</a></li>
<li><a href="fileformat.wiki">File Format &mdash; Fossil</a></li>
<li><a href="globs.md"><b>File Name Glob Patterns</b></a></li>
<li><a href="unvers.wiki">Files &mdash; Unversioned</a></li>
<li><a href="branching.wiki">Forking, Merging, and Tagging &mdash; Branching,</a></li>
<li><a href="delta_format.wiki">Format &mdash; Fossil Delta</a></li>
<li><a href="fileformat.wiki">Format &mdash; Fossil File</a></li>
<li><a href="../../../md_rules">Formatting Rules &mdash; Markdown</a></li>
<li><a href="../../../wiki_rules">Formatting Rules &mdash; Wiki</a></li>
<li><a href="forum.wiki">Forums &mdash; Fossil</a></li>
<li><a href="changes.wiki"><b>Fossil Changelog</b></a></li>
<li><a href="concepts.wiki"><b>Fossil Core Concepts</b></a></li>
<li><a href="delta_encoder_algorithm.wiki"><b>Fossil Delta Encoding Algorithm</b></a></li>
<li><a href="delta_format.wiki"><b>Fossil Delta Format</b></a></li>
<li><a href="fileformat.wiki"><b>Fossil File Format</b></a></li>
<li><a href="forum.wiki"><b>Fossil Forums</b></a></li>
<li><a href="quickstart.wiki"><b>Fossil Quick Start Guide</b></a></li>
<li><a href="selfcheck.wiki"><b>Fossil Repository Integrity Self Checks</b></a></li>
<li><a href="selfhost.wiki"><b>Fossil Self Hosting Repositories</b></a></li>
<li><a href="settings.wiki"><b>Fossil Settings</b></a></li>
<li><a href="hints.wiki"><b>Fossil Tips And Usage Hints</b></a></li>
<li><a href="fossil-v-git.wiki"><b>Fossil Versus Git</b></a></li>
<li><a href="quotes.wiki">Fossil, Git, and DVCSes in General &mdash; Quotes: What People Are Saying About</a></li>
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
<li><a href="branching.wiki">Merging, and Tagging &mdash; Branching, Forking,</a></li>
<li><a href="fossil-from-msvc.wiki">Microsoft Express 2010 IDE &mdash; Integrating Fossil in the</a></li>
<li><a href="fiveminutes.wiki">Minutes as a Single User &mdash; Up and Running in 5</a></li>
<li><a href="globs.md">Name Glob Patterns &mdash; File</a></li>
<li><a href="checkin_names.wiki">Names &mdash; Check-in And Version</a></li>
<li><a href="adding_code.wiki">New Features To Fossil &mdash; Adding</a></li>
<li><a href="newrepo.wiki">New Fossil Repository &mdash; How To Create A</a></li>
<li><a href="alerts.md">Notification &mdash; Email Alerts And</a></li>
<li><a href="foss-cklist.wiki">Open-Source Projects &mdash; Checklist For Successful</a></li>
<li><a href="pop.wiki">Operation &mdash; Principles Of</a></li>
<li><a href="env-opts.md">Options &mdash; Environment Variables and Global</a></li>
<li><a href="tech_overview.wiki">Overview Of The Design And Implementation Of Fossil &mdash; A Technical</a></li>
<li><a href="index.wiki">Page &mdash; Home</a></li>
<li><a href="aboutdownload.wiki">Page Works &mdash; How The Download</a></li>
<li><a href="customskin.md">Pages &mdash; Theming: Customizing The Appearance of Web</a></li>







|







151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
<li><a href="branching.wiki">Merging, and Tagging &mdash; Branching, Forking,</a></li>
<li><a href="fossil-from-msvc.wiki">Microsoft Express 2010 IDE &mdash; Integrating Fossil in the</a></li>
<li><a href="fiveminutes.wiki">Minutes as a Single User &mdash; Up and Running in 5</a></li>
<li><a href="globs.md">Name Glob Patterns &mdash; File</a></li>
<li><a href="checkin_names.wiki">Names &mdash; Check-in And Version</a></li>
<li><a href="adding_code.wiki">New Features To Fossil &mdash; Adding</a></li>
<li><a href="newrepo.wiki">New Fossil Repository &mdash; How To Create A</a></li>
<li><a href="alerts.md">Notifications &mdash; Email Alerts And</a></li>
<li><a href="foss-cklist.wiki">Open-Source Projects &mdash; Checklist For Successful</a></li>
<li><a href="pop.wiki">Operation &mdash; Principles Of</a></li>
<li><a href="env-opts.md">Options &mdash; Environment Variables and Global</a></li>
<li><a href="tech_overview.wiki">Overview Of The Design And Implementation Of Fossil &mdash; A Technical</a></li>
<li><a href="index.wiki">Page &mdash; Home</a></li>
<li><a href="aboutdownload.wiki">Page Works &mdash; How The Download</a></li>
<li><a href="customskin.md">Pages &mdash; Theming: Customizing The Appearance of Web</a></li>