Many hyperlinks are disabled.
Use anonymous login
to enable hyperlinks.
Overview
Comment: | Merge trunk. Update to Beta 7 of OpenSSL 1.1.1 (pre release 9, with TLS 1.3 support !!!) |
---|---|
Downloads: | Tarball | ZIP archive |
Timelines: | family | ancestors | descendants | both | openssl-1.1 |
Files: | files | file ages | folders |
SHA3-256: |
cbdbc0a0ceaba9f90481f9f0ed1daee0 |
User & Date: | jan.nijtmans 2018-08-23 15:14:16.069 |
Context
2018-08-24
| ||
08:25 | Add configurations "enable-capieng" (use Windows Certificate Store) and "no-weak-ssl-ciphers" (disable RC4 support) ... (check-in: b5a11142 user: jan.nijtmans tags: openssl-1.1) | |
2018-08-23
| ||
15:14 | Merge trunk. Update to Beta 7 of OpenSSL 1.1.1 (pre release 9, with TLS 1.3 support !!!) ... (check-in: cbdbc0a0 user: jan.nijtmans tags: openssl-1.1) | |
15:07 | Add the meta-viewport header to the repository list page. ... (check-in: bb7c2d91 user: drh tags: trunk) | |
2018-04-11
| ||
08:16 | Merge trunk. Update to Beta 2 of OpenSSL 1.1.1 (with TLS 1.3 support !!!) ... (check-in: 87f3c946 user: jan.nijtmans tags: openssl-1.1) | |
Changes
Changes to Makefile.classic.
︙ | ︙ | |||
17 18 19 20 21 22 23 24 25 26 27 28 29 30 | #### C Compiler and options for use in building executables that # will run on the platform that is doing the build. This is used # to compile code-generator programs as part of the build process. # See TCC below for the C compiler for building the finished binary. # BCC = gcc #### The suffix to add to final executable file. When cross-compiling # to windows, make this ".exe". Otherwise leave it blank. # E = #### C Compile and options for use in building executables that | > | 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 | #### C Compiler and options for use in building executables that # will run on the platform that is doing the build. This is used # to compile code-generator programs as part of the build process. # See TCC below for the C compiler for building the finished binary. # BCC = gcc BCCFLAGS = $(CFLAGS) #### The suffix to add to final executable file. When cross-compiling # to windows, make this ".exe". Otherwise leave it blank. # E = #### C Compile and options for use in building executables that |
︙ | ︙ | |||
40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 | # To use the included miniz library # FOSSIL_ENABLE_MINIZ = 1 # TCC += -DFOSSIL_ENABLE_MINIZ # To add support for HTTPS TCC += -DFOSSIL_ENABLE_SSL #### We sometimes add the -static option here so that we can build a # static executable that will run in a chroot jail. #LIB = -static TCC += -DFOSSIL_DYNAMIC_BUILD=1 #### Extra arguments for linking the finished binary. Fossil needs # to link against the Z-Lib compression library unless the miniz # library in the source tree is being used. There are no other # required dependencies. ZLIB_LIB.0 = -lz ZLIB_LIB.1 = | > > > > > | 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 | # To use the included miniz library # FOSSIL_ENABLE_MINIZ = 1 # TCC += -DFOSSIL_ENABLE_MINIZ # To add support for HTTPS TCC += -DFOSSIL_ENABLE_SSL # To enable legacy mv/rm support TCC += -DFOSSIL_ENABLE_LEGACY_MV_RM=1 #### We sometimes add the -static option here so that we can build a # static executable that will run in a chroot jail. #LIB = -static TCC += -DFOSSIL_DYNAMIC_BUILD=1 TCCFLAGS = $(CFLAGS) #### Extra arguments for linking the finished binary. Fossil needs # to link against the Z-Lib compression library unless the miniz # library in the source tree is being used. There are no other # required dependencies. ZLIB_LIB.0 = -lz ZLIB_LIB.1 = |
︙ | ︙ |
Changes to Makefile.in.
︙ | ︙ | |||
36 37 38 39 40 41 42 | #### Tcl shell for use in running the fossil testsuite. If you do not # care about testing the end result, this can be blank. # TCLSH = tclsh CFLAGS = @CFLAGS@ LIB = @LDFLAGS@ @EXTRA_LDFLAGS@ @LIBS@ | | | | 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 | #### Tcl shell for use in running the fossil testsuite. If you do not # care about testing the end result, this can be blank. # TCLSH = tclsh CFLAGS = @CFLAGS@ LIB = @LDFLAGS@ @EXTRA_LDFLAGS@ @LIBS@ BCCFLAGS = @CPPFLAGS@ $(CFLAGS) TCCFLAGS = @EXTRA_CFLAGS@ @CPPFLAGS@ $(CFLAGS) -DHAVE_AUTOCONFIG_H -D_HAVE_SQLITE_CONFIG_H INSTALLDIR = $(DESTDIR)@prefix@/bin USE_SYSTEM_SQLITE = @USE_SYSTEM_SQLITE@ USE_LINENOISE = @USE_LINENOISE@ USE_MMAN_H = @USE_MMAN_H@ USE_SEE = @USE_SEE@ FOSSIL_ENABLE_MINIZ = @FOSSIL_ENABLE_MINIZ@ |
︙ | ︙ |
Added Makefile.osx-jaguar.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 | #!/usr/bin/make # # This is a specially modified version of the Makefile that will build # Fossil on Mac OSX Jaguar (10.2) circa 2002. This Makefile is used for # testing on an old PPC iBook. The use of this old platform helps to verify # Fossil and SQLite running on big-endian hardware. # # To build with this Makefile, run: # # make -f Makefile.osx-jaguar # # # This is the top-level makefile for Fossil when the build is occurring # on a unix platform. This works out-of-the-box on most unix platforms. # But you are free to vary some of the definitions if desired. # #### The toplevel directory of the source tree. Fossil can be built # in a directory that is separate from the source tree. Just change # the following to point from the build directory to the src/ folder. # SRCDIR = ./src #### The directory into which object code files should be written. # Having a "./" prefix in the value of this variable breaks our use of the # "makeheaders" tool when running make on the MinGW platform, apparently # due to some command line argument manipulation performed automatically # by the shell. # # OBJDIR = bld #### C Compiler and options for use in building executables that # will run on the platform that is doing the build. This is used # to compile code-generator programs as part of the build process. # See TCC below for the C compiler for building the finished binary. # BCC = cc BCCFLAGS = $(CFLAGS) #### The suffix to add to final executable file. When cross-compiling # to windows, make this ".exe". Otherwise leave it blank. # E = TCC = cc TCCFLAGS = $(CFLAGS) #### Tcl shell for use in running the fossil testsuite. If you do not # care about testing the end result, this can be blank. # TCLSH = tclsh # LIB = -lz LIB = compat/zlib/libz.a TCC += -g -O0 -DHAVE_AUTOCONFIG_H TCC += -Icompat/zlib TCC += -DSQLITE_WITHOUT_ZONEMALLOC TCC += -D_BSD_SOURCE=1 TCC += -DWITHOUT_ICONV TCC += -Dsocklen_t=int TCC += -DSQLITE_MAX_MMAP_SIZE=0 TCC += -DFOSSIL_ENABLE_LEGACY_MV_RM=1 INSTALLDIR = $(DESTDIR)/usr/local/bin USE_SYSTEM_SQLITE = USE_LINENOISE = 1 # FOSSIL_ENABLE_TCL = @FOSSIL_ENABLE_TCL@ FOSSIL_ENABLE_TCL = 0 FOSSIL_ENABLE_MINIZ = 0 include $(SRCDIR)/main.mk distclean: clean rm -f autoconfig.h config.log Makefile |
Changes to VERSION.
|
| | | 1 | 2.6 |
Changes to auto.def.
1 2 3 4 5 6 7 8 9 10 11 12 | # System autoconfiguration. Try: ./configure --help use cc cc-lib options { with-openssl:path|auto|tree|none => {Look for OpenSSL in the given path, automatically, in the source tree, or none} with-miniz=0 => {Use miniz from the source tree} with-zlib:path|auto|tree => {Look for zlib in the given path, automatically, or in the source tree} with-exec-rel-paths=0 => {Enable relative paths for external diff/gdiff} | | | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | # System autoconfiguration. Try: ./configure --help use cc cc-lib options { with-openssl:path|auto|tree|none => {Look for OpenSSL in the given path, automatically, in the source tree, or none} with-miniz=0 => {Use miniz from the source tree} with-zlib:path|auto|tree => {Look for zlib in the given path, automatically, or in the source tree} with-exec-rel-paths=0 => {Enable relative paths for external diff/gdiff} with-legacy-mv-rm=1 => {Enable legacy behavior for mv/rm (skip checkout files)} with-th1-docs=0 => {Enable TH1 for embedded documentation pages} with-th1-hooks=0 => {Enable TH1 hooks for commands and web pages} with-tcl:path => {Enable Tcl integration, with Tcl in the specified path} with-tcl-stubs=0 => {Enable Tcl integration via stubs library mechanism} with-tcl-private-stubs=0 => {Enable Tcl integration via private stubs mechanism} with-mman=0 => {Enable use of POSIX memory APIs from "sys/mman.h"} |
︙ | ︙ | |||
89 90 91 92 93 94 95 | # 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 | | | | | | 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 | # 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 |
︙ | ︙ | |||
132 133 134 135 136 137 138 | } if {[string match *-solaris* [get-define host]]} { define-append EXTRA_CFLAGS {-D_XOPEN_SOURCE=500 -D__EXTENSIONS__} } if {[opt-bool fossil-debug]} { | < > | 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 | } if {[string match *-solaris* [get-define host]]} { define-append EXTRA_CFLAGS {-D_XOPEN_SOURCE=500 -D__EXTENSIONS__} } if {[opt-bool fossil-debug]} { define CFLAGS {-g -O0 -Wall} define-append CFLAGS -DFOSSIL_DEBUG msg-result "Debugging support enabled" } if {[opt-bool no-opt]} { define CFLAGS {-g -O0 -Wall} msg-result "Builting without compiler optimization" } |
︙ | ︙ | |||
165 166 167 168 169 170 171 | # reading config.h first. define-append EXTRA_CFLAGS -DFOSSIL_ENABLE_JSON define FOSSIL_ENABLE_JSON msg-result "JSON support enabled" } if {[opt-bool with-legacy-mv-rm]} { | | | 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 | # reading config.h first. define-append EXTRA_CFLAGS -DFOSSIL_ENABLE_JSON define FOSSIL_ENABLE_JSON msg-result "JSON support enabled" } if {[opt-bool with-legacy-mv-rm]} { define-append EXTRA_CFLAGS -DFOSSIL_ENABLE_LEGACY_MV_RM=1 define FOSSIL_ENABLE_LEGACY_MV_RM msg-result "Legacy mv/rm support enabled" } if {[opt-bool with-exec-rel-paths]} { define-append EXTRA_CFLAGS -DFOSSIL_ENABLE_EXEC_REL_PATHS define FOSSIL_ENABLE_EXEC_REL_PATHS |
︙ | ︙ | |||
476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 | cc-check-function-in-lib gethostbyname nsl if {![cc-check-function-in-lib socket {socket network}]} { # Last resort, may be Windows if {[is_mingw]} { define-append LIBS -lwsock32 } } cc-check-functions utime cc-check-functions usleep cc-check-functions strchrnul cc-check-functions pledge # Check for getloadavg(), and if it doesn't exist, define FOSSIL_OMIT_LOAD_AVERAGE if {![cc-check-functions getloadavg]} { define FOSSIL_OMIT_LOAD_AVERAGE 1 msg-result "Load average support unavailable" } # Check for getpassphrase() for Solaris 10 where getpass() truncates to 10 chars if {![cc-check-functions getpassphrase]} { # Haiku needs this cc-check-function-in-lib getpass bsd } | > > < > > > > > > > > | 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 | cc-check-function-in-lib gethostbyname nsl if {![cc-check-function-in-lib socket {socket network}]} { # Last resort, may be Windows if {[is_mingw]} { define-append LIBS -lwsock32 } } cc-check-function-in-lib ns_name_uncompress resolv cc-check-functions utime cc-check-functions usleep cc-check-functions strchrnul cc-check-functions pledge cc-check-functions backtrace # Check for getloadavg(), and if it doesn't exist, define FOSSIL_OMIT_LOAD_AVERAGE if {![cc-check-functions getloadavg]} { define FOSSIL_OMIT_LOAD_AVERAGE 1 msg-result "Load average support unavailable" } # Check for getpassphrase() for Solaris 10 where getpass() truncates to 10 chars if {![cc-check-functions getpassphrase]} { # Haiku needs this cc-check-function-in-lib getpass bsd } cc-check-function-in-lib sin m # Check for the FuseFS library if {[opt-bool fusefs]} { if {[cc-check-function-in-lib fuse_mount fuse]} { define-append EXTRA_CFLAGS -DFOSSIL_HAVE_FUSEFS define FOSSIL_HAVE_FUSEFS 1 define-append LIBS -lfuse msg-result "FuseFS support enabled" } } # Finally, append -ldl to make sure it's the last in the list. # The library order matters in case of static linking. if {[check-function-in-lib dlopen dl]} { # Some platforms (*BSD) have the dl functions already in libc and no libdl. # In such case we can link directly without -ldl. define-append LIBS [get-define lib_dlopen] } make-template Makefile.in make-config-header autoconfig.h -auto {USE_* FOSSIL_*} |
Changes to skins/default/css.txt.
1 2 | body { margin: 0 auto; | < | 1 2 3 4 5 6 7 8 9 | body { margin: 0 auto; padding: 0px 20px; background-color: white; font-family: sans-serif; font-size:14pt; -moz-text-size-adjust: none; -webkit-text-size-adjust: none; -mx-text-size-adjust: none; |
︙ | ︙ | |||
81 82 83 84 85 86 87 88 89 90 91 92 93 94 | .mainmenu { font-size:.8em; clear:both; padding:10px; background:#eaeaea linear-gradient(#fafafa, #eaeaea) repeat-x; border:1px solid #eaeaea; border-radius:5px; } .mainmenu a { padding: 10px 20px; text-decoration:none; color: #777; border-right:1px solid #eaeaea; | > > | 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 | .mainmenu { font-size:.8em; clear:both; padding:10px; background:#eaeaea linear-gradient(#fafafa, #eaeaea) repeat-x; border:1px solid #eaeaea; border-radius:5px; overflow-x: auto; white-space: nowrap; } .mainmenu a { padding: 10px 20px; text-decoration:none; color: #777; border-right:1px solid #eaeaea; |
︙ | ︙ | |||
198 199 200 201 202 203 204 | div.timelineDate { font-weight: bold; white-space: nowrap; } span.submenuctrl, span.submenuctrl input, select.submenuctrl { color: #777; } | > > > > > > | 199 200 201 202 203 204 205 206 207 208 209 210 211 | div.timelineDate { font-weight: bold; white-space: nowrap; } span.submenuctrl, span.submenuctrl input, select.submenuctrl { color: #777; } span.submenuctrl { white-space: nowrap; } div.submenu label { white-space: nowrap; } |
Changes to skins/eagle/header.txt.
︙ | ︙ | |||
82 83 84 85 86 87 88 | return n < 10 ? '0' + n : n; } e.innerHTML = d.getUTCFullYear()+ '-' + f(d.getUTCMonth() + 1) + '-' + f(d.getUTCDate()) + ' ' + f(d.getUTCHours()) + ':' + f(d.getUTCMinutes()); | | | 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 | return n < 10 ? '0' + n : n; } e.innerHTML = d.getUTCFullYear()+ '-' + f(d.getUTCMonth() + 1) + '-' + f(d.getUTCDate()) + ' ' + f(d.getUTCHours()) + ':' + f(d.getUTCMinutes()); setTimeout(updateClock,(60-d.getUTCSeconds())*1000); } } updateClock(); </script> <div class="mainmenu"> <th1> proc menulink {url name} { |
︙ | ︙ |
Changes to skins/enhanced1/header.txt.
︙ | ︙ | |||
82 83 84 85 86 87 88 | return n < 10 ? '0' + n : n; } e.innerHTML = d.getUTCFullYear()+ '-' + f(d.getUTCMonth() + 1) + '-' + f(d.getUTCDate()) + ' ' + f(d.getUTCHours()) + ':' + f(d.getUTCMinutes()); | | | 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 | return n < 10 ? '0' + n : n; } e.innerHTML = d.getUTCFullYear()+ '-' + f(d.getUTCMonth() + 1) + '-' + f(d.getUTCDate()) + ' ' + f(d.getUTCHours()) + ':' + f(d.getUTCMinutes()); setTimeout(updateClock,(60-d.getUTCSeconds())*1000); } } updateClock(); </script> <div class="mainmenu"> <th1> proc menulink {url name} { |
︙ | ︙ |
Changes to skins/plain_gray/details.txt.
1 | timeline-arrowheads: 1 | | | 1 2 3 4 | timeline-arrowheads: 1 timeline-circle-nodes: 1 timeline-color-graph-lines: 0 white-foreground: 0 |
Changes to skins/xekri/header.txt.
︙ | ︙ | |||
82 83 84 85 86 87 88 | return n < 10 ? '0' + n : n; } e.innerHTML = d.getUTCFullYear()+ '-' + f(d.getUTCMonth() + 1) + '-' + f(d.getUTCDate()) + ' ' + f(d.getUTCHours()) + ':' + f(d.getUTCMinutes()); | | | 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 | return n < 10 ? '0' + n : n; } e.innerHTML = d.getUTCFullYear()+ '-' + f(d.getUTCMonth() + 1) + '-' + f(d.getUTCDate()) + ' ' + f(d.getUTCHours()) + ':' + f(d.getUTCMinutes()); setTimeout(updateClock,(60-d.getUTCSeconds())*1000); } } updateClock(); </script> <div class="mainmenu"> <th1> proc menulink {url name} { |
︙ | ︙ |
Changes to src/add.c.
︙ | ︙ | |||
251 252 253 254 255 256 257 | ** Make arrangements to add one or more files or directories to the ** current checkout at the next commit. ** ** When adding files or directories recursively, filenames that begin ** with "." are excluded by default. To include such files, add ** the "--dotfiles" option to the command-line. ** | | | 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 | ** Make arrangements to add one or more files or directories to the ** current checkout at the next commit. ** ** When adding files or directories recursively, filenames that begin ** with "." are excluded by default. To include such files, add ** the "--dotfiles" option to the command-line. ** ** The --ignore and --clean options are comma-separated lists of glob patterns ** for files to be excluded. Example: '*.o,*.obj,*.exe' If the --ignore ** option does not appear on the command line then the "ignore-glob" setting ** is used. If the --clean option does not appear on the command line then ** the "clean-glob" setting is used. ** ** If files are attempted to be added explicitly on the command line which ** match "ignore-glob", a confirmation is asked first. This can be prevented |
︙ | ︙ | |||
857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 | void mv_cmd(void){ int i; int vid; int moveFiles; int dryRunFlag; int softFlag; int hardFlag; char *zDest; Blob dest; Stmt q; db_must_be_within_tree(); dryRunFlag = find_option("dry-run","n",0)!=0; softFlag = find_option("soft",0,0)!=0; hardFlag = find_option("hard",0,0)!=0; /* We should be done with options.. */ verify_all_options(); vid = db_lget_int("checkout", 0); if( vid==0 ){ | > > | | 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 | void mv_cmd(void){ int i; int vid; int moveFiles; int dryRunFlag; int softFlag; int hardFlag; int origType; int destType; char *zDest; Blob dest; Stmt q; db_must_be_within_tree(); dryRunFlag = find_option("dry-run","n",0)!=0; softFlag = find_option("soft",0,0)!=0; hardFlag = find_option("hard",0,0)!=0; /* We should be done with options.. */ verify_all_options(); vid = db_lget_int("checkout", 0); if( vid==0 ){ fossil_fatal("no checkout in which to rename files"); } if( g.argc<4 ){ usage("OLDNAME NEWNAME"); } zDest = g.argv[g.argc-1]; db_begin_transaction(); if( g.argv[1][0]=='r' ){ /* i.e. "rename" */ |
︙ | ︙ | |||
898 899 900 901 902 903 904 | file_tree_name(zDest, &dest, 0, 1); db_multi_exec( "UPDATE vfile SET origname=pathname WHERE origname IS NULL;" ); db_multi_exec( "CREATE TEMP TABLE mv(f TEXT UNIQUE ON CONFLICT IGNORE, t TEXT);" ); | < < | > > | | > > > > > > > > | 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 | file_tree_name(zDest, &dest, 0, 1); db_multi_exec( "UPDATE vfile SET origname=pathname WHERE origname IS NULL;" ); db_multi_exec( "CREATE TEMP TABLE mv(f TEXT UNIQUE ON CONFLICT IGNORE, t TEXT);" ); if( g.argc!=4 ){ origType = -1; }else{ origType = (file_isdir(g.argv[2], RepoFILE) == 1); } destType = file_isdir(zDest, RepoFILE); if( origType==-1 && destType!=1 ){ usage("OLDNAME NEWNAME"); }else if( origType==1 && destType==2 ){ fossil_fatal("cannot rename '%s' to '%s' since another file named" " '%s' exists", g.argv[2], zDest, zDest); }else if( origType==0 && destType!=1 ){ Blob orig; file_tree_name(g.argv[2], &orig, 0, 1); db_multi_exec( "INSERT INTO mv VALUES(%B,%B)", &orig, &dest ); }else{ if( blob_eq(&dest, ".") ){ blob_reset(&dest); |
︙ | ︙ | |||
934 935 936 937 938 939 940 941 942 943 944 945 946 947 | ); while( db_step(&q)==SQLITE_ROW ){ const char *zPath = db_column_text(&q, 0); int nPath = db_column_bytes(&q, 0); const char *zTail; if( nPath==nOrig ){ zTail = file_tail(zPath); }else{ zTail = &zPath[nOrig+1]; } db_multi_exec( "INSERT INTO mv VALUES('%q','%q%q')", zPath, blob_str(&dest), zTail ); | > > | 944 945 946 947 948 949 950 951 952 953 954 955 956 957 958 959 | ); while( db_step(&q)==SQLITE_ROW ){ const char *zPath = db_column_text(&q, 0); int nPath = db_column_bytes(&q, 0); const char *zTail; if( nPath==nOrig ){ zTail = file_tail(zPath); }else if( destType==1 ){ zTail = &zPath[nOrig-strlen(file_tail(zOrig))]; }else{ zTail = &zPath[nOrig+1]; } db_multi_exec( "INSERT INTO mv VALUES('%q','%q%q')", zPath, blob_str(&dest), zTail ); |
︙ | ︙ |
Changes to src/attach.c.
︙ | ︙ | |||
104 105 106 107 108 109 110 | }else if( type==2 ){ zUrlTail = mprintf("technote=%s&file=%t", zTarget, zFilename); }else{ zUrlTail = mprintf("page=%t&file=%t", zTarget, zFilename); } @ <li><p> @ Attachment %z(href("%R/ainfo/%!S",zUuid))%S(zUuid)</a> | | < < | | 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 | }else if( type==2 ){ zUrlTail = mprintf("technote=%s&file=%t", zTarget, zFilename); }else{ zUrlTail = mprintf("page=%t&file=%t", zTarget, zFilename); } @ <li><p> @ Attachment %z(href("%R/ainfo/%!S",zUuid))%S(zUuid)</a> moderation_pending_www(attachid); @ <br /><a href="%R/attachview?%s(zUrlTail)">%h(zFilename)</a> @ [<a href="%R/attachdownload/%t(zFilename)?%s(zUrlTail)">download</a>]<br> if( zComment ) while( fossil_isspace(zComment[0]) ) zComment++; if( zComment && zComment[0] ){ @ %!W(zComment)<br /> } if( zPage==0 && zTkt==0 && zTechNote==0 ){ if( zSrc==0 || zSrc[0]==0 ){ zSrc = "Deleted from"; |
︙ | ︙ | |||
375 376 377 378 379 380 381 | zTargetType = mprintf("Ticket <a href=\"%R/tktview/%s\">%S</a>", zTkt, zTkt); } if( zFrom==0 ) zFrom = mprintf("%s/home", g.zTop); if( P("cancel") ){ cgi_redirect(zFrom); } | | | 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 | zTargetType = mprintf("Ticket <a href=\"%R/tktview/%s\">%S</a>", zTkt, zTkt); } if( zFrom==0 ) zFrom = mprintf("%s/home", g.zTop); if( P("cancel") ){ cgi_redirect(zFrom); } if( P("ok") && szContent>0 && (goodCaptcha = captcha_is_correct(0)) ){ int needModerator = (zTkt!=0 && ticket_need_moderation(0)) || (zPage!=0 && wiki_need_moderation(0)); const char *zComment = PD("comment", ""); attach_commit(zName, zTarget, aContent, szContent, needModerator, zComment); cgi_redirect(zFrom); } style_header("Add Attachment"); |
︙ | ︙ | |||
562 563 564 565 566 567 568 | @ <div class="section">Overview</div> @ <p><table class="label-value"> @ <tr><th>Artifact ID:</th> @ <td>%z(href("%R/artifact/%!S",zUuid))%s(zUuid)</a> if( g.perm.Setup ){ @ (%d(rid)) } | | < < < | 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 | @ <div class="section">Overview</div> @ <p><table class="label-value"> @ <tr><th>Artifact ID:</th> @ <td>%z(href("%R/artifact/%!S",zUuid))%s(zUuid)</a> if( g.perm.Setup ){ @ (%d(rid)) } modPending = moderation_pending_www(rid); if( zTktUuid ){ @ <tr><th>Ticket:</th> @ <td>%z(href("%R/tktview/%s",zTktUuid))%s(zTktUuid)</a></td></tr> } if( zTNUuid ){ @ <tr><th>Tech Note:</th> @ <td>%z(href("%R/technote/%s",zTNUuid))%s(zTNUuid)</a></td></tr> |
︙ | ︙ |
Added src/backoffice.c.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 | /* ** Copyright (c) 2018 D. Richard Hipp ** ** This program is free software; you can redistribute it and/or ** modify it under the terms of the Simplified BSD License (also ** known as the "2-Clause License" or "FreeBSD License".) ** ** This program is distributed in the hope that it will be useful, ** but without any warranty; without even the implied warranty of ** merchantability or fitness for a particular purpose. ** ** Author contact information: ** drh@hwaci.com ** http://www.hwaci.com/drh/ ** ******************************************************************************* ** ** This file contains code used to manage a background processes that ** occur after user interaction with the repository. Examples of ** backoffice processing includes: ** ** * Sending alerts and notifications ** * Processing the email queue ** * Automatically syncing to peer repositories ** ** Backoffice processing is automatically started whenever there are ** changes to the repository. The backoffice process dies off after ** a period of inactivity. ** ** Steps are taken to ensure that only a single backoffice process is ** running at a time. Otherwise, there could be race conditions that ** cause adverse effects such as multiple alerts for the same changes. ** ** At the same time, we do not want a backoffice process to run forever. ** Backoffice processes should die off after doing whatever work they need ** to do. In this way, we avoid having lots of idle processes in the ** process table, doing nothing on rarely accessed repositories, and ** if the Fossil binary is updated on a system, the backoffice processes ** will restart using the new binary automatically. ** ** At any point in time there should be at most two backoffice processes. ** There is a main process that is doing the actually work, and there is ** a second stand-by process that is waiting for the main process to finish ** and that will become the main process after a delay. ** ** After any successful web page reply, the backoffice_check_if_needed() ** routine is called. That routine checks to see if both one or both of ** the backoffice processes are already running. That routine remembers the ** status in a global variable. ** ** Later, after the repository database is closed, the ** backoffice_run_if_needed() routine is called. If the prior call ** to backoffice_check_if_needed() indicated that backoffice processing ** might be required, the run_if_needed() attempts to kick off a backoffice ** process. ** ** All work performance by the backoffice is in the backoffice_work() ** routine. */ #if defined(_WIN32) # if defined(_WIN32_WINNT) # undef _WIN32_WINNT # endif # define _WIN32_WINNT 0x501 #endif #include "config.h" #include "backoffice.h" #include <time.h> #if defined(_WIN32) # include <windows.h> # include <stdio.h> # include <process.h> # if defined(__MINGW32__) # include <wchar.h> # endif # define GETPID (int)GetCurrentProcessId #else # include <unistd.h> # include <sys/types.h> # include <signal.h> # include <errno.h> # include <fcntl.h> # define GETPID getpid #endif /* ** The BKOFCE_LEASE_TIME is the amount of time for which a single backoffice ** processing run is valid. Each backoffice run monopolizes the lease for ** at least this amount of time. Hopefully all backoffice processing is ** finished much faster than this - usually in less than a second. But ** regardless of how long each invocation lasts, successive backoffice runs ** must be spaced out by at least this much time. */ #define BKOFCE_LEASE_TIME 60 /* Length of lease validity in seconds */ #if LOCAL_INTERFACE /* ** An instance of the following object describes a lease on the backoffice ** processing timeslot. This lease is used to help ensure that no more than ** one process is running backoffice at a time. */ struct Lease { sqlite3_uint64 idCurrent; /* process ID for the current lease holder */ sqlite3_uint64 tmCurrent; /* Expiration of the current lease */ sqlite3_uint64 idNext; /* process ID for the next lease holder on queue */ sqlite3_uint64 tmNext; /* Expiration of the next lease */ }; #endif /*************************************************************************** ** Local state variables ** ** Set to prevent backoffice processing from ever entering sleep or ** otherwise taking a long time to complete. Set this when a user-visible ** process might need to wait for backoffice to complete. */ static int backofficeNoDelay = 0; /* This variable is set to the name of a database on which backoffice ** should run if backoffice process is needed. It is set by the ** backoffice_check_if_needed() routine which must be run while the database ** file is open. Later, after the database is closed, the ** backoffice_run_if_needed() will consult this variable to see if it ** should be a no-op. */ static char *backofficeDb = 0; /* End of state variables ****************************************************************************/ /* ** This function emits a diagnostic message related to the processing in ** this module. */ #if defined(_WIN32) # define BKOFCE_ALWAYS_TRACE (1) extern void sqlite3_win32_write_debug(const char *, int); #else # define BKOFCE_ALWAYS_TRACE (0) #endif static void backofficeTrace(const char *zFormat, ...){ char *zMsg = 0; if( BKOFCE_ALWAYS_TRACE || g.fAnyTrace ){ va_list ap; va_start(ap, zFormat); zMsg = sqlite3_vmprintf(zFormat, ap); va_end(ap); #if defined(_WIN32) sqlite3_win32_write_debug(zMsg, -1); #endif } if( g.fAnyTrace ) fprintf(stderr, "%s", zMsg); if( zMsg ) sqlite3_free(zMsg); } /* ** Do not allow backoffice processes to sleep waiting on a timeslot. ** They must either do their work immediately or exit. ** ** In a perfect world, this interface would not exist, as there would ** never be a problem with waiting backoffice threads. But in some cases ** a backoffice will delay a UI thread, so we don't want them to run for ** longer than needed. */ void backoffice_no_delay(void){ backofficeNoDelay = 1; } /* ** Sleeps for the specified number of milliseconds -OR- until interrupted ** by another thread (if supported by the underlying platform). Non-zero ** will be returned if the sleep was interrupted. */ static int backofficeSleep(int milliseconds){ #if defined(_WIN32) assert( milliseconds>=0 ); if( SleepEx((DWORD)milliseconds, TRUE)==WAIT_IO_COMPLETION ){ return 1; } #else sqlite3_sleep(milliseconds); #endif return 0; } /* ** Parse a unsigned 64-bit integer from a string. Return a pointer ** to the character of z[] that occurs after the integer. */ static const char *backofficeParseInt(const char *z, sqlite3_uint64 *pVal){ *pVal = 0; if( z==0 ) return 0; while( fossil_isspace(z[0]) ){ z++; } while( fossil_isdigit(z[0]) ){ *pVal = (*pVal)*10 + z[0] - '0'; z++; } return z; } /* ** Read the "backoffice" property and parse it into a Lease object. ** ** The backoffice property should consist of four integers: ** ** (1) Process ID for the active backoffice process. ** (2) Time (seconds since 1970) for when the active backoffice ** lease expires. ** (3) Process ID for the on-deck backoffice process. ** (4) Time when the on-deck process should expire. ** ** No other process should start active backoffice processing until ** process (1) no longer exists and the current time exceeds (2). */ static void backofficeReadLease(Lease *pLease){ Stmt q; memset(pLease, 0, sizeof(*pLease)); db_prepare(&q, "SELECT value FROM repository.config" " WHERE name='backoffice'"); if( db_step(&q)==SQLITE_ROW ){ const char *z = db_column_text(&q,0); z = backofficeParseInt(z, &pLease->idCurrent); z = backofficeParseInt(z, &pLease->tmCurrent); z = backofficeParseInt(z, &pLease->idNext); backofficeParseInt(z, &pLease->tmNext); } db_finalize(&q); } /* ** Return a string that describes how long it has been since the ** last backoffice run. The string is obtained from fossil_malloc(). */ char *backoffice_last_run(void){ Lease x; sqlite3_uint64 tmNow; double rAge; backofficeReadLease(&x); tmNow = time(0); if( x.tmCurrent==0 ){ return fossil_strdup("never"); } if( tmNow<=(x.tmCurrent-BKOFCE_LEASE_TIME) ){ return fossil_strdup("moments ago"); } rAge = (tmNow - (x.tmCurrent-BKOFCE_LEASE_TIME))/86400.0; return mprintf("%z ago", human_readable_age(rAge)); } /* ** Write a lease to the backoffice property */ static void backofficeWriteLease(Lease *pLease){ db_multi_exec( "REPLACE INTO repository.config(name,value,mtime)" " VALUES('backoffice','%lld %lld %lld %lld',now())", pLease->idCurrent, pLease->tmCurrent, pLease->idNext, pLease->tmNext); } /* ** Check to see if the specified Win32 process is still alive. It ** should be noted that even if this function returns non-zero, the ** process may die before another operation on it can be completed. */ #if defined(_WIN32) #ifndef PROCESS_QUERY_LIMITED_INFORMATION # define PROCESS_QUERY_LIMITED_INFORMATION (0x1000) #endif static int backofficeWin32ProcessExists(DWORD dwProcessId){ HANDLE hProcess; hProcess = OpenProcess(PROCESS_QUERY_LIMITED_INFORMATION,FALSE,dwProcessId); if( hProcess==NULL ) return 0; CloseHandle(hProcess); return 1; } #endif /* ** Check to see if the process identified by pid is alive. If ** we cannot prove the the process is dead, return true. */ static int backofficeProcessExists(sqlite3_uint64 pid){ #if defined(_WIN32) return pid>0 && backofficeWin32ProcessExists((DWORD)pid)!=0; #else return pid>0 && kill((pid_t)pid, 0)==0; #endif } /* ** Check to see if the process identified by pid has finished. If ** we cannot prove the the process is still running, return true. */ static int backofficeProcessDone(sqlite3_uint64 pid){ #if defined(_WIN32) return pid<=0 || backofficeWin32ProcessExists((DWORD)pid)==0; #else return pid<=0 || kill((pid_t)pid, 0)!=0; #endif } /* ** Return a process id number for the current process */ static sqlite3_uint64 backofficeProcessId(void){ return (sqlite3_uint64)GETPID(); } /* ** COMMAND: test-process-id ** ** Usage: %fossil [--sleep N] PROCESS-ID ... ** ** Show the current process id, and also tell whether or not all other ** processes IDs on the command line are running or not. If the --sleep N ** option is provide, then sleep for N seconds before exiting. */ void test_process_id_command(void){ const char *zSleep = find_option("sleep",0,1); int i; verify_all_options(); fossil_print("ProcessID for this process: %lld\n", backofficeProcessId()); if( zSleep ) sqlite3_sleep(1000*atoi(zSleep)); for(i=2; i<g.argc; i++){ sqlite3_uint64 x = (sqlite3_uint64)atoi(g.argv[i]); fossil_print("ProcessId %lld: exists %d done %d\n", x, backofficeProcessExists(x), backofficeProcessDone(x)); } } /* ** COMMAND: test-backoffice-lease ** ** Usage: %fossil test-backoffice-lease ** ** Print out information about the backoffice "lease" entry in the ** config table that controls whether or not backoffice should run. */ void test_backoffice_lease(void){ sqlite3_int64 tmNow = time(0); Lease x; const char *zLease; db_find_and_open_repository(0,0); verify_all_options(); zLease = db_get("backoffice",""); fossil_print("now: %lld\n", tmNow); fossil_print("lease: \"%s\"\n", zLease); backofficeReadLease(&x); fossil_print("idCurrent: %-20lld", x.idCurrent); if( backofficeProcessExists(x.idCurrent) ) fossil_print(" (exists)"); if( backofficeProcessDone(x.idCurrent) ) fossil_print(" (done)"); fossil_print("\n"); fossil_print("tmCurrent: %-20lld", x.tmCurrent); if( x.tmCurrent>0 ){ fossil_print(" (now%+d)\n",x.tmCurrent-tmNow); }else{ fossil_print("\n"); } fossil_print("idNext: %-20lld", x.idNext); if( backofficeProcessExists(x.idNext) ) fossil_print(" (exists)"); if( backofficeProcessDone(x.idNext) ) fossil_print(" (done)"); fossil_print("\n"); fossil_print("tmNext: %-20lld", x.tmNext); if( x.tmNext>0 ){ fossil_print(" (now%+d)\n",x.tmNext-tmNow); }else{ fossil_print("\n"); } } /* ** If backoffice processing is needed set the backofficeDb variable to the ** name of the database file. If no backoffice processing is needed, ** this routine makes no changes to state. */ void backoffice_check_if_needed(void){ Lease x; sqlite3_uint64 tmNow; if( backofficeDb ) return; if( g.zRepositoryName==0 ) return; if( g.db==0 ) return; tmNow = time(0); backofficeReadLease(&x); if( x.tmNext>=tmNow && backofficeProcessExists(x.idNext) ){ /* Another backoffice process is already queued up to run. This ** process does not need to do any backoffice work. */ return; }else{ /* We need to run backup to be (at a minimum) on-deck */ backofficeDb = fossil_strdup(g.zRepositoryName); } } /* ** Check for errors prior to running backoffice_thread() or backoffice_run(). */ static void backoffice_error_check_one(int *pOnce){ if( *pOnce ){ fossil_panic("multiple calls to backoffice()"); } *pOnce = 1; if( g.db==0 ){ fossil_panic("database not open for backoffice processing"); } if( db_transaction_nesting_depth()!=0 ){ fossil_panic("transaction %s not closed prior to backoffice processing", db_transaction_start_point()); } } /* This is the main loop for backoffice processing. ** ** If another process is already working as the current backoffice and ** the on-deck backoffice, then this routine returns very quickly ** without doing any work. ** ** If no backoffice processes are running at all, this routine becomes ** the main backoffice. ** ** If a primary backoffice is running, but a on-deck backoffice is ** needed, this routine becomes that on-desk backoffice. */ static void backoffice_thread(void){ Lease x; sqlite3_uint64 tmNow; sqlite3_uint64 idSelf; int lastWarning = 0; int warningDelay = 30; static int once = 0; backoffice_error_check_one(&once); idSelf = backofficeProcessId(); while(1){ tmNow = time(0); db_begin_write(); backofficeReadLease(&x); if( x.tmNext>=tmNow && x.idNext!=idSelf && backofficeProcessExists(x.idNext) ){ /* Another backoffice process is already queued up to run. This ** process does not need to do any backoffice work and can stop ** immediately. */ db_end_transaction(0); break; } if( x.tmCurrent<tmNow && backofficeProcessDone(x.idCurrent) ){ /* This process can start doing backoffice work immediately */ x.idCurrent = idSelf; x.tmCurrent = tmNow + BKOFCE_LEASE_TIME; x.idNext = 0; x.tmNext = 0; backofficeWriteLease(&x); db_end_transaction(0); backofficeTrace("/***** Begin Backoffice Processing %d *****/\n", GETPID()); backoffice_work(); break; } if( backofficeNoDelay || db_get_boolean("backoffice-nodelay",0) ){ /* If the no-delay flag is set, exit immediately rather than queuing ** up. Assume that some future request will come along and handle any ** necessary backoffice work. */ db_end_transaction(0); break; } /* This process needs to queue up and wait for the current lease ** to expire before continuing. */ x.idNext = idSelf; x.tmNext = (tmNow>x.tmCurrent ? tmNow : x.tmCurrent) + BKOFCE_LEASE_TIME; backofficeWriteLease(&x); db_end_transaction(0); backofficeTrace("/***** Backoffice On-deck %d *****/\n", GETPID()); if( x.tmCurrent >= tmNow ){ if( backofficeSleep(1000*(x.tmCurrent - tmNow + 1)) ){ /* The sleep was interrupted by a signal from another thread. */ backofficeTrace("/***** Backoffice Interrupt %d *****/\n", GETPID()); db_end_transaction(0); break; } }else{ if( lastWarning+warningDelay < tmNow ){ fossil_warning( "backoffice process %lld still running after %d seconds", x.idCurrent, (int)(BKOFCE_LEASE_TIME + tmNow - x.tmCurrent)); lastWarning = tmNow; warningDelay *= 2; } if( backofficeSleep(1000) ){ /* The sleep was interrupted by a signal from another thread. */ backofficeTrace("/***** Backoffice Interrupt %d *****/\n", GETPID()); db_end_transaction(0); break; } } } return; } /* ** This routine runs to do the backoffice processing. When adding new ** backoffice processing tasks, add them here. */ void backoffice_work(void){ /* Log the backoffice run for testing purposes. For production deployments ** the "backoffice-logfile" property should be unset and the following code ** should be a no-op. */ char *zLog = db_get("backoffice-logfile",0); if( zLog && zLog[0] ){ FILE *pLog = fossil_fopen(zLog, "a"); if( pLog ){ char *zDate = db_text(0, "SELECT datetime('now');"); fprintf(pLog, "%s (%d) backoffice running\n", zDate, GETPID()); fclose(pLog); } } /* Here is where the actual work of the backoffice happens */ email_backoffice(0); smtp_cleanup(); } /* ** COMMAND: backoffice ** ** Usage: backoffice [-R repository] ** ** Run backoffice processing. This might be done by a cron job or ** similar to make sure backoffice processing happens periodically. */ void backoffice_command(void){ if( find_option("trace",0,0)!=0 ) g.fAnyTrace = 1; db_find_and_open_repository(0,0); verify_all_options(); backoffice_thread(); } /* ** This is the main interface to backoffice from the rest of the system. ** This routine launches either backoffice_thread() directly or as a ** subprocess. */ void backoffice_run_if_needed(void){ if( backofficeDb==0 ) return; if( strcmp(backofficeDb,"x")==0 ) return; if( g.db ) return; if( g.repositoryOpen ) return; #if defined(_WIN32) { int i; intptr_t x; char *argv[4]; wchar_t *ax[5]; argv[0] = g.nameOfExe; argv[1] = "backoffice"; argv[2] = "-R"; argv[3] = backofficeDb; ax[4] = 0; for(i=0; i<=3; i++) ax[i] = fossil_utf8_to_unicode(argv[i]); x = _wspawnv(_P_NOWAIT, ax[0], (const wchar_t * const *)ax); for(i=0; i<=3; i++) fossil_unicode_free(ax[i]); backofficeTrace( "/***** Subprocess %d creates backoffice child %lu *****/\n", GETPID(), GetProcessId((HANDLE)x)); if( x>=0 ) return; } #else /* unix */ { pid_t pid = fork(); if( pid>0 ){ /* This is the parent in a successful fork(). Return immediately. */ backofficeTrace( "/***** Subprocess %d creates backoffice child %d *****/\n", GETPID(), (int)pid); return; } if( pid==0 ){ /* This is the child of a successful fork(). Run backoffice. */ int i; setsid(); for(i=0; i<=2; i++){ close(i); open("/dev/null", O_RDWR); } for(i=3; i<100; i++){ close(i); } db_open_repository(backofficeDb); backofficeDb = "x"; backoffice_thread(); db_close(1); backofficeTrace("/***** Backoffice Child %d exits *****/\n", GETPID()); exit(0); } fossil_warning("backoffice process %d fork failed, errno %d", GETPID(), errno); } #endif /* Fork() failed or is unavailable. Run backoffice in this process, but ** do so with the no-delay setting. */ backofficeNoDelay = 1; db_open_repository(backofficeDb); backofficeDb = "x"; backoffice_thread(); db_close(1); } |
Changes to src/blob.c.
︙ | ︙ | |||
291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 | blob_panic(); } } memcpy(&pBlob->aData[pBlob->nUsed], aData, nData); pBlob->nUsed += nData; pBlob->aData[pBlob->nUsed] = 0; /* Blobs are always nul-terminated */ } /* ** Copy a blob */ void blob_copy(Blob *pTo, Blob *pFrom){ blob_is_init(pFrom); blob_zero(pTo); blob_append(pTo, blob_buffer(pFrom), blob_size(pFrom)); } /* ** Return a pointer to a null-terminated string for a blob. */ char *blob_str(Blob *p){ blob_is_init(p); if( p->nUsed==0 ){ | > > > > > > > > > > > > > | | | 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 | blob_panic(); } } memcpy(&pBlob->aData[pBlob->nUsed], aData, nData); pBlob->nUsed += nData; pBlob->aData[pBlob->nUsed] = 0; /* Blobs are always nul-terminated */ } /* ** Append a single character to the blob */ void blob_append_char(Blob *pBlob, char c){ if( pBlob->nUsed+1 >= pBlob->nAlloc ){ pBlob->xRealloc(pBlob, pBlob->nUsed + pBlob->nAlloc + 100); if( pBlob->nUsed + 1 >= pBlob->nAlloc ){ blob_panic(); } } pBlob->aData[pBlob->nUsed++] = c; } /* ** Copy a blob */ void blob_copy(Blob *pTo, Blob *pFrom){ blob_is_init(pFrom); blob_zero(pTo); blob_append(pTo, blob_buffer(pFrom), blob_size(pFrom)); } /* ** Return a pointer to a null-terminated string for a blob. */ char *blob_str(Blob *p){ blob_is_init(p); if( p->nUsed==0 ){ blob_append_char(p, 0); /* NOTE: Changes nUsed. */ p->nUsed = 0; } if( p->aData[p->nUsed]!=0 ){ blob_materialize(p); } return p->aData; } /* ** Return a pointer to a null-terminated string for a blob that has ** been created using blob_append_sql() and not blob_appendf(). If ** text was ever added using blob_appendf() then throw an error. */ char *blob_sql_text(Blob *p){ blob_is_init(p); if( (p->blobFlags & BLOBFLAG_NotSQL) ){ fossil_panic("use of blob_appendf() to construct SQL text"); } return blob_str(p); } /* ** Return a pointer to a null-terminated string for a blob. |
︙ | ︙ | |||
478 479 480 481 482 483 484 485 486 487 488 489 490 491 | /* ** Rewind the cursor on a blob back to the beginning. */ void blob_rewind(Blob *p){ p->iCursor = 0; } /* ** Seek the cursor in a blob to the indicated offset. */ int blob_seek(Blob *p, int offset, int whence){ if( whence==BLOB_SEEK_SET ){ p->iCursor = offset; | > > > > > > > | 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 | /* ** Rewind the cursor on a blob back to the beginning. */ void blob_rewind(Blob *p){ p->iCursor = 0; } /* ** Truncate a blob back to zero length */ void blob_truncate(Blob *p, int sz){ if( sz>=0 && sz<p->nUsed ) p->nUsed = sz; } /* ** Seek the cursor in a blob to the indicated offset. */ int blob_seek(Blob *p, int offset, int whence){ if( whence==BLOB_SEEK_SET ){ p->iCursor = offset; |
︙ | ︙ | |||
651 652 653 654 655 656 657 658 659 660 661 662 663 664 | i++; } if( pTo ){ blob_append(pTo, &pFrom->aData[pFrom->iCursor], i - pFrom->iCursor); } pFrom->iCursor = i; } /* ** Return true if the blob contains a valid base16 identifier artifact hash. ** ** The value returned is actually one of HNAME_SHA1 OR HNAME_K256 if the ** hash is valid. Both of these are non-zero and therefore "true". ** If the hash is not valid, then HNAME_ERROR is returned, which is zero or | > > > > > > > > > > | 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 | i++; } if( pTo ){ blob_append(pTo, &pFrom->aData[pFrom->iCursor], i - pFrom->iCursor); } pFrom->iCursor = i; } /* ** Ensure that the text in pBlob ends with '\n' */ void blob_add_final_newline(Blob *pBlob){ if( pBlob->nUsed<=0 ) return; if( pBlob->aData[pBlob->nUsed-1]!='\n' ){ blob_append_char(pBlob, '\n'); } } /* ** Return true if the blob contains a valid base16 identifier artifact hash. ** ** The value returned is actually one of HNAME_SHA1 OR HNAME_K256 if the ** hash is valid. Both of these are non-zero and therefore "true". ** If the hash is not valid, then HNAME_ERROR is returned, which is zero or |
︙ | ︙ | |||
1235 1236 1237 1238 1239 1240 1241 | zIn, blob_str(&bad), c); } if( !needEscape && !fossil_isalnum(c) && c!='/' && c!='.' && c!='_' ){ needEscape = 1; } } if( n>0 && !fossil_isspace(z[n-1]) ){ | | | | | 1265 1266 1267 1268 1269 1270 1271 1272 1273 1274 1275 1276 1277 1278 1279 1280 1281 1282 1283 1284 | zIn, blob_str(&bad), c); } if( !needEscape && !fossil_isalnum(c) && c!='/' && c!='.' && c!='_' ){ needEscape = 1; } } if( n>0 && !fossil_isspace(z[n-1]) ){ blob_append_char(pBlob, ' '); } if( needEscape ) blob_append_char(pBlob, cQuote); if( zIn[0]=='-' ) blob_append(pBlob, "./", 2); blob_append(pBlob, zIn, -1); if( needEscape ) blob_append_char(pBlob, cQuote); } /* ** A read(2)-like impl for the Blob class. Reads (copies) up to nLen ** bytes from pIn, starting at position pIn->iCursor, and copies them ** to pDest (which must be valid memory at least nLen bytes long). ** |
︙ | ︙ | |||
1311 1312 1313 1314 1315 1316 1317 | /* swap bytes of unicode representation */ char zTemp = zUtf8[--i]; zUtf8[i] = zUtf8[i-1]; zUtf8[--i] = zTemp; } } /* Make sure the blob contains two terminating 0-bytes */ | | | 1341 1342 1343 1344 1345 1346 1347 1348 1349 1350 1351 1352 1353 1354 1355 | /* swap bytes of unicode representation */ char zTemp = zUtf8[--i]; zUtf8[i] = zUtf8[i-1]; zUtf8[--i] = zTemp; } } /* Make sure the blob contains two terminating 0-bytes */ blob_append_char(pBlob, 0); zUtf8 = blob_str(pBlob) + bomSize; zUtf8 = fossil_unicode_to_utf8(zUtf8); blob_set_dynamic(pBlob, zUtf8); }else if( useMbcs && invalid_utf8(pBlob) ){ #if defined(_WIN32) || defined(__CYGWIN__) zUtf8 = fossil_mbcs_to_utf8(blob_str(pBlob)); blob_reset(pBlob); |
︙ | ︙ |
Changes to src/browse.c.
︙ | ︙ | |||
119 120 121 122 123 124 125 | ** type=TYPE TYPE=flat: use this display ** TYPE=tree: use the /tree display instead */ void page_dir(void){ char *zD = fossil_strdup(P("name")); int nD = zD ? strlen(zD)+1 : 0; int mxLen; | < < | 119 120 121 122 123 124 125 126 127 128 129 130 131 132 | ** type=TYPE TYPE=flat: use this display ** TYPE=tree: use the /tree display instead */ void page_dir(void){ char *zD = fossil_strdup(P("name")); int nD = zD ? strlen(zD)+1 : 0; int mxLen; char *zPrefix; Stmt q; const char *zCI = P("ci"); int rid = 0; char *zUuid = 0; Blob dirname; Manifest *pM = 0; |
︙ | ︙ | |||
267 268 269 270 271 272 273 | ); } /* Generate a multi-column table listing the contents of zD[] ** directory. */ mxLen = db_int(12, "SELECT max(length(x)) FROM localfiles /*scan*/"); | < < < < < > | < < < < < < | | 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 | ); } /* Generate a multi-column table listing the contents of zD[] ** directory. */ mxLen = db_int(12, "SELECT max(length(x)) FROM localfiles /*scan*/"); if( mxLen<12 ) mxLen = 12; db_prepare(&q, "SELECT x, u FROM localfiles ORDER BY x /*scan*/"); @ <div class="columns" style="column-width: %d(mxLen+(mxLen+9)/10)ex;"> @ <ul class="browser"> while( db_step(&q)==SQLITE_ROW ){ const char *zFN; zFN = db_column_text(&q, 0); if( zFN[0]=='/' ){ zFN++; @ <li class="dir">%z(href("%s%T",zSubdirLink,zFN))%h(zFN)</a></li> }else{ const char *zLink; if( zCI ){ const char *zUuid = db_column_text(&q, 1); zLink = href("%R/artifact/%!S",zUuid); }else{ zLink = href("%R/finfo?name=%T%T",zPrefix,zFN); } @ <li class="%z(fileext_class(zFN))">%z(zLink)%h(zFN)</a></li> } } db_finalize(&q); manifest_destroy(pM); @ </ul></div> /* If the directory contains a readme file, then display its content below ** the list of files */ db_prepare(&q, "SELECT x, u FROM localfiles" " WHERE x COLLATE nocase IN" |
︙ | ︙ |
Changes to src/bundle.c.
︙ | ︙ | |||
58 59 60 61 62 63 64 | char *zErrMsg = 0; char *zSql; if( !doInit && file_size(zFile, ExtFILE)<0 ){ fossil_fatal("no such file: %s", zFile); } assert( g.db ); zSql = sqlite3_mprintf("ATTACH %Q AS %Q", zFile, zBName); | | | | | 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 | char *zErrMsg = 0; char *zSql; if( !doInit && file_size(zFile, ExtFILE)<0 ){ fossil_fatal("no such file: %s", zFile); } assert( g.db ); zSql = sqlite3_mprintf("ATTACH %Q AS %Q", zFile, zBName); if( zSql==0 ) fossil_panic("out of memory"); rc = sqlite3_exec(g.db, zSql, 0, 0, &zErrMsg); sqlite3_free(zSql); if( rc!=SQLITE_OK || zErrMsg ){ if( zErrMsg==0 ) zErrMsg = (char*)sqlite3_errmsg(g.db); fossil_fatal("not a valid bundle: %s", zFile); } if( doInit ){ db_multi_exec(zBundleInit /*works-like:"%w%w"*/, zBName, zBName); }else{ sqlite3_stmt *pStmt; zSql = sqlite3_mprintf("SELECT bcname, bcvalue" " FROM \"%w\".bconfig", zBName); if( zSql==0 ) fossil_panic("out of memory"); rc = sqlite3_prepare(g.db, zSql, -1, &pStmt, 0); if( rc ) fossil_fatal("not a valid bundle: %s", zFile); sqlite3_free(zSql); sqlite3_finalize(pStmt); zSql = sqlite3_mprintf("SELECT blobid, uuid, sz, delta, notes, data" " FROM \"%w\".bblob", zBName); if( zSql==0 ) fossil_panic("out of memory"); rc = sqlite3_prepare(g.db, zSql, -1, &pStmt, 0); if( rc ) fossil_fatal("not a valid bundle: %s", zFile); sqlite3_free(zSql); sqlite3_finalize(pStmt); } } |
︙ | ︙ |
Changes to src/cache.c.
︙ | ︙ | |||
59 60 61 62 63 64 65 | } rc = sqlite3_open(zDbName, &db); fossil_free(zDbName); if( rc ){ sqlite3_close(db); return 0; } | > > | | | | | | | | | | | | | | > | | | > | 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 | } rc = sqlite3_open(zDbName, &db); fossil_free(zDbName); if( rc ){ sqlite3_close(db); return 0; } sqlite3_busy_timeout(db, 5000); if( sqlite3_table_column_metadata(db,0,"blob","key",0,0,0,0,0)!=SQLITE_OK ){ rc = sqlite3_exec(db, "PRAGMA page_size=8192;" "CREATE TABLE IF NOT EXISTS blob(id INTEGER PRIMARY KEY, data BLOB);" "CREATE TABLE IF NOT EXISTS cache(" "key TEXT PRIMARY KEY," /* Key used to access the cache */ "id INT REFERENCES blob," /* The cache content */ "sz INT," /* Size of content in bytes */ "tm INT," /* Last access time (unix timestampe) */ "nref INT" /* Number of uses */ ");" "CREATE TRIGGER IF NOT EXISTS cacheDel AFTER DELETE ON cache BEGIN" " DELETE FROM blob WHERE id=OLD.id;" "END;", 0, 0, 0 ); if( rc!=SQLITE_OK ){ sqlite3_close(db); return 0; } } return db; } /* ** Attempt to construct a prepared statement for the cache database. */ |
︙ | ︙ |
Added src/capabilities.c.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 | /* ** Copyright (c) 2018 D. Richard Hipp ** ** This program is free software; you can redistribute it and/or ** modify it under the terms of the Simplified BSD License (also ** known as the "2-Clause License" or "FreeBSD License".) ** ** This program is distributed in the hope that it will be useful, ** but without any warranty; without even the implied warranty of ** merchantability or fitness for a particular purpose. ** ** Author contact information: ** drh@hwaci.com ** http://www.hwaci.com/drh/ ** ******************************************************************************* ** ** This file contains code used managing user capability strings. */ #include "config.h" #include "capabilities.h" #include <assert.h> #if INTERFACE /* ** A capability string object holds all defined capabilities in a ** vector format that is subject to boolean operations. */ struct CapabilityString { unsigned char x[128]; }; #endif /* ** Add capabilities to a CapabilityString. If pIn is NULL, then create ** a new capability string. ** ** Call capability_free() on the allocated CapabilityString object to ** deallocate. */ CapabilityString *capability_add(CapabilityString *pIn, const char *zCap){ int c; int i; if( pIn==0 ){ pIn = fossil_malloc( sizeof(*pIn) ); memset(pIn, 0, sizeof(*pIn)); } if( zCap ){ for(i=0; (c = zCap[i])!=0; i++){ if( c>='0' && c<='z' ) pIn->x[c] = 1; } } return pIn; } /* ** Remove capabilities from a CapabilityString. */ CapabilityString *capability_remove(CapabilityString *pIn, const char *zCap){ int c; int i; if( pIn==0 ){ pIn = fossil_malloc( sizeof(*pIn) ); memset(pIn, 0, sizeof(*pIn)); } if( zCap ){ for(i=0; (c = zCap[i])!=0; i++){ if( c>='0' && c<='z' ) pIn->x[c] = 0; } } return pIn; } /* ** Return true if any of the capabilities in zNeeded are found in pCap */ int capability_has_any(CapabilityString *p, const char *zNeeded){ if( p==0 ) return 0; if( zNeeded==0 ) return 0; while( zNeeded[0] ){ int c = zNeeded[0]; if( fossil_isalnum(c) && p->x[c] ) return 1; zNeeded++; } return 0; } /* ** Delete a CapabilityString object. */ void capability_free(CapabilityString *p){ fossil_free(p); } /* ** Expand the capability string by including all capabilities for ** special users "nobody" and "anonymous". Also include "reader" ** if "u" is present and "developer" if "v" is present. */ void capability_expand(CapabilityString *pIn){ static char *zNobody = 0; static char *zAnon = 0; static char *zReader = 0; static char *zDev = 0; int doneV = 0; if( pIn==0 ){ fossil_free(zNobody); zNobody = 0; fossil_free(zAnon); zAnon = 0; fossil_free(zReader); zReader = 0; fossil_free(zDev); zDev = 0; return; } if( zNobody==0 ){ zNobody = db_text(0, "SELECT cap FROM user WHERE login='nobody'"); zAnon = db_text(0, "SELECT cap FROM user WHERE login='anonymous'"); zReader = db_text(0, "SELECT cap FROM user WHERE login='reader'"); zDev = db_text(0, "SELECT cap FROM user WHERE login='developer'"); } pIn = capability_add(pIn, zAnon); pIn = capability_add(pIn, zNobody); if( pIn->x['v'] ){ pIn = capability_add(pIn, zDev); doneV = 1; } if( pIn->x['u'] ){ pIn = capability_add(pIn, zReader); if( pIn->x['v'] && !doneV ){ pIn = capability_add(pIn, zDev); } } } /* ** Render a capability string in canonical string format. Space to hold ** the returned string is obtained from fossil_malloc() can should be freed ** by the caller. */ char *capability_string(CapabilityString *p){ Blob out; int i; int j = 0; char buf[100]; blob_init(&out, 0, 0); for(i='a'; i<='z'; i++){ if( p->x[i] ) buf[j++] = i; } for(i='0'; i<='9'; i++){ if( p->x[i] ) buf[j++] = i; } for(i='A'; i<='Z'; i++){ if( p->x[i] ) buf[j++] = i; } buf[j] = 0; return fossil_strdup(buf); } /* ** The next two routines implement an aggregate SQL function that ** takes multiple capability strings and in the end returns their ** union. Example usage: ** ** SELECT capunion(cap) FROM user WHERE login IN ('nobody','anonymous'); */ void capability_union_step( sqlite3_context *context, int argc, sqlite3_value **argv ){ CapabilityString *p; const char *zIn; zIn = (const char*)sqlite3_value_text(argv[0]); if( zIn==0 ) return; p = (CapabilityString*)sqlite3_aggregate_context(context, sizeof(*p)); p = capability_add(p, zIn); } void capability_union_finalize(sqlite3_context *context){ CapabilityString *p; p = sqlite3_aggregate_context(context, 0); if( p ){ char *zOut = capability_string(p); sqlite3_result_text(context, zOut, -1, fossil_free); } } /* ** The next routines takes the raw USER.CAP field and expands it with ** capabilities from special users. Example: ** ** SELECT fullcap(cap) FROM user WHERE login=?1 */ void capability_fullcap( sqlite3_context *context, int argc, sqlite3_value **argv ){ CapabilityString *p; const char *zIn; char *zOut; zIn = (const char*)sqlite3_value_text(argv[0]); if( zIn==0 ) zIn = ""; p = capability_add(0, zIn); capability_expand(p); zOut = capability_string(p); sqlite3_result_text(context, zOut, -1, fossil_free); capability_free(p); } /* ** Generate HTML that lists all of the capability letters together with ** a brief summary of what each letter means. */ void capabilities_table(void){ @ <table> @ <tr><th valign="top">a</th> @ <td><i>Admin:</i> Create and delete users</td></tr> @ <tr><th valign="top">b</th> @ <td><i>Attach:</i> Add attachments to wiki or tickets</td></tr> @ <tr><th valign="top">c</th> @ <td><i>Append-Tkt:</i> Append to tickets</td></tr> @ <tr><th valign="top">d</th> @ <td><i>Delete:</i> Delete wiki and tickets</td></tr> @ <tr><th valign="top">e</th> @ <td><i>View-PII:</i> \ @ View sensitive data such as email addresses</td></tr> @ <tr><th valign="top">f</th> @ <td><i>New-Wiki:</i> Create new wiki pages</td></tr> @ <tr><th valign="top">g</th> @ <td><i>Clone:</i> Clone the repository</td></tr> @ <tr><th valign="top">h</th> @ <td><i>Hyperlinks:</i> Show hyperlinks to detailed @ repository history</td></tr> @ <tr><th valign="top">i</th> @ <td><i>Check-In:</i> Commit new versions in the repository</td></tr> @ <tr><th valign="top">j</th> @ <td><i>Read-Wiki:</i> View wiki pages</td></tr> @ <tr><th valign="top">k</th> @ <td><i>Write-Wiki:</i> Edit wiki pages</td></tr> @ <tr><th valign="top">l</th> @ <td><i>Mod-Wiki:</i> Moderator for wiki pages</td></tr> @ <tr><th valign="top">m</th> @ <td><i>Append-Wiki:</i> Append to wiki pages</td></tr> @ <tr><th valign="top">n</th> @ <td><i>New-Tkt:</i> Create new tickets</td></tr> @ <tr><th valign="top">o</th> @ <td><i>Check-Out:</i> Check out versions</td></tr> @ <tr><th valign="top">p</th> @ <td><i>Password:</i> Change your own password</td></tr> @ <tr><th valign="top">q</th> @ <td><i>Mod-Tkt:</i> Moderator for tickets</td></tr> @ <tr><th valign="top">r</th> @ <td><i>Read-Tkt:</i> View tickets</td></tr> @ <tr><th valign="top">s</th> @ <td><i>Setup/Super-user:</i> Setup and configure this website</td></tr> @ <tr><th valign="top">t</th> @ <td><i>Tkt-Report:</i> Create new bug summary reports</td></tr> @ <tr><th valign="top">u</th> @ <td><i>Reader:</i> Inherit privileges of @ user <tt>reader</tt></td></tr> @ <tr><th valign="top">v</th> @ <td><i>Developer:</i> Inherit privileges of @ user <tt>developer</tt></td></tr> @ <tr><th valign="top">w</th> @ <td><i>Write-Tkt:</i> Edit tickets</td></tr> @ <tr><th valign="top">x</th> @ <td><i>Private:</i> Push and/or pull private branches</td></tr> @ <tr><th valign="top">y</th> @ <td><i>Write-Unver:</i> Push unversioned files</td></tr> @ <tr><th valign="top">z</th> @ <td><i>Zip download:</i> Download a ZIP archive or tarball</td></tr> @ <tr><th valign="top">2</th> @ <td><i>Forum-Read:</i> Read forum posts by others </td></tr> @ <tr><th valign="top">3</th> @ <td><i>Forum-Append:</i> Add new forum posts</td></tr> @ <tr><th valign="top">4</th> @ <td><i>Forum-Trusted:</i> Add pre-approved forum posts </td></tr> @ <tr><th valign="top">5</th> @ <td><i>Forum-Moderator:</i> Approve or disapprove forum posts</td></tr> @ <tr><th valign="top">6</th> @ <td><i>Forum-Supervisor:</i> \ @ Forum administrator: Set or remove capability "4" for other users @ <tr><th valign="top">7</th> @ <td><i>Email-Alerts:</i> Sign up for email nofications</td></tr> @ <tr><th valign="top">A</th> @ <td><i>Announce:</i> Send announcements</td></tr> @ <tr><th valign="top">D</th> @ <td><i>Debug:</i> Enable debugging features</td></tr> @ </table> } /* ** Generate a "capability summary table" that shows the major capabilities ** against the various user categories. */ void capability_summary(void){ Stmt q; db_prepare(&q, "WITH t(id,seq) AS (VALUES('nobody',1),('anonymous',2),('reader',3)," "('developer',4))" " SELECT id, fullcap(user.cap),seq,1" " FROM t LEFT JOIN user ON t.id=user.login" " UNION ALL" " SELECT 'New User Default', fullcap(%Q), 10, 1" " UNION ALL" " SELECT 'Regular User', fullcap(capunion(cap)), 20, count(*) FROM user" " WHERE cap NOT GLOB '*[as]*'" " UNION ALL" " SELECT 'Adminstator', fullcap(capunion(cap)), 30, count(*) FROM user" " WHERE cap GLOB '*[as]*'" " ORDER BY 3 ASC", db_get("default-perms","") ); @ <table id='capabilitySummary' cellpadding="0" cellspacing="0" border="1"> @ <tr><th> <th>Code<th>Forum<th>Tickets<th>Wiki\ @ <th>Unversioned Content</th></tr> while( db_step(&q)==SQLITE_ROW ){ const char *zId = db_column_text(&q, 0); const char *zCap = db_column_text(&q, 1); int n = db_column_int(&q, 3); int eType; static const char *azType[] = { "off", "read", "write" }; static const char *azClass[] = { "capsumOff", "capsumRead", "capsumWrite" }; if( n==0 ) continue; /* Code */ if( db_column_int(&q,2)<10 ){ @ <tr><th align="right"><tt>"%h(zId)"</tt></th> }else if( n>1 ){ @ <tr><th align="right">%d(n) %h(zId)s</th> }else{ @ <tr><th align="right">%h(zId)</th> } if( sqlite3_strglob("*[asi]*",zCap)==0 ){ eType = 2; }else if( sqlite3_strglob("*[oz]*",zCap)==0 ){ eType = 1; }else{ eType = 0; } @ <td class="%s(azClass[eType])">%s(azType[eType])</td> /* Forum */ if( sqlite3_strglob("*[as3456]*",zCap)==0 ){ eType = 2; }else if( sqlite3_strglob("*2*",zCap)==0 ){ eType = 1; }else{ eType = 0; } @ <td class="%s(azClass[eType])">%s(azType[eType])</td> /* Ticket */ if( sqlite3_strglob("*[ascdnqtw]*",zCap)==0 ){ eType = 2; }else if( sqlite3_strglob("*r*",zCap)==0 ){ eType = 1; }else{ eType = 0; } @ <td class="%s(azClass[eType])">%s(azType[eType])</td> /* Wiki */ if( sqlite3_strglob("*[asdfklm]*",zCap)==0 ){ eType = 2; }else if( sqlite3_strglob("*j*",zCap)==0 ){ eType = 1; }else{ eType = 0; } @ <td class="%s(azClass[eType])">%s(azType[eType])</td> /* Unversioned */ if( sqlite3_strglob("*y*",zCap)==0 ){ eType = 2; }else if( sqlite3_strglob("*[ioas]*",zCap)==0 ){ eType = 1; }else{ eType = 0; } @ <td class="%s(azClass[eType])">%s(azType[eType])</td> } db_finalize(&q); @ </table> } |
Changes to src/captcha.c.
︙ | ︙ | |||
495 496 497 498 499 500 501 | ** ** If no captcha is required or if the correct captcha is supplied, return ** true (non-zero). ** ** The query parameters examined are "captchaseed" for the seed value and ** "captcha" for text that the user types in response to the captcha prompt. */ | | | | 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 | ** ** If no captcha is required or if the correct captcha is supplied, return ** true (non-zero). ** ** The query parameters examined are "captchaseed" for the seed value and ** "captcha" for text that the user types in response to the captcha prompt. */ int captcha_is_correct(int bAlwaysNeeded){ const char *zSeed; const char *zEntered; const char *zDecode; char z[30]; int i; if( !bAlwaysNeeded && !captcha_needed() ){ return 1; /* No captcha needed */ } zSeed = P("captchaseed"); if( zSeed==0 ) return 0; zEntered = P("captcha"); if( zEntered==0 || strlen(zEntered)!=8 ) return 0; zDecode = captcha_decode((unsigned int)atoi(zSeed)); |
︙ | ︙ | |||
591 592 593 594 595 596 597 | return 0; } } #endif zCookieName = mprintf("fossil-cc-%.10s", db_get("project-code","x")); zCookieValue = P(zCookieName); if( zCookieValue && atoi(zCookieValue)==1 ) return 0; | | | 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 | return 0; } } #endif zCookieName = mprintf("fossil-cc-%.10s", db_get("project-code","x")); zCookieValue = P(zCookieName); if( zCookieValue && atoi(zCookieValue)==1 ) return 0; if( captcha_is_correct(0) ){ cgi_set_cookie(zCookieName, "1", login_cookie_path(), 8*3600); return 0; } /* This appears to be a spider. Offer the captcha */ style_header("Verification"); @ <form method='POST' action='%s(g.zPath)'> |
︙ | ︙ |
Changes to src/cgi.c.
︙ | ︙ | |||
54 55 56 57 58 59 60 61 62 63 64 65 66 67 | ** does the same except "y" is returned in place of NULL if there is not match. */ #define P(x) cgi_parameter((x),0) #define PD(x,y) cgi_parameter((x),(y)) #define PT(x) cgi_parameter_trimmed((x),0) #define PDT(x,y) cgi_parameter_trimmed((x),(y)) #define PB(x) cgi_parameter_boolean(x) /* ** Destinations for output text. */ #define CGI_HEADER 0 #define CGI_BODY 1 | > > | 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 | ** does the same except "y" is returned in place of NULL if there is not match. */ #define P(x) cgi_parameter((x),0) #define PD(x,y) cgi_parameter((x),(y)) #define PT(x) cgi_parameter_trimmed((x),0) #define PDT(x,y) cgi_parameter_trimmed((x),(y)) #define PB(x) cgi_parameter_boolean(x) #define PCK(x) cgi_parameter_checked(x,1) #define PIF(x,y) cgi_parameter_checked(x,y) /* ** Destinations for output text. */ #define CGI_HEADER 0 #define CGI_BODY 1 |
︙ | ︙ | |||
196 197 198 199 200 201 202 | ** Append text to the header of an HTTP reply */ void cgi_append_header(const char *zLine){ blob_append(&extraHeader, zLine, -1); } /* | | > | > | | 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 | ** Append text to the header of an HTTP reply */ void cgi_append_header(const char *zLine){ blob_append(&extraHeader, zLine, -1); } /* ** Set a cookie by queuing up the appropriate HTTP header output. If ** !g.isHTTP, this is a no-op. ** ** Zero lifetime implies a session cookie. */ void cgi_set_cookie( const char *zName, /* Name of the cookie */ const char *zValue, /* Value of the cookie. Automatically escaped */ const char *zPath, /* Path cookie applies to. NULL means "/" */ int lifetime /* Expiration of the cookie in seconds from now */ ){ char const *zSecure = ""; if(!g.isHTTP) return /* e.g. JSON CLI mode, where g.zTop is not set */; else if( zPath==0 ){ zPath = g.zTop; if( zPath[0]==0 ) zPath = "/"; } if( g.zBaseURL!=0 && strncmp(g.zBaseURL, "https:", 6)==0 ){ zSecure = " secure;"; } if( lifetime>0 ){ |
︙ | ︙ | |||
230 231 232 233 234 235 236 237 238 239 240 241 242 243 | } /* ** Return true if the response should be sent with Content-Encoding: gzip. */ static int is_gzippable(void){ if( strstr(PD("HTTP_ACCEPT_ENCODING", ""), "gzip")==0 ) return 0; return strncmp(zContentType, "text/", 5)==0 || sqlite3_strglob("application/*xml", zContentType)==0 || sqlite3_strglob("application/*javascript", zContentType)==0; } /* | > | 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 | } /* ** Return true if the response should be sent with Content-Encoding: gzip. */ static int is_gzippable(void){ if( g.fNoHttpCompress ) return 0; if( strstr(PD("HTTP_ACCEPT_ENCODING", ""), "gzip")==0 ) return 0; return strncmp(zContentType, "text/", 5)==0 || sqlite3_strglob("application/*xml", zContentType)==0 || sqlite3_strglob("application/*javascript", zContentType)==0; } /* |
︙ | ︙ | |||
332 333 334 335 336 337 338 339 340 341 342 343 344 345 | if( size>0 ){ fwrite(blob_buffer(&cgiContent[i]), 1, size, g.httpOut); } } } fflush(g.httpOut); CGIDEBUG(("DONE\n")); } /* ** Do a redirect request to the URL given in the argument. ** ** The URL must be relative to the base of the fossil server. */ | > > > > > > > > | 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 | if( size>0 ){ fwrite(blob_buffer(&cgiContent[i]), 1, size, g.httpOut); } } } fflush(g.httpOut); CGIDEBUG(("DONE\n")); /* After the webpage has been sent, do any useful background ** processing. */ g.cgiOutput = 2; if( g.db!=0 && iReplyStatus==200 ){ backoffice_check_if_needed(); } } /* ** Do a redirect request to the URL given in the argument. ** ** The URL must be relative to the base of the fossil server. */ |
︙ | ︙ | |||
874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 | if( z==0 ){ if( pLog ) fclose(pLog); pLog = 0; return; } if( pLog==0 ){ char zFile[50]; unsigned r; sqlite3_randomness(sizeof(r), &r); sqlite3_snprintf(sizeof(zFile), zFile, "httplog-%08x.txt", r); pLog = fossil_fopen(zFile, "wb"); if( pLog ){ fprintf(stderr, "# open log on %s\n", zFile); }else{ fprintf(stderr, "# failed to open %s\n", zFile); return; } | > > > > | 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 | if( z==0 ){ if( pLog ) fclose(pLog); pLog = 0; return; } if( pLog==0 ){ char zFile[50]; #if defined(_WIN32) unsigned r; sqlite3_randomness(sizeof(r), &r); sqlite3_snprintf(sizeof(zFile), zFile, "httplog-%08x.txt", r); #else sqlite3_snprintf(sizeof(zFile), zFile, "httplog-%05d.txt", getpid()); #endif pLog = fossil_fopen(zFile, "wb"); if( pLog ){ fprintf(stderr, "# open log on %s\n", zFile); }else{ fprintf(stderr, "# failed to open %s\n", zFile); return; } |
︙ | ︙ | |||
1091 1092 1093 1094 1095 1096 1097 | } CGIDEBUG(("no-match [%s]\n", zName)); return zDefault; } /* ** Return the value of a CGI parameter with leading and trailing | | | | > > > > > | | > > > > > > > > > > > > > > > > > > > > > > > | 1108 1109 1110 1111 1112 1113 1114 1115 1116 1117 1118 1119 1120 1121 1122 1123 1124 1125 1126 1127 1128 1129 1130 1131 1132 1133 1134 1135 1136 1137 1138 1139 1140 1141 1142 1143 1144 1145 1146 1147 1148 1149 1150 1151 1152 1153 1154 1155 1156 1157 1158 1159 1160 1161 1162 1163 1164 1165 1166 1167 1168 1169 1170 1171 1172 1173 | } CGIDEBUG(("no-match [%s]\n", zName)); return zDefault; } /* ** Return the value of a CGI parameter with leading and trailing ** spaces removed and with internal \r\n changed to just \n */ char *cgi_parameter_trimmed(const char *zName, const char *zDefault){ const char *zIn; char *zOut, c; int i, j; zIn = cgi_parameter(zName, 0); if( zIn==0 ) zIn = zDefault; if( zIn==0 ) return 0; while( fossil_isspace(zIn[0]) ) zIn++; zOut = fossil_strdup(zIn); for(i=j=0; (c = zOut[i])!=0; i++){ if( c=='\r' && zOut[i+1]=='\n' ) continue; zOut[j++] = c; } zOut[j] = 0; while( j>0 && fossil_isspace(zOut[j-1]) ) zOut[--j] = 0; return zOut; } /* ** Return true if the CGI parameter zName exists and is not equal to 0, ** or "no" or "off". */ int cgi_parameter_boolean(const char *zName){ const char *zIn = cgi_parameter(zName, 0); if( zIn==0 ) return 0; return zIn[0]==0 || is_truth(zIn); } /* ** Return either an empty string "" or the string "checked" depending ** on whether or not parameter zName has value iValue. If parameter ** zName does not exist, that is assumed to be the same as value 0. ** ** This routine implements the PCK(x) and PIF(x,y) macros. The PIF(x,y) ** macro generateds " checked" if the value of parameter x equals integer y. ** PCK(x) is the same as PIF(x,1). These macros are used to generate ** the "checked" attribute on checkbox and radio controls of forms. */ const char *cgi_parameter_checked(const char *zName, int iValue){ const char *zIn = cgi_parameter(zName,0); int x; if( zIn==0 ){ x = 0; }else if( !fossil_isdigit(zIn[0]) ){ x = is_truth(zIn); }else{ x = atoi(zIn); } return x==iValue ? "checked" : ""; } /* ** Return the name of the i-th CGI parameter. Return NULL if there ** are fewer than i registered CGI parameters. */ const char *cgi_parameter_name(int i){ if( i>=0 && i<nUsedQP ){ |
︙ | ︙ | |||
1179 1180 1181 1182 1183 1184 1185 | /* ** Print all query parameters on standard output. Format the ** parameters as HTML. This is used for testing and debugging. ** ** Omit the values of the cookies unless showAll is true. */ | | > > > | > | 1224 1225 1226 1227 1228 1229 1230 1231 1232 1233 1234 1235 1236 1237 1238 1239 1240 1241 1242 1243 1244 1245 1246 1247 1248 1249 1250 1251 | /* ** Print all query parameters on standard output. Format the ** parameters as HTML. This is used for testing and debugging. ** ** Omit the values of the cookies unless showAll is true. */ void cgi_print_all(int showAll, int onConsole){ int i; cgi_parameter("",""); /* Force the parameters into sorted order */ for(i=0; i<nUsedQP; i++){ const char *zName = aParamQP[i].zName; if( !showAll ){ if( fossil_stricmp("HTTP_COOKIE",zName)==0 ) continue; if( fossil_strnicmp("fossil-",zName,7)==0 ) continue; } if( onConsole ){ fossil_trace("%s = %s\n", zName, aParamQP[i].zValue); }else{ cgi_printf("%h = %h <br />\n", zName, aParamQP[i].zValue); } } } /* ** Export all untagged query parameters (but not cookies or environment ** variables) as hidden values of a form. */ |
︙ | ︙ | |||
1336 1337 1338 1339 1340 1341 1342 1343 1344 1345 1346 1347 1348 1349 1350 1351 1352 1353 1354 1355 1356 | *zInput = 0; zInput++; while( fossil_isspace(*zInput) ){ zInput++; } } if( zLeftOver ){ *zLeftOver = zInput; } return zResult; } /* ** This routine handles a single HTTP request which is coming in on ** g.httpIn and which replies on g.httpOut ** ** The HTTP request is read from g.httpIn and is used to initialize ** entries in the cgi_parameter() hash, as if those entries were ** environment variables. A call to cgi_init() completes ** the setup. Once all the setup is finished, this procedure returns ** and subsequent code handles the actual generation of the webpage. */ void cgi_handle_http_request(const char *zIpAddr){ char *z, *zToken; int i; | > > > > > > > > > > > > > > > > > > > > > > > > > > < < | 1385 1386 1387 1388 1389 1390 1391 1392 1393 1394 1395 1396 1397 1398 1399 1400 1401 1402 1403 1404 1405 1406 1407 1408 1409 1410 1411 1412 1413 1414 1415 1416 1417 1418 1419 1420 1421 1422 1423 1424 1425 1426 1427 1428 1429 1430 1431 1432 1433 1434 1435 1436 1437 1438 | *zInput = 0; zInput++; while( fossil_isspace(*zInput) ){ zInput++; } } if( zLeftOver ){ *zLeftOver = zInput; } return zResult; } /* ** Determine the IP address on the other side of a connection. ** Return a pointer to a string. Or return 0 if unable. ** ** The string is held in a static buffer that is overwritten on ** each call. */ char *cgi_remote_ip(int fd){ #if 0 static char zIp[100]; struct sockaddr_in6 addr; socklen_t sz = sizeof(addr); if( getpeername(fd, &addr, &sz) ) return 0; zIp[0] = 0; if( inet_ntop(AF_INET6, &addr, zIp, sizeof(zIp))==0 ){ return 0; } return zIp; #else struct sockaddr_in remoteName; socklen_t size = sizeof(struct sockaddr_in); if( getpeername(fd, (struct sockaddr*)&remoteName, &size) ) return 0; return inet_ntoa(remoteName.sin_addr); #endif } /* ** This routine handles a single HTTP request which is coming in on ** g.httpIn and which replies on g.httpOut ** ** The HTTP request is read from g.httpIn and is used to initialize ** entries in the cgi_parameter() hash, as if those entries were ** environment variables. A call to cgi_init() completes ** the setup. Once all the setup is finished, this procedure returns ** and subsequent code handles the actual generation of the webpage. */ void cgi_handle_http_request(const char *zIpAddr){ char *z, *zToken; int i; char zLine[2000]; /* A single line of input. */ g.fullHttpReply = 1; if( fgets(zLine, sizeof(zLine),g.httpIn)==0 ){ malformed_request("missing HTTP header"); } blob_append(&g.httpHeader, zLine, -1); cgi_trace(zLine); |
︙ | ︙ | |||
1379 1380 1381 1382 1383 1384 1385 | } cgi_setenv("REQUEST_URI", zToken); cgi_setenv("SCRIPT_NAME", ""); for(i=0; zToken[i] && zToken[i]!='?'; i++){} if( zToken[i] ) zToken[i++] = 0; cgi_setenv("PATH_INFO", zToken); cgi_setenv("QUERY_STRING", &zToken[i]); | | | < < < | 1452 1453 1454 1455 1456 1457 1458 1459 1460 1461 1462 1463 1464 1465 1466 1467 | } cgi_setenv("REQUEST_URI", zToken); cgi_setenv("SCRIPT_NAME", ""); for(i=0; zToken[i] && zToken[i]!='?'; i++){} if( zToken[i] ) zToken[i++] = 0; cgi_setenv("PATH_INFO", zToken); cgi_setenv("QUERY_STRING", &zToken[i]); if( zIpAddr==0 ){ zIpAddr = cgi_remote_ip(fileno(g.httpIn)); } if( zIpAddr ){ cgi_setenv("REMOTE_ADDR", zIpAddr); g.zIpAddr = mprintf("%s", zIpAddr); } /* Get all the optional fields that follow the first line. |
︙ | ︙ | |||
1847 1848 1849 1850 1851 1852 1853 | int nErr = 0, fd; close(0); fd = dup(connection); if( fd!=0 ) nErr++; close(1); fd = dup(connection); if( fd!=1 ) nErr++; | | > > > > | 1917 1918 1919 1920 1921 1922 1923 1924 1925 1926 1927 1928 1929 1930 1931 1932 1933 1934 1935 1936 1937 1938 1939 1940 1941 1942 1943 1944 1945 1946 1947 1948 1949 1950 1951 1952 | int nErr = 0, fd; close(0); fd = dup(connection); if( fd!=0 ) nErr++; close(1); fd = dup(connection); if( fd!=1 ) nErr++; if( 0 && !g.fAnyTrace ){ close(2); fd = dup(connection); if( fd!=2 ) nErr++; } close(connection); g.nPendingRequest = nchildren+1; g.nRequest = nRequest+1; return nErr; } } } /* Bury dead children */ if( nchildren ){ while(1){ int iStatus = 0; pid_t x = waitpid(-1, &iStatus, WNOHANG); if( x<=0 ) break; if( WIFSIGNALED(iStatus) && g.fAnyTrace ){ fprintf(stderr, "/***** Child %d exited on signal %d (%s) *****/\n", x, WTERMSIG(iStatus), strsignal(WTERMSIG(iStatus))); } nchildren--; } } } /* NOT REACHED */ fossil_exit(1); #endif |
︙ | ︙ | |||
1890 1891 1892 1893 1894 1895 1896 | {"Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec", 0}; /* ** Returns an RFC822-formatted time string suitable for HTTP headers. ** The timezone is always GMT. The value returned is always a | | | | | 1964 1965 1966 1967 1968 1969 1970 1971 1972 1973 1974 1975 1976 1977 1978 1979 1980 1981 1982 1983 1984 1985 1986 1987 1988 1989 1990 | {"Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec", 0}; /* ** Returns an RFC822-formatted time string suitable for HTTP headers. ** The timezone is always GMT. The value returned is always a ** string obtained from mprintf() and must be freed using fossil_free() ** to avoid a memory leak. ** ** See http://www.faqs.org/rfcs/rfc822.html, section 5 ** and http://www.faqs.org/rfcs/rfc2616.html, section 3.3. */ char *cgi_rfc822_datestamp(time_t now){ struct tm *pTm; pTm = gmtime(&now); if( pTm==0 ){ return mprintf(""); }else{ return mprintf("%s, %d %s %02d %02d:%02d:%02d +0000", azDays[pTm->tm_wday], pTm->tm_mday, azMonths[pTm->tm_mon], pTm->tm_year+1900, pTm->tm_hour, pTm->tm_min, pTm->tm_sec); } } /* ** Parse an RFC822-formatted timestamp as we'd expect from HTTP and return |
︙ | ︙ |
Changes to src/checkin.c.
︙ | ︙ | |||
1190 1191 1192 1193 1194 1195 1196 | #if defined(__CYGWIN__) zEditor = fossil_utf8_to_path(zEditor, 0); blob_add_cr(pPrompt); #endif } #endif if( zEditor==0 ){ | > | | | | | | | > | | 1190 1191 1192 1193 1194 1195 1196 1197 1198 1199 1200 1201 1202 1203 1204 1205 1206 1207 1208 1209 1210 1211 1212 1213 1214 1215 1216 1217 1218 1219 1220 1221 1222 1223 1224 1225 1226 1227 1228 1229 1230 | #if defined(__CYGWIN__) zEditor = fossil_utf8_to_path(zEditor, 0); blob_add_cr(pPrompt); #endif } #endif if( zEditor==0 ){ if( blob_size(pPrompt)>0 ){ blob_append(pPrompt, "#\n" "# Since no default text editor is set using EDITOR or VISUAL\n" "# environment variables or the \"fossil set editor\" command,\n" "# and because no comment was specified using the \"-m\" or \"-M\"\n" "# command-line options, you will need to enter the comment below.\n" "# Type \".\" on a line by itself when you are done:\n", -1); } zFile = mprintf("-"); }else{ Blob fname; blob_zero(&fname); if( g.zLocalRoot!=0 ){ file_relative_name(g.zLocalRoot, &fname, 1); zFile = db_text(0, "SELECT '%qci-comment-'||hex(randomblob(6))||'.txt'", blob_str(&fname)); }else{ file_tempname(&fname, "ci-comment"); zFile = mprintf("%s", blob_str(&fname)); } blob_reset(&fname); } #if defined(_WIN32) blob_add_cr(pPrompt); #endif if( blob_size(pPrompt)>0 ) blob_write_to_file(pPrompt, zFile); if( zEditor ){ zCmd = mprintf("%s \"%s\"", zEditor, zFile); fossil_print("%s\n", zCmd); if( fossil_system(zCmd) ){ fossil_fatal("editor aborted: \"%s\"", zCmd); } |
︙ | ︙ | |||
2121 2122 2123 2124 2125 2126 2127 | if( db_get_boolean("clearsign", 0)==0 ){ noSign = 1; } useCksum = db_get_boolean("repo-cksum", 1); outputManifest = db_get_manifest_setting(); verify_all_options(); /* Do not allow the creation of a new branch using an existing open ** branch name unless the --force flag is used */ | | > > > > | 2123 2124 2125 2126 2127 2128 2129 2130 2131 2132 2133 2134 2135 2136 2137 2138 2139 2140 2141 | if( db_get_boolean("clearsign", 0)==0 ){ noSign = 1; } useCksum = db_get_boolean("repo-cksum", 1); outputManifest = db_get_manifest_setting(); verify_all_options(); /* Do not allow the creation of a new branch using an existing open ** branch name unless the --force flag is used */ if( sCiInfo.zBranch!=0 && !forceFlag && fossil_strcmp(sCiInfo.zBranch,"private")!=0 && branch_is_open(sCiInfo.zBranch) ){ fossil_fatal("an open branch named \"%s\" already exists - use --force" " to override", sCiInfo.zBranch); } /* Escape special characters in tags and put all tags in sorted order */ if( nTag ){ int i; |
︙ | ︙ |
Changes to src/checkout.c.
︙ | ︙ | |||
289 290 291 292 293 294 295 296 297 298 299 300 301 302 | return; } }else{ zVers = g.argv[2]; } vid = load_vfile(zVers, forceMissingFlag); if( prior==vid ){ return; } if( !keepFlag ){ uncheckout(prior); } db_multi_exec("DELETE FROM vfile WHERE vid!=%d", vid); if( !keepFlag ){ | > | 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 | return; } }else{ zVers = g.argv[2]; } vid = load_vfile(zVers, forceMissingFlag); if( prior==vid ){ db_end_transaction(0); return; } if( !keepFlag ){ uncheckout(prior); } db_multi_exec("DELETE FROM vfile WHERE vid!=%d", vid); if( !keepFlag ){ |
︙ | ︙ |
Changes to src/clone.c.
︙ | ︙ | |||
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 | ** ** By default, your current login name is used to create the default ** admin user. This can be overridden using the -A|--admin-user ** parameter. ** ** Options: ** --admin-user|-A USERNAME Make USERNAME the administrator ** --once Don't remember the URI. ** --private Also clone private branches ** --ssl-identity FILENAME Use the SSL identity if requested by the server ** --ssh-command|-c SSH Use SSH as the "ssh" command ** --httpauth|-B USER:PASS Add HTTP Basic Authorization to requests ** -u|--unversioned Also sync unversioned content ** -v|--verbose Show more statistics in output ** ** See also: init */ void clone_cmd(void){ char *zPassword; const char *zDefaultUser; /* Optional name of the default user */ const char *zHttpAuth; /* HTTP Authorization user:pass information */ int nErr = 0; int urlFlags = URL_PROMPT_PW | URL_REMEMBER; int syncFlags = SYNC_CLONE; /* Also clone private branches */ if( find_option("private",0,0)!=0 ) syncFlags |= SYNC_PRIVATE; if( find_option("once",0,0)!=0) urlFlags &= ~URL_REMEMBER; if( find_option("verbose","v",0)!=0) syncFlags |= SYNC_VERBOSE; if( find_option("unversioned","u",0)!=0 ) syncFlags |= SYNC_UNVERSIONED; zHttpAuth = find_option("httpauth","B",1); | > > | 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 | ** ** By default, your current login name is used to create the default ** admin user. This can be overridden using the -A|--admin-user ** parameter. ** ** Options: ** --admin-user|-A USERNAME Make USERNAME the administrator ** --nocompress Omit extra delta compression ** --once Don't remember the URI. ** --private Also clone private branches ** --ssl-identity FILENAME Use the SSL identity if requested by the server ** --ssh-command|-c SSH Use SSH as the "ssh" command ** --httpauth|-B USER:PASS Add HTTP Basic Authorization to requests ** -u|--unversioned Also sync unversioned content ** -v|--verbose Show more statistics in output ** ** See also: init */ void clone_cmd(void){ char *zPassword; const char *zDefaultUser; /* Optional name of the default user */ const char *zHttpAuth; /* HTTP Authorization user:pass information */ int nErr = 0; int urlFlags = URL_PROMPT_PW | URL_REMEMBER; int syncFlags = SYNC_CLONE; int noCompress = find_option("nocompress",0,0)!=0; /* Also clone private branches */ if( find_option("private",0,0)!=0 ) syncFlags |= SYNC_PRIVATE; if( find_option("once",0,0)!=0) urlFlags &= ~URL_REMEMBER; if( find_option("verbose","v",0)!=0) syncFlags |= SYNC_VERBOSE; if( find_option("unversioned","u",0)!=0 ) syncFlags |= SYNC_UNVERSIONED; zHttpAuth = find_option("httpauth","B",1); |
︙ | ︙ | |||
209 210 211 212 213 214 215 | fossil_fatal("server returned an error - clone aborted"); } db_open_repository(g.argv[3]); } db_begin_transaction(); fossil_print("Rebuilding repository meta-data...\n"); rebuild_db(0, 1, 0); | > | | > > | | 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 | fossil_fatal("server returned an error - clone aborted"); } db_open_repository(g.argv[3]); } db_begin_transaction(); fossil_print("Rebuilding repository meta-data...\n"); rebuild_db(0, 1, 0); if( !noCompress ){ fossil_print("Extra delta compression... "); fflush(stdout); extra_deltification(); fossil_print("\n"); } db_end_transaction(0); fossil_print("Vacuuming the database... "); fflush(stdout); if( db_int(0, "PRAGMA page_count")>1000 && db_int(0, "PRAGMA page_size")<8192 ){ db_multi_exec("PRAGMA page_size=8192;"); } db_multi_exec("VACUUM"); fossil_print("\nproject-id: %s\n", db_get("project-code", 0)); fossil_print("server-id: %s\n", db_get("server-code", 0)); |
︙ | ︙ |
Changes to src/codecheck1.c.
︙ | ︙ | |||
37 38 39 40 41 42 43 44 45 46 47 48 49 50 | */ #include <stdio.h> #include <stdlib.h> #include <ctype.h> #include <string.h> #include <assert.h> /* ** Malloc, aborting if it fails. */ void *safe_malloc(int nByte){ void *x = malloc(nByte); if( x==0 ){ fprintf(stderr, "failed to allocate %d bytes\n", nByte); | > > > > > | 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 | */ #include <stdio.h> #include <stdlib.h> #include <ctype.h> #include <string.h> #include <assert.h> /* ** Debugging switch */ static int eVerbose = 0; /* ** Malloc, aborting if it fails. */ void *safe_malloc(int nByte){ void *x = malloc(nByte); if( x==0 ){ fprintf(stderr, "failed to allocate %d bytes\n", nByte); |
︙ | ︙ | |||
196 197 198 199 200 201 202 203 204 205 206 207 208 209 | /* ** Return the first non-whitespace characters in z[] */ static const char *skip_space(const char *z){ while( isspace(z[0]) ){ z++; } return z; } /* ** Return true if the input is a string literal. */ static int is_string_lit(const char *z){ int nu1, nu2; z = next_non_whitespace(z, &nu1, &nu2); | > > > > > > > > > > > > > > > > > > > > > > | 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 | /* ** Return the first non-whitespace characters in z[] */ static const char *skip_space(const char *z){ while( isspace(z[0]) ){ z++; } return z; } /* ** Remove excess whitespace and nested "()" from string z. */ static char *simplify_expr(char *z){ int n = (int)strlen(z); while( n>0 ){ if( isspace(z[0]) ){ z++; n--; continue; } if( z[0]=='(' && z[n-1]==')' ){ z++; n -= 2; continue; } break; } z[n] = 0; return z; } /* ** Return true if the input is a string literal. */ static int is_string_lit(const char *z){ int nu1, nu2; z = next_non_whitespace(z, &nu1, &nu2); |
︙ | ︙ | |||
266 267 268 269 270 271 272 | "db_setting_inop_rhs", }; /* ** Return true if the input is an argument that is safe to use with %s ** while building an SQL statement. */ | | | 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 | "db_setting_inop_rhs", }; /* ** Return true if the input is an argument that is safe to use with %s ** while building an SQL statement. */ static int is_sql_safe(const char *z){ int len, eType; int i; /* A string literal is safe for use with %s */ if( is_string_lit(z) ) return 1; /* Certain functions are guaranteed to return a string that is safe |
︙ | ︙ | |||
295 296 297 298 299 300 301 302 303 304 305 | /* If the "safe-for-%s" comment appears in the argument, then ** let it through */ if( strstr(z, "/*safe-for-%s*/")!=0 ) return 1; return 0; } /* ** Processing flags */ | > > > > > > > > > > > > > > > | > > > | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | 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 | /* If the "safe-for-%s" comment appears in the argument, then ** let it through */ if( strstr(z, "/*safe-for-%s*/")!=0 ) return 1; return 0; } /* ** Return true if the input is an argument that is never safe for use ** with %s. */ static int never_safe(const char *z){ if( strstr(z,"/*safe-for-%s*/")!=0 ) return 0; if( z[0]=='P' ){ if( strncmp(z,"PIF(",4)==0 ) return 0; if( strncmp(z,"PCK(",4)==0 ) return 0; return 1; } if( strncmp(z,"cgi_param",9)==0 ) return 1; return 0; } /* ** Processing flags */ #define FMT_SQL 0x00001 /* Generates SQL text */ #define FMT_HTML 0x00002 /* Generates HTML text */ #define FMT_URL 0x00004 /* Generates URLs */ #define FMT_SAFE 0x00008 /* Always safe for %s */ /* ** A list of internal Fossil interfaces that take a printf-style format ** string. */ struct { const char *zFName; /* Name of the function */ int iFmtArg; /* Index of format argument. Leftmost is 1. */ unsigned fmtFlags; /* Processing flags */ } aFmtFunc[] = { { "admin_log", 1, 0 }, { "blob_append_sql", 2, FMT_SQL }, { "blob_appendf", 2, 0 }, { "cgi_debug", 1, FMT_SAFE }, { "cgi_panic", 1, FMT_SAFE }, { "cgi_printf", 1, FMT_HTML }, { "cgi_redirectf", 1, FMT_URL }, { "chref", 2, FMT_URL }, { "db_blob", 2, FMT_SQL }, { "db_debug", 1, FMT_SQL }, { "db_double", 2, FMT_SQL }, { "db_err", 1, 0 }, { "db_exists", 1, FMT_SQL }, { "db_get_mprintf", 2, 0 }, { "db_int", 2, FMT_SQL }, { "db_int64", 2, FMT_SQL }, { "db_multi_exec", 1, FMT_SQL }, { "db_optional_sql", 2, FMT_SQL }, { "db_prepare", 2, FMT_SQL }, { "db_prepare_ignore_error", 2, FMT_SQL }, { "db_set_mprintf", 3, 0 }, { "db_static_prepare", 2, FMT_SQL }, { "db_text", 2, FMT_SQL }, { "db_unset_mprintf", 2, 0 }, { "form_begin", 2, FMT_URL }, { "fossil_error", 2, FMT_SAFE }, { "fossil_errorlog", 1, FMT_SAFE }, { "fossil_fatal", 1, FMT_SAFE }, { "fossil_fatal_recursive", 1, FMT_SAFE }, { "fossil_panic", 1, FMT_SAFE }, { "fossil_print", 1, FMT_SAFE }, { "fossil_trace", 1, FMT_SAFE }, { "fossil_warning", 1, FMT_SAFE }, { "href", 1, FMT_URL }, { "json_new_string_f", 1, 0 }, { "json_set_err", 2, 0 }, { "json_warn", 2, 0 }, { "mprintf", 1, 0 }, { "socket_set_errmsg", 1, 0 }, { "ssl_set_errmsg", 1, 0 }, { "style_header", 1, FMT_HTML }, { "style_set_current_page", 1, FMT_URL }, { "style_submenu_element", 2, FMT_URL }, { "style_submenu_sql", 3, FMT_SQL }, { "webpage_error", 1, FMT_SAFE }, { "xhref", 2, FMT_URL }, }; /* ** Determine if the indentifier zIdent of length nIndent is a Fossil ** internal interface that uses a printf-style argument. Return zero if not. ** Return the index of the format string if true with the left-most ** argument having an index of 1. |
︙ | ︙ | |||
462 463 464 465 466 467 468 469 470 | zCopy = safe_malloc( len + 1 ); memcpy(zCopy, zStart+1, len); zCopy[len] = 0; azArg = 0; nArg = 0; z = zCopy; while( z[0] ){ len = distance_to(z, ','); azArg = safe_realloc((char*)azArg, (sizeof(azArg[0])+1)*(nArg+1)); | > > > | | < < | 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 | zCopy = safe_malloc( len + 1 ); memcpy(zCopy, zStart+1, len); zCopy[len] = 0; azArg = 0; nArg = 0; z = zCopy; while( z[0] ){ char cEnd; len = distance_to(z, ','); cEnd = z[len]; z[len] = 0; azArg = safe_realloc((char*)azArg, (sizeof(azArg[0])+1)*(nArg+1)); azArg[nArg++] = simplify_expr(z); if( cEnd==0 ) break; z += len + 1; } acType = (char*)&azArg[nArg]; if( fmtArg>nArg ){ printf("%s:%d: too few arguments to %.*s()\n", zFilename, lnFCall, szFName, zFCall); nErr++; |
︙ | ︙ | |||
490 491 492 493 494 495 496 | }else if( (k = formatArgCount(zFmt, nArg, acType))>=0 && nArg!=fmtArg+k ){ printf("%s:%d: too %s arguments to %.*s() " "- got %d and expected %d\n", zFilename, lnFCall, (nArg<fmtArg+k ? "few" : "many"), szFName, zFCall, nArg, fmtArg+k); nErr++; | | | | > > > > > | > | | | > | > > | | 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 | }else if( (k = formatArgCount(zFmt, nArg, acType))>=0 && nArg!=fmtArg+k ){ printf("%s:%d: too %s arguments to %.*s() " "- got %d and expected %d\n", zFilename, lnFCall, (nArg<fmtArg+k ? "few" : "many"), szFName, zFCall, nArg, fmtArg+k); nErr++; }else if( (fmtFlags & FMT_SAFE)==0 ){ for(i=0; i<nArg && i<k; i++){ if( (acType[i]=='s' || acType[i]=='z' || acType[i]=='b') ){ const char *zExpr = azArg[fmtArg+i]; if( never_safe(zExpr) ){ printf("%s:%d: Argument %d to %.*s() is not safe for" " a query parameter\n", zFilename, lnFCall, i+fmtArg, szFName, zFCall); nErr++; }else if( (fmtFlags & FMT_SQL)!=0 && !is_sql_safe(zExpr) ){ printf("%s:%d: Argument %d to %.*s() not safe for SQL\n", zFilename, lnFCall, i+fmtArg, szFName, zFCall); nErr++; } } } } } if( nErr ){ for(i=0; i<nArg; i++){ printf(" arg[%d]: %s\n", i, azArg[i]); } }else if( eVerbose>1 ){ printf("%s:%d: %.*s() ok for %d arguments\n", zFilename, lnFCall, szFName, zFCall, nArg); } free((char*)azArg); free(zCopy); return nErr; } /* |
︙ | ︙ | |||
561 562 563 564 565 566 567 568 569 570 571 572 | } return nErr; } /* ** Check for format-string design rule violations on all files listed ** on the command-line. */ int main(int argc, char **argv){ int i; int nErr = 0; for(i=1; i<argc; i++){ | > > > > > > > > | | 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 | } return nErr; } /* ** Check for format-string design rule violations on all files listed ** on the command-line. ** ** The eVerbose global variable is incremented with each "-v" argument. */ int main(int argc, char **argv){ int i; int nErr = 0; for(i=1; i<argc; i++){ char *zFile; if( strcmp(argv[i],"-v")==0 ){ eVerbose++; continue; } if( eVerbose>0 ) printf("Processing %s...\n", argv[i]); zFile = read_file(argv[i]); nErr += scan_file(argv[i], zFile); free(zFile); } return nErr; } |
Changes to src/configure.c.
︙ | ︙ | |||
34 35 36 37 38 39 40 | #define CONFIGSET_TKT 0x000004 /* Ticket configuration */ #define CONFIGSET_PROJ 0x000008 /* Project name */ #define CONFIGSET_SHUN 0x000010 /* Shun settings */ #define CONFIGSET_USER 0x000020 /* The USER table */ #define CONFIGSET_ADDR 0x000040 /* The CONCEALED table */ #define CONFIGSET_XFER 0x000080 /* Transfer configuration */ #define CONFIGSET_ALIAS 0x000100 /* URL Aliases */ | | | | | | | | | | | | | > | | 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 | #define CONFIGSET_TKT 0x000004 /* Ticket configuration */ #define CONFIGSET_PROJ 0x000008 /* Project name */ #define CONFIGSET_SHUN 0x000010 /* Shun settings */ #define CONFIGSET_USER 0x000020 /* The USER table */ #define CONFIGSET_ADDR 0x000040 /* The CONCEALED table */ #define CONFIGSET_XFER 0x000080 /* Transfer configuration */ #define CONFIGSET_ALIAS 0x000100 /* URL Aliases */ #define CONFIGSET_SCRIBER 0x000200 /* Email subscribers */ #define CONFIGSET_ALL 0x0003ff /* Everything */ #define CONFIGSET_OVERWRITE 0x100000 /* Causes overwrite instead of merge */ /* ** This mask is used for the common TH1 configuration settings (i.e. those ** that are not specific to one particular subsystem, such as the transfer ** subsystem). */ #define CONFIGSET_TH1 (CONFIGSET_SKIN|CONFIGSET_TKT|CONFIGSET_XFER) #endif /* INTERFACE */ /* ** Names of the configuration sets */ static struct { const char *zName; /* Name of the configuration set */ int groupMask; /* Mask for that configuration set */ const char *zHelp; /* What it does */ } aGroupName[] = { { "/email", CONFIGSET_ADDR, "Concealed email addresses in tickets" }, { "/project", CONFIGSET_PROJ, "Project name and description" }, { "/skin", CONFIGSET_SKIN | CONFIGSET_CSS, "Web interface appearance settings" }, { "/css", CONFIGSET_CSS, "Style sheet" }, { "/shun", CONFIGSET_SHUN, "List of shunned artifacts" }, { "/ticket", CONFIGSET_TKT, "Ticket setup", }, { "/user", CONFIGSET_USER, "Users and privilege settings" }, { "/xfer", CONFIGSET_XFER, "Transfer setup", }, { "/alias", CONFIGSET_ALIAS, "URL Aliases", }, { "/subscriber", CONFIGSET_SCRIBER,"Email notification subscriber list" }, { "/all", CONFIGSET_ALL, "All of the above" }, }; /* ** The following is a list of settings that we are willing to ** transfer. ** |
︙ | ︙ | |||
155 156 157 158 159 160 161 162 163 164 165 166 167 168 | { "@concealed", CONFIGSET_ADDR }, { "@shun", CONFIGSET_SHUN }, { "@alias", CONFIGSET_ALIAS }, { "xfer-common-script", CONFIGSET_XFER }, { "xfer-push-script", CONFIGSET_XFER }, { "xfer-commit-script", CONFIGSET_XFER }, { "xfer-ticket-script", CONFIGSET_XFER }, }; static int iConfig = 0; | > > | 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 | { "@concealed", CONFIGSET_ADDR }, { "@shun", CONFIGSET_SHUN }, { "@alias", CONFIGSET_ALIAS }, { "@subscriber", CONFIGSET_SCRIBER }, { "xfer-common-script", CONFIGSET_XFER }, { "xfer-push-script", CONFIGSET_XFER }, { "xfer-commit-script", CONFIGSET_XFER }, { "xfer-ticket-script", CONFIGSET_XFER }, }; static int iConfig = 0; |
︙ | ︙ | |||
211 212 213 214 215 216 217 | return blob_sql_text(&x); } /* ** Return the mask for the named configuration parameter if it can be ** safely exported. Return 0 if the parameter is not safe to export. ** | | | | 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 | return blob_sql_text(&x); } /* ** Return the mask for the named configuration parameter if it can be ** safely exported. Return 0 if the parameter is not safe to export. ** ** "Safe" in the previous paragraph means the permission is granted to ** export the property. In other words, the requesting side has presented ** login credentials and has sufficient capabilities to access the requested ** information. */ int configure_is_exportable(const char *zName){ int i; int n = strlen(zName); if( n>2 && zName[0]=='\'' && zName[n-1]=='\'' ){ zName++; n -= 2; } for(i=0; i<count(aConfig); i++){ if( strncmp(zName, aConfig[i].zName, n)==0 && aConfig[i].zName[n]==0 ){ int m = aConfig[i].groupMask; if( !g.perm.Admin ){ m &= ~(CONFIGSET_USER|CONFIGSET_SCRIBER); } if( !g.perm.RdAddr ){ m &= ~CONFIGSET_ADDR; } return m; } } |
︙ | ︙ | |||
310 311 312 313 314 315 316 | ** sync session. ** ** Mask consists of one or more CONFIGSET_* values ORed together, to ** designate what types of configuration we are allowed to receive. ** ** NEW FORMAT: ** | | > > > > < | < < < < < < < < < < < < < < < < > > > | | | | | | | | | | > > > > > | > > > > > > | > > | | 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 | ** sync session. ** ** Mask consists of one or more CONFIGSET_* values ORed together, to ** designate what types of configuration we are allowed to receive. ** ** NEW FORMAT: ** ** zName is one of: ** ** "/config", "/user", "/shun", "/reportfmt", "/concealed", ** "/subscriber", ** ** zName indicates the table that holds the configuration information being ** transferred. pContent is a string that consist of alternating Fossil ** and SQL tokens. The First token is a timestamp in seconds since 1970. ** The second token is a primary key for the table identified by zName. If ** The entry with the corresponding primary key exists and has a more recent ** mtime, then nothing happens. If the entry does not exist or if it has ** an older mtime, then the content described by subsequent token pairs is ** inserted. The first element of each token pair is a column name and ** the second is its value. ** ** In overview, we have: ** ** NAME CONTENT ** ------- ----------------------------------------------------------- ** /config $MTIME $NAME value $VALUE ** /user $MTIME $LOGIN pw $VALUE cap $VALUE info $VALUE photo $VALUE ** /shun $MTIME $UUID scom $VALUE ** /reportfmt $MTIME $TITLE owner $VALUE cols $VALUE sqlcode $VALUE ** /concealed $MTIME $HASH content $VALUE ** /subscriber $SMTIME $SEMAIL suname $V ... */ void configure_receive(const char *zName, Blob *pContent, int groupMask){ int checkMask; /* Masks for which we must first check existance of tables */ checkMask = CONFIGSET_SCRIBER; if( zName[0]=='/' ){ /* The new format */ char *azToken[24]; int nToken = 0; int ii, jj; int thisMask; Blob name, value, sql; static const struct receiveType { const char *zName; /* Configuration key for this table */ const char *zPrimKey; /* Primary key column */ int nField; /* Number of data fields */ const char *azField[6]; /* Names of the data fields */ } aType[] = { { "/config", "name", 1, { "value", 0,0,0,0,0 } }, { "@user", "login", 4, { "pw","cap","info","photo",0,0} }, { "@shun", "uuid", 1, { "scom", 0,0,0,0,0} }, { "@reportfmt", "title", 3, { "owner","cols","sqlcode",0,0,0}}, { "@concealed", "hash", 1, { "content", 0,0,0,0,0 } }, { "@subscriber","semail",6, { "suname","sdigest","sdonotcall","ssub","sctime","smip"} }, }; /* Locate the receiveType in aType[ii] */ for(ii=0; ii<count(aType); ii++){ if( fossil_strcmp(&aType[ii].zName[1],&zName[1])==0 ) break; } if( ii>=count(aType) ) return; while( blob_token(pContent, &name) && blob_sqltoken(pContent, &value) ){ char *z = blob_terminate(&name); if( !safeSql(z) ) return; if( nToken>0 ){ for(jj=0; jj<aType[ii].nField; jj++){ if( fossil_strcmp(aType[ii].azField[jj], z)==0 ) break; } if( jj>=aType[ii].nField ) continue; }else{ if( !safeInt(z) ) return; } azToken[nToken++] = z; azToken[nToken++] = z = blob_terminate(&value); if( !safeSql(z) ) return; if( nToken>=count(azToken)-1 ) break; } if( nToken<2 ) return; if( aType[ii].zName[0]=='/' ){ thisMask = configure_is_exportable(azToken[1]); }else{ thisMask = configure_is_exportable(aType[ii].zName); } if( (thisMask & groupMask)==0 ) return; if( (thisMask & checkMask)!=0 ){ if( (thisMask & CONFIGSET_SCRIBER)!=0 ){ email_schema(1); } checkMask &= ~thisMask; } blob_zero(&sql); if( groupMask & CONFIGSET_OVERWRITE ){ if( (thisMask & configHasBeenReset)==0 && aType[ii].zName[0]!='/' ){ db_multi_exec("DELETE FROM \"%w\"", &aType[ii].zName[1]); configHasBeenReset |= thisMask; } blob_append_sql(&sql, "REPLACE INTO "); }else{ blob_append_sql(&sql, "INSERT OR IGNORE INTO "); } blob_append_sql(&sql, "\"%w\"(\"%w\",mtime", &zName[1], aType[ii].zPrimKey); if( fossil_stricmp(zName,"/subscriber") ) email_schema(0); for(jj=2; jj<nToken; jj+=2){ blob_append_sql(&sql, ",\"%w\"", azToken[jj]); } blob_append_sql(&sql,") VALUES(%s,%s", azToken[1] /*safe-for-%s*/, azToken[0]/*safe-for-%s*/); for(jj=2; jj<nToken; jj+=2){ blob_append_sql(&sql, ",%s", azToken[jj+1] /*safe-for-%s*/); } db_multi_exec("%s)", blob_sql_text(&sql)); if( db_changes()==0 ){ blob_reset(&sql); blob_append_sql(&sql, "UPDATE \"%w\" SET mtime=%s", |
︙ | ︙ | |||
571 572 573 574 575 576 577 578 579 580 581 582 583 584 | ); blob_appendf(pOut, "config /config %d\n%s\n", blob_size(&rec), blob_str(&rec)); nCard++; blob_reset(&rec); } db_finalize(&q); } db_prepare(&q, "SELECT mtime, quote(name), quote(value) FROM config" " WHERE name=:name AND mtime>=%lld", iStart); for(ii=0; ii<count(aConfig); ii++){ if( (aConfig[ii].groupMask & groupMask)!=0 && aConfig[ii].zName[0]!='@' ){ db_bind_text(&q, ":name", aConfig[ii].zName); while( db_step(&q)==SQLITE_ROW ){ | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 | ); blob_appendf(pOut, "config /config %d\n%s\n", blob_size(&rec), blob_str(&rec)); nCard++; blob_reset(&rec); } db_finalize(&q); } if( (groupMask & CONFIGSET_SCRIBER)!=0 && db_table_exists("repository","subscriber") ){ db_prepare(&q, "SELECT mtime, quote(semail)," " quote(suname), quote(sdigest)," " quote(sdonotcall), quote(ssub)," " quote(sctime), quote(smip)" " FROM subscriber WHERE sverified" " AND mtime>=%lld", iStart); while( db_step(&q)==SQLITE_ROW ){ blob_appendf(&rec, "%lld %s suname %s sdigest %s sdonotcall %s ssub %s" " sctime %s smip %s", db_column_int64(&q, 0), /* mtime */ db_column_text(&q, 1), /* semail (PK) */ db_column_text(&q, 2), /* suname */ db_column_text(&q, 3), /* sdigest */ db_column_text(&q, 4), /* sdonotcall */ db_column_text(&q, 5), /* ssub */ db_column_text(&q, 6), /* sctime */ db_column_text(&q, 7) /* smip */ ); blob_appendf(pOut, "config /subscriber %d\n%s\n", blob_size(&rec), blob_str(&rec)); nCard++; blob_reset(&rec); } db_finalize(&q); } db_prepare(&q, "SELECT mtime, quote(name), quote(value) FROM config" " WHERE name=:name AND mtime>=%lld", iStart); for(ii=0; ii<count(aConfig); ii++){ if( (aConfig[ii].groupMask & groupMask)!=0 && aConfig[ii].zName[0]!='@' ){ db_bind_text(&q, ":name", aConfig[ii].zName); while( db_step(&q)==SQLITE_ROW ){ |
︙ | ︙ | |||
610 611 612 613 614 615 616 | if( strncmp(z, &aGroupName[i].zName[1], n)==0 ){ return aGroupName[i].groupMask; } } if( notFoundIsFatal ){ fossil_print("Available configuration areas:\n"); for(i=0; i<count(aGroupName); i++){ | > | | 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 | if( strncmp(z, &aGroupName[i].zName[1], n)==0 ){ return aGroupName[i].groupMask; } } if( notFoundIsFatal ){ fossil_print("Available configuration areas:\n"); for(i=0; i<count(aGroupName); i++){ fossil_print(" %-13s %s\n", &aGroupName[i].zName[1], aGroupName[i].zHelp); } fossil_fatal("no such configuration area: \"%s\"", z); } return 0; } /* |
︙ | ︙ | |||
653 654 655 656 657 658 659 | ** ** Where METHOD is one of: export import merge pull push reset. All methods ** accept the -R or --repository option to specify a repository. ** ** %fossil configuration export AREA FILENAME ** ** Write to FILENAME exported configuration information for AREA. | | > > | 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 | ** ** Where METHOD is one of: export import merge pull push reset. All methods ** accept the -R or --repository option to specify a repository. ** ** %fossil configuration export AREA FILENAME ** ** Write to FILENAME exported configuration information for AREA. ** AREA can be one of: ** ** all email project shun skin ticket user alias subscriber ** ** %fossil configuration import FILENAME ** ** Read a configuration from FILENAME, overwriting the current ** configuration. ** ** %fossil configuration merge FILENAME |
︙ | ︙ | |||
790 791 792 793 794 795 796 797 798 799 800 801 802 803 | }else if( fossil_strcmp(zName,"@user")==0 ){ db_multi_exec("DELETE FROM user"); db_create_default_users(0, 0); }else if( fossil_strcmp(zName,"@concealed")==0 ){ db_multi_exec("DELETE FROM concealed"); }else if( fossil_strcmp(zName,"@shun")==0 ){ db_multi_exec("DELETE FROM shun"); }else if( fossil_strcmp(zName,"@reportfmt")==0 ){ db_multi_exec("DELETE FROM reportfmt"); assert( strchr(zRepositorySchemaDefaultReports,'%')==0 ); db_multi_exec(zRepositorySchemaDefaultReports /*works-like:""*/); } } db_end_transaction(0); | > > > > > > > > > | 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 | }else if( fossil_strcmp(zName,"@user")==0 ){ db_multi_exec("DELETE FROM user"); db_create_default_users(0, 0); }else if( fossil_strcmp(zName,"@concealed")==0 ){ db_multi_exec("DELETE FROM concealed"); }else if( fossil_strcmp(zName,"@shun")==0 ){ db_multi_exec("DELETE FROM shun"); }else if( fossil_strcmp(zName,"@subscriber")==0 ){ if( db_table_exists("repository","subscriber") ){ db_multi_exec("DELETE FROM subscriber"); } }else if( fossil_strcmp(zName,"@forum")==0 ){ if( db_table_exists("repository","forumpost") ){ db_multi_exec("DELETE FROM forumpost"); db_multi_exec("DELETE FROM forumthread"); } }else if( fossil_strcmp(zName,"@reportfmt")==0 ){ db_multi_exec("DELETE FROM reportfmt"); assert( strchr(zRepositorySchemaDefaultReports,'%')==0 ); db_multi_exec(zRepositorySchemaDefaultReports /*works-like:""*/); } } db_end_transaction(0); |
︙ | ︙ |
Changes to src/db.c.
︙ | ︙ | |||
68 69 70 71 72 73 74 | #endif /* INTERFACE */ const struct Stmt empty_Stmt = empty_Stmt_m; /* ** Call this routine when a database error occurs. */ static void db_err(const char *zFormat, ...){ | < < < < < < | | < < < < < < < < < < | | 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 | #endif /* INTERFACE */ const struct Stmt empty_Stmt = empty_Stmt_m; /* ** Call this routine when a database error occurs. */ static void db_err(const char *zFormat, ...){ va_list ap; char *z; va_start(ap, zFormat); z = vmprintf(zFormat, ap); va_end(ap); #ifdef FOSSIL_ENABLE_JSON if( g.json.isJsonMode ){ json_err( 0, z, 1 ); } else #endif /* FOSSIL_ENABLE_JSON */ if( g.xferPanic && g.cgiOutput==1 ){ cgi_reset_content(); @ error Database\serror:\s%F(z) cgi_reply(); } fossil_panic("Database error: %s", z); } /* ** All static variable that a used by only this file are gathered into ** the following structure. */ static struct DbLocalData { |
︙ | ︙ | |||
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 | int (*xHook)(void); /* Functions to call at db_end_transaction() */ int sequence; /* Call functions in sequence order */ } aHook[5]; char *azDeleteOnFail[3]; /* Files to delete on a failure */ char *azBeforeCommit[5]; /* Commands to run prior to COMMIT */ int nBeforeCommit; /* Number of entries in azBeforeCommit */ int nPriorChanges; /* sqlite3_total_changes() at transaction start */ } db = {0, 0, 0, 0, 0, 0, }; /* ** Arrange for the given file to be deleted on a failure. */ void db_delete_on_failure(const char *zFilename){ assert( db.nDeleteOnFail<count(db.azDeleteOnFail) ); db.azDeleteOnFail[db.nDeleteOnFail++] = fossil_strdup(zFilename); } /* ** This routine is called by the SQLite commit-hook mechanism ** just prior to each commit. All this routine does is verify ** that nBegin really is zero. That insures that transactions ** cannot commit by any means other than by calling db_end_transaction() ** below. ** ** This is just a safety and sanity check. */ static int db_verify_at_commit(void *notUsed){ if( db.nBegin ){ fossil_panic("illegal commit attempt"); return 1; } return 0; } /* | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | | > > > > > > > > > > > > > > > > > > > > > > > > > | > > > | > > > > > | > > | 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 | int (*xHook)(void); /* Functions to call at db_end_transaction() */ int sequence; /* Call functions in sequence order */ } aHook[5]; char *azDeleteOnFail[3]; /* Files to delete on a failure */ char *azBeforeCommit[5]; /* Commands to run prior to COMMIT */ int nBeforeCommit; /* Number of entries in azBeforeCommit */ int nPriorChanges; /* sqlite3_total_changes() at transaction start */ const char *zStartFile; /* File in which transaction was started */ int iStartLine; /* Line of zStartFile where transaction started */ } db = {0, 0, 0, 0, 0, 0, }; /* ** Arrange for the given file to be deleted on a failure. */ void db_delete_on_failure(const char *zFilename){ assert( db.nDeleteOnFail<count(db.azDeleteOnFail) ); db.azDeleteOnFail[db.nDeleteOnFail++] = fossil_strdup(zFilename); } /* ** Return the transaction nesting depth. 0 means we are currently ** not in a transaction. */ int db_transaction_nesting_depth(void){ return db.nBegin; } /* ** Return a pointer to a string that is the code point where the ** current transaction was started. */ char *db_transaction_start_point(void){ return mprintf("%s:%d", db.zStartFile, db.iStartLine); } /* ** This routine is called by the SQLite commit-hook mechanism ** just prior to each commit. All this routine does is verify ** that nBegin really is zero. That insures that transactions ** cannot commit by any means other than by calling db_end_transaction() ** below. ** ** This is just a safety and sanity check. */ static int db_verify_at_commit(void *notUsed){ if( db.nBegin ){ fossil_panic("illegal commit attempt"); return 1; } return 0; } /* ** Silently add the filename and line number as parameter to each ** db_begin_transaction call. */ #if INTERFACE #define db_begin_transaction() db_begin_transaction_real(__FILE__,__LINE__) #define db_begin_write() db_begin_write_real(__FILE__,__LINE__) #define db_commit_transaction() db_end_transaction(0) #define db_rollback_transaction() db_end_transaction(1) #endif /* ** Begin a nested transaction */ void db_begin_transaction_real(const char *zStartFile, int iStartLine){ if( db.nBegin==0 ){ db_multi_exec("BEGIN"); sqlite3_commit_hook(g.db, db_verify_at_commit, 0); db.nPriorChanges = sqlite3_total_changes(g.db); db.doRollback = 0; db.zStartFile = zStartFile; db.iStartLine = iStartLine; } db.nBegin++; } /* ** Begin a new transaction for writing. */ void db_begin_write_real(const char *zStartFile, int iStartLine){ if( db.nBegin==0 ){ db_multi_exec("BEGIN IMMEDIATE"); sqlite3_commit_hook(g.db, db_verify_at_commit, 0); db.nPriorChanges = sqlite3_total_changes(g.db); db.doRollback = 0; db.zStartFile = zStartFile; db.iStartLine = iStartLine; }else{ fossil_warning("read txn at %s:%d might cause SQLITE_BUSY " "for the write txn at %s:%d", db.zStartFile, db.iStartLine, zStartFile, iStartLine); } db.nBegin++; } /* End a transaction previously started using db_begin_transaction() ** or db_begin_write(). */ void db_end_transaction(int rollbackFlag){ if( g.db==0 ) return; if( db.nBegin<=0 ){ fossil_warning("Extra call to db_end_transaction"); return; } if( rollbackFlag ){ db.doRollback = 1; if( g.fSqlTrace ) fossil_trace("-- ROLLBACK by request\n"); } db.nBegin--; if( db.nBegin==0 ){ int i; if( db.doRollback==0 && db.nPriorChanges<sqlite3_total_changes(g.db) ){ i = 0; while( db.nBeforeCommit ){ db.nBeforeCommit--; sqlite3_exec(g.db, db.azBeforeCommit[i], 0, 0, 0); sqlite3_free(db.azBeforeCommit[i]); i++; } leaf_do_pending_checks(); } for(i=0; db.doRollback==0 && i<db.nCommitHook; i++){ int rc = db.aHook[i].xHook(); if( rc ){ db.doRollback = 1; if( g.fSqlTrace ) fossil_trace("-- ROLLBACK due to aHook[%d]\n", i); } } while( db.pAllStmt ){ db_finalize(db.pAllStmt); } db_multi_exec("%s", db.doRollback ? "ROLLBACK" : "COMMIT"); db.doRollback = 0; } |
︙ | ︙ | |||
270 271 272 273 274 275 276 | va_end(ap); zSql = blob_str(&pStmt->sql); db.nPrepare++; if( flags & DB_PREPARE_PERSISTENT ){ prepFlags = SQLITE_PREPARE_PERSISTENT; } rc = sqlite3_prepare_v3(g.db, zSql, -1, prepFlags, &pStmt->pStmt, 0); | | | > > > > > > > < < < < > > > > > > > > > > > > > > > > > > > > > | 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 | va_end(ap); zSql = blob_str(&pStmt->sql); db.nPrepare++; if( flags & DB_PREPARE_PERSISTENT ){ prepFlags = SQLITE_PREPARE_PERSISTENT; } rc = sqlite3_prepare_v3(g.db, zSql, -1, prepFlags, &pStmt->pStmt, 0); if( rc!=0 && (flags & DB_PREPARE_IGNORE_ERROR)==0 ){ db_err("%s\n%s", sqlite3_errmsg(g.db), zSql); } pStmt->pNext = db.pAllStmt; pStmt->pPrev = 0; if( db.pAllStmt ) db.pAllStmt->pPrev = pStmt; db.pAllStmt = pStmt; pStmt->nStep = 0; pStmt->rc = rc; return rc; } int db_prepare(Stmt *pStmt, const char *zFormat, ...){ int rc; va_list ap; va_start(ap, zFormat); rc = db_vprepare(pStmt, 0, zFormat, ap); va_end(ap); return rc; } int db_prepare_ignore_error(Stmt *pStmt, const char *zFormat, ...){ int rc; va_list ap; va_start(ap, zFormat); rc = db_vprepare(pStmt, DB_PREPARE_IGNORE_ERROR, zFormat, ap); va_end(ap); return rc; } /* This variant of db_prepare() checks to see if the statement has ** already been prepared, and if it has it becomes a no-op. */ int db_static_prepare(Stmt *pStmt, const char *zFormat, ...){ int rc = SQLITE_OK; if( blob_size(&pStmt->sql)==0 ){ va_list ap; va_start(ap, zFormat); rc = db_vprepare(pStmt, DB_PREPARE_PERSISTENT, zFormat, ap); va_end(ap); } return rc; } /* Prepare a statement using text placed inside a Blob ** using blob_append_sql(). */ int db_prepare_blob(Stmt *pStmt, Blob *pSql){ int rc; char *zSql; pStmt->sql = *pSql; blob_init(pSql, 0, 0); zSql = blob_sql_text(&pStmt->sql); db.nPrepare++; rc = sqlite3_prepare_v3(g.db, zSql, -1, 0, &pStmt->pStmt, 0); if( rc!=0 ){ db_err("%s\n%s", sqlite3_errmsg(g.db), zSql); } pStmt->pNext = pStmt->pPrev = 0; pStmt->nStep = 0; pStmt->rc = rc; return rc; } /* ** Return the index of a bind parameter */ static int paramIdx(Stmt *pStmt, const char *zParamName){ int i = sqlite3_bind_parameter_index(pStmt->pStmt, zParamName); if( i==0 ){ |
︙ | ︙ | |||
402 403 404 405 406 407 408 | db_stats(pStmt); rc = sqlite3_reset(pStmt->pStmt); db_check_result(rc); return rc; } int db_finalize(Stmt *pStmt){ int rc; | < < < < < > > > > > | | 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 | db_stats(pStmt); rc = sqlite3_reset(pStmt->pStmt); db_check_result(rc); return rc; } int db_finalize(Stmt *pStmt){ int rc; if( pStmt->pNext ){ pStmt->pNext->pPrev = pStmt->pPrev; } if( pStmt->pPrev ){ pStmt->pPrev->pNext = pStmt->pNext; }else if( db.pAllStmt==pStmt ){ db.pAllStmt = pStmt->pNext; } pStmt->pNext = 0; pStmt->pPrev = 0; db_stats(pStmt); blob_reset(&pStmt->sql); rc = sqlite3_finalize(pStmt->pStmt); db_check_result(rc); pStmt->pStmt = 0; return rc; } /* ** Return the rowid of the most recent insert */ int db_last_insert_rowid(void){ i64 x = sqlite3_last_insert_rowid(g.db); if( x<0 || x>(i64)2147483647 ){ fossil_panic("rowid out of range (0..2147483647)"); } return (int)x; } /* ** Return the number of rows that were changed by the most recent ** INSERT, UPDATE, or DELETE. Auxiliary changes caused by triggers |
︙ | ︙ | |||
477 478 479 480 481 482 483 484 485 486 487 488 489 490 | } char *db_column_malloc(Stmt *pStmt, int N){ return mprintf("%s", db_column_text(pStmt, N)); } void db_column_blob(Stmt *pStmt, int N, Blob *pBlob){ blob_append(pBlob, sqlite3_column_blob(pStmt->pStmt, N), sqlite3_column_bytes(pStmt->pStmt, N)); } /* ** Initialize a blob to an ephemeral copy of the content of a ** column in the current row. The data in the blob will become ** invalid when the statement is stepped or reset. */ | > > > > > > | 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 | } char *db_column_malloc(Stmt *pStmt, int N){ return mprintf("%s", db_column_text(pStmt, N)); } void db_column_blob(Stmt *pStmt, int N, Blob *pBlob){ blob_append(pBlob, sqlite3_column_blob(pStmt->pStmt, N), sqlite3_column_bytes(pStmt->pStmt, N)); } Blob db_column_text_as_blob(Stmt *pStmt, int N){ Blob x; blob_init(&x, (char*)sqlite3_column_text(pStmt->pStmt,N), sqlite3_column_bytes(pStmt->pStmt,N)); return x; } /* ** Initialize a blob to an ephemeral copy of the content of a ** column in the current row. The data in the blob will become ** invalid when the statement is stepped or reset. */ |
︙ | ︙ | |||
873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 | if( g.fTimeFormat==1 ){ sqlite3_result_text(context, "0 seconds", -1, SQLITE_STATIC); }else{ sqlite3_result_text(context, "utc", -1, SQLITE_STATIC); } } /* ** Register the SQL functions that are useful both to the internal ** representation and to the "fossil sql" command. */ void db_add_aux_functions(sqlite3 *db){ sqlite3_create_function(db, "checkin_mtime", 2, SQLITE_UTF8, 0, db_checkin_mtime_function, 0, 0); sqlite3_create_function(db, "symbolic_name_to_rid", 1, SQLITE_UTF8, 0, db_sym2rid_function, 0, 0); sqlite3_create_function(db, "symbolic_name_to_rid", 2, SQLITE_UTF8, 0, db_sym2rid_function, 0, 0); sqlite3_create_function(db, "now", 0, SQLITE_UTF8, 0, db_now_function, 0, 0); sqlite3_create_function(db, "toLocal", 0, SQLITE_UTF8, 0, db_tolocal_function, 0, 0); sqlite3_create_function(db, "fromLocal", 0, SQLITE_UTF8, 0, db_fromlocal_function, 0, 0); } #if USE_SEE /* ** This is a pointer to the saved database encryption key string. */ static char *zSavedKey = 0; | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 951 952 953 954 955 956 957 958 959 960 961 962 963 964 965 966 967 968 969 970 971 972 973 974 975 976 977 978 979 980 981 982 983 984 985 986 987 988 989 990 991 992 993 994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 | if( g.fTimeFormat==1 ){ sqlite3_result_text(context, "0 seconds", -1, SQLITE_STATIC); }else{ sqlite3_result_text(context, "utc", -1, SQLITE_STATIC); } } /* ** If the input is a hexadecimal string, convert that string into a BLOB. ** If the input is not a hexadecimal string, return NULL. */ void db_hextoblob( sqlite3_context *context, int argc, sqlite3_value **argv ){ const unsigned char *zIn = sqlite3_value_text(argv[0]); int nIn = sqlite3_value_bytes(argv[0]); unsigned char *zOut; if( zIn==0 ) return; if( nIn&1 ) return; if( !validate16((const char*)zIn, nIn) ) return; zOut = sqlite3_malloc64( nIn/2 + 1 ); if( zOut==0 ){ sqlite3_result_error_nomem(context); return; } decode16(zIn, zOut, nIn); sqlite3_result_blob(context, zOut, nIn/2, sqlite3_free); } /* ** Register the SQL functions that are useful both to the internal ** representation and to the "fossil sql" command. */ void db_add_aux_functions(sqlite3 *db){ sqlite3_create_function(db, "checkin_mtime", 2, SQLITE_UTF8, 0, db_checkin_mtime_function, 0, 0); sqlite3_create_function(db, "symbolic_name_to_rid", 1, SQLITE_UTF8, 0, db_sym2rid_function, 0, 0); sqlite3_create_function(db, "symbolic_name_to_rid", 2, SQLITE_UTF8, 0, db_sym2rid_function, 0, 0); sqlite3_create_function(db, "now", 0, SQLITE_UTF8, 0, db_now_function, 0, 0); sqlite3_create_function(db, "toLocal", 0, SQLITE_UTF8, 0, db_tolocal_function, 0, 0); sqlite3_create_function(db, "fromLocal", 0, SQLITE_UTF8, 0, db_fromlocal_function, 0, 0); sqlite3_create_function(db, "hextoblob", 1, SQLITE_UTF8, 0, db_hextoblob, 0, 0); sqlite3_create_function(db, "capunion", 1, SQLITE_UTF8, 0, 0, capability_union_step, capability_union_finalize); sqlite3_create_function(db, "fullcap", 1, SQLITE_UTF8, 0, capability_fullcap, 0, 0); sqlite3_create_function(db, "find_emailaddr", 1, SQLITE_UTF8, 0, email_find_emailaddr_func, 0, 0); } #if USE_SEE /* ** This is a pointer to the saved database encryption key string. */ static char *zSavedKey = 0; |
︙ | ︙ | |||
937 938 939 940 941 942 943 | size_t blobSize = 0; blobSize = blob_size(pKey); if( blobSize==0 ) return; fossil_get_page_size(&pageSize); assert( pageSize>0 ); if( blobSize>pageSize ){ | | | 1046 1047 1048 1049 1050 1051 1052 1053 1054 1055 1056 1057 1058 1059 1060 | size_t blobSize = 0; blobSize = blob_size(pKey); if( blobSize==0 ) return; fossil_get_page_size(&pageSize); assert( pageSize>0 ); if( blobSize>pageSize ){ fossil_panic("key blob too large: %u versus %u", blobSize, pageSize); } p = fossil_secure_alloc_page(&n); assert( p!=NULL ); assert( n==pageSize ); assert( n>=blobSize ); memcpy(p, blob_str(pKey), blobSize); zSavedKey = p; |
︙ | ︙ | |||
971 972 973 974 975 976 977 | ){ if( zSavedKey!=NULL ){ size_t blobSize = blob_size(pKey); if( blobSize==0 ){ db_unsave_encryption_key(); }else{ if( blobSize>savedKeySize ){ | | | 1080 1081 1082 1083 1084 1085 1086 1087 1088 1089 1090 1091 1092 1093 1094 | ){ if( zSavedKey!=NULL ){ size_t blobSize = blob_size(pKey); if( blobSize==0 ){ db_unsave_encryption_key(); }else{ if( blobSize>savedKeySize ){ fossil_panic("key blob too large: %u versus %u", blobSize, savedKeySize); } fossil_secure_zero(zSavedKey, savedKeySize); memcpy(zSavedKey, blob_str(pKey), blobSize); } }else{ db_save_encryption_key(pKey); |
︙ | ︙ | |||
1001 1002 1003 1004 1005 1006 1007 | size_t n = 0; size_t pageSize = 0; HANDLE hProcess = NULL; fossil_get_page_size(&pageSize); assert( pageSize>0 ); if( nSize>pageSize ){ | | | | | | 1110 1111 1112 1113 1114 1115 1116 1117 1118 1119 1120 1121 1122 1123 1124 1125 1126 1127 1128 1129 1130 1131 1132 1133 1134 1135 1136 1137 1138 1139 1140 1141 1142 1143 1144 1145 1146 1147 1148 1149 | size_t n = 0; size_t pageSize = 0; HANDLE hProcess = NULL; fossil_get_page_size(&pageSize); assert( pageSize>0 ); if( nSize>pageSize ){ fossil_panic("key too large: %u versus %u", nSize, pageSize); } p = fossil_secure_alloc_page(&n); assert( p!=NULL ); assert( n==pageSize ); assert( n>=nSize ); hProcess = OpenProcess(PROCESS_VM_READ, FALSE, processId); if( hProcess!=NULL ){ SIZE_T nRead = 0; if( ReadProcessMemory(hProcess, pAddress, p, nSize, &nRead) ){ CloseHandle(hProcess); if( nRead==nSize ){ db_unsave_encryption_key(); zSavedKey = p; savedKeySize = n; }else{ fossil_panic("bad size read, %u out of %u bytes at %p from pid %lu", nRead, nSize, pAddress, processId); } }else{ CloseHandle(hProcess); fossil_panic("failed read, %u bytes at %p from pid %lu: %lu", nSize, pAddress, processId, GetLastError()); } }else{ fossil_panic("failed to open pid %lu: %lu", processId, GetLastError()); } } #endif /* defined(_WIN32) */ #endif /* USE_SEE */ /* ** If the database file zDbFile has a name that suggests that it is |
︙ | ︙ | |||
1090 1091 1092 1093 1094 1095 1096 1097 1098 1099 1100 1101 1102 1103 | ** connection. An error results in process abort. */ LOCAL sqlite3 *db_open(const char *zDbName){ int rc; sqlite3 *db; if( g.fSqlTrace ) fossil_trace("-- sqlite3_open: [%s]\n", zDbName); rc = sqlite3_open_v2( zDbName, &db, SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE, g.zVfsName ); if( rc!=SQLITE_OK ){ db_err("[%s]: %s", zDbName, sqlite3_errmsg(db)); | > > > > > > > | 1199 1200 1201 1202 1203 1204 1205 1206 1207 1208 1209 1210 1211 1212 1213 1214 1215 1216 1217 1218 1219 | ** connection. An error results in process abort. */ LOCAL sqlite3 *db_open(const char *zDbName){ int rc; sqlite3 *db; if( g.fSqlTrace ) fossil_trace("-- sqlite3_open: [%s]\n", zDbName); if( strcmp(zDbName, g.nameOfExe)==0 ){ extern int sqlite3_appendvfs_init( sqlite3 *, char **, const sqlite3_api_routines * ); sqlite3_appendvfs_init(0,0,0); g.zVfsName = "apndvfs"; } rc = sqlite3_open_v2( zDbName, &db, SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE, g.zVfsName ); if( rc!=SQLITE_OK ){ db_err("[%s]: %s", zDbName, sqlite3_errmsg(db)); |
︙ | ︙ | |||
1165 1166 1167 1168 1169 1170 1171 | ** the database connection. ** ** After calling this routine, db_database_slot(zLabel) should ** return 0. */ void db_set_main_schemaname(sqlite3 *db, const char *zLabel){ if( sqlite3_db_config(db, SQLITE_DBCONFIG_MAINDBNAME, zLabel) ){ | | | 1281 1282 1283 1284 1285 1286 1287 1288 1289 1290 1291 1292 1293 1294 1295 | ** the database connection. ** ** After calling this routine, db_database_slot(zLabel) should ** return 0. */ void db_set_main_schemaname(sqlite3 *db, const char *zLabel){ if( sqlite3_db_config(db, SQLITE_DBCONFIG_MAINDBNAME, zLabel) ){ fossil_panic("Fossil requires a version of SQLite that supports the " "SQLITE_DBCONFIG_MAINDBNAME interface."); } } /* ** Return the slot number for database zLabel. The first database ** opened is slot 0. The "temp" database is slot 1. Attached databases |
︙ | ︙ | |||
1222 1223 1224 1225 1226 1227 1228 1229 | g.zConfigDbName = 0; }else if( g.dbConfig ){ sqlite3_wal_checkpoint(g.dbConfig, 0); sqlite3_close(g.dbConfig); g.dbConfig = 0; g.zConfigDbName = 0; }else if( g.db && 0==iSlot ){ sqlite3_wal_checkpoint(g.db, 0); | > | > | 1338 1339 1340 1341 1342 1343 1344 1345 1346 1347 1348 1349 1350 1351 1352 1353 1354 1355 | g.zConfigDbName = 0; }else if( g.dbConfig ){ sqlite3_wal_checkpoint(g.dbConfig, 0); sqlite3_close(g.dbConfig); g.dbConfig = 0; g.zConfigDbName = 0; }else if( g.db && 0==iSlot ){ int rc; sqlite3_wal_checkpoint(g.db, 0); rc = sqlite3_close(g.db); if( g.fSqlTrace ) fossil_trace("-- db_close_config(%d)\n", rc); g.db = 0; g.zConfigDbName = 0; } } /* ** Open the user database in "~/.fossil". Create the database anew if |
︙ | ︙ | |||
1264 1265 1266 1267 1268 1269 1270 | char *zPath = fossil_getenv("HOMEPATH"); if( zDrive && zPath ) zHome = mprintf("%s%s", zDrive, zPath); } } } if( zHome==0 ){ if( isOptional ) return 0; | | | | | | | 1382 1383 1384 1385 1386 1387 1388 1389 1390 1391 1392 1393 1394 1395 1396 1397 1398 1399 1400 1401 1402 1403 1404 1405 1406 1407 1408 1409 1410 1411 1412 1413 1414 1415 1416 1417 1418 1419 1420 1421 1422 1423 1424 1425 1426 1427 1428 1429 | char *zPath = fossil_getenv("HOMEPATH"); if( zDrive && zPath ) zHome = mprintf("%s%s", zDrive, zPath); } } } if( zHome==0 ){ if( isOptional ) return 0; fossil_panic("cannot locate home directory - please set the " "FOSSIL_HOME, LOCALAPPDATA, APPDATA, or HOMEPATH " "environment variables"); } #else if( zHome==0 ){ zHome = fossil_getenv("HOME"); } if( zHome==0 ){ if( isOptional ) return 0; fossil_panic("cannot locate home directory - please set the " "FOSSIL_HOME or HOME environment variables"); } #endif if( file_isdir(zHome, ExtFILE)!=1 ){ if( isOptional ) return 0; fossil_panic("invalid home directory: %s", zHome); } #if defined(_WIN32) || defined(__CYGWIN__) /* . filenames give some window systems problems and many apps problems */ zDbName = mprintf("%//_fossil", zHome); #else zDbName = mprintf("%s/.fossil", zHome); #endif if( file_size(zDbName, ExtFILE)<1024*3 ){ if( file_access(zHome, W_OK) ){ if( isOptional ) return 0; fossil_panic("home directory %s must be writeable", zHome); } db_init_database(zDbName, zConfigSchema, (char*)0); } if( file_access(zDbName, W_OK) ){ if( isOptional ) return 0; fossil_panic("configuration file %s must be writeable", zDbName); } if( useAttach ){ db_open_or_attach(zDbName, "configdb"); g.dbConfig = 0; }else{ g.dbConfig = db_open(zDbName); db_set_main_schemaname(g.dbConfig, "configdb"); |
︙ | ︙ | |||
1502 1503 1504 1505 1506 1507 1508 | } } if( file_access(zDbName, R_OK) || file_size(zDbName, ExtFILE)<1024 ){ if( file_access(zDbName, F_OK) ){ #ifdef FOSSIL_ENABLE_JSON g.json.resultCode = FSL_JSON_E_DB_NOT_FOUND; #endif | | | | > > > > > > > > > > > > > > > > > | 1620 1621 1622 1623 1624 1625 1626 1627 1628 1629 1630 1631 1632 1633 1634 1635 1636 1637 1638 1639 1640 1641 1642 1643 1644 1645 1646 1647 1648 1649 1650 1651 1652 1653 1654 1655 1656 1657 1658 1659 1660 1661 1662 1663 1664 1665 1666 1667 1668 1669 1670 1671 1672 1673 1674 1675 1676 1677 1678 1679 1680 1681 1682 | } } if( file_access(zDbName, R_OK) || file_size(zDbName, ExtFILE)<1024 ){ if( file_access(zDbName, F_OK) ){ #ifdef FOSSIL_ENABLE_JSON g.json.resultCode = FSL_JSON_E_DB_NOT_FOUND; #endif fossil_fatal("repository does not exist or" " is in an unreadable directory: %s", zDbName); }else if( file_access(zDbName, R_OK) ){ #ifdef FOSSIL_ENABLE_JSON g.json.resultCode = FSL_JSON_E_DENIED; #endif fossil_fatal("read permission denied for repository %s", zDbName); }else{ #ifdef FOSSIL_ENABLE_JSON g.json.resultCode = FSL_JSON_E_DB_NOT_VALID; #endif fossil_fatal("not a valid repository: %s", zDbName); } } g.zRepositoryName = mprintf("%s", zDbName); db_open_or_attach(g.zRepositoryName, "repository"); g.repositoryOpen = 1; sqlite3_file_control(g.db, "repository", SQLITE_FCNTL_DATA_VERSION, &g.iRepoDataVers); /* Cache "allow-symlinks" option, because we'll need it on every stat call */ g.allowSymlinks = db_get_boolean("allow-symlinks", db_allow_symlinks_by_default()); g.zAuxSchema = db_get("aux-schema",""); g.eHashPolicy = db_get_int("hash-policy",-1); if( g.eHashPolicy<0 ){ g.eHashPolicy = hname_default_policy(); db_set_int("hash-policy", g.eHashPolicy, 0); } /* Make a change to the CHECK constraint on the BLOB table for ** version 2.0 and later. */ rebuild_schema_update_2_0(); /* Do the Fossil-2.0 schema updates */ } /* ** Return true if there have been any changes to the repository ** database since it was opened. ** ** Changes to "config" and "localdb" and "temp" do not count. ** This routine only returns true if there have been changes ** to "repository". */ int db_repository_has_changed(void){ unsigned int v; if( !g.repositoryOpen ) return 0; sqlite3_file_control(g.db, "repository", SQLITE_FCNTL_DATA_VERSION, &v); return g.iRepoDataVers != v; } /* ** Flags for the db_find_and_open_repository() function. */ #if INTERFACE #define OPEN_OK_NOT_FOUND 0x001 /* Do not error out if not found */ #define OPEN_ANY_SCHEMA 0x002 /* Do not error if schema is wrong */ |
︙ | ︙ | |||
1702 1703 1704 1705 1706 1707 1708 | sqlite3_status(SQLITE_STATUS_PAGECACHE_OVERFLOW, &cur, &hiwtr, 0); fprintf(stderr, "-- PCACHE_OVFLOW %10d %10d\n", cur, hiwtr); fprintf(stderr, "-- prepared statements %10d\n", db.nPrepare); } while( db.pAllStmt ){ db_finalize(db.pAllStmt); } | > > > | > | > > > > > > > > > > > > > > > > | 1837 1838 1839 1840 1841 1842 1843 1844 1845 1846 1847 1848 1849 1850 1851 1852 1853 1854 1855 1856 1857 1858 1859 1860 1861 1862 1863 1864 1865 1866 1867 1868 1869 1870 1871 1872 1873 1874 1875 1876 1877 1878 1879 1880 1881 1882 1883 1884 1885 1886 1887 1888 1889 1890 1891 1892 1893 1894 1895 1896 1897 1898 1899 1900 1901 1902 1903 1904 1905 | sqlite3_status(SQLITE_STATUS_PAGECACHE_OVERFLOW, &cur, &hiwtr, 0); fprintf(stderr, "-- PCACHE_OVFLOW %10d %10d\n", cur, hiwtr); fprintf(stderr, "-- prepared statements %10d\n", db.nPrepare); } while( db.pAllStmt ){ db_finalize(db.pAllStmt); } if( db.nBegin && reportErrors ){ fossil_warning("Transaction started at %s:%d never commits", db.zStartFile, db.iStartLine); db_end_transaction(1); } pStmt = 0; g.dbIgnoreErrors++; /* Stop "database locked" warnings from PRAGMA optimize */ sqlite3_exec(g.db, "PRAGMA optimize", 0, 0, 0); g.dbIgnoreErrors--; db_close_config(); /* If the localdb has a lot of unused free space, ** then VACUUM it as we shut down. */ if( db_database_slot("localdb")>=0 ){ int nFree = db_int(0, "PRAGMA localdb.freelist_count"); int nTotal = db_int(0, "PRAGMA localdb.page_count"); if( nFree>nTotal/4 ){ db_multi_exec("VACUUM localdb;"); } } if( g.db ){ int rc; sqlite3_wal_checkpoint(g.db, 0); rc = sqlite3_close(g.db); if( g.fSqlTrace ) fossil_trace("-- sqlite3_close(%d)\n", rc); if( rc==SQLITE_BUSY && reportErrors ){ while( (pStmt = sqlite3_next_stmt(g.db, pStmt))!=0 ){ fossil_warning("unfinalized SQL statement: [%s]", sqlite3_sql(pStmt)); } } g.db = 0; } g.repositoryOpen = 0; g.localOpen = 0; assert( g.dbConfig==0 ); assert( g.zConfigDbName==0 ); backoffice_run_if_needed(); } /* ** Close the database as quickly as possible without unnecessary processing. */ void db_panic_close(void){ if( g.db ){ int rc; sqlite3_wal_checkpoint(g.db, 0); rc = sqlite3_close(g.db); if( g.fSqlTrace ) fossil_trace("-- sqlite3_close(%d)\n", rc); } g.db = 0; g.repositoryOpen = 0; g.localOpen = 0; } /* ** Create a new empty repository database with the given name. ** ** Only the schema is initialized. The required VAR tables entries ** are not set by this routine and must be set separately in order ** to make the new file a valid database. |
︙ | ︙ | |||
2741 2742 2743 2744 2745 2746 2747 | info_cmd(); } /* ** Print the current value of a setting identified by the pSetting ** pointer. */ | | | 2896 2897 2898 2899 2900 2901 2902 2903 2904 2905 2906 2907 2908 2909 2910 | info_cmd(); } /* ** Print the current value of a setting identified by the pSetting ** pointer. */ void print_setting(const Setting *pSetting){ Stmt q; if( g.repositoryOpen ){ db_prepare(&q, "SELECT '(local)', value FROM config WHERE name=%Q" " UNION ALL " "SELECT '(global)', value FROM global_config WHERE name=%Q", pSetting->name, pSetting->name |
︙ | ︙ | |||
2867 2868 2869 2870 2871 2872 2873 2874 2875 2876 2877 2878 2879 2880 | */ /* ** SETTING: autosync-tries width=16 default=1 ** If autosync is enabled setting this to a value greater ** than zero will cause autosync to try no more than this ** number of attempts if there is a sync failure. */ /* ** SETTING: binary-glob width=40 versionable block-text ** The VALUE of this setting is a comma or newline-separated list of ** GLOB patterns that should be treated as binary files ** for committing and merging purposes. Example: *.jpg */ #if defined(_WIN32)||defined(__CYGWIN__)||defined(__DARWIN__) | > > > > > > > > > > > > > | 3022 3023 3024 3025 3026 3027 3028 3029 3030 3031 3032 3033 3034 3035 3036 3037 3038 3039 3040 3041 3042 3043 3044 3045 3046 3047 3048 | */ /* ** SETTING: autosync-tries width=16 default=1 ** If autosync is enabled setting this to a value greater ** than zero will cause autosync to try no more than this ** number of attempts if there is a sync failure. */ /* ** SETTING: backoffice-nodelay boolean default=off ** If backoffice-nodelay is true, then the backoffice processing ** will never invoke sleep(). If it has nothing useful to do, ** it simply exits. */ /* ** SETTING: backoffice-logfile width=40 ** If backoffice-logfile is not an empty string and is a valid ** filename, then a one-line message is appended to that file ** every time the backoffice runs. This can be used for debugging, ** to ensure that backoffice is running appropriately. */ /* ** SETTING: binary-glob width=40 versionable block-text ** The VALUE of this setting is a comma or newline-separated list of ** GLOB patterns that should be treated as binary files ** for committing and merging purposes. Example: *.jpg */ #if defined(_WIN32)||defined(__CYGWIN__)||defined(__DARWIN__) |
︙ | ︙ |
Changes to src/default_css.txt.
︙ | ︙ | |||
182 183 184 185 186 187 188 | } span.infoTag { font-weight: bold; } span.wikiTagCancelled { text-decoration: line-through; } | | > | > > | > < < > | | 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 | } span.infoTag { font-weight: bold; } span.wikiTagCancelled { text-decoration: line-through; } div.columns { padding: 0 2em 0 2em; max-width: 1000px; } div.columns ul { margin: 0; padding: 0; } div.columns ul li:first-child { margin-top:0px; } .filetree { margin: 1em 0; line-height: 1.5; } .filetree > ul { display: inline-block; |
︙ | ︙ | |||
347 348 349 350 351 352 353 | text-align: center; padding-right: 15px; } td.usetupListCon { text-align: left } div.ueditCapBox { | < | 350 351 352 353 354 355 356 357 358 359 360 361 362 363 | text-align: center; padding-right: 15px; } td.usetupListCon { text-align: left } div.ueditCapBox { margin-right: 20px; margin-bottom: 20px; } td.usetupEditLabel { text-align: right; vertical-align: top; white-space: nowrap; |
︙ | ︙ | |||
532 533 534 535 536 537 538 539 540 541 542 543 544 545 | word-wrap: break-word; } pre.th1error { white-space: pre-wrap; word-wrap: break-word; color: red; } .statistics-report-graph-line { background-color: #446979; } .statistics-report-table-events th { padding: 0 1em 0 1em; } .statistics-report-table-events td { | > > > > | 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 | word-wrap: break-word; } pre.th1error { white-space: pre-wrap; word-wrap: break-word; color: red; } pre.textPlain { white-space: pre-wrap; word-wrap: break-word; } .statistics-report-graph-line { background-color: #446979; } .statistics-report-table-events th { padding: 0 1em 0 1em; } .statistics-report-table-events td { |
︙ | ︙ | |||
629 630 631 632 633 634 635 | display: none; } table.label-value th { vertical-align: top; text-align: right; padding: 0.2ex 1ex; } | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 | display: none; } table.label-value th { vertical-align: top; text-align: right; padding: 0.2ex 1ex; } table.forum_post { margin-top: 1ex; margin-bottom: 1ex; margin-left: 0; margin-right: 0; border-spacing: 0; } span.forum_author { color: #888; font-size: 75%; } span.forum_author::after { content: " | "; } span.forum_age { color: #888; font-size: 85%; } span.forum_buttons { font-size: 85%; } span.forum_buttons::before { color: #888; content: " | "; } span.forum_npost { color: #888; font-size: 75%; } table.forumeditform td { vertical-align: top; border-collapse: collapse; padding: 1px; } div.forum_body p { margin-top: 0; } td.form_label { vertical-align: top; text-align: right; } .debug { background-color: #ffc; border: 2px solid #ff0; } div.forumEdit { border: 1px solid black; padding-left: 1ex; padding-right: 1ex; } div.forumHier, div.forumTime { border: 1px solid black; padding-left: 1ex; padding-right: 1ex; margin-top: 1ex; } div.forumSel { background-color: #cef; } div.forumObs { color: #bbb; } #capabilitySummary { text-align: center; } #capabilitySummary td { padding-left: 3ex; padding-right: 3ex; } #capabilitySummary th { padding-left: 1ex; padding-right: 1ex; } .capsumOff { background-color: #bbb; } .capsumRead { background-color: #bfb; } .capsumWrite { background-color: #ffb; } label { white-space: nowrap; } |
Changes to src/diff.c.
︙ | ︙ | |||
2214 2215 2216 2217 2218 2219 2220 2221 2222 2223 2224 2225 2226 2227 2228 2229 2230 2231 2232 2233 2234 2235 2236 2237 2238 2239 | int rid; /* Artifact ID of the file being annotated */ int fnid; /* Filename ID */ Stmt q; /* Query returning all ancestor versions */ int cnt = 0; /* Number of versions analyzed */ int iLimit; /* Maximum number of versions to analyze */ sqlite3_int64 mxTime; /* Halt at this time if not already complete */ if( zLimit ){ if( strcmp(zLimit,"none")==0 ){ iLimit = 0; mxTime = 0; }else if( sqlite3_strglob("*[0-9]s", zLimit)==0 ){ iLimit = 0; mxTime = current_time_in_milliseconds() + 1000.0*atof(zLimit); }else{ iLimit = atoi(zLimit); if( iLimit<=0 ) iLimit = 30; mxTime = 0; } }else{ /* Default limit is as much as we can do in 1.000 seconds */ iLimit = 0; mxTime = current_time_in_milliseconds()+1000; } db_begin_transaction(); | > > | | 2214 2215 2216 2217 2218 2219 2220 2221 2222 2223 2224 2225 2226 2227 2228 2229 2230 2231 2232 2233 2234 2235 2236 2237 2238 2239 2240 2241 2242 2243 2244 2245 2246 2247 2248 2249 | int rid; /* Artifact ID of the file being annotated */ int fnid; /* Filename ID */ Stmt q; /* Query returning all ancestor versions */ int cnt = 0; /* Number of versions analyzed */ int iLimit; /* Maximum number of versions to analyze */ sqlite3_int64 mxTime; /* Halt at this time if not already complete */ memset(p, 0, sizeof(*p)); if( zLimit ){ if( strcmp(zLimit,"none")==0 ){ iLimit = 0; mxTime = 0; }else if( sqlite3_strglob("*[0-9]s", zLimit)==0 ){ iLimit = 0; mxTime = current_time_in_milliseconds() + 1000.0*atof(zLimit); }else{ iLimit = atoi(zLimit); if( iLimit<=0 ) iLimit = 30; mxTime = 0; } }else{ /* Default limit is as much as we can do in 1.000 seconds */ iLimit = 0; mxTime = current_time_in_milliseconds()+1000; } db_begin_transaction(); /* Get the artifact ID for the check-in begin analyzed */ if( zRevision ){ cid = name_to_typed_rid(zRevision, "ci"); }else{ db_must_be_within_tree(); cid = db_lget_int("checkout", 0); } origid = zOrigin ? name_to_typed_rid(zOrigin, "ci") : 0; |
︙ | ︙ | |||
2306 2307 2308 2309 2310 2311 2312 2313 2314 2315 2316 2317 2318 2319 | blob_to_utf8_no_bom(&step, 0); annotation_step(p, &step, p->nVers-1, annFlags); blob_reset(&step); } p->nVers++; cnt++; } db_finalize(&q); db_end_transaction(0); } /* ** Return a color from a gradient. */ | > > > > > > > > > | 2308 2309 2310 2311 2312 2313 2314 2315 2316 2317 2318 2319 2320 2321 2322 2323 2324 2325 2326 2327 2328 2329 2330 | blob_to_utf8_no_bom(&step, 0); annotation_step(p, &step, p->nVers-1, annFlags); blob_reset(&step); } p->nVers++; cnt++; } if( p->nVers==0 ){ if( zRevision ){ fossil_fatal("file %s does not exist in check-in %s", zFilename, zRevision); }else{ fossil_fatal("no history for file: %s", zFilename); } } db_finalize(&q); db_end_transaction(0); } /* ** Return a color from a gradient. */ |
︙ | ︙ | |||
2348 2349 2350 2351 2352 2353 2354 | ** check-in). /blame and /praise also show the user who made the check-in. ** ** Reverse Annotations: Normally, these web pages look at versions of ** FILENAME moving backwards in time back toward the root check-in. However, ** if the origin= query parameter is used to specify some future check-in ** (example: "origin=trunk") then these pages show changes moving towards ** that alternative origin. Thus using "origin=trunk" on an historical | | | < | 2359 2360 2361 2362 2363 2364 2365 2366 2367 2368 2369 2370 2371 2372 2373 2374 2375 2376 2377 2378 2379 2380 2381 2382 2383 2384 2385 2386 2387 2388 2389 2390 | ** check-in). /blame and /praise also show the user who made the check-in. ** ** Reverse Annotations: Normally, these web pages look at versions of ** FILENAME moving backwards in time back toward the root check-in. However, ** if the origin= query parameter is used to specify some future check-in ** (example: "origin=trunk") then these pages show changes moving towards ** that alternative origin. Thus using "origin=trunk" on an historical ** version of the file shows the first time each line in the file was changed ** or removed by any subsequent check-in. ** ** Query parameters: ** ** checkin=ID The check-in at which to start the annotation ** filename=FILENAME The filename. ** filevers=BOOLEAN Show file versions rather than check-in versions ** limit=LIMIT Limit the amount of analysis: ** "none" No limit ** "Xs" As much as can be computed in X seconds ** "N" N versions ** log=BOOLEAN Show a log of versions analyzed ** origin=ID The origin checkin. If unspecified, the root ** check-in over the entire repository is used. ** Specify "origin=trunk" or similar for a reverse ** annotation ** w=BOOLEAN Ignore whitespace */ void annotation_page(void){ int i; const char *zLimit; /* Depth limit */ u64 annFlags = DIFF_STRIP_EOLCR; int showLog; /* True to display the log */ int fileVers; /* Show file version instead of check-in versions */ |
︙ | ︙ | |||
2536 2537 2538 2539 2540 2541 2542 | ** ** Reverse Annotations: Normally, these commands look at versions of ** FILENAME moving backwards in time back toward the root check-in, and ** thus the output shows the most recent change to each line. However, ** if the -o|--origin option is used to specify some future check-in ** (example: "-o trunk") then these commands show changes moving towards ** that alternative origin. Thus using "-o trunk" on an historical version | | | | 2546 2547 2548 2549 2550 2551 2552 2553 2554 2555 2556 2557 2558 2559 2560 2561 | ** ** Reverse Annotations: Normally, these commands look at versions of ** FILENAME moving backwards in time back toward the root check-in, and ** thus the output shows the most recent change to each line. However, ** if the -o|--origin option is used to specify some future check-in ** (example: "-o trunk") then these commands show changes moving towards ** that alternative origin. Thus using "-o trunk" on an historical version ** of the file shows the first time each line in the file was changed or ** removed by any subsequent check-in. ** ** Options: ** --filevers Show file version numbers rather than ** check-in versions ** -r|--revision VERSION The specific check-in containing the file ** -l|--log List all versions analyzed ** -n|--limit LIMIT Limit the amount of analysis: |
︙ | ︙ |
Changes to src/diffcmd.c.
︙ | ︙ | |||
834 835 836 837 838 839 840 841 842 843 844 845 846 847 | ** --command PROG External diff program - overrides "diff-command" ** --context|-c N Use N lines of context ** --diff-binary BOOL Include binary files when using external commands ** --exec-abs-paths Force absolute path names with external commands. ** --exec-rel-paths Force relative path names with external commands. ** --from|-r VERSION Select VERSION as source for the diff ** --internal|-i Use internal diff logic ** --numstat Show only the number of lines delete and added ** --side-by-side|-y Side-by-side diff ** --strip-trailing-cr Strip trailing CR ** --tk Launch a Tcl/Tk GUI for display ** --to VERSION Select VERSION as target for the diff ** --undo Diff against the "undo" buffer ** --unified Unified diff | > | 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 | ** --command PROG External diff program - overrides "diff-command" ** --context|-c N Use N lines of context ** --diff-binary BOOL Include binary files when using external commands ** --exec-abs-paths Force absolute path names with external commands. ** --exec-rel-paths Force relative path names with external commands. ** --from|-r VERSION Select VERSION as source for the diff ** --internal|-i Use internal diff logic ** --new-file|-N Show complete text of added and deleted files ** --numstat Show only the number of lines delete and added ** --side-by-side|-y Side-by-side diff ** --strip-trailing-cr Strip trailing CR ** --tk Launch a Tcl/Tk GUI for display ** --to VERSION Select VERSION as target for the diff ** --undo Diff against the "undo" buffer ** --unified Unified diff |
︙ | ︙ |
Changes to src/dispatch.c.
︙ | ︙ | |||
347 348 349 350 351 352 353 | }else{ @ <blockquote> help_to_html(pCmd->zHelp, cgi_output_blob()); @ </blockquote> } } }else{ | | > | | < < < < < < < < < < < < < < | < < | < < > | | < < < < < < < < < < < < < | < < | | < < > | | < > > > > | > > | > > > > | | < < < < < < < | < < | < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < | 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 | }else{ @ <blockquote> help_to_html(pCmd->zHelp, cgi_output_blob()); @ </blockquote> } } }else{ int i; style_header("Help"); @ <a name='commands'></a> @ <h1>Available commands:</h1> @ <div class="columns" style="column-width: 12ex;"> @ <ul> for(i=0; i<MX_COMMAND; i++){ const char *z = aCommand[i].zName; const char *zBoldOn = aCommand[i].eCmdFlags&CMDFLAG_1ST_TIER?"<b>" :""; const char *zBoldOff = aCommand[i].eCmdFlags&CMDFLAG_1ST_TIER?"</b>":""; if( '/'==*z || strncmp(z,"test",4)==0 ) continue; if( (aCommand[i].eCmdFlags & CMDFLAG_SETTING)!=0 ) continue; @ <li><a href="%R/help?cmd=%s(z)">%s(zBoldOn)%s(z)%s(zBoldOff)</a></li> } @ </ui></div> @ <a name='webpages'></a> @ <h1>Available web UI pages:</h1> @ <div class="columns" style="column-width: 18ex;"> @ <ul> for(i=0; i<MX_COMMAND; i++){ const char *z = aCommand[i].zName; if( '/'!=*z ) continue; if( aCommand[i].zHelp[0] ){ @ <li><a href="%R/help?cmd=%s(z)">%s(z+1)</a></li> }else{ @ <li>%s(z+1)</li> } } @ </ul></div> @ <a name='unsupported'></a> @ <h1>Unsupported commands:</h1> @ <div class="columns" style="column-width: 20ex;"> @ <ul> for(i=0; i<MX_COMMAND; i++){ const char *z = aCommand[i].zName; if( strncmp(z,"test",4)!=0 ) continue; if( aCommand[i].zHelp[0] ){ @ <li><a href="%R/help?cmd=%s(z)">%s(z)</a></li> }else{ @ <li>%s(z)</li> } } @ </ul></div> @ <a name='settings'></a> @ <h1>Settings:</h1> @ <div class="columns" style="column-width: 20ex;"> @ <ul> for(i=0; i<MX_COMMAND; i++){ const char *z = aCommand[i].zName; if( (aCommand[i].eCmdFlags & CMDFLAG_SETTING)==0 ) continue; if( aCommand[i].zHelp[0] ){ @ <li><a href="%R/help?cmd=%s(z)">%s(z)</a></li> }else{ @ <li>%s(z)</li> } } @ </ul></div> } style_footer(); } /* ** WEBPAGE: test-all-help |
︙ | ︙ |
Changes to src/doc.c.
︙ | ︙ | |||
296 297 298 299 300 301 302 | ** Verify that all entries in the aMime[] table are in sorted order. ** Abort with a fatal error if any is out-of-order. */ static void mimetype_verify(void){ int i; for(i=1; i<count(aMime); i++){ if( fossil_strcmp(aMime[i-1].zSuffix,aMime[i].zSuffix)>=0 ){ | | | 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 | ** Verify that all entries in the aMime[] table are in sorted order. ** Abort with a fatal error if any is out-of-order. */ static void mimetype_verify(void){ int i; for(i=1; i<count(aMime); i++){ if( fossil_strcmp(aMime[i-1].zSuffix,aMime[i].zSuffix)>=0 ){ fossil_panic("mimetypes out of sequence: %s before %s", aMime[i-1].zSuffix, aMime[i].zSuffix); } } } /* ** Guess the mime-type of a document based on its name. |
︙ | ︙ | |||
662 663 664 665 666 667 668 | zFullpath = mprintf("%s/%s", g.zLocalRoot, zName); if( file_isfile(zFullpath, RepoFILE) && blob_read_from_file(&filebody, zFullpath, RepoFILE)>0 ){ rid = 1; /* Fake RID just to get the loop to end */ } fossil_free(zFullpath); }else{ | | | | 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 | zFullpath = mprintf("%s/%s", g.zLocalRoot, zName); if( file_isfile(zFullpath, RepoFILE) && blob_read_from_file(&filebody, zFullpath, RepoFILE)>0 ){ rid = 1; /* Fake RID just to get the loop to end */ } fossil_free(zFullpath); }else{ vid = symbolic_name_to_rid(zCheckin, "ci"); rid = vid>0 ? doc_load_content(vid, zName, &filebody) : 0; } } g.zPath = mprintf("%s/%s", g.zPath, zPathSuffix); if( rid==0 ) goto doc_not_found; blob_to_utf8_no_bom(&filebody, 0); /* The file is now contained in the filebody blob. Deliver the |
︙ | ︙ | |||
762 763 764 765 766 767 768 | cgi_set_status(404, "Not Found"); style_header("Not Found"); @ <p>Document %h(zOrigName) not found if( fossil_strcmp(zCheckin,"ckout")!=0 ){ @ in %z(href("%R/tree?ci=%T",zCheckin))%h(zCheckin)</a> } style_footer(); | < | 762 763 764 765 766 767 768 769 770 771 772 773 774 775 | cgi_set_status(404, "Not Found"); style_header("Not Found"); @ <p>Document %h(zOrigName) not found if( fossil_strcmp(zCheckin,"ckout")!=0 ){ @ in %z(href("%R/tree?ci=%T",zCheckin))%h(zCheckin)</a> } style_footer(); return; } /* ** The default logo. */ static const unsigned char aLogo[] = { |
︙ | ︙ |
Added src/email.c.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952 953 954 955 956 957 958 959 960 961 962 963 964 965 966 967 968 969 970 971 972 973 974 975 976 977 978 979 980 981 982 983 984 985 986 987 988 989 990 991 992 993 994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 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 1064 1065 1066 1067 1068 1069 1070 1071 1072 1073 1074 1075 1076 1077 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 1105 1106 1107 1108 1109 1110 1111 1112 1113 1114 1115 1116 1117 1118 1119 1120 1121 1122 1123 1124 1125 1126 1127 1128 1129 1130 1131 1132 1133 1134 1135 1136 1137 1138 1139 1140 1141 1142 1143 1144 1145 1146 1147 1148 1149 1150 1151 1152 1153 1154 1155 1156 1157 1158 1159 1160 1161 1162 1163 1164 1165 1166 1167 1168 1169 1170 1171 1172 1173 1174 1175 1176 1177 1178 1179 1180 1181 1182 1183 1184 1185 1186 1187 1188 1189 1190 1191 1192 1193 1194 1195 1196 1197 1198 1199 1200 1201 1202 1203 1204 1205 1206 1207 1208 1209 1210 1211 1212 1213 1214 1215 1216 1217 1218 1219 1220 1221 1222 1223 1224 1225 1226 1227 1228 1229 1230 1231 1232 1233 1234 1235 1236 1237 1238 1239 1240 1241 1242 1243 1244 1245 1246 1247 1248 1249 1250 1251 1252 1253 1254 1255 1256 1257 1258 1259 1260 1261 1262 1263 1264 1265 1266 1267 1268 1269 1270 1271 1272 1273 1274 1275 1276 1277 1278 1279 1280 1281 1282 1283 1284 1285 1286 1287 1288 1289 1290 1291 1292 1293 1294 1295 1296 1297 1298 1299 1300 1301 1302 1303 1304 1305 1306 1307 1308 1309 1310 1311 1312 1313 1314 1315 1316 1317 1318 1319 1320 1321 1322 1323 1324 1325 1326 1327 1328 1329 1330 1331 1332 1333 1334 1335 1336 1337 1338 1339 1340 1341 1342 1343 1344 1345 1346 1347 1348 1349 1350 1351 1352 1353 1354 1355 1356 1357 1358 1359 1360 1361 1362 1363 1364 1365 1366 1367 1368 1369 1370 1371 1372 1373 1374 1375 1376 1377 1378 1379 1380 1381 1382 1383 1384 1385 1386 1387 1388 1389 1390 1391 1392 1393 1394 1395 1396 1397 1398 1399 1400 1401 1402 1403 1404 1405 1406 1407 1408 1409 1410 1411 1412 1413 1414 1415 1416 1417 1418 1419 1420 1421 1422 1423 1424 1425 1426 1427 1428 1429 1430 1431 1432 1433 1434 1435 1436 1437 1438 1439 1440 1441 1442 1443 1444 1445 1446 1447 1448 1449 1450 1451 1452 1453 1454 1455 1456 1457 1458 1459 1460 1461 1462 1463 1464 1465 1466 1467 1468 1469 1470 1471 1472 1473 1474 1475 1476 1477 1478 1479 1480 1481 1482 1483 1484 1485 1486 1487 1488 1489 1490 1491 1492 1493 1494 1495 1496 1497 1498 1499 1500 1501 1502 1503 1504 1505 1506 1507 1508 1509 1510 1511 1512 1513 1514 1515 1516 1517 1518 1519 1520 1521 1522 1523 1524 1525 1526 1527 1528 1529 1530 1531 1532 1533 1534 1535 1536 1537 1538 1539 1540 1541 1542 1543 1544 1545 1546 1547 1548 1549 1550 1551 1552 1553 1554 1555 1556 1557 1558 1559 1560 1561 1562 1563 1564 1565 1566 1567 1568 1569 1570 1571 1572 1573 1574 1575 1576 1577 1578 1579 1580 1581 1582 1583 1584 1585 1586 1587 1588 1589 1590 1591 1592 1593 1594 1595 1596 1597 1598 1599 1600 1601 1602 1603 1604 1605 1606 1607 1608 1609 1610 1611 1612 1613 1614 1615 1616 1617 1618 1619 1620 1621 1622 1623 1624 1625 1626 1627 1628 1629 1630 1631 1632 1633 1634 1635 1636 1637 1638 1639 1640 1641 1642 1643 1644 1645 1646 1647 1648 1649 1650 1651 1652 1653 1654 1655 1656 1657 1658 1659 1660 1661 1662 1663 1664 1665 1666 1667 1668 1669 1670 1671 1672 1673 1674 1675 1676 1677 1678 1679 1680 1681 1682 1683 1684 1685 1686 1687 1688 1689 1690 1691 1692 1693 1694 1695 1696 1697 1698 1699 1700 1701 1702 1703 1704 1705 1706 1707 1708 1709 1710 1711 1712 1713 1714 1715 1716 1717 1718 1719 1720 1721 1722 1723 1724 1725 1726 1727 1728 1729 1730 1731 1732 1733 1734 1735 1736 1737 1738 1739 1740 1741 1742 1743 1744 1745 1746 1747 1748 1749 1750 1751 1752 1753 1754 1755 1756 1757 1758 1759 1760 1761 1762 1763 1764 1765 1766 1767 1768 1769 1770 1771 1772 1773 1774 1775 1776 1777 1778 1779 1780 1781 1782 1783 1784 1785 1786 1787 1788 1789 1790 1791 1792 1793 1794 1795 1796 1797 1798 1799 1800 1801 1802 1803 1804 1805 1806 1807 1808 1809 1810 1811 1812 1813 1814 1815 1816 1817 1818 1819 1820 1821 1822 1823 1824 1825 1826 1827 1828 1829 1830 1831 1832 1833 1834 1835 1836 1837 1838 1839 1840 1841 1842 1843 1844 1845 1846 1847 1848 1849 1850 1851 1852 1853 1854 1855 1856 1857 1858 1859 1860 1861 1862 1863 1864 1865 1866 1867 1868 1869 1870 1871 1872 1873 1874 1875 1876 1877 1878 1879 1880 1881 1882 1883 1884 1885 1886 1887 1888 1889 1890 1891 1892 1893 1894 1895 1896 1897 1898 1899 1900 1901 1902 1903 1904 1905 1906 1907 1908 1909 1910 1911 1912 1913 1914 1915 1916 1917 1918 1919 1920 1921 1922 1923 1924 1925 1926 1927 1928 1929 1930 1931 1932 1933 1934 1935 1936 1937 1938 1939 1940 1941 1942 1943 1944 1945 1946 1947 1948 1949 1950 1951 1952 1953 1954 1955 1956 1957 1958 1959 1960 1961 1962 1963 1964 1965 1966 1967 1968 1969 1970 1971 1972 1973 1974 1975 1976 1977 1978 1979 1980 1981 1982 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 2027 2028 2029 2030 2031 2032 2033 2034 2035 2036 2037 2038 2039 2040 2041 2042 2043 2044 2045 2046 2047 2048 2049 2050 2051 2052 2053 2054 2055 2056 2057 2058 2059 2060 2061 2062 2063 2064 2065 2066 2067 2068 2069 2070 2071 2072 2073 2074 2075 2076 2077 2078 2079 2080 2081 2082 2083 2084 2085 2086 2087 2088 2089 2090 2091 2092 2093 2094 2095 2096 2097 2098 2099 2100 2101 2102 2103 2104 2105 2106 2107 2108 2109 2110 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 2138 2139 2140 2141 2142 2143 2144 2145 2146 2147 2148 2149 2150 2151 2152 2153 2154 2155 2156 2157 2158 2159 2160 2161 2162 2163 2164 2165 2166 2167 2168 2169 2170 2171 2172 2173 2174 2175 2176 2177 2178 2179 2180 2181 2182 2183 2184 2185 2186 2187 2188 2189 2190 2191 2192 2193 2194 2195 2196 2197 2198 2199 2200 2201 2202 2203 2204 2205 2206 2207 2208 2209 2210 2211 2212 2213 2214 2215 2216 2217 2218 2219 2220 2221 2222 2223 2224 2225 2226 2227 2228 2229 2230 2231 2232 2233 2234 2235 2236 2237 2238 2239 2240 2241 2242 2243 2244 2245 2246 2247 2248 2249 2250 2251 2252 2253 2254 2255 2256 2257 2258 2259 2260 2261 2262 2263 2264 2265 2266 2267 2268 2269 2270 2271 2272 2273 2274 2275 2276 2277 2278 2279 2280 2281 2282 2283 2284 2285 2286 2287 2288 2289 2290 2291 2292 2293 2294 2295 2296 2297 2298 2299 2300 2301 2302 2303 2304 2305 2306 2307 2308 2309 2310 2311 2312 2313 2314 2315 2316 2317 2318 2319 2320 2321 2322 2323 2324 2325 2326 2327 2328 2329 2330 2331 2332 2333 2334 2335 2336 2337 2338 2339 2340 2341 2342 2343 2344 2345 2346 2347 2348 2349 2350 2351 2352 2353 2354 2355 2356 2357 2358 2359 2360 2361 2362 2363 2364 2365 2366 2367 2368 2369 2370 2371 2372 2373 2374 2375 2376 2377 2378 2379 2380 2381 2382 2383 2384 2385 2386 2387 2388 2389 2390 2391 2392 2393 2394 2395 2396 2397 2398 2399 2400 2401 2402 2403 2404 2405 2406 2407 2408 2409 2410 2411 2412 2413 2414 2415 2416 2417 2418 2419 2420 2421 2422 2423 2424 2425 2426 2427 2428 2429 2430 2431 2432 2433 2434 2435 2436 2437 2438 2439 2440 2441 2442 2443 2444 2445 2446 2447 2448 2449 2450 2451 2452 2453 2454 2455 2456 2457 2458 2459 2460 2461 2462 2463 2464 2465 2466 2467 2468 2469 2470 2471 2472 2473 2474 2475 2476 2477 2478 2479 2480 2481 2482 2483 2484 2485 2486 2487 2488 2489 2490 2491 2492 2493 2494 2495 2496 2497 2498 2499 2500 2501 2502 2503 2504 2505 2506 2507 2508 2509 2510 2511 2512 2513 2514 2515 2516 2517 2518 2519 2520 2521 2522 2523 2524 2525 2526 2527 2528 2529 2530 2531 2532 2533 2534 2535 2536 2537 2538 2539 2540 2541 2542 2543 2544 2545 2546 2547 2548 2549 2550 2551 2552 2553 2554 2555 2556 2557 2558 2559 2560 2561 2562 2563 2564 2565 2566 2567 2568 2569 2570 2571 2572 2573 2574 2575 2576 2577 2578 2579 2580 2581 2582 2583 2584 2585 2586 2587 2588 2589 2590 2591 2592 2593 2594 2595 2596 2597 2598 2599 2600 2601 2602 2603 2604 2605 2606 2607 2608 2609 2610 2611 2612 2613 2614 2615 2616 2617 2618 2619 2620 2621 2622 2623 2624 2625 2626 2627 2628 2629 2630 2631 2632 2633 2634 2635 2636 2637 2638 2639 2640 2641 2642 2643 2644 2645 2646 2647 2648 2649 2650 2651 2652 2653 2654 2655 2656 2657 2658 2659 2660 2661 2662 2663 2664 2665 2666 2667 2668 2669 2670 2671 2672 2673 2674 2675 2676 2677 2678 2679 2680 2681 2682 2683 2684 2685 2686 2687 2688 2689 2690 2691 2692 2693 2694 2695 2696 2697 2698 2699 2700 2701 2702 2703 2704 2705 2706 2707 2708 2709 2710 2711 2712 2713 2714 2715 2716 2717 2718 2719 2720 2721 2722 2723 2724 2725 2726 | /* ** Copyright (c) 2018 D. Richard Hipp ** ** This program is free software; you can redistribute it and/or ** modify it under the terms of the Simplified BSD License (also ** known as the "2-Clause License" or "FreeBSD License".) ** ** This program is distributed in the hope that it will be useful, ** but without any warranty; without even the implied warranty of ** merchantability or fitness for a particular purpose. ** ** Author contact information: ** drh@hwaci.com ** http://www.hwaci.com/drh/ ** ******************************************************************************* ** ** Logic for email notification, also known as "alerts". ** ** Are you looking for the code that reads and writes the internet ** email protocol? That is not here. See the "smtp.c" file instead. ** Yes, the choice of source code filenames is not the greatest, but ** it is not so bad that changing them seems justified. */ #include "config.h" #include "email.h" #include <assert.h> #include <time.h> /* ** Maximum size of the subscriberCode blob, in bytes */ #define SUBSCRIBER_CODE_SZ 32 /* ** SQL code to implement the tables needed by the email notification ** system. */ static const char zEmailInit[] = @ DROP TABLE IF EXISTS repository.subscriber; @ -- Subscribers are distinct from users. A person can have a log-in in @ -- the USER table without being a subscriber. Or a person can be a @ -- subscriber without having a USER table entry. Or they can have both. @ -- In the last case the suname column points from the subscriber entry @ -- to the USER entry. @ -- @ -- The ssub field is a string where each character indicates a particular @ -- type of event to subscribe to. Choices: @ -- a - Announcements @ -- c - Check-ins @ -- t - Ticket changes @ -- w - Wiki changes @ -- Probably different codes will be added in the future. In the future @ -- we might also add a separate table that allows subscribing to email @ -- notifications for specific branches or tags or tickets. @ -- @ CREATE TABLE repository.subscriber( @ subscriberId INTEGER PRIMARY KEY, -- numeric subscriber ID. Internal use @ subscriberCode BLOB DEFAULT (randomblob(32)) UNIQUE, -- UUID for subscriber @ semail TEXT UNIQUE COLLATE nocase,-- email address @ suname TEXT, -- corresponding USER entry @ sverified BOOLEAN DEFAULT true, -- email address verified @ sdonotcall BOOLEAN, -- true for Do Not Call @ sdigest BOOLEAN, -- true for daily digests only @ ssub TEXT, -- baseline subscriptions @ sctime INTDATE, -- When this entry was created. unixtime @ mtime INTDATE, -- Last change. unixtime @ smip TEXT -- IP address of last change @ ); @ CREATE INDEX repository.subscriberUname @ ON subscriber(suname) WHERE suname IS NOT NULL; @ @ DROP TABLE IF EXISTS repository.pending_alert; @ -- Email notifications that need to be sent. @ -- @ -- The first character of the eventid determines the event type. @ -- Remaining characters determine the specific event. For example, @ -- 'c4413' means check-in with rid=4413. @ -- @ CREATE TABLE repository.pending_alert( @ eventid TEXT PRIMARY KEY, -- Object that changed @ sentSep BOOLEAN DEFAULT false, -- individual alert sent @ sentDigest BOOLEAN DEFAULT false, -- digest alert sent @ sentMod BOOLEAN DEFAULT false -- pending moderation alert sent @ ) WITHOUT ROWID; @ @ DROP TABLE IF EXISTS repository.email_bounce; @ -- Record bounced emails. If too many bounces are received within @ -- some defined time range, then cancel the subscription. Older @ -- entries are periodically purged. @ -- @ CREATE TABLE repository.email_bounce( @ subscriberId INTEGER, -- to whom the email was sent. @ sendTime INTEGER, -- seconds since 1970 when email was sent @ rcvdTime INTEGER -- seconds since 1970 when bounce was received @ ); ; /* ** Return true if the email notification tables exist. */ int email_tables_exist(void){ return db_table_exists("repository", "subscriber"); } /* ** Make sure the table needed for email notification exist in the repository. ** ** If the bOnlyIfEnabled option is true, then tables are only created ** if the email-send-method is something other than "off". */ void email_schema(int bOnlyIfEnabled){ if( !email_tables_exist() ){ if( bOnlyIfEnabled && fossil_strcmp(db_get("email-send-method","off"),"off")==0 ){ return; /* Don't create table for disabled email */ } db_multi_exec(zEmailInit/*works-like:""*/); email_triggers_enable(); }else if( !db_table_has_column("repository","pending_alert","sentMod") ){ db_multi_exec( "ALTER TABLE repository.pending_alert" " ADD COLUMN sentMod BOOLEAN DEFAULT false;" ); } } /* ** Enable triggers that automatically populate the pending_alert ** table. */ void email_triggers_enable(void){ if( !db_table_exists("repository","pending_alert") ) return; db_multi_exec( "CREATE TRIGGER IF NOT EXISTS repository.email_trigger1\n" "AFTER INSERT ON event BEGIN\n" " INSERT INTO pending_alert(eventid)\n" " SELECT printf('%%.1c%%d',new.type,new.objid) WHERE true\n" " ON CONFLICT(eventId) DO NOTHING;\n" "END;" ); } /* ** Disable triggers the event_pending triggers. ** ** This must be called before rebuilding the EVENT table, for example ** via the "fossil rebuild" command. */ void email_triggers_disable(void){ db_multi_exec( "DROP TRIGGER IF EXISTS repository.email_trigger1;\n" ); } /* ** Return true if email alerts are active. */ int email_enabled(void){ if( !email_tables_exist() ) return 0; if( fossil_strcmp(db_get("email-send-method","off"),"off")==0 ) return 0; return 1; } /* ** If the subscriber table does not exist, then paint an error message ** web page and return true. ** ** If the subscriber table does exist, return 0 without doing anything. */ static int email_webpages_disabled(void){ if( email_tables_exist() ) return 0; style_header("Email Alerts Are Disabled"); @ <p>Email alerts are disabled on this server</p> style_footer(); return 1; } /* ** Insert a "Subscriber List" submenu link if the current user ** is an administrator. */ void email_submenu_common(void){ if( g.perm.Admin ){ if( fossil_strcmp(g.zPath,"subscribers") ){ style_submenu_element("List Subscribers","%R/subscribers"); } if( fossil_strcmp(g.zPath,"subscribe") ){ style_submenu_element("Add New Subscriber","%R/subscribe"); } } } /* ** WEBPAGE: setup_notification ** ** Administrative page for configuring and controlling email notification. ** Normally accessible via the /Admin/Notification menu. */ void setup_notification(void){ static const char *const azSendMethods[] = { "off", "Disabled", "pipe", "Pipe to a command", "db", "Store in a database", "dir", "Store in a directory", "relay", "SMTP relay" }; login_check_credentials(); if( !g.perm.Setup ){ login_needed(0); return; } db_begin_transaction(); email_submenu_common(); style_submenu_element("Send Announcement","%R/announce"); style_header("Email Notification Setup"); @ <h1>Status</h1> @ <table class="label-value"> if( email_enabled() ){ stats_for_email(); }else{ @ <th>Disabled</th> } @ </table> @ <hr> @ <h1> Configuration </h1> @ <form action="%R/setup_notification" method="post"><div> @ <input type="submit" name="submit" value="Apply Changes" /><hr> login_insert_csrf_secret(); entry_attribute("Canonical Server URL", 40, "email-url", "eurl", "", 0); @ <p><b>Required.</b> @ This is URL used as the basename for hyperlinks included in @ email alert text. Omit the trailing "/". @ Suggested value: "%h(g.zBaseURL)" @ (Property: "email-url")</p> @ <hr> entry_attribute("\"Return-Path\" email address", 20, "email-self", "eself", "", 0); @ <p><b>Required.</b> @ This is the email to which email notification bounces should be sent. @ In cases where the email notification does not align with a specific @ Fossil login account (for example, digest messages), this is also @ the "From:" address of the email notification. @ The system administrator should arrange for emails sent to this address @ to be handed off to the "fossil email incoming" command so that Fossil @ can handle bounces. (Property: "email-self")</p> @ <hr> entry_attribute("Repository Nickname", 16, "email-subname", "enn", "", 0); @ <p><b>Required.</b> @ This is short name used to identifies the repository in the @ Subject: line of email alerts. Traditionally this name is @ included in square brackets. Examples: "[fossil-src]", "[sqlite-src]". @ (Property: "email-subname")</p> @ <hr> multiple_choice_attribute("Email Send Method", "email-send-method", "esm", "off", count(azSendMethods)/2, azSendMethods); @ <p>How to send email. Requires auxiliary information from the fields @ that follow. Hint: Use the <a href="%R/announce">/announce</a> page @ to send test message to debug this setting. @ (Property: "email-send-method")</p> email_schema(1); entry_attribute("Pipe Email Text Into This Command", 60, "email-send-command", "ecmd", "sendmail -ti", 0); @ <p>When the send method is "pipe to a command", this is the command @ that is run. Email messages are piped into the standard input of this @ command. The command is expected to extract the sender address, @ recepient addresses, and subject from the header of the piped email @ text. (Property: "email-send-command")</p> entry_attribute("Store Emails In This Database", 60, "email-send-db", "esdb", "", 0); @ <p>When the send method is "store in a databaes", each email message is @ stored in an SQLite database file with the name given here. @ (Property: "email-send-db")</p> entry_attribute("Store Emails In This Directory", 60, "email-send-dir", "esdir", "", 0); @ <p>When the send method is "store in a directory", each email message is @ stored as a separate file in the directory shown here. @ (Property: "email-send-dir")</p> entry_attribute("SMTP Relay Host", 60, "email-send-relayhost", "esrh", "", 0); @ <p>When the send method is "SMTP relay", each email message is @ transmitted via the SMTP protocol (rfc5321) to a "Mail Submission @ Agent" or "MSA" (rfc4409) at the hostname shown here. Optionally @ append a colon and TCP port number (ex: smtp.example.com:587). @ The default TCP port number is 25. @ (Property: "email-send-relayhost")</p> @ <hr> entry_attribute("Administrator email address", 40, "email-admin", "eadmin", "", 0); @ <p>This is the email for the human administrator for the system. @ Abuse and trouble reports are send here. @ (Property: "email-admin")</p> @ <hr> @ <p><input type="submit" name="submit" value="Apply Changes" /></p> @ </div></form> db_end_transaction(0); style_footer(); } #if 0 /* ** Encode pMsg as MIME base64 and append it to pOut */ static void append_base64(Blob *pOut, Blob *pMsg){ int n, i, k; char zBuf[100]; n = blob_size(pMsg); for(i=0; i<n; i+=54){ k = translateBase64(blob_buffer(pMsg)+i, i+54<n ? 54 : n-i, zBuf); blob_append(pOut, zBuf, k); blob_append(pOut, "\r\n", 2); } } #endif /* ** Encode pMsg using the quoted-printable email encoding and ** append it onto pOut */ static void append_quoted(Blob *pOut, Blob *pMsg){ char *zIn = blob_str(pMsg); char c; int iCol = 0; while( (c = *(zIn++))!=0 ){ if( (c>='!' && c<='~' && c!='=' && c!=':') || (c==' ' && zIn[0]!='\r' && zIn[0]!='\n') ){ blob_append_char(pOut, c); iCol++; if( iCol>=70 ){ blob_append(pOut, "=\r\n", 3); iCol = 0; } }else if( c=='\r' && zIn[0]=='\n' ){ zIn++; blob_append(pOut, "\r\n", 2); iCol = 0; }else if( c=='\n' ){ blob_append(pOut, "\r\n", 2); iCol = 0; }else{ char x[3]; x[0] = '='; x[1] = "0123456789ABCDEF"[(c>>4)&0xf]; x[2] = "0123456789ABCDEF"[c&0xf]; blob_append(pOut, x, 3); iCol += 3; } } } #if defined(_WIN32) || defined(WIN32) # undef popen # define popen _popen # undef pclose # define pclose _pclose #endif #if INTERFACE /* ** An instance of the following object is used to send emails. */ struct EmailSender { sqlite3 *db; /* Database emails are sent to */ sqlite3_stmt *pStmt; /* Stmt to insert into the database */ const char *zDest; /* How to send email. */ const char *zDb; /* Name of database file */ const char *zDir; /* Directory in which to store as email files */ const char *zCmd; /* Command to run for each email */ const char *zFrom; /* Emails come from here */ SmtpSession *pSmtp; /* SMTP relay connection */ Blob out; /* For zDest=="blob" */ char *zErr; /* Error message */ u32 mFlags; /* Flags */ int bImmediateFail; /* On any error, call fossil_fatal() */ }; /* Allowed values for mFlags to email_sender_new(). */ #define EMAIL_IMMEDIATE_FAIL 0x0001 /* Call fossil_fatal() on any error */ #define EMAIL_TRACE 0x0002 /* Log sending process on console */ #endif /* INTERFACE */ /* ** Shutdown an emailer. Clear all information other than the error message. */ static void emailerShutdown(EmailSender *p){ sqlite3_finalize(p->pStmt); p->pStmt = 0; sqlite3_close(p->db); p->db = 0; p->zDb = 0; p->zDir = 0; p->zCmd = 0; if( p->pSmtp ){ smtp_client_quit(p->pSmtp); smtp_session_free(p->pSmtp); p->pSmtp = 0; } blob_reset(&p->out); } /* ** Put the EmailSender into an error state. */ static void emailerError(EmailSender *p, const char *zFormat, ...){ va_list ap; fossil_free(p->zErr); va_start(ap, zFormat); p->zErr = vmprintf(zFormat, ap); va_end(ap); emailerShutdown(p); if( p->mFlags & EMAIL_IMMEDIATE_FAIL ){ fossil_fatal("%s", p->zErr); } } /* ** Free an email sender object */ void email_sender_free(EmailSender *p){ if( p ){ emailerShutdown(p); fossil_free(p->zErr); fossil_free(p); } } /* ** Get an email setting value. Report an error if not configured. ** Return 0 on success and one if there is an error. */ static int emailerGetSetting( EmailSender *p, /* Where to report the error */ const char **pzVal, /* Write the setting value here */ const char *zName /* Name of the setting */ ){ const char *z = db_get(zName, 0); int rc = 0; if( z==0 || z[0]==0 ){ emailerError(p, "missing \"%s\" setting", zName); rc = 1; }else{ *pzVal = z; } return rc; } /* ** Create a new EmailSender object. ** ** The method used for sending email is determined by various email-* ** settings, and especially email-send-method. The repository ** email-send-method can be overridden by the zAltDest argument to ** cause a different sending mechanism to be used. Pass "stdout" to ** zAltDest to cause all emails to be printed to the console for ** debugging purposes. ** ** The EmailSender object returned must be freed using email_sender_free(). */ EmailSender *email_sender_new(const char *zAltDest, u32 mFlags){ EmailSender *p; p = fossil_malloc(sizeof(*p)); memset(p, 0, sizeof(*p)); blob_init(&p->out, 0, 0); p->mFlags = mFlags; if( zAltDest ){ p->zDest = zAltDest; }else{ p->zDest = db_get("email-send-method","off"); } if( fossil_strcmp(p->zDest,"off")==0 ) return p; if( emailerGetSetting(p, &p->zFrom, "email-self") ) return p; if( fossil_strcmp(p->zDest,"db")==0 ){ char *zErr; int rc; if( emailerGetSetting(p, &p->zDb, "email-send-db") ) return p; rc = sqlite3_open(p->zDb, &p->db); if( rc ){ emailerError(p, "unable to open output database file \"%s\": %s", p->zDb, sqlite3_errmsg(p->db)); return p; } rc = sqlite3_exec(p->db, "CREATE TABLE IF NOT EXISTS email(\n" " emailid INTEGER PRIMARY KEY,\n" " msg TEXT\n);", 0, 0, &zErr); if( zErr ){ emailerError(p, "CREATE TABLE failed with \"%s\"", zErr); sqlite3_free(zErr); return p; } rc = sqlite3_prepare_v2(p->db, "INSERT INTO email(msg) VALUES(?1)", -1, &p->pStmt, 0); if( rc ){ emailerError(p, "cannot prepare INSERT statement: %s", sqlite3_errmsg(p->db)); return p; } }else if( fossil_strcmp(p->zDest, "pipe")==0 ){ emailerGetSetting(p, &p->zCmd, "email-send-command"); }else if( fossil_strcmp(p->zDest, "dir")==0 ){ emailerGetSetting(p, &p->zDir, "email-send-dir"); }else if( fossil_strcmp(p->zDest, "blob")==0 ){ blob_init(&p->out, 0, 0); }else if( fossil_strcmp(p->zDest, "relay")==0 ){ const char *zRelay = 0; emailerGetSetting(p, &zRelay, "email-send-relayhost"); if( zRelay ){ u32 smtpFlags = SMTP_DIRECT; if( mFlags & EMAIL_TRACE ) smtpFlags |= SMTP_TRACE_STDOUT; p->pSmtp = smtp_session_new(p->zFrom, zRelay, smtpFlags); smtp_client_startup(p->pSmtp); } } return p; } /* ** Scan the header of the email message in pMsg looking for the ** (first) occurrance of zField. Fill pValue with the content of ** that field. ** ** This routine initializes pValue. Any prior content of pValue is ** discarded (leaked). ** ** Return non-zero on success. Return 0 if no instance of the header ** is found. */ int email_header_value(Blob *pMsg, const char *zField, Blob *pValue){ int nField = (int)strlen(zField); Blob line; blob_rewind(pMsg); blob_init(pValue,0,0); while( blob_line(pMsg, &line) ){ int n, i; char *z; blob_trim(&line); n = blob_size(&line); if( n==0 ) return 0; if( n<nField+1 ) continue; z = blob_buffer(&line); if( sqlite3_strnicmp(z, zField, nField)==0 && z[nField]==':' ){ for(i=nField+1; i<n && fossil_isspace(z[i]); i++){} blob_init(pValue, z+i, n-i); while( blob_line(pMsg, &line) ){ blob_trim(&line); n = blob_size(&line); if( n==0 ) break; z = blob_buffer(&line); if( !fossil_isspace(z[0]) ) break; for(i=1; i<n && fossil_isspace(z[i]); i++){} blob_append(pValue, " ", 1); blob_append(pValue, z+i, n-i); } return 1; } } return 0; } /* ** Make a copy of the input string up to but not including the ** first cTerm character. ** ** Verify that the string really that is to be copied really is a ** valid email address. If it is not, then return NULL. ** ** This routine is more restrictive than necessary. It does not ** allow comments, IP address, quoted strings, or certain uncommon ** characters. The only non-alphanumerics allowed in the local ** part are "_", "+", "-" and "+". */ char *email_copy_addr(const char *z, char cTerm ){ int i; int nAt = 0; int nDot = 0; char c; if( z[0]=='.' ) return 0; /* Local part cannot begin with "." */ for(i=0; (c = z[i])!=0 && c!=cTerm; i++){ if( fossil_isalnum(c) ){ /* Alphanumerics are always ok */ }else if( c=='@' ){ if( nAt ) return 0; /* Only a single "@" allowed */ if( i>64 ) return 0; /* Local part too big */ nAt = 1; nDot = 0; if( i==0 ) return 0; /* Disallow empty local part */ if( z[i-1]=='.' ) return 0; /* Last char of local cannot be "." */ if( z[i+1]=='.' || z[i+1]=='-' ){ return 0; /* Domain cannot begin with "." or "-" */ } }else if( c=='-' ){ if( z[i+1]==cTerm ) return 0; /* Last character cannot be "-" */ }else if( c=='.' ){ if( z[i+1]=='.' ) return 0; /* Do not allow ".." */ if( z[i+1]==cTerm ) return 0; /* Domain may not end with . */ nDot++; }else if( (c=='_' || c=='+') && nAt==0 ){ /* _ and + are ok in the local part */ }else{ return 0; /* Anything else is an error */ } } if( c!=cTerm ) return 0; /* Missing terminator */ if( nAt==0 ) return 0; /* No "@" found anywhere */ if( nDot==0 ) return 0; /* No "." in the domain */ /* If we reach this point, the email address is valid */ return mprintf("%.*s", i, z); } /* ** Scan the input string for a valid email address enclosed in <...> ** If the string contains one or more email addresses, extract the first ** one into memory obtained from mprintf() and return a pointer to it. ** If no valid email address can be found, return NULL. */ char *email_find_emailaddr(const char *zIn){ char *zOut = 0; while( zIn!=0 ){ zIn = (const char*)strchr(zIn, '<'); if( zIn==0 ) break; zIn++; zOut = email_copy_addr(zIn, '>'); if( zOut!=0 ) break; } return zOut; } /* ** SQL function: find_emailaddr(X) ** ** Return the first valid email address of the form <...> in input string ** X. Or return NULL if not found. */ void email_find_emailaddr_func( sqlite3_context *context, int argc, sqlite3_value **argv ){ const char *zIn = (const char*)sqlite3_value_text(argv[0]); char *zOut = email_find_emailaddr(zIn); if( zOut ){ sqlite3_result_text(context, zOut, -1, fossil_free); } } /* ** Return the hostname portion of an email address - the part following ** the @ */ char *email_hostname(const char *zAddr){ char *z = strchr(zAddr, '@'); if( z ){ z++; }else{ z = (char*)zAddr; } return z; } /* ** Return a pointer to a fake email mailbox name that corresponds ** to human-readable name zFromName. The fake mailbox name is based ** on a hash. No huge problems arise if there is a hash collisions, ** but it is still better if collisions can be avoided. ** ** The returned string is held in a static buffer and is overwritten ** by each subsequent call to this routine. */ static char *email_mailbox_name(const char *zFromName){ static char zHash[20]; unsigned int x = 0; int n = 0; while( zFromName[0] ){ n++; x = x*1103515245 + 12345 + ((unsigned char*)zFromName)[0]; zFromName++; } sqlite3_snprintf(sizeof(zHash), zHash, "noreply%x%08x", n, x); return zHash; } /* ** COMMAND: test-mailbox-hashname ** ** Usage: %fossil test-mailbox-hashname HUMAN-NAME ... ** ** Return the mailbox hash name corresponding to each human-readable ** name on the command line. This is a test interface for the ** email_mailbox_name() function. */ void email_test_mailbox_hashname(void){ int i; for(i=2; i<g.argc; i++){ fossil_print("%30s: %s\n", g.argv[i], email_mailbox_name(g.argv[i])); } } /* ** Extract all To: header values from the email header supplied. ** Store them in the array list. */ void email_header_to(Blob *pMsg, int *pnTo, char ***pazTo){ int nTo = 0; char **azTo = 0; Blob v; char *z, *zAddr; int i; email_header_value(pMsg, "to", &v); z = blob_str(&v); for(i=0; z[i]; i++){ if( z[i]=='<' && (zAddr = email_copy_addr(&z[i+1],'>'))!=0 ){ azTo = fossil_realloc(azTo, sizeof(azTo[0])*(nTo+1) ); azTo[nTo++] = zAddr; } } *pnTo = nTo; *pazTo = azTo; } /* ** Free a list of To addresses obtained from a prior call to ** email_header_to() */ void email_header_to_free(int nTo, char **azTo){ int i; for(i=0; i<nTo; i++) fossil_free(azTo[i]); fossil_free(azTo); } /* ** Send a single email message. ** ** The recepient(s) must be specified using "To:" or "Cc:" or "Bcc:" fields ** in the header. Likewise, the header must contains a "Subject:" line. ** The header might also include fields like "Message-Id:" or ** "In-Reply-To:". ** ** This routine will add fields to the header as follows: ** ** From: ** Date: ** Message-Id: ** Content-Type: ** Content-Transfer-Encoding: ** MIME-Version: ** X-Fossil-From: ** ** The caller maintains ownership of the input Blobs. This routine will ** read the Blobs and send them onward to the email system, but it will ** not free them. ** ** The Message-Id: field is added if there is not already a Message-Id ** in the pHdr parameter. ** ** If the zFromName argument is not NULL, then it should be a human-readable ** name or handle for the sender. In that case, "From:" becomes a made-up ** email address based on a hash of zFromName and the domain of email-self, ** and an additional "X-Fossil-From:" field is inserted with the email-self ** address. Downstream software might use the X-Fossil-From header to set ** the envelope-from address of the email. If zFromName is a NULL pointer, ** then the "From:" is set to the email-self value and X-Fossil-From is ** omitted. */ void email_send( EmailSender *p, /* Emailer context */ Blob *pHdr, /* Email header (incomplete) */ Blob *pBody, /* Email body */ const char *zFromName /* Optional human-readable name of sender */ ){ Blob all, *pOut; u64 r1, r2; if( p->mFlags & EMAIL_TRACE ){ fossil_print("Sending email\n"); } if( fossil_strcmp(p->zDest, "off")==0 ){ return; } if( fossil_strcmp(p->zDest, "blob")==0 ){ pOut = &p->out; if( blob_size(pOut) ){ blob_appendf(pOut, "%.72c\n", '='); } }else{ blob_init(&all, 0, 0); pOut = &all; } blob_append(pOut, blob_buffer(pHdr), blob_size(pHdr)); if( zFromName ){ blob_appendf(pOut, "From: %s <%s@%s>\r\n", zFromName, email_mailbox_name(zFromName), email_hostname(p->zFrom)); blob_appendf(pOut, "X-Fossil-From: <%s>\r\n", p->zFrom); }else{ blob_appendf(pOut, "From: <%s>\r\n", p->zFrom); } blob_appendf(pOut, "Date: %z\r\n", cgi_rfc822_datestamp(time(0))); if( strstr(blob_str(pHdr), "\r\nMessage-Id:")==0 ){ /* Message-id format: "<$(date)x$(random)@$(from-host)>" where $(date) is ** the current unix-time in hex, $(random) is a 64-bit random number, ** and $(from) is the domain part of the email-self setting. */ sqlite3_randomness(sizeof(r1), &r1); r2 = time(0); blob_appendf(pOut, "Message-Id: <%llxx%016llx@%s>\r\n", r2, r1, email_hostname(p->zFrom)); } blob_add_final_newline(pBody); blob_appendf(pOut, "MIME-Version: 1.0\r\n"); blob_appendf(pOut, "Content-Type: text/plain; charset=\"UTF-8\"\r\n"); #if 0 blob_appendf(pOut, "Content-Transfer-Encoding: base64\r\n\r\n"); append_base64(pOut, pBody); #else blob_appendf(pOut, "Content-Transfer-Encoding: quoted-printable\r\n\r\n"); append_quoted(pOut, pBody); #endif if( p->pStmt ){ int i, rc; sqlite3_bind_text(p->pStmt, 1, blob_str(&all), -1, SQLITE_TRANSIENT); for(i=0; i<100 && sqlite3_step(p->pStmt)==SQLITE_BUSY; i++){ sqlite3_sleep(10); } rc = sqlite3_reset(p->pStmt); if( rc!=SQLITE_OK ){ emailerError(p, "Failed to insert email message into output queue.\n" "%s", sqlite3_errmsg(p->db)); } }else if( p->zCmd ){ FILE *out = popen(p->zCmd, "w"); if( out ){ fwrite(blob_buffer(&all), 1, blob_size(&all), out); fclose(out); }else{ emailerError(p, "Could not open output pipe \"%s\"", p->zCmd); } }else if( p->zDir ){ char *zFile = file_time_tempname(p->zDir, ".email"); blob_write_to_file(&all, zFile); fossil_free(zFile); }else if( p->pSmtp ){ char **azTo = 0; int nTo = 0; email_header_to(pHdr, &nTo, &azTo); if( nTo>0 ){ smtp_send_msg(p->pSmtp, p->zFrom, nTo, (const char**)azTo,blob_str(&all)); email_header_to_free(nTo, azTo); } }else if( strcmp(p->zDest, "stdout")==0 ){ char **azTo = 0; int nTo = 0; int i; email_header_to(pHdr, &nTo, &azTo); for(i=0; i<nTo; i++){ fossil_print("X-To-Test-%d: [%s]\r\n", i, azTo[i]); } email_header_to_free(nTo, azTo); blob_add_final_newline(&all); fossil_print("%s", blob_str(&all)); } blob_reset(&all); } /* ** SETTING: email-send-method width=5 default=off ** Determine the method used to send email. Allowed values are ** "off", "relay", "pipe", "dir", "db", and "stdout". The "off" value ** means no email is ever sent. The "relay" value means emails are sent ** to an Mail Sending Agent using SMTP located at email-send-relayhost. ** The "pipe" value means email messages are piped into a command ** determined by the email-send-command setting. The "dir" value means ** emails are written to individual files in a directory determined ** by the email-send-dir setting. The "db" value means that emails ** are added to an SQLite database named by the* email-send-db setting. ** The "stdout" value writes email text to standard output, for debugging. */ /* ** SETTING: email-send-command width=40 ** This is a command to which outbound email content is piped when the ** email-send-method is set to "pipe". The command must extract ** recipient, sender, subject, and all other relevant information ** from the email header. */ /* ** SETTING: email-send-dir width=40 ** This is a directory into which outbound emails are written as individual ** files if the email-send-method is set to "dir". */ /* ** SETTING: email-send-db width=40 ** This is an SQLite database file into which outbound emails are written ** if the email-send-method is set to "db". */ /* ** SETTING: email-self width=40 ** This is the email address for the repository. Outbound emails add ** this email address as the "From:" field. */ /* ** SETTING: email-send-relayhost width=40 ** This is the hostname and TCP port to which output email messages ** are sent when email-send-method is "relay". There should be an ** SMTP server configured as a Mail Submission Agent listening on the ** designated host and port and all times. */ /* ** COMMAND: alerts ** ** Usage: %fossil alerts SUBCOMMAND ARGS... ** ** Subcommands: ** ** pending Show all pending alerts. Useful for debugging. ** ** reset Hard reset of all email notification tables ** in the repository. This erases all subscription ** information. ** Use with extreme care ** ** ** send Compose and send pending email alerts. ** Some installations may want to do this via ** a cron-job to make sure alerts are sent ** in a timely manner. ** Options: ** ** --digest Send digests ** --test Write to standard output ** ** settings [NAME VALUE] With no arguments, list all email settings. ** Or change the value of a single email setting. ** ** status Report on the status of the email alert ** subsystem ** ** subscribers [PATTERN] List all subscribers matching PATTERN. ** ** test-message TO [OPTS] Send a single email message using whatever ** email sending mechanism is currently configured. ** Use this for testing the email notification ** configuration. Options: ** ** --body FILENAME ** --smtp-trace ** --stdout ** --subject|-S SUBJECT ** ** unsubscribe EMAIL Remove a single subscriber with the given EMAIL. */ void email_cmd(void){ const char *zCmd; int nCmd; db_find_and_open_repository(0, 0); email_schema(0); zCmd = g.argc>=3 ? g.argv[2] : "x"; nCmd = (int)strlen(zCmd); if( strncmp(zCmd, "pending", nCmd)==0 ){ Stmt q; verify_all_options(); if( g.argc!=3 ) usage("pending"); db_prepare(&q,"SELECT eventid, sentSep, sentDigest, sentMod" " FROM pending_alert"); while( db_step(&q)==SQLITE_ROW ){ fossil_print("%10s %7s %10s %7s\n", db_column_text(&q,0), db_column_int(&q,1) ? "sentSep" : "", db_column_int(&q,2) ? "sentDigest" : "", db_column_int(&q,3) ? "sentMod" : ""); } db_finalize(&q); }else if( strncmp(zCmd, "reset", nCmd)==0 ){ int c; int bForce = find_option("force","f",0)!=0; verify_all_options(); if( bForce ){ c = 'y'; }else{ Blob yn; fossil_print( "This will erase all content in the repository tables, thus\n" "deleting all subscriber information. The information will be\n" "unrecoverable.\n"); prompt_user("Continue? (y/N) ", &yn); c = blob_str(&yn)[0]; blob_reset(&yn); } if( c=='y' ){ email_triggers_disable(); db_multi_exec( "DROP TABLE IF EXISTS subscriber;\n" "DROP TABLE IF EXISTS pending_alert;\n" "DROP TABLE IF EXISTS email_bounce;\n" /* Legacy */ "DROP TABLE IF EXISTS email_pending;\n" "DROP TABLE IF EXISTS subscription;\n" ); email_schema(0); } }else if( strncmp(zCmd, "send", nCmd)==0 ){ u32 eFlags = 0; if( find_option("digest",0,0)!=0 ) eFlags |= SENDALERT_DIGEST; if( find_option("test",0,0)!=0 ){ eFlags |= SENDALERT_PRESERVE|SENDALERT_STDOUT; } verify_all_options(); email_send_alerts(eFlags); }else if( strncmp(zCmd, "settings", nCmd)==0 ){ int isGlobal = find_option("global",0,0)!=0; int nSetting; const Setting *pSetting = setting_info(&nSetting); db_open_config(1, 0); verify_all_options(); if( g.argc!=3 && g.argc!=5 ) usage("setting [NAME VALUE]"); if( g.argc==5 ){ const char *zLabel = g.argv[3]; if( strncmp(zLabel, "email-", 6)!=0 || (pSetting = db_find_setting(zLabel, 1))==0 ){ fossil_fatal("not a valid email setting: \"%s\"", zLabel); } db_set(pSetting->name, g.argv[4], isGlobal); g.argc = 3; } pSetting = setting_info(&nSetting); for(; nSetting>0; nSetting--, pSetting++ ){ if( strncmp(pSetting->name,"email-",6)!=0 ) continue; print_setting(pSetting); } }else if( strncmp(zCmd, "status", nCmd)==0 ){ int nSetting, n; static const char *zFmt = "%-29s %d\n"; const Setting *pSetting = setting_info(&nSetting); db_open_config(1, 0); verify_all_options(); if( g.argc!=3 ) usage("status"); pSetting = setting_info(&nSetting); for(; nSetting>0; nSetting--, pSetting++ ){ if( strncmp(pSetting->name,"email-",6)!=0 ) continue; print_setting(pSetting); } n = db_int(0,"SELECT count(*) FROM pending_alert WHERE NOT sentSep"); fossil_print(zFmt/*works-like:"%s%d"*/, "pending-alerts", n); n = db_int(0,"SELECT count(*) FROM pending_alert WHERE NOT sentDigest"); fossil_print(zFmt/*works-like:"%s%d"*/, "pending-digest-alerts", n); n = db_int(0,"SELECT count(*) FROM subscriber"); fossil_print(zFmt/*works-like:"%s%d"*/, "total-subscribers", n); n = db_int(0, "SELECT count(*) FROM subscriber WHERE sverified" " AND NOT sdonotcall AND length(ssub)>1"); fossil_print(zFmt/*works-like:"%s%d"*/, "active-subscribers", n); }else if( strncmp(zCmd, "subscribers", nCmd)==0 ){ Stmt q; verify_all_options(); if( g.argc!=3 && g.argc!=4 ) usage("subscribers [PATTERN]"); if( g.argc==4 ){ char *zPattern = g.argv[3]; db_prepare(&q, "SELECT semail FROM subscriber" " WHERE semail LIKE '%%%q%%' OR suname LIKE '%%%q%%'" " OR semail GLOB '*%q*' or suname GLOB '*%q*'" " ORDER BY semail", zPattern, zPattern, zPattern, zPattern); }else{ db_prepare(&q, "SELECT semail FROM subscriber" " ORDER BY semail"); } while( db_step(&q)==SQLITE_ROW ){ fossil_print("%s\n", db_column_text(&q, 0)); } db_finalize(&q); }else if( strncmp(zCmd, "test-message", nCmd)==0 ){ Blob prompt, body, hdr; const char *zDest = find_option("stdout",0,0)!=0 ? "stdout" : 0; int i; u32 mFlags = EMAIL_IMMEDIATE_FAIL; const char *zSubject = find_option("subject", "S", 1); const char *zSource = find_option("body", 0, 1); EmailSender *pSender; if( find_option("smtp-trace",0,0)!=0 ) mFlags |= EMAIL_TRACE; verify_all_options(); blob_init(&prompt, 0, 0); blob_init(&body, 0, 0); blob_init(&hdr, 0, 0); blob_appendf(&hdr,"To: "); for(i=3; i<g.argc; i++){ if( i>3 ) blob_append(&hdr, ", ", 2); blob_appendf(&hdr, "<%s>", g.argv[i]); } blob_append(&hdr,"\r\n",2); if( zSubject==0 ) zSubject = "fossil alerts test-message"; blob_appendf(&hdr, "Subject: %s\r\n", zSubject); if( zSource ){ blob_read_from_file(&body, zSource, ExtFILE); }else{ prompt_for_user_comment(&body, &prompt); } blob_add_final_newline(&body); pSender = email_sender_new(zDest, mFlags); email_send(pSender, &hdr, &body, 0); email_sender_free(pSender); blob_reset(&hdr); blob_reset(&body); blob_reset(&prompt); }else if( strncmp(zCmd, "unsubscribe", nCmd)==0 ){ verify_all_options(); if( g.argc!=4 ) usage("unsubscribe EMAIL"); db_multi_exec( "DELETE FROM subscriber WHERE semail=%Q", g.argv[3]); }else { usage("pending|reset|send|setting|status|" "subscribers|test-message|unsubscribe"); } } /* ** Do error checking on a submitted subscription form. Return TRUE ** if the submission is valid. Return false if any problems are seen. */ static int subscribe_error_check( int *peErr, /* Type of error */ char **pzErr, /* Error message text */ int needCaptcha /* True if captcha check needed */ ){ const char *zEAddr; int i, j, n; char c; *peErr = 0; *pzErr = 0; /* Check the validity of the email address. ** ** (1) Exactly one '@' character. ** (2) No other characters besides [a-zA-Z0-9._+-] ** ** The local part is currently more restrictive than RFC 5322 allows: ** https://stackoverflow.com/a/2049510/142454 We will expand this as ** necessary. */ zEAddr = P("e"); if( zEAddr==0 ) return 0; for(i=j=n=0; (c = zEAddr[i])!=0; i++){ if( c=='@' ){ n = i; j++; continue; } if( !fossil_isalnum(c) && c!='.' && c!='_' && c!='-' && c!='+' ){ *peErr = 1; *pzErr = mprintf("illegal character in email address: 0x%x '%c'", c, c); return 0; } } if( j!=1 ){ *peErr = 1; *pzErr = mprintf("email address should contain exactly one '@'"); return 0; } if( n<1 ){ *peErr = 1; *pzErr = mprintf("name missing before '@' in email address"); return 0; } if( n>i-5 ){ *peErr = 1; *pzErr = mprintf("email domain too short"); return 0; } /* Verify the captcha */ if( needCaptcha && !captcha_is_correct(1) ){ *peErr = 2; *pzErr = mprintf("incorrect security code"); return 0; } /* Check to make sure the email address is available for reuse */ if( db_exists("SELECT 1 FROM subscriber WHERE semail=%Q", zEAddr) ){ *peErr = 1; *pzErr = mprintf("this email address is used by someone else"); return 0; } /* If we reach this point, all is well */ return 1; } /* ** Text of email message sent in order to confirm a subscription. */ static const char zConfirmMsg[] = @ Someone has signed you up for email alerts on the Fossil repository @ at %s. @ @ To confirm your subscription and begin receiving alerts, click on @ the following hyperlink: @ @ %s/alerts/%s @ @ Save the hyperlink above! You can reuse this same hyperlink to @ unsubscribe or to change the kinds of alerts you receive. @ @ If you do not want to subscribe, you can simply ignore this message. @ You will not be contacted again. @ ; /* ** Append the text of an email confirmation message to the given ** Blob. The security code is in zCode. */ void email_append_confirmation_message(Blob *pMsg, const char *zCode){ blob_appendf(pMsg, zConfirmMsg/*works-like:"%s%s%s"*/, g.zBaseURL, g.zBaseURL, zCode); } /* ** WEBPAGE: subscribe ** ** Allow users to subscribe to email notifications. ** ** This page is usually run by users who are not logged in. ** A logged-in user can add email notifications on the /alerts page. ** Access to this page by a logged in user (other than an ** administrator) results in a redirect to the /alerts page. ** ** Administrators can visit this page in order to sign up other ** users. ** ** The Email-Alerts permission ("7") is required to access this ** page. To allow anonymous passers-by to sign up for email ** notification, set Email-Alerts on user "nobody" or "anonymous". */ void subscribe_page(void){ int needCaptcha; unsigned int uSeed; const char *zDecoded; char *zCaptcha = 0; char *zErr = 0; int eErr = 0; if( email_webpages_disabled() ) return; login_check_credentials(); if( !g.perm.EmailAlert ){ login_needed(g.anon.EmailAlert); return; } if( login_is_individual() && db_exists("SELECT 1 FROM subscriber WHERE suname=%Q",g.zLogin) ){ /* This person is already signed up for email alerts. Jump ** to the screen that lets them edit their alert preferences. ** Except, administrators can create subscriptions for others so ** do not jump for them. */ if( g.perm.Admin ){ /* Admins get a link to admin their own account, but they ** stay on this page so that they can create subscriptions ** for other people. */ style_submenu_element("My Subscription","%R/alerts"); }else{ /* Everybody else jumps to the page to administer their own ** account only. */ cgi_redirectf("%R/alerts"); return; } } email_submenu_common(); needCaptcha = !login_is_individual(); if( P("submit") && cgi_csrf_safe(1) && subscribe_error_check(&eErr,&zErr,needCaptcha) ){ /* A validated request for a new subscription has been received. */ char ssub[20]; const char *zEAddr = P("e"); sqlite3_int64 id; /* New subscriber Id */ const char *zCode; /* New subscriber code (in hex) */ int nsub = 0; const char *suname = PT("suname"); if( suname==0 && needCaptcha==0 && !g.perm.Admin ) suname = g.zLogin; if( suname && suname[0]==0 ) suname = 0; if( PB("sa") ) ssub[nsub++] = 'a'; if( g.perm.Read && PB("sc") ) ssub[nsub++] = 'c'; if( g.perm.RdForum && PB("sf") ) ssub[nsub++] = 'f'; if( g.perm.RdTkt && PB("st") ) ssub[nsub++] = 't'; if( g.perm.RdWiki && PB("sw") ) ssub[nsub++] = 'w'; ssub[nsub] = 0; db_multi_exec( "INSERT INTO subscriber(semail,suname," " sverified,sdonotcall,sdigest,ssub,sctime,mtime,smip)" "VALUES(%Q,%Q,%d,0,%d,%Q,now(),now(),%Q)", /* semail */ zEAddr, /* suname */ suname, /* sverified */ needCaptcha==0, /* sdigest */ PB("di"), /* ssub */ ssub, /* smip */ g.zIpAddr ); id = db_last_insert_rowid(); zCode = db_text(0, "SELECT hex(subscriberCode) FROM subscriber WHERE subscriberId=%lld", id); if( !needCaptcha ){ /* The new subscription has been added on behalf of a logged-in user. ** No verification is required. Jump immediately to /alerts page. */ cgi_redirectf("%R/alerts/%s", zCode); return; }else{ /* We need to send a verification email */ Blob hdr, body; EmailSender *pSender = email_sender_new(0,0); blob_init(&hdr,0,0); blob_init(&body,0,0); blob_appendf(&hdr, "To: <%s>\n", zEAddr); blob_appendf(&hdr, "Subject: Subscription verification\n"); email_append_confirmation_message(&body, zCode); email_send(pSender, &hdr, &body, 0); style_header("Email Alert Verification"); if( pSender->zErr ){ @ <h1>Internal Error</h1> @ <p>The following internal error was encountered while trying @ to send the confirmation email: @ <blockquote><pre> @ %h(pSender->zErr) @ </pre></blockquote> }else{ @ <p>An email has been sent to "%h(zEAddr)". That email contains a @ hyperlink that you must click on in order to activate your @ subscription.</p> } email_sender_free(pSender); style_footer(); } return; } style_header("Signup For Email Alerts"); if( P("submit")==0 ){ /* If this is the first visit to this page (if this HTTP request did not ** come from a prior Submit of the form) then default all of the ** subscription options to "on" */ cgi_set_parameter_nocopy("sa","1",1); if( g.perm.Read ) cgi_set_parameter_nocopy("sc","1",1); if( g.perm.RdForum ) cgi_set_parameter_nocopy("sf","1",1); if( g.perm.RdTkt ) cgi_set_parameter_nocopy("st","1",1); if( g.perm.RdWiki ) cgi_set_parameter_nocopy("sw","1",1); } @ <p>To receive email notifications for changes to this @ repository, fill out the form below and press "Submit" button.</p> form_begin(0, "%R/subscribe"); @ <table class="subscribe"> @ <tr> @ <td class="form_label">Email Address:</td> @ <td><input type="text" name="e" value="%h(PD("e",""))" size="30"></td> if( eErr==1 ){ @ <td><span class="loginError">← %h(zErr)</span></td> } @ </tr> if( needCaptcha ){ uSeed = captcha_seed(); zDecoded = captcha_decode(uSeed); zCaptcha = captcha_render(zDecoded); @ <tr> @ <td class="form_label">Security Code:</td> @ <td><input type="text" name="captcha" value="" size="30"> @ <input type="hidden" name="captchaseed" value="%u(uSeed)"></td> if( eErr==2 ){ @ <td><span class="loginError">← %h(zErr)</span></td> } @ </tr> } if( g.perm.Admin ){ @ <tr> @ <td class="form_label">User:</td> @ <td><input type="text" name="suname" value="%h(PD("suname",g.zLogin))" \ @ size="30"></td> if( eErr==3 ){ @ <td><span class="loginError">← %h(zErr)</span></td> } @ </tr> } @ <tr> @ <td class="form_label">Options:</td> @ <td><label><input type="checkbox" name="sa" %s(PCK("sa"))> \ @ Announcements</label><br> if( g.perm.Read ){ @ <label><input type="checkbox" name="sc" %s(PCK("sc"))> \ @ Check-ins</label><br> } if( g.perm.RdForum ){ @ <label><input type="checkbox" name="sf" %s(PCK("sf"))> \ @ Forum Posts</label><br> } if( g.perm.RdTkt ){ @ <label><input type="checkbox" name="st" %s(PCK("st"))> \ @ Ticket changes</label><br> } if( g.perm.RdWiki ){ @ <label><input type="checkbox" name="sw" %s(PCK("sw"))> \ @ Wiki</label><br> } @ <label><input type="checkbox" name="di" %s(PCK("di"))> \ @ Daily digest only</label><br> if( g.perm.Admin ){ @ <label><input type="checkbox" name="vi" %s(PCK("vi"))> \ @ Verified</label><br> @ <label><input type="checkbox" name="dnc" %s(PCK("dnc"))> \ @ Do not call</label><br> } @ </td> @ </tr> @ <tr> @ <td></td> if( needCaptcha && !email_enabled() ){ @ <td><input type="submit" name="submit" value="Submit" disabled> @ (Email current disabled)</td> }else{ @ <td><input type="submit" name="submit" value="Submit"></td> } @ </tr> @ </table> if( needCaptcha ){ @ <div class="captcha"><table class="captcha"><tr><td><pre> @ %h(zCaptcha) @ </pre> @ Enter the 8 characters above in the "Security Code" box @ </td></tr></table></div> } @ </form> fossil_free(zErr); style_footer(); } /* ** Either shutdown or completely delete a subscription entry given ** by the hex value zName. Then paint a webpage that explains that ** the entry has been removed. */ static void email_unsubscribe(const char *zName){ char *zEmail; zEmail = db_text(0, "SELECT semail FROM subscriber" " WHERE subscriberCode=hextoblob(%Q)", zName); if( zEmail==0 ){ style_header("Unsubscribe Fail"); @ <p>Unable to locate a subscriber with the requested key</p> }else{ db_multi_exec( "DELETE FROM subscriber WHERE subscriberCode=hextoblob(%Q)", zName ); style_header("Unsubscribed"); @ <p>The "%h(zEmail)" email address has been delisted. @ All traces of that email address have been removed</p> } style_footer(); return; } /* ** WEBPAGE: alerts ** ** Edit email alert and notification settings. ** ** The subscriber is identified in either of two ways: ** ** (1) The name= query parameter contains the subscriberCode. ** ** (2) The user is logged into an account other than "nobody" or ** "anonymous". In that case the notification settings ** associated with that account can be edited without needing ** to know the subscriber code. */ void alerts_page(void){ const char *zName = P("name"); Stmt q; int sa, sc, sf, st, sw; int sdigest, sdonotcall, sverified; const char *ssub; const char *semail; const char *smip; const char *suname; const char *mtime; const char *sctime; int eErr = 0; char *zErr = 0; if( email_webpages_disabled() ) return; login_check_credentials(); if( zName==0 && login_is_individual() ){ zName = db_text(0, "SELECT hex(subscriberCode) FROM subscriber" " WHERE suname=%Q", g.zLogin); } if( zName==0 || !validate16(zName, -1) ){ cgi_redirect("subscribe"); return; } email_submenu_common(); if( P("submit")!=0 && cgi_csrf_safe(1) ){ int sdonotcall = PB("sdonotcall"); int sdigest = PB("sdigest"); char ssub[10]; int nsub = 0; if( PB("sa") ) ssub[nsub++] = 'a'; if( g.perm.Read && PB("sc") ) ssub[nsub++] = 'c'; if( g.perm.RdForum && PB("sf") ) ssub[nsub++] = 'f'; if( g.perm.RdTkt && PB("st") ) ssub[nsub++] = 't'; if( g.perm.RdWiki && PB("sw") ) ssub[nsub++] = 'w'; ssub[nsub] = 0; if( g.perm.Admin ){ const char *suname = PT("suname"); int sverified = PB("sverified"); if( suname && suname[0]==0 ) suname = 0; db_multi_exec( "UPDATE subscriber SET" " sdonotcall=%d," " sdigest=%d," " ssub=%Q," " mtime=strftime('%%s','now')," " smip=%Q," " suname=%Q," " sverified=%d" " WHERE subscriberCode=hextoblob(%Q)", sdonotcall, sdigest, ssub, g.zIpAddr, suname, sverified, zName ); }else{ db_multi_exec( "UPDATE subscriber SET" " sdonotcall=%d," " sdigest=%d," " ssub=%Q," " mtime=strftime('%%s','now')," " smip=%Q" " WHERE subscriberCode=hextoblob(%Q)", sdonotcall, sdigest, ssub, g.zIpAddr, zName ); } } if( P("delete")!=0 && cgi_csrf_safe(1) ){ if( !PB("dodelete") ){ eErr = 9; zErr = mprintf("Select this checkbox and press \"Unsubscribe\" to" " unsubscribe"); }else{ email_unsubscribe(zName); return; } } db_prepare(&q, "SELECT" " semail," /* 0 */ " sverified," /* 1 */ " sdonotcall," /* 2 */ " sdigest," /* 3 */ " ssub," /* 4 */ " smip," /* 5 */ " suname," /* 6 */ " datetime(mtime,'unixepoch')," /* 7 */ " datetime(sctime,'unixepoch')" /* 8 */ " FROM subscriber WHERE subscriberCode=hextoblob(%Q)", zName); if( db_step(&q)!=SQLITE_ROW ){ db_finalize(&q); cgi_redirect("subscribe"); return; } style_header("Update Subscription"); semail = db_column_text(&q, 0); sverified = db_column_int(&q, 1); sdonotcall = db_column_int(&q, 2); sdigest = db_column_int(&q, 3); ssub = db_column_text(&q, 4); sa = strchr(ssub,'a')!=0; sc = strchr(ssub,'c')!=0; sf = strchr(ssub,'f')!=0; st = strchr(ssub,'t')!=0; sw = strchr(ssub,'w')!=0; smip = db_column_text(&q, 5); suname = db_column_text(&q, 6); mtime = db_column_text(&q, 7); sctime = db_column_text(&q, 8); if( !g.perm.Admin && !sverified ){ db_multi_exec( "UPDATE subscriber SET sverified=1 WHERE subscriberCode=hextoblob(%Q)", zName); @ <h1>Your email alert subscription has been verified!</h1> @ <p>Use the form below to update your subscription information.</p> @ <p>Hint: Bookmark this page so that you can more easily update @ your subscription information in the future</p> }else{ @ <p>Make changes to the email subscription shown below and @ press "Submit".</p> } form_begin(0, "%R/alerts"); @ <input type="hidden" name="name" value="%h(zName)"> @ <table class="subscribe"> @ <tr> @ <td class="form_label">Email Address:</td> @ <td>%h(semail)</td> @ </tr> if( g.perm.Admin ){ @ <tr> @ <td class='form_label'>Created:</td> @ <td>%h(sctime)</td> @ </tr> @ <tr> @ <td class='form_label'>Last Modified:</td> @ <td>%h(mtime)</td> @ </tr> @ <tr> @ <td class='form_label'>IP Address:</td> @ <td>%h(smip)</td> @ </tr> @ <tr> @ <td class="form_label">User:</td> @ <td><input type="text" name="suname" value="%h(suname?suname:"")" \ @ size="30"></td> @ </tr> } @ <tr> @ <td class="form_label">Options:</td> @ <td><label><input type="checkbox" name="sa" %s(sa?"checked":"")>\ @ Announcements</label><br> if( g.perm.Read ){ @ <label><input type="checkbox" name="sc" %s(sc?"checked":"")>\ @ Check-ins</label><br> } if( g.perm.RdForum ){ @ <label><input type="checkbox" name="sf" %s(sf?"checked":"")>\ @ Forum Posts</label><br> } if( g.perm.RdTkt ){ @ <label><input type="checkbox" name="st" %s(st?"checked":"")>\ @ Ticket changes</label><br> } if( g.perm.RdWiki ){ @ <label><input type="checkbox" name="sw" %s(sw?"checked":"")>\ @ Wiki</label><br> } @ <label><input type="checkbox" name="sdigest" %s(sdigest?"checked":"")>\ @ Daily digest only</label><br> if( g.perm.Admin ){ @ <label><input type="checkbox" name="sdonotcall" \ @ %s(sdonotcall?"checked":"")> Do not call</label><br> @ <label><input type="checkbox" name="sverified" \ @ %s(sverified?"checked":"")>\ @ Verified</label><br> } @ <label><input type="checkbox" name="dodelete"> @ Unsubscribe</label> \ if( eErr==9 ){ @ <span class="loginError">← %h(zErr)</span>\ } @ <br> @ </td></tr> @ <tr> @ <td></td> @ <td><input type="submit" name="submit" value="Submit"> @ <input type="submit" name="delete" value="Unsubscribe"> @ </tr> @ </table> @ </form> fossil_free(zErr); db_finalize(&q); style_footer(); } /* This is the message that gets sent to describe how to change ** or modify a subscription */ static const char zUnsubMsg[] = @ To changes your subscription settings at %s visit this link: @ @ %s/alerts/%s @ @ To completely unsubscribe from %s, visit the following link: @ @ %s/unsubscribe/%s ; /* ** WEBPAGE: unsubscribe ** ** Users visit this page to be delisted from email alerts. ** ** If a valid subscriber code is supplied in the name= query parameter, ** then that subscriber is delisted. ** ** Otherwise, If the users is logged in, then they are redirected ** to the /alerts page where they have an unsubscribe button. ** ** Non-logged-in users with no name= query parameter are invited to enter ** an email address to which will be sent the unsubscribe link that ** contains the correct subscriber code. */ void unsubscribe_page(void){ const char *zName = P("name"); char *zErr = 0; int eErr = 0; unsigned int uSeed; const char *zDecoded; char *zCaptcha = 0; int dx; int bSubmit; const char *zEAddr; char *zCode = 0; /* If a valid subscriber code is supplied, then unsubscribe immediately. */ if( zName && db_exists("SELECT 1 FROM subscriber WHERE subscriberCode=hextoblob(%Q)", zName) ){ email_unsubscribe(zName); return; } /* Logged in users are redirected to the /alerts page */ login_check_credentials(); if( login_is_individual() ){ cgi_redirectf("%R/alerts"); return; } zEAddr = PD("e",""); dx = atoi(PD("dx","0")); bSubmit = P("submit")!=0 && P("e")!=0 && cgi_csrf_safe(1); if( bSubmit ){ if( !captcha_is_correct(1) ){ eErr = 2; zErr = mprintf("enter the security code shown below"); bSubmit = 0; } } if( bSubmit ){ zCode = db_text(0,"SELECT hex(subscriberCode) FROM subscriber" " WHERE semail=%Q", zEAddr); if( zCode==0 ){ eErr = 1; zErr = mprintf("not a valid email address"); bSubmit = 0; } } if( bSubmit ){ /* If we get this far, it means that a valid unsubscribe request has ** been submitted. Send the appropriate email. */ Blob hdr, body; EmailSender *pSender = email_sender_new(0,0); blob_init(&hdr,0,0); blob_init(&body,0,0); blob_appendf(&hdr, "To: <%s>\r\n", zEAddr); blob_appendf(&hdr, "Subject: Unsubscribe Instructions\r\n"); blob_appendf(&body, zUnsubMsg/*works-like:"%s%s%s%s%s%s"*/, g.zBaseURL, g.zBaseURL, zCode, g.zBaseURL, g.zBaseURL, zCode); email_send(pSender, &hdr, &body, 0); style_header("Unsubscribe Instructions Sent"); if( pSender->zErr ){ @ <h1>Internal Error</h1> @ <p>The following error was encountered while trying to send an @ email to %h(zEAddr): @ <blockquote><pre> @ %h(pSender->zErr) @ </pre></blockquote> }else{ @ <p>An email has been sent to "%h(zEAddr)" that explains how to @ unsubscribe and/or modify your subscription settings</p> } email_sender_free(pSender); style_footer(); return; } /* Non-logged-in users have to enter an email address to which is ** sent a message containing the unsubscribe link. */ style_header("Unsubscribe Request"); @ <p>Fill out the form below to request an email message that will @ explain how to unsubscribe and/or change your subscription settings.</p> @ form_begin(0, "%R/unsubscribe"); @ <table class="subscribe"> @ <tr> @ <td class="form_label">Email Address:</td> @ <td><input type="text" name="e" value="%h(zEAddr)" size="30"></td> if( eErr==1 ){ @ <td><span class="loginError">← %h(zErr)</span></td> } @ </tr> uSeed = captcha_seed(); zDecoded = captcha_decode(uSeed); zCaptcha = captcha_render(zDecoded); @ <tr> @ <td class="form_label">Security Code:</td> @ <td><input type="text" name="captcha" value="" size="30"> @ <input type="hidden" name="captchaseed" value="%u(uSeed)"></td> if( eErr==2 ){ @ <td><span class="loginError">← %h(zErr)</span></td> } @ </tr> @ <tr> @ <td class="form_label">Options:</td> @ <td><label><input type="radio" name="dx" value="0" %s(dx?"":"checked")>\ @ Modify subscription</label><br> @ <label><input type="radio" name="dx" value="1" %s(dx?"checked":"")>\ @ Completely unsubscribe</label><br> @ <tr> @ <td></td> @ <td><input type="submit" name="submit" value="Submit"></td> @ </tr> @ </table> @ <div class="captcha"><table class="captcha"><tr><td><pre> @ %h(zCaptcha) @ </pre> @ Enter the 8 characters above in the "Security Code" box @ </td></tr></table></div> @ </form> fossil_free(zErr); style_footer(); } /* ** WEBPAGE: subscribers ** ** This page, accessible to administrators only, ** shows a list of email notification email addresses. ** Clicking on an email takes one to the /alerts page ** for that email where the delivery settings can be ** modified. */ void subscriber_list_page(void){ Blob sql; Stmt q; sqlite3_int64 iNow; if( email_webpages_disabled() ) return; login_check_credentials(); if( !g.perm.Admin ){ login_needed(0); return; } email_submenu_common(); style_header("Subscriber List"); blob_init(&sql, 0, 0); blob_append_sql(&sql, "SELECT hex(subscriberCode)," /* 0 */ " semail," /* 1 */ " ssub," /* 2 */ " suname," /* 3 */ " sverified," /* 4 */ " sdigest," /* 5 */ " mtime," /* 6 */ " date(sctime,'unixepoch')" /* 7 */ " FROM subscriber" ); if( P("only")!=0 ){ blob_append_sql(&sql, " WHERE ssub LIKE '%%%q%%'", P("only")); style_submenu_element("Show All","%R/subscribers"); } blob_append_sql(&sql," ORDER BY mtime DESC"); db_prepare_blob(&q, &sql); iNow = time(0); @ <table border='1' class='sortable' \ @ data-init-sort='6' data-column-types='tttttKt'> @ <thead> @ <tr> @ <th>Email @ <th>Events @ <th>Digest-Only? @ <th>User @ <th>Verified? @ <th>Last change @ <th>Created @ </tr> @ </thead><tbody> while( db_step(&q)==SQLITE_ROW ){ sqlite3_int64 iMtime = db_column_int64(&q, 6); double rAge = (iNow - iMtime)/86400.0; @ <tr> @ <td><a href='%R/alerts/%s(db_column_text(&q,0))'>\ @ %h(db_column_text(&q,1))</a></td> @ <td>%h(db_column_text(&q,2))</td> @ <td>%s(db_column_int(&q,5)?"digest":"")</td> @ <td>%h(db_column_text(&q,3))</td> @ <td>%s(db_column_int(&q,4)?"yes":"pending")</td> @ <td data-sortkey='%010llx(iMtime)'>%z(human_readable_age(rAge))</td> @ <td>%h(db_column_text(&q,7))</td> @ </tr> } @ </tbody></table> db_finalize(&q); style_table_sorter(); style_footer(); } #if LOCAL_INTERFACE /* ** A single event that might appear in an alert is recorded as an ** instance of the following object. */ struct EmailEvent { int type; /* 'c', 'f', 'm', 't', 'w' */ int needMod; /* Pending moderator approval */ Blob hdr; /* Header content, for forum entries */ Blob txt; /* Text description to appear in an alert */ char *zFromName; /* Human name of the sender */ EmailEvent *pNext; /* Next in chronological order */ }; #endif /* ** Free a linked list of EmailEvent objects */ void email_free_eventlist(EmailEvent *p){ while( p ){ EmailEvent *pNext = p->pNext; blob_reset(&p->txt); blob_reset(&p->hdr); fossil_free(p->zFromName); fossil_free(p); p = pNext; } } /* ** Compute and return a linked list of EmailEvent objects ** corresponding to the current content of the temp.wantalert ** table which should be defined as follows: ** ** CREATE TEMP TABLE wantalert(eventId TEXT, needMod BOOLEAN); */ EmailEvent *email_compute_event_text(int *pnEvent, int doDigest){ Stmt q; EmailEvent *p; EmailEvent anchor; EmailEvent *pLast; const char *zUrl = db_get("email-url","http://localhost:8080"); const char *zFrom; const char *zSub; /* First do non-forum post events */ db_prepare(&q, "SELECT" " blob.uuid," /* 0 */ " datetime(event.mtime)," /* 1 */ " coalesce(ecomment,comment)" " || ' (user: ' || coalesce(euser,user,'?')" " || (SELECT case when length(x)>0 then ' tags: ' || x else '' end" " FROM (SELECT group_concat(substr(tagname,5), ', ') AS x" " FROM tag, tagxref" " WHERE tagname GLOB 'sym-*' AND tag.tagid=tagxref.tagid" " AND tagxref.rid=blob.rid AND tagxref.tagtype>0))" " || ')' as comment," /* 2 */ " wantalert.eventId," /* 3 */ " wantalert.needMod" /* 4 */ " FROM temp.wantalert, event, blob" " WHERE blob.rid=event.objid" " AND event.objid=substr(wantalert.eventId,2)+0" " AND (%d OR eventId NOT GLOB 'f*')" " ORDER BY event.mtime", doDigest ); memset(&anchor, 0, sizeof(anchor)); pLast = &anchor; *pnEvent = 0; while( db_step(&q)==SQLITE_ROW ){ const char *zType = ""; p = fossil_malloc( sizeof(EmailEvent) ); pLast->pNext = p; pLast = p; p->type = db_column_text(&q, 3)[0]; p->needMod = db_column_int(&q, 4); p->zFromName = 0; p->pNext = 0; switch( p->type ){ case 'c': zType = "Check-In"; break; case 'f': zType = "Forum post"; break; case 't': zType = "Wiki Edit"; break; case 'w': zType = "Ticket Change"; break; } blob_init(&p->hdr, 0, 0); blob_init(&p->txt, 0, 0); blob_appendf(&p->txt,"== %s %s ==\n%s\n%s/info/%.20s\n", db_column_text(&q,1), zType, db_column_text(&q,2), zUrl, db_column_text(&q,0) ); if( p->needMod ){ blob_appendf(&p->txt, "** Pending moderator approval (%s/modreq) **\n", zUrl ); } (*pnEvent)++; } db_finalize(&q); /* Early-out if forumpost is not a table in this repository */ if( !db_table_exists("repository","forumpost") ){ return anchor.pNext; } /* For digests, the previous loop also handled forumposts already */ if( doDigest ){ return anchor.pNext; } /* If we reach this point, it means that forumposts exist and this ** is a normal email alert. Construct full-text forum post alerts ** using a format that enables them to be sent as separate emails. */ db_prepare(&q, "SELECT" " forumpost.fpid," /* 0 */ " (SELECT uuid FROM blob WHERE rid=forumpost.fpid)," /* 1 */ " datetime(event.mtime)," /* 2 */ " substr(comment,instr(comment,':')+2)," /* 3 */ " (SELECT uuid FROM blob WHERE rid=forumpost.firt)," /* 4 */ " wantalert.needMod," /* 5 */ " coalesce(trim(substr(info,1,instr(info,'<')-1)),euser,user)" /* 6 */ " FROM temp.wantalert, event, forumpost" " LEFT JOIN user ON (login=coalesce(euser,user))" " WHERE event.objid=substr(wantalert.eventId,2)+0" " AND eventId GLOB 'f*'" " AND forumpost.fpid=event.objid" " ORDER BY event.mtime" ); zFrom = db_get("email-self",0); zSub = db_get("email-subname",""); while( db_step(&q)==SQLITE_ROW ){ Manifest *pPost = manifest_get(db_column_int(&q,0), CFTYPE_FORUM, 0); const char *zIrt; const char *zUuid; const char *zTitle; const char *z; if( pPost==0 ) continue; p = fossil_malloc( sizeof(EmailEvent) ); pLast->pNext = p; pLast = p; p->type = 'f'; p->needMod = db_column_int(&q, 5); z = db_column_text(&q,6); p->zFromName = z && z[0] ? fossil_strdup(z) : 0; p->pNext = 0; blob_init(&p->hdr, 0, 0); zUuid = db_column_text(&q, 1); zTitle = db_column_text(&q, 3); if( p->needMod ){ blob_appendf(&p->hdr, "Subject: %s Pending Moderation: %s\r\n", zSub, zTitle); }else{ blob_appendf(&p->hdr, "Subject: %s %s\r\n", zSub, zTitle); blob_appendf(&p->hdr, "Message-Id: <%.32s@%s>\r\n", zUuid, email_hostname(zFrom)); zIrt = db_column_text(&q, 4); if( zIrt && zIrt[0] ){ blob_appendf(&p->hdr, "In-Reply-To: <%.32s@%s>\r\n", zIrt, email_hostname(zFrom)); } } blob_init(&p->txt, 0, 0); if( p->needMod ){ blob_appendf(&p->txt, "** Pending moderator approval (%s/modreq) **\n", zUrl ); } blob_appendf(&p->txt, "Forum post by %s on %s\n", pPost->zUser, db_column_text(&q, 2)); blob_appendf(&p->txt, "%s/forumpost/%S\n\n", zUrl, zUuid); blob_append(&p->txt, pPost->zWiki, -1); manifest_destroy(pPost); (*pnEvent)++; } db_finalize(&q); return anchor.pNext; } /* ** Put a header on an alert email */ void email_header(Blob *pOut){ blob_appendf(pOut, "This is an automated email reporting changes " "on Fossil repository %s (%s/timeline)\n", db_get("email-subname","(unknown)"), db_get("email-url","http://localhost:8080")); } /* ** Append the "unsubscribe" notification and other footer text to ** the end of an email alert being assemblied in pOut. */ void email_footer(Blob *pOut){ blob_appendf(pOut, "\n-- \nTo unsubscribe: %s/unsubscribe\n", db_get("email-url","http://localhost:8080")); } /* ** COMMAND: test-alert ** ** Usage: %fossil test-alert EVENTID ... ** ** Generate the text of an email alert for all of the EVENTIDs ** listed on the command-line. Or if no events are listed on the ** command line, generate text for all events named in the ** pending_alert table. ** ** This command is intended for testing and debugging the logic ** that generates email alert text. ** ** Options: ** ** --digest Generate digest alert text ** --needmod Assume all events are pending moderator approval */ void test_alert_cmd(void){ Blob out; int nEvent; int needMod; int doDigest; EmailEvent *pEvent, *p; doDigest = find_option("digest",0,0)!=0; needMod = find_option("needmod",0,0)!=0; db_find_and_open_repository(0, 0); verify_all_options(); db_begin_transaction(); email_schema(0); db_multi_exec("CREATE TEMP TABLE wantalert(eventid TEXT, needMod BOOLEAN)"); if( g.argc==2 ){ db_multi_exec( "INSERT INTO wantalert(eventId,needMod)" " SELECT eventid, %d FROM pending_alert", needMod); }else{ int i; for(i=2; i<g.argc; i++){ db_multi_exec("INSERT INTO wantalert(eventId,needMod) VALUES(%Q,%d)", g.argv[i], needMod); } } blob_init(&out, 0, 0); email_header(&out); pEvent = email_compute_event_text(&nEvent, doDigest); for(p=pEvent; p; p=p->pNext){ blob_append(&out, "\n", 1); if( blob_size(&p->hdr) ){ blob_append(&out, blob_buffer(&p->hdr), blob_size(&p->hdr)); blob_append(&out, "\n", 1); } blob_append(&out, blob_buffer(&p->txt), blob_size(&p->txt)); } email_free_eventlist(pEvent); email_footer(&out); fossil_print("%s", blob_str(&out)); blob_reset(&out); db_end_transaction(0); } /* ** COMMAND: test-add-alerts ** ** Usage: %fossil test-add-alerts [OPTIONS] EVENTID ... ** ** Add one or more events to the pending_alert queue. Use this ** command during testing to force email notifications for specific ** events. ** ** EVENTIDs are text. The first character is 'c', 'f', 't', or 'w' ** for check-in, forum, ticket, or wiki. The remaining text is a ** integer that references the EVENT.OBJID value for the event. ** Run /timeline?showid to see these OBJID values. ** ** Options: ** ** --backoffice Run email_backoffice() after all alerts have ** been added. This will cause the alerts to be ** sent out with the SENDALERT_TRACE option. ** ** --debug Like --backoffice, but add the SENDALERT_STDOUT ** so that emails are printed to standard output ** rather than being sent. ** ** --digest Process emails using SENDALERT_DIGEST */ void test_add_alert_cmd(void){ int i; int doAuto = find_option("backoffice",0,0)!=0; unsigned mFlags = 0; if( find_option("debug",0,0)!=0 ){ doAuto = 1; mFlags = SENDALERT_STDOUT; } if( find_option("digest",0,0)!=0 ){ mFlags |= SENDALERT_DIGEST; } db_find_and_open_repository(0, 0); verify_all_options(); db_begin_write(); email_schema(0); for(i=2; i<g.argc; i++){ db_multi_exec("REPLACE INTO pending_alert(eventId) VALUES(%Q)", g.argv[i]); } db_end_transaction(0); if( doAuto ){ email_backoffice(SENDALERT_TRACE|mFlags); } } #if INTERFACE /* ** Flags for email_send_alerts() */ #define SENDALERT_DIGEST 0x0001 /* Send a digest */ #define SENDALERT_PRESERVE 0x0002 /* Do not mark the task as done */ #define SENDALERT_STDOUT 0x0004 /* Print emails instead of sending */ #define SENDALERT_TRACE 0x0008 /* Trace operation for debugging */ #endif /* INTERFACE */ /* ** Send alert emails to subscribers. ** ** This procedure is run by either the backoffice, or in response to the ** "fossil alerts send" command. Details of operation are controlled by ** the flags parameter. ** ** Here is a summary of what happens: ** ** (1) Create a TEMP table wantalert(eventId,needMod) and fill it with ** all the events that we want to send alerts about. The needMod ** flags is set if and only if the event is still awaiting ** moderator approval. Events with the needMod flag are only ** shown to users that have moderator privileges. ** ** (2) Call email_compute_event_text() to compute a list of EmailEvent ** objects that describe all events about which we want to send ** alerts. ** ** (3) Loop over all subscribers. Compose and send one or more email ** messages to each subscriber that describe the events for ** which the subscriber has expressed interest and has ** appropriate privileges. ** ** (4) Update the pending_alerts table to indicate that alerts have been ** sent. ** ** Update 2018-08-09: Do step (3) before step (4). Update the ** pending_alerts table *before* the emails are sent. That way, if ** the process malfunctions or crashes, some notifications may never ** be sent. But that is better than some recurring bug causing ** subscribers to be flooded with repeated notifications every 60 ** seconds! */ void email_send_alerts(u32 flags){ EmailEvent *pEvents, *p; int nEvent = 0; Stmt q; const char *zDigest = "false"; Blob hdr, body; const char *zUrl; const char *zRepoName; const char *zFrom; const char *zDest = (flags & SENDALERT_STDOUT) ? "stdout" : 0; EmailSender *pSender = 0; u32 senderFlags = 0; if( g.fSqlTrace ) fossil_trace("-- BEGIN email_send_alerts(%u)\n", flags); email_schema(0); if( !email_enabled() ) goto send_alerts_done; zUrl = db_get("email-url",0); if( zUrl==0 ) goto send_alerts_done; zRepoName = db_get("email-subname",0); if( zRepoName==0 ) goto send_alerts_done; zFrom = db_get("email-self",0); if( zFrom==0 ) goto send_alerts_done; if( flags & SENDALERT_TRACE ){ senderFlags |= EMAIL_TRACE; } pSender = email_sender_new(zDest, senderFlags); /* Step (1): Compute the alerts that need sending */ db_multi_exec( "DROP TABLE IF EXISTS temp.wantalert;" "CREATE TEMP TABLE wantalert(eventId TEXT, needMod BOOLEAN, sentMod);" ); if( flags & SENDALERT_DIGEST ){ /* Unmoderated changes are never sent as part of a digest */ db_multi_exec( "INSERT INTO wantalert(eventId,needMod)" " SELECT eventid, 0" " FROM pending_alert" " WHERE sentDigest IS FALSE" " AND NOT EXISTS(SELECT 1 FROM private WHERE rid=substr(eventid,2));" ); zDigest = "true"; }else{ /* Immediate alerts might include events that are subject to ** moderator approval */ db_multi_exec( "INSERT INTO wantalert(eventId,needMod,sentMod)" " SELECT eventid," " EXISTS(SELECT 1 FROM private WHERE rid=substr(eventid,2))," " sentMod" " FROM pending_alert" " WHERE sentSep IS FALSE;" "DELETE FROM wantalert WHERE needMod AND sentMod;" ); } /* Step 2: compute EmailEvent objects for every notification that ** needs sending. */ pEvents = email_compute_event_text(&nEvent, (flags & SENDALERT_DIGEST)!=0); if( nEvent==0 ) goto send_alerts_done; /* Step 4a: Update the pending_alerts table to designate the ** alerts as having all been sent. This is done *before* step (3) ** so that a crash will not cause alerts to be sent multiple times. ** Better a missed alert than being spammed with hundreds of alerts ** due to a bug. */ if( (flags & SENDALERT_PRESERVE)==0 ){ if( flags & SENDALERT_DIGEST ){ db_multi_exec( "UPDATE pending_alert SET sentDigest=true" " WHERE eventid IN (SELECT eventid FROM wantalert);" ); }else{ db_multi_exec( "UPDATE pending_alert SET sentSep=true" " WHERE eventid IN (SELECT eventid FROM wantalert WHERE NOT needMod);" "UPDATE pending_alert SET sentMod=true" " WHERE eventid IN (SELECT eventid FROM wantalert WHERE needMod);" ); } } /* Step 3: Loop over subscribers. Send alerts */ blob_init(&hdr, 0, 0); blob_init(&body, 0, 0); db_prepare(&q, "SELECT" " hex(subscriberCode)," /* 0 */ " semail," /* 1 */ " ssub," /* 2 */ " fullcap(user.cap)" /* 3 */ " FROM subscriber LEFT JOIN user ON (login=suname)" " WHERE sverified AND NOT sdonotcall" " AND sdigest IS %s", zDigest/*safe-for-%s*/ ); while( db_step(&q)==SQLITE_ROW ){ const char *zCode = db_column_text(&q, 0); const char *zSub = db_column_text(&q, 2); const char *zEmail = db_column_text(&q, 1); const char *zCap = db_column_text(&q, 3); int nHit = 0; for(p=pEvents; p; p=p->pNext){ if( strchr(zSub,p->type)==0 ) continue; if( p->needMod ){ /* For events that require moderator approval, only send an alert ** if the recipient is a moderator for that type of event */ char xType = '*'; switch( p->type ){ case 'f': xType = '5'; break; case 't': xType = 'q'; break; case 'w': xType = 'l'; break; } if( strchr(zCap,xType)==0 ) continue; }else if( strchr(zCap,'s')!=0 || strchr(zCap,'a')!=0 ){ /* Setup and admin users can get any notification that does not ** require moderation */ }else{ /* Other users only see the alert if they have sufficient ** privilege to view the event itself */ char xType = '*'; switch( p->type ){ case 'c': xType = 'o'; break; case 'f': xType = '2'; break; case 't': xType = 'r'; break; case 'w': xType = 'j'; break; } if( strchr(zCap,xType)==0 ) continue; } if( blob_size(&p->hdr)>0 ){ /* This alert should be sent as a separate email */ Blob fhdr, fbody; blob_init(&fhdr, 0, 0); blob_appendf(&fhdr, "To: <%s>\r\n", zEmail); blob_append(&fhdr, blob_buffer(&p->hdr), blob_size(&p->hdr)); blob_init(&fbody, blob_buffer(&p->txt), blob_size(&p->txt)); blob_appendf(&fbody, "\n-- \nSubscription info: %s/alerts/%s\n", zUrl, zCode); email_send(pSender,&fhdr,&fbody,p->zFromName); blob_reset(&fhdr); blob_reset(&fbody); }else{ /* Events other than forum posts are gathered together into ** a single email message */ if( nHit==0 ){ blob_appendf(&hdr,"To: <%s>\r\n", zEmail); blob_appendf(&hdr,"Subject: %s activity alert\r\n", zRepoName); blob_appendf(&body, "This is an automated email sent by the Fossil repository " "at %s to report changes.\n", zUrl ); } nHit++; blob_append(&body, "\n", 1); blob_append(&body, blob_buffer(&p->txt), blob_size(&p->txt)); } } if( nHit==0 ) continue; blob_appendf(&body,"\n-- \nSubscription info: %s/alerts/%s\n", zUrl, zCode); email_send(pSender,&hdr,&body,0); blob_truncate(&hdr, 0); blob_truncate(&body, 0); } blob_reset(&hdr); blob_reset(&body); db_finalize(&q); email_free_eventlist(pEvents); /* Step 4b: Update the pending_alerts table to remove all of the ** alerts that have been completely sent. */ db_multi_exec("DELETE FROM pending_alert WHERE sentDigest AND sentSep;"); send_alerts_done: email_sender_free(pSender); if( g.fSqlTrace ) fossil_trace("-- END email_send_alerts(%u)\n", flags); } /* ** Do backoffice processing for email notifications. In other words, ** check to see if any email notifications need to occur, and then ** do them. ** ** This routine is intended to run in the background, after webpages. ** ** The mFlags option is zero or more of the SENDALERT_* flags. Normally ** this flag is zero, but the test-set-alert command sets it to ** SENDALERT_TRACE. */ void email_backoffice(u32 mFlags){ int iJulianDay; if( !email_tables_exist() ) return; email_send_alerts(mFlags); iJulianDay = db_int(0, "SELECT julianday('now')"); if( iJulianDay>db_get_int("email-last-digest",0) ){ db_set_int("email-last-digest",iJulianDay,0); email_send_alerts(SENDALERT_DIGEST|mFlags); } } /* ** WEBPAGE: contact_admin ** ** A web-form to send an email message to the repository administrator, ** or (with appropriate permissions) to anybody. */ void contact_admin_page(void){ const char *zAdminEmail = db_get("email-admin",0); unsigned int uSeed = 0; const char *zDecoded; char *zCaptcha = 0; login_check_credentials(); if( zAdminEmail==0 || zAdminEmail[0]==0 ){ style_header("Outbound Email Disabled"); @ <p>Outbound email is disabled on this repository style_footer(); return; } if( P("submit")!=0 && P("subject")!=0 && P("msg")!=0 && P("from")!=0 && cgi_csrf_safe(1) && captcha_is_correct(0) ){ Blob hdr, body; EmailSender *pSender = email_sender_new(0,0); blob_init(&hdr, 0, 0); blob_appendf(&hdr, "To: <%s>\r\nSubject: %s administrator message\r\n", zAdminEmail, db_get("email-subname","Fossil Repo")); blob_init(&body, 0, 0); blob_appendf(&body, "Message from [%s]\n", PT("from")/*safe-for-%s*/); blob_appendf(&body, "Subject: [%s]\n\n", PT("subject")/*safe-for-%s*/); blob_appendf(&body, "%s", PT("msg")/*safe-for-%s*/); email_send(pSender, &hdr, &body, 0); style_header("Message Sent"); if( pSender->zErr ){ @ <h1>Internal Error</h1> @ <p>The following error was reported by the system: @ <blockquote><pre> @ %h(pSender->zErr) @ </pre></blockquote> }else{ @ <p>Your message has been sent to the repository administrator. @ Thank you for your input.</p> } email_sender_free(pSender); style_footer(); return; } if( captcha_needed() ){ uSeed = captcha_seed(); zDecoded = captcha_decode(uSeed); zCaptcha = captcha_render(zDecoded); } style_header("Message To Administrator"); form_begin(0, "%R/contact_admin"); @ <p>Enter a message to the repository administrator below:</p> @ <table class="subscribe"> if( zCaptcha ){ @ <tr> @ <td class="form_label">Security Code:</td> @ <td><input type="text" name="captcha" value="" size="10"> @ <input type="hidden" name="captchaseed" value="%u(uSeed)"></td> @ </tr> } @ <tr> @ <td class="form_label">Your Email Address:</td> @ <td><input type="text" name="from" value="%h(PT("from"))" size="30"></td> @ </tr> @ <tr> @ <td class="form_label">Subject:</td> @ <td><input type="text" name="subject" value="%h(PT("subject"))"\ @ size="80"></td> @ </tr> @ <tr> @ <td class="form_label">Message:</td> @ <td><textarea name="msg" cols="80" rows="10" wrap="virtual">\ @ %h(PT("msg"))</textarea> @ </tr> @ <tr> @ <td></td> @ <td><input type="submit" name="submit" value="Send Message"> @ </tr> @ </table> if( zCaptcha ){ @ <div class="captcha"><table class="captcha"><tr><td><pre> @ %h(zCaptcha) @ </pre> @ Enter the 8 characters above in the "Security Code" box @ </td></tr></table></div> } @ </form> style_footer(); } /* ** Send an annoucement message described by query parameter. ** Permission to do this has already been verified. */ static char *email_send_announcement(void){ EmailSender *pSender; char *zErr; const char *zTo = PT("to"); char *zSubject = PT("subject"); int bAll = PB("all"); int bAA = PB("aa"); const char *zSub = db_get("email-subname", "[Fossil Repo]"); int bTest2 = fossil_strcmp(P("name"),"test2")==0; Blob hdr, body; blob_init(&body, 0, 0); blob_init(&hdr, 0, 0); blob_appendf(&body, "%s", PT("msg")/*safe-for-%s*/); pSender = email_sender_new(bTest2 ? "blob" : 0, 0); if( zTo[0] ){ blob_appendf(&hdr, "To: <%s>\r\nSubject: %s %s\r\n", zTo, zSub, zSubject); email_send(pSender, &hdr, &body, 0); } if( bAll || bAA ){ Stmt q; int nUsed = blob_size(&body); const char *zURL = db_get("email-url",0); db_prepare(&q, "SELECT semail, hex(subscriberCode) FROM subscriber " " WHERE sverified AND NOT sdonotcall %s", bAll ? "" : " AND ssub LIKE '%a%'"); while( db_step(&q)==SQLITE_ROW ){ const char *zCode = db_column_text(&q, 1); zTo = db_column_text(&q, 0); blob_truncate(&hdr, 0); blob_appendf(&hdr, "To: <%s>\r\nSubject: %s %s\r\n", zTo, zSub, zSubject); if( zURL ){ blob_truncate(&body, nUsed); blob_appendf(&body,"\n-- \nSubscription info: %s/alerts/%s\n", zURL, zCode); } email_send(pSender, &hdr, &body, 0); } db_finalize(&q); } if( bTest2 ){ /* If the URL is /announce/test2 instead of just /announce, then no ** email is actually sent. Instead, the text of the email that would ** have been sent is displayed in the result window. */ @ <pre style='border: 2px solid blue; padding: 1ex'> @ %h(blob_str(&pSender->out)) @ </pre> } zErr = pSender->zErr; pSender->zErr = 0; email_sender_free(pSender); return zErr; } /* ** WEBPAGE: announce ** ** A web-form, available to users with the "Send-Announcement" or "A" ** capability, that allows one to send announcements to whomever ** has subscribed to receive announcements. The administrator can ** also send a message to an arbitrary email address and/or to all ** subscribers regardless of whether or not they have elected to ** receive announcements. */ void announce_page(void){ login_check_credentials(); if( !g.perm.Announce ){ login_needed(0); return; } if( fossil_strcmp(P("name"),"test1")==0 ){ /* Visit the /announce/test1 page to see the CGI variables */ @ <p style='border: 1px solid black; padding: 1ex;'> cgi_print_all(0, 0); @ </p> }else if( P("submit")!=0 && cgi_csrf_safe(1) ){ char *zErr = email_send_announcement(); style_header("Announcement Sent"); if( zErr ){ @ <h1>Internal Error</h1> @ <p>The following error was reported by the system: @ <blockquote><pre> @ %h(zErr) @ </pre></blockquote> }else{ @ <p>The announcement has been sent.</p> } style_footer(); return; } style_header("Send Announcement"); @ <form method="POST"> @ <table class="subscribe"> if( g.perm.Admin ){ int aa = PB("aa"); int all = PB("all"); const char *aack = aa ? "checked" : ""; const char *allck = all ? "checked" : ""; @ <tr> @ <td class="form_label">To:</td> @ <td><input type="text" name="to" value="%h(PT("to"))" size="30"><br> @ <label><input type="checkbox" name="aa" %s(aack)> \ @ All "announcement" subscribers</label> \ @ <a href="%R/subscribers?only=a" target="_blank">(list)</a><br> @ <label><input type="checkbox" name="all" %s(allck)> \ @ All subscribers</label> \ @ <a href="%R/subscribers" target="_blank">(list)</a><br></td> @ </tr> } @ <tr> @ <td class="form_label">Subject:</td> @ <td><input type="text" name="subject" value="%h(PT("subject"))"\ @ size="80"></td> @ </tr> @ <tr> @ <td class="form_label">Message:</td> @ <td><textarea name="msg" cols="80" rows="10" wrap="virtual">\ @ %h(PT("msg"))</textarea> @ </tr> @ <tr> @ <td></td> @ <td><input type="submit" name="submit" value="Send Message"> @ </tr> @ </table> @ </form> style_footer(); } |
Changes to src/encode.c.
︙ | ︙ | |||
432 433 434 435 436 437 438 | /* ** The characters used for HTTP base64 encoding. */ static unsigned char zBase[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; /* | > | < < | | < < < < < < > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < | | | 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 | /* ** The characters used for HTTP base64 encoding. */ static unsigned char zBase[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; /* ** Translate nData bytes of content from zData into ** ((nData+2)/3)*4) bytes of base64 encoded content and ** put the result in z64. Add a zero-terminator at the end. */ int translateBase64(const char *zData, int nData, char *z64){ int i, n; for(i=n=0; i+2<nData; i+=3){ z64[n++] = zBase[ (zData[i]>>2) & 0x3f ]; z64[n++] = zBase[ ((zData[i]<<4) & 0x30) | ((zData[i+1]>>4) & 0x0f) ]; z64[n++] = zBase[ ((zData[i+1]<<2) & 0x3c) | ((zData[i+2]>>6) & 0x03) ]; z64[n++] = zBase[ zData[i+2] & 0x3f ]; } if( i+1<nData ){ z64[n++] = zBase[ (zData[i]>>2) & 0x3f ]; z64[n++] = zBase[ ((zData[i]<<4) & 0x30) | ((zData[i+1]>>4) & 0x0f) ]; z64[n++] = zBase[ ((zData[i+1]<<2) & 0x3c) ]; z64[n++] = '='; }else if( i<nData ){ z64[n++] = zBase[ (zData[i]>>2) & 0x3f ]; z64[n++] = zBase[ ((zData[i]<<4) & 0x30) ]; z64[n++] = '='; z64[n++] = '='; } z64[n] = 0; return n; } /* ** Encode a string using a base-64 encoding. ** The encoding can be reversed using the <b>decode64</b> function. ** ** Space to hold the result comes from malloc(). */ char *encode64(const char *zData, int nData){ char *z64; if( nData<=0 ){ nData = strlen(zData); } z64 = fossil_malloc( (nData*4)/3 + 8 ); translateBase64(zData, nData, z64); return z64; } /* ** COMMAND: test-encode64 ** ** Usage: %fossil test-encode64 STRING */ void test_encode64_cmd(void){ char *z; int i; for(i=2; i<g.argc; i++){ z = encode64(g.argv[i], -1); fossil_print("%s\n", z); free(z); } } /* Decode base64 text. Write the output into zData. The caller ** must ensure that zData is large enough. It is ok for z64 and ** zData to be the same buffer. In other words, it is ok to decode ** in-place. A zero terminator is always placed at the end of zData. */ void decodeBase64(const char *z64, int *pnByte, char *zData){ const unsigned char *zIn = (const unsigned char*)z64; int i, j, k; int x[4]; static int isInit = 0; static signed char trans[256]; if( !isInit ){ for(i=0; i<256; i++){ trans[i] = -1; } for(i=0; zBase[i]; i++){ trans[zBase[i] & 0x7f] = i; } isInit = 1; } for(j=k=0; zIn[0]; zIn++){ int v = trans[zIn[0]]; if( v>=0 ){ x[k++] = v; if( k==4 ){ zData[j++] = ((x[0]<<2) & 0xfc) | ((x[1]>>4) & 0x03); zData[j++] = ((x[1]<<4) & 0xf0) | ((x[2]>>2) & 0x0f); zData[j++] = ((x[2]<<6) & 0xc0) | (x[3] & 0x3f); k = 0; } } } if( k>=2 ){ zData[j++] = ((x[0]<<2) & 0xfc) | ((x[1]>>4) & 0x03); if( k==3 ){ zData[j++] = ((x[1]<<4) & 0xf0) | ((x[2]>>2) & 0x0f); } } zData[j] = 0; *pnByte = j; } /* ** This function treats its input as a base-64 string and returns the ** decoded value of that string. Characters of input that are not ** valid base-64 characters (such as spaces and newlines) are ignored. ** ** Space to hold the decoded string is obtained from malloc(). ** ** The number of bytes decoded is returned in *pnByte */ char *decode64(const char *z64, int *pnByte){ char *zData; int n64 = (int)strlen(z64); while( n64>0 && z64[n64-1]=='=' ) n64--; zData = fossil_malloc( (n64*3)/4 + 4 ); decodeBase64(z64, pnByte, zData); return zData; } /* ** COMMAND: test-decode64 ** ** Usage: %fossil test-decode64 STRING */ void test_decode64_cmd(void){ char *z; int i, n; for(i=2; i<g.argc; i++){ z = decode64(g.argv[i], &n); fossil_print("%d: %s\n", n, z); fossil_free(z); } } /* ** The base-16 encoding using the following characters: ** ** 0123456789abcdef |
︙ | ︙ | |||
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 | /* ** Return true if the input string contains only valid base-16 digits. ** If any invalid characters appear in the string, return false. */ int validate16(const char *zIn, int nIn){ int i; for(i=0; i<nIn; i++, zIn++){ if( zDecode[zIn[0]&0xff]>63 ){ return zIn[0]==0; } } return 1; } /* ** The input string is a base16 value. Convert it into its canonical ** form. This means that digits are all lower case and that conversions ** like "l"->"1" and "O"->"0" occur. */ void canonical16(char *z, int n){ while( *z && n-- ){ *z = zEncode[zDecode[(*z)&0x7f]&0x1f]; z++; } } /* Randomness used for XOR-ing by the obscure() and unobscure() routines */ static const unsigned char aObscurer[16] = { 0xa7, 0x21, 0x31, 0xe3, 0x2a, 0x50, 0x2c, 0x86, 0x4c, 0xa4, 0x52, 0x25, 0xff, 0x49, 0x35, 0x85 }; | > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 | /* ** Return true if the input string contains only valid base-16 digits. ** If any invalid characters appear in the string, return false. */ int validate16(const char *zIn, int nIn){ int i; if( nIn<0 ) nIn = (int)strlen(zIn); for(i=0; i<nIn; i++, zIn++){ if( zDecode[zIn[0]&0xff]>63 ){ return zIn[0]==0; } } return 1; } /* ** The input string is a base16 value. Convert it into its canonical ** form. This means that digits are all lower case and that conversions ** like "l"->"1" and "O"->"0" occur. */ void canonical16(char *z, int n){ while( *z && n-- ){ *z = zEncode[zDecode[(*z)&0x7f]&0x1f]; z++; } } /* ** Decode a string encoded using "quoted-printable". ** ** (1) "=" followed by two hex digits becomes a single ** byte specified by the two digits ** ** The decoding is done in-place. */ void decodeQuotedPrintable(char *z, int *pnByte){ int i, j, c; for(i=j=0; (c = z[i])!=0; i++){ if( c=='=' ){ if( z[i+1]!='\r' ){ decode16((unsigned char*)&z[i+1], (unsigned char*)&z[j], 2); j++; } i += 2; }else{ z[j++] = c; } } if( pnByte ) *pnByte = j; z[j] = 0; } /* Randomness used for XOR-ing by the obscure() and unobscure() routines */ static const unsigned char aObscurer[16] = { 0xa7, 0x21, 0x31, 0xe3, 0x2a, 0x50, 0x2c, 0x86, 0x4c, 0xa4, 0x52, 0x25, 0xff, 0x49, 0x35, 0x85 }; |
︙ | ︙ |
Changes to src/etag.c.
︙ | ︙ | |||
124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 | if( strcmp(zIfNoneMatch,zETag)!=0 ) return; /* If we get this far, it means that the content has ** not changed and we can do a 304 reply */ cgi_reset_content(); cgi_set_status(304, "Not Modified"); cgi_reply(); fossil_exit(0); } /* ** Accept a new Last-Modified time. This routine should be called by ** page generators that know a valid last-modified time. This routine ** might generate a 304 Not Modified reply and exit(), never returning. ** Or, if not, it will cause a Last-Modified: header to be included in the ** reply. */ void etag_last_modified(sqlite3_int64 mtime){ const char *zIfModifiedSince; | > | | > | | > | 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 | if( strcmp(zIfNoneMatch,zETag)!=0 ) return; /* If we get this far, it means that the content has ** not changed and we can do a 304 reply */ cgi_reset_content(); cgi_set_status(304, "Not Modified"); cgi_reply(); db_close(0); fossil_exit(0); } /* ** Accept a new Last-Modified time. This routine should be called by ** page generators that know a valid last-modified time. This routine ** might generate a 304 Not Modified reply and exit(), never returning. ** Or, if not, it will cause a Last-Modified: header to be included in the ** reply. */ void etag_last_modified(sqlite3_int64 mtime){ const char *zIfModifiedSince; sqlite3_int64 x; assert( iEtagMtime==0 ); /* Only call this routine once */ assert( mtime>0 ); /* Only call with a valid mtime */ iEtagMtime = mtime; /* Check to see the If-Modified-Since constraint is satisfied */ zIfModifiedSince = P("HTTP_IF_MODIFIED_SINCE"); if( zIfModifiedSince==0 ) return; x = cgi_rfc822_parsedate(zIfModifiedSince); if( x<mtime ) return; #if 0 /* If the Fossil executable is more recent than If-Modified-Since, ** go ahead and regenerate the resource. */ if( file_mtime(g.nameOfExe, ExtFILE)>x ) return; #endif /* If we reach this point, it means that the resource has not changed ** and that we should generate a 304 Not Modified reply */ cgi_reset_content(); cgi_set_status(304, "Not Modified"); cgi_reply(); db_close(0); fossil_exit(0); } /* Return the ETag, if there is one. */ const char *etag_tag(void){ return zETag; |
︙ | ︙ |
Changes to src/event.c.
︙ | ︙ | |||
585 586 587 588 589 590 591 | user_select(); if (event_commit_common(rid, zId, blob_str(pContent), zETime, zMimeType, zComment, zTags, zClr)==0 ){ #ifdef FOSSIL_ENABLE_JSON g.json.resultCode = FSL_JSON_E_ASSERT; #endif | | | 585 586 587 588 589 590 591 592 593 594 595 | user_select(); if (event_commit_common(rid, zId, blob_str(pContent), zETime, zMimeType, zComment, zTags, zClr)==0 ){ #ifdef FOSSIL_ENABLE_JSON g.json.resultCode = FSL_JSON_E_ASSERT; #endif fossil_panic("Internal error: Fossil tried to make an " "invalid artifact for the technote."); } } |
Changes to src/export.c.
︙ | ︙ | |||
598 599 600 601 602 603 604 605 606 607 608 | } db_finalize(&q); db_finalize(&q2); db_finalize(&q3); /* Output the commit records. */ db_prepare(&q, "SELECT strftime('%%s',mtime), objid, coalesce(ecomment,comment)," " coalesce(euser,user)," " (SELECT value FROM tagxref WHERE rid=objid AND tagid=%d)" | > | > > | | | > > | 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 | } db_finalize(&q); db_finalize(&q2); db_finalize(&q3); /* Output the commit records. */ topological_sort_checkins(0); db_prepare(&q, "SELECT strftime('%%s',mtime), objid, coalesce(ecomment,comment)," " coalesce(euser,user)," " (SELECT value FROM tagxref WHERE rid=objid AND tagid=%d)" " FROM toponode, event" " WHERE toponode.tid=event.objid" " AND event.type='ci'" " AND NOT EXISTS (SELECT 1 FROM oldcommit WHERE toponode.tid=rid)" " ORDER BY toponode.tseq ASC", TAG_BRANCH ); db_prepare(&q2, "INSERT INTO oldcommit VALUES (:rid)"); while( db_step(&q)==SQLITE_ROW ){ Stmt q4; const char *zSecondsSince1970 = db_column_text(&q, 0); int ckinId = db_column_int(&q, 1); const char *zComment = db_column_text(&q, 2); const char *zUser = db_column_text(&q, 3); const char *zBranch = db_column_text(&q, 4); char *zMark; bag_insert(&vers, ckinId); db_bind_int(&q2, ":rid", ckinId); db_step(&q2); db_reset(&q2); if( zBranch==0 || fossil_strcmp(zBranch, "trunk")==0 ){ zBranch = gexport.zTrunkName; } zMark = mark_name_from_rid(ckinId, &unused_mark); printf("commit refs/heads/"); print_ref(zBranch); printf("\nmark %s\n", zMark); free(zMark); printf("committer"); print_person(zUser); |
︙ | ︙ | |||
735 736 737 738 739 740 741 | if( ferror(f)!=0 || fclose(f)!=0 ){ fossil_fatal("error while writing %s", markfile_out); } } bag_clear(&blobs); bag_clear(&vers); } | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 | if( ferror(f)!=0 || fclose(f)!=0 ){ fossil_fatal("error while writing %s", markfile_out); } } bag_clear(&blobs); bag_clear(&vers); } /* ** Construct the temporary table toposort as follows: ** ** CREATE TEMP TABLE toponode( ** tid INTEGER PRIMARY KEY, -- Check-in id ** tseq INT -- integer total order on check-ins. ** ); ** ** This table contains all check-ins of the repository in topological ** order. "Topological order" means that every parent check-in comes ** before all of its children. Topological order is *almost* the same ** thing as "ORDER BY event.mtime". Differences only arrise when there ** are timewarps. In as much as Git hates timewarps, we have to compute ** a correct topological order when doing an export. ** ** Since mtime is a usually already nearly in topological order, the ** algorithm is to start with mtime, then make adjustments as necessary ** for timewarps. This is not a great algorithm for the general case, ** but it is very fast for the overwhelmingly common case where there ** are few timewarps. */ int topological_sort_checkins(int bVerbose){ int nChange = 0; Stmt q1; Stmt chng; db_multi_exec( "CREATE TEMP TABLE toponode(\n" " tid INTEGER PRIMARY KEY,\n" " tseq INT\n" ");\n" "INSERT INTO toponode(tid,tseq) " " SELECT objid, CAST(mtime*8640000 AS int) FROM event WHERE type='ci';\n" "CREATE TEMP TABLE topolink(\n" " tparent INT,\n" " tchild INT,\n" " PRIMARY KEY(tparent,tchild)\n" ") WITHOUT ROWID;" "INSERT INTO topolink(tparent,tchild)" " SELECT pid, cid FROM plink;\n" "CREATE INDEX topolink_child ON topolink(tchild);\n" ); /* Find a timewarp instance */ db_prepare(&q1, "SELECT P.tseq, C.tid, C.tseq\n" " FROM toponode P, toponode C, topolink X\n" " WHERE X.tparent=P.tid\n" " AND X.tchild=C.tid\n" " AND P.tseq>=C.tseq;" ); /* Update the timestamp on :tid to have value :tseq */ db_prepare(&chng, "UPDATE toponode SET tseq=:tseq WHERE tid=:tid" ); while( db_step(&q1)==SQLITE_ROW ){ i64 iParentTime = db_column_int64(&q1, 0); int iChild = db_column_int(&q1, 1); i64 iChildTime = db_column_int64(&q1, 2); nChange++; if( nChange>10000 ){ fossil_fatal("failed to fix all timewarps after 100000 attempts"); } db_reset(&q1); db_bind_int64(&chng, ":tid", iChild); db_bind_int64(&chng, ":tseq", iParentTime+1); db_step(&chng); db_reset(&chng); if( bVerbose ){ fossil_print("moving %d from %lld to %lld\n", iChild, iChildTime, iParentTime+1); } } db_finalize(&q1); db_finalize(&chng); return nChange; } /* ** COMMAND: test-topological-sort ** ** Invoke the topological_sort_checkins() interface for testing ** purposes. */ void test_topological_sort(void){ int n; db_find_and_open_repository(0, 0); n = topological_sort_checkins(1); fossil_print("%d reorderings required\n", n); } |
Changes to src/file.c.
︙ | ︙ | |||
20 21 22 23 24 25 26 27 28 29 30 31 32 33 | #include "config.h" #include <sys/types.h> #include <sys/stat.h> #include <unistd.h> #include <stdio.h> #include <string.h> #include <errno.h> #include "file.h" /* ** On Windows, include the Platform SDK header file. */ #ifdef _WIN32 # include <direct.h> | > | 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 | #include "config.h" #include <sys/types.h> #include <sys/stat.h> #include <unistd.h> #include <stdio.h> #include <string.h> #include <errno.h> #include <time.h> #include "file.h" /* ** On Windows, include the Platform SDK header file. */ #ifdef _WIN32 # include <direct.h> |
︙ | ︙ | |||
894 895 896 897 898 899 900 | */ void file_getcwd(char *zBuf, int nBuf){ #ifdef _WIN32 win32_getcwd(zBuf, nBuf); #else if( getcwd(zBuf, nBuf-1)==0 ){ if( errno==ERANGE ){ | | | | 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 | */ void file_getcwd(char *zBuf, int nBuf){ #ifdef _WIN32 win32_getcwd(zBuf, nBuf); #else if( getcwd(zBuf, nBuf-1)==0 ){ if( errno==ERANGE ){ fossil_panic("pwd too big: max %d", nBuf-1); }else{ fossil_panic("cannot find current working directory; %s", strerror(errno)); } } #endif } /* |
︙ | ︙ | |||
1466 1467 1468 1469 1470 1471 1472 1473 1474 1475 | z = blob_buffer(pBuf); for(i=0; z[i]; i++) if( z[i]=='\\' ) z[i] = '/'; #else fossil_path_free((char *)azDirs[0]); #endif } /* ** COMMAND: test-tempname | > > > > > > > > > > > > > > > > > | | > > > > > > > > > | | | > | 1467 1468 1469 1470 1471 1472 1473 1474 1475 1476 1477 1478 1479 1480 1481 1482 1483 1484 1485 1486 1487 1488 1489 1490 1491 1492 1493 1494 1495 1496 1497 1498 1499 1500 1501 1502 1503 1504 1505 1506 1507 1508 1509 1510 1511 1512 1513 1514 1515 1516 1517 1518 1519 1520 1521 | z = blob_buffer(pBuf); for(i=0; z[i]; i++) if( z[i]=='\\' ) z[i] = '/'; #else fossil_path_free((char *)azDirs[0]); #endif } /* ** Compute a temporary filename in zDir. The filename is based on ** the current time. */ char *file_time_tempname(const char *zDir, const char *zSuffix){ struct tm *tm; unsigned int r; static unsigned int cnt = 0; time_t t; t = time(0); tm = gmtime(&t); sqlite3_randomness(sizeof(r), &r); return mprintf("%s/%04d%02d%02d%02d%02d%02d%04d%06d%s", zDir, tm->tm_year+1900, tm->tm_mon+1, tm->tm_mday, tm->tm_hour, tm->tm_min, tm->tm_sec, cnt++, r%1000000, zSuffix); } /* ** COMMAND: test-tempname ** Usage: fossil test-name [--time SUFFIX] BASENAME ... ** ** Generate temporary filenames derived from BASENAME. Use the --time ** option to generate temp names based on the time of day. */ void file_test_tempname(void){ int i; const char *zSuffix = find_option("time",0,1); Blob x = BLOB_INITIALIZER; char *z; verify_all_options(); for(i=2; i<g.argc; i++){ if( zSuffix ){ z = file_time_tempname(g.argv[i], zSuffix); fossil_print("%s\n", z); fossil_free(z); }else{ file_tempname(&x, g.argv[i]); fossil_print("%s\n", blob_str(&x)); blob_reset(&x); } } } /* ** Return true if a file named zName exists and has identical content ** to the blob pContent. If zName does not exist or if the content is |
︙ | ︙ | |||
1612 1613 1614 1615 1616 1617 1618 | #if defined(_WIN32) || defined(__CYGWIN__) if( z[0]=='/' && fossil_isalpha(z[1]) && z[2]==':' && z[3]=='/' ) z++; #else while( z[0]=='/' && z[1]=='/' ) z++; #endif return z; } | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1640 1641 1642 1643 1644 1645 1646 1647 1648 1649 1650 1651 1652 1653 1654 1655 1656 1657 1658 1659 1660 1661 1662 1663 1664 1665 1666 1667 1668 1669 1670 1671 1672 1673 1674 1675 1676 1677 1678 1679 1680 1681 1682 1683 1684 1685 1686 1687 1688 1689 1690 1691 1692 1693 1694 1695 1696 1697 1698 1699 | #if defined(_WIN32) || defined(__CYGWIN__) if( z[0]=='/' && fossil_isalpha(z[1]) && z[2]==':' && z[3]=='/' ) z++; #else while( z[0]=='/' && z[1]=='/' ) z++; #endif return z; } /* ** Count the number of objects (files and subdirectores) in a given ** directory. Return the count. Return -1 of the object is not a ** directory. */ int file_directory_size(const char *zDir, const char *zGlob, int omitDotFiles){ void *zNative; DIR *d; int n = -1; zNative = fossil_utf8_to_path(zDir,1); d = opendir(zNative); if( d ){ struct dirent *pEntry; n = 0; while( (pEntry=readdir(d))!=0 ){ if( pEntry->d_name[0]==0 ) continue; if( omitDotFiles && pEntry->d_name[0]=='.' ) continue; if( zGlob ){ char *zUtf8 = fossil_path_to_utf8(pEntry->d_name); int rc = sqlite3_strglob(zGlob, zUtf8); fossil_path_free(zUtf8); if( rc ) continue; } n++; } closedir(d); } fossil_path_free(zNative); return n; } /* ** COMMAND: test-dir-size ** ** Usage: %fossil test-dir-size NAME [GLOB] [--nodots] ** ** Return the number of objects in the directory NAME. If GLOB is ** provided, then only count objects that match the GLOB pattern. ** if --nodots is specified, omit files that begin with ".". */ void test_dir_size_cmd(void){ int omitDotFiles = find_option("nodots",0,0)!=0; const char *zGlob; const char *zDir; verify_all_options(); if( g.argc!=3 && g.argc!=4 ){ usage("NAME [GLOB] [-nodots]"); } zDir = g.argv[2]; zGlob = g.argc==4 ? g.argv[3] : 0; fossil_print("%d\n", file_directory_size(zDir, zGlob, omitDotFiles)); } |
Added src/forum.c.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952 953 954 955 956 957 958 959 960 961 962 963 964 965 966 967 968 969 970 971 972 973 974 975 976 977 978 979 980 981 982 983 984 985 986 987 988 989 990 991 992 993 994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 | /* ** Copyright (c) 2018 D. Richard Hipp ** ** This program is free software; you can redistribute it and/or ** modify it under the terms of the Simplified BSD License (also ** known as the "2-Clause License" or "FreeBSD License".) ** ** This program is distributed in the hope that it will be useful, ** but without any warranty; without even the implied warranty of ** merchantability or fitness for a particular purpose. ** ** Author contact information: ** drh@hwaci.com ** http://www.hwaci.com/drh/ ** ******************************************************************************* ** ** This file contains code used to generate the user forum. */ #include "config.h" #include <assert.h> #include "forum.h" /* ** Default to using Markdown markup */ #define DEFAULT_FORUM_MIMETYPE "text/x-markdown" #if INTERFACE /* ** Each instance of the following object represents a single message - ** either the initial post, an edit to a post, a reply, or an edit to ** a reply. */ struct ForumEntry { int fpid; /* rid for this entry */ int fprev; /* zero if initial entry. non-zero if an edit */ int firt; /* This entry replies to firt */ int mfirt; /* Root in-reply-to */ char *zUuid; /* Artifact hash */ ForumEntry *pLeaf; /* Most recent edit for this entry */ ForumEntry *pEdit; /* This entry is an edit of pEditee */ ForumEntry *pNext; /* Next in chronological order */ ForumEntry *pPrev; /* Previous in chronological order */ ForumEntry *pDisplay; /* Next in display order */ int nIndent; /* Number of levels of indentation for this entry */ }; /* ** A single instance of the following tracks all entries for a thread. */ struct ForumThread { ForumEntry *pFirst; /* First entry in chronological order */ ForumEntry *pLast; /* Last entry in chronological order */ ForumEntry *pDisplay; /* Entries in display order */ ForumEntry *pTail; /* Last on the display list */ }; #endif /* INTERFACE */ /* ** Delete a complete ForumThread and all its entries. */ static void forumthread_delete(ForumThread *pThread){ ForumEntry *pEntry, *pNext; for(pEntry=pThread->pFirst; pEntry; pEntry = pNext){ pNext = pEntry->pNext; fossil_free(pEntry->zUuid); fossil_free(pEntry); } fossil_free(pThread); } #if 0 /* not used */ /* ** Search a ForumEntry list forwards looking for the entry with fpid */ static ForumEntry *forumentry_forward(ForumEntry *p, int fpid){ while( p && p->fpid!=fpid ) p = p->pNext; return p; } #endif /* ** Search backwards for a ForumEntry */ static ForumEntry *forumentry_backward(ForumEntry *p, int fpid){ while( p && p->fpid!=fpid ) p = p->pPrev; return p; } /* ** Add an entry to the display list */ static void forumentry_add_to_display(ForumThread *pThread, ForumEntry *p){ if( pThread->pDisplay==0 ){ pThread->pDisplay = p; }else{ pThread->pTail->pDisplay = p; } pThread->pTail = p; } /* ** Extend the display list for pThread by adding all entries that ** reference fpid. The first such entry will be no earlier then ** entry "p". */ static void forumthread_display_order( ForumThread *pThread, ForumEntry *p, int fpid, int nIndent ){ while( p ){ if( p->fprev==0 && p->mfirt==fpid ){ p->nIndent = nIndent; forumentry_add_to_display(pThread, p); forumthread_display_order(pThread, p->pNext, p->fpid, nIndent+1); } p = p->pNext; } } /* ** Construct a ForumThread object given the root record id. */ static ForumThread *forumthread_create(int froot, int computeHierarchy){ ForumThread *pThread; ForumEntry *pEntry; Stmt q; pThread = fossil_malloc( sizeof(*pThread) ); memset(pThread, 0, sizeof(*pThread)); db_prepare(&q, "SELECT fpid, firt, fprev, (SELECT uuid FROM blob WHERE rid=fpid)" " FROM forumpost" " WHERE froot=%d ORDER BY fmtime", froot ); while( db_step(&q)==SQLITE_ROW ){ pEntry = fossil_malloc( sizeof(*pEntry) ); memset(pEntry, 0, sizeof(*pEntry)); pEntry->fpid = db_column_int(&q, 0); pEntry->firt = db_column_int(&q, 1); pEntry->fprev = db_column_int(&q, 2); pEntry->zUuid = fossil_strdup(db_column_text(&q,3)); pEntry->mfirt = pEntry->firt; pEntry->pPrev = pThread->pLast; pEntry->pNext = 0; if( pThread->pLast==0 ){ pThread->pFirst = pEntry; }else{ pThread->pLast->pNext = pEntry; } pThread->pLast = pEntry; } db_finalize(&q); /* Establish which entries are the latest edit. After this loop ** completes, entries that have non-NULL pLeaf should not be ** displayed. */ for(pEntry=pThread->pFirst; pEntry; pEntry=pEntry->pNext){ if( pEntry->fprev ){ ForumEntry *pBase = 0, *p; p = forumentry_backward(pEntry->pPrev, pEntry->fprev); pEntry->pEdit = p; while( p ){ pBase = p; p->pLeaf = pEntry; p = pBase->pEdit; } for(p=pEntry->pNext; p; p=p->pNext){ if( p->mfirt==pEntry->fpid ) p->mfirt = pBase->fpid; } } } if( computeHierarchy ){ /* Compute the hierarchical display order */ pEntry = pThread->pFirst; pEntry->nIndent = 1; forumentry_add_to_display(pThread, pEntry); forumthread_display_order(pThread, pEntry, pEntry->fpid, 2); } /* Return the result */ return pThread; } /* ** COMMAND: test-forumthread ** ** Usage: %fossil test-forumthread THREADID ** ** Display a summary of all messages on a thread. */ void forumthread_cmd(void){ int fpid; int froot; const char *zName; ForumThread *pThread; ForumEntry *p; db_find_and_open_repository(0,0); verify_all_options(); if( g.argc!=3 ) usage("THREADID"); zName = g.argv[2]; fpid = symbolic_name_to_rid(zName, "f"); if( fpid<=0 ){ fossil_fatal("Unknown or ambiguous forum id: \"%s\"", zName); } froot = db_int(0, "SELECT froot FROM forumpost WHERE fpid=%d", fpid); if( froot==0 ){ fossil_fatal("Not a forum post: \"%s\"", zName); } fossil_print("fpid = %d\n", fpid); fossil_print("froot = %d\n", froot); pThread = forumthread_create(froot, 1); fossil_print("Chronological:\n"); /* 123456789 123456789 123456789 123456789 123456789 */ fossil_print(" fpid firt fprev mfirt pLeaf\n"); for(p=pThread->pFirst; p; p=p->pNext){ fossil_print("%9d %9d %9d %9d %9d\n", p->fpid, p->firt, p->fprev, p->mfirt, p->pLeaf ? p->pLeaf->fpid : 0); } fossil_print("\nDisplay\n"); for(p=pThread->pDisplay; p; p=p->pDisplay){ fossil_print("%*s", (p->nIndent-1)*3, ""); if( p->pLeaf ){ fossil_print("%d->%d\n", p->fpid, p->pLeaf->fpid); }else{ fossil_print("%d\n", p->fpid); } } forumthread_delete(pThread); } /* ** Render a forum post for display */ void forum_render( const char *zTitle, /* The title. Might be NULL for no title */ const char *zMimetype, /* Mimetype of the message */ const char *zContent, /* Content of the message */ const char *zClass /* Put in a <div> if not NULL */ ){ if( zClass ){ @ <div class='%s(zClass)'> } if( zTitle ){ if( zTitle[0] ){ @ <h1>%h(zTitle)</h1> }else{ @ <h1><i>Deleted</i></h1> } } if( zContent && zContent[0] ){ Blob x; blob_init(&x, 0, 0); blob_append(&x, zContent, -1); wiki_render_by_mimetype(&x, zMimetype); blob_reset(&x); }else{ @ <i>Deleted</i> } if( zClass ){ @ </div> } } /* ** Display all posts in a forum thread in chronological order */ static void forum_display_chronological(int froot, int target){ ForumThread *pThread = forumthread_create(froot, 0); ForumEntry *p; int notAnon = login_is_individual(); for(p=pThread->pFirst; p; p=p->pNext){ char *zDate; Manifest *pPost; int isPrivate; /* True for posts awaiting moderation */ int sameUser; /* True if author is also the reader */ pPost = manifest_get(p->fpid, CFTYPE_FORUM, 0); if( pPost==0 ) continue; if( p->fpid==target ){ @ <div id="forum%d(p->fpid)" class="forumTime forumSel"> }else if( p->pLeaf!=0 ){ @ <div id="forum%d(p->fpid)" class="forumTime forumObs"> }else{ @ <div id="forum%d(p->fpid)" class="forumTime"> } if( pPost->zThreadTitle ){ @ <h1>%h(pPost->zThreadTitle)</h1> } zDate = db_text(0, "SELECT datetime(%.17g)", pPost->rDate); @ <p>By %h(pPost->zUser) on %h(zDate) (%d(p->fpid)) fossil_free(zDate); if( p->pEdit ){ @ edit of %z(href("%R/forumpost/%S?t",p->pEdit->zUuid))%d(p->fprev)</a> } if( p->firt ){ ForumEntry *pIrt = p->pPrev; while( pIrt && pIrt->fpid!=p->firt ) pIrt = pIrt->pPrev; if( pIrt ){ @ reply to %z(href("%R/forumpost/%S?t",pIrt->zUuid))%d(p->firt)</a> } } if( p->pLeaf ){ @ updated by %z(href("%R/forumpost/%S?t",p->pLeaf->zUuid))\ @ %d(p->pLeaf->fpid)</a> } if( g.perm.Debug ){ @ <span class="debug">\ @ <a href="%R/artifact/%h(p->zUuid)">artifact</a></span> } if( p->fpid!=target ){ @ %z(href("%R/forumpost/%S?t",p->zUuid))[link]</a> } isPrivate = content_is_private(p->fpid); sameUser = notAnon && fossil_strcmp(pPost->zUser, g.zLogin)==0; if( isPrivate && !g.perm.ModForum && !sameUser ){ @ <p><span class="modpending">Awaiting Moderator Approval</span></p> }else{ forum_render(0, pPost->zMimetype, pPost->zWiki, 0); } if( g.perm.WrForum && p->pLeaf==0 ){ int sameUser = login_is_individual() && fossil_strcmp(pPost->zUser, g.zLogin)==0; @ <p><form action="%R/forumedit" method="POST"> @ <input type="hidden" name="fpid" value="%s(p->zUuid)"> if( !isPrivate ){ /* Reply and Edit are only available if the post has already ** been approved */ @ <input type="submit" name="reply" value="Reply"> if( g.perm.Admin || sameUser ){ @ <input type="submit" name="edit" value="Edit"> @ <input type="submit" name="nullout" value="Delete"> } }else if( g.perm.ModForum ){ /* Provide moderators with moderation buttons for posts that ** are pending moderation */ @ <input type="submit" name="approve" value="Approve"> @ <input type="submit" name="reject" value="Reject"> }else if( sameUser ){ /* A post that is pending moderation can be deleted by the ** person who originally submitted the post */ @ <input type="submit" name="reject" value="Delete"> } @ </form></p> } manifest_destroy(pPost); @ </div> } forumthread_delete(pThread); } /* ** Display all messages in a forumthread with indentation. */ static int forum_display_hierarchical(int froot, int target){ ForumThread *pThread; ForumEntry *p; Manifest *pPost, *pOPost; int fpid; const char *zUuid; char *zDate; const char *zSel; int notAnon = login_is_individual(); pThread = forumthread_create(froot, 1); for(p=pThread->pFirst; p; p=p->pNext){ if( p->fpid==target ){ while( p->pEdit ) p = p->pEdit; target = p->fpid; break; } } for(p=pThread->pDisplay; p; p=p->pDisplay){ int isPrivate; /* True for posts awaiting moderation */ int sameUser; /* True if reader is also the poster */ pOPost = manifest_get(p->fpid, CFTYPE_FORUM, 0); if( p->pLeaf ){ fpid = p->pLeaf->fpid; zUuid = p->pLeaf->zUuid; pPost = manifest_get(fpid, CFTYPE_FORUM, 0); }else{ fpid = p->fpid; zUuid = p->zUuid; pPost = pOPost; } zSel = p->fpid==target ? " forumSel" : ""; if( p->nIndent==1 ){ @ <div id='forum%d(fpid)' class='forumHierRoot%s(zSel)'> }else{ @ <div id='forum%d(fpid)' class='forumHier%s(zSel)' \ @ style='margin-left: %d((p->nIndent-1)*3)ex;'> } pPost = manifest_get(fpid, CFTYPE_FORUM, 0); if( pPost==0 ) continue; if( pPost->zThreadTitle ){ @ <h1>%h(pPost->zThreadTitle)</h1> } zDate = db_text(0, "SELECT datetime(%.17g)", pOPost->rDate); @ <p>By %h(pOPost->zUser) on %h(zDate) fossil_free(zDate); if( g.perm.Debug ){ @ <span class="debug">\ @ <a href="%R/artifact/%h(p->zUuid)">(%d(p->fpid))</a></span> } if( p->pLeaf ){ zDate = db_text(0, "SELECT datetime(%.17g)", pPost->rDate); if( fossil_strcmp(pOPost->zUser,pPost->zUser)==0 ){ @ and edited on %h(zDate) }else{ @ as edited by %h(pPost->zUser) on %h(zDate) } fossil_free(zDate); if( g.perm.Debug ){ @ <span class="debug">\ @ <a href="%R/artifact/%h(p->pLeaf->zUuid)">(%d(fpid))</a></span> } manifest_destroy(pOPost); } if( fpid!=target ){ @ %z(href("%R/forumpost/%S",zUuid))[link]</a> } isPrivate = content_is_private(fpid); sameUser = notAnon && fossil_strcmp(pPost->zUser, g.zLogin)==0; if( isPrivate && !g.perm.ModForum && !sameUser ){ @ <p><span class="modpending">Awaiting Moderator Approval</span></p> }else{ forum_render(0, pPost->zMimetype, pPost->zWiki, 0); } if( g.perm.WrForum ){ @ <p><form action="%R/forumedit" method="POST"> @ <input type="hidden" name="fpid" value="%s(zUuid)"> if( !isPrivate ){ /* Reply and Edit are only available if the post has already ** been approved */ @ <input type="submit" name="reply" value="Reply"> if( g.perm.Admin || sameUser ){ @ <input type="submit" name="edit" value="Edit"> @ <input type="submit" name="nullout" value="Delete"> } }else if( g.perm.ModForum ){ /* Provide moderators with moderation buttons for posts that ** are pending moderation */ @ <input type="submit" name="approve" value="Approve"> @ <input type="submit" name="reject" value="Reject"> }else if( sameUser ){ /* A post that is pending moderation can be deleted by the ** person who originally submitted the post */ @ <input type="submit" name="reject" value="Delete"> } @ </form></p> } manifest_destroy(pPost); @ </div> } forumthread_delete(pThread); return target; } /* ** WEBPAGE: forumpost ** ** Show a single forum posting. The posting is shown in context with ** it's entire thread. The selected posting is enclosed within ** <div class='forumSel'>...</div>. Javascript is used to move the ** selected posting into view after the page loads. ** ** Query parameters: ** ** name=X REQUIRED. The hash of the post to display ** t Show a chronologic listing instead of hierarchical */ void forumpost_page(void){ forumthread_page(); } /* ** WEBPAGE: forumthread ** ** Show all forum messages associated with a particular message thread. ** The result is basically the same as /forumpost except that none of ** the postings in the thread are selected. ** ** Query parameters: ** ** name=X REQUIRED. The hash of any post of the thread. ** t Show a chronologic listing instead of hierarchical */ void forumthread_page(void){ int fpid; int froot; const char *zName = P("name"); login_check_credentials(); if( !g.perm.RdForum ){ login_needed(g.anon.RdForum); return; } if( zName==0 ){ webpage_error("Missing \"name=\" query parameter"); } fpid = symbolic_name_to_rid(zName, "f"); if( fpid<=0 ){ webpage_error("Unknown or ambiguous forum id: \"%s\"", zName); } style_header("Forum"); froot = db_int(0, "SELECT froot FROM forumpost WHERE fpid=%d", fpid); if( froot==0 ){ webpage_error("Not a forum post: \"%s\"", zName); } if( fossil_strcmp(g.zPath,"forumthread")==0 ) fpid = 0; if( P("t") ){ if( g.perm.Debug ){ style_submenu_element("Hierarchical", "%R/%s/%s", g.zPath, zName); } forum_display_chronological(froot, fpid); }else{ if( g.perm.Debug ){ style_submenu_element("Chronological", "%R/%s/%s?t", g.zPath, zName); } forum_display_hierarchical(froot, fpid); } style_load_js("forum.js"); style_footer(); } /* ** Return true if a forum post should be moderated. */ static int forum_need_moderation(void){ if( P("domod") ) return 1; if( g.perm.WrTForum ) return 0; if( g.perm.ModForum ) return 0; return 1; } /* ** Add a new Forum Post artifact to the repository. ** ** Return true if a redirect occurs. */ static int forum_post( const char *zTitle, /* Title. NULL for replies */ int iInReplyTo, /* Post replying to. 0 for new threads */ int iEdit, /* Post being edited, or zero for a new post */ const char *zUser, /* Username. NULL means use login name */ const char *zMimetype, /* Mimetype of content. */ const char *zContent /* Content */ ){ char *zDate; char *zI; char *zG; int iBasis; Blob x, cksum, formatCheck, errMsg; Manifest *pPost; schema_forum(); if( iInReplyTo==0 && iEdit>0 ){ iBasis = iEdit; iInReplyTo = db_int(0, "SELECT firt FROM forumpost WHERE fpid=%d", iEdit); }else{ iBasis = iInReplyTo; } webpage_assert( (zTitle==0)+(iInReplyTo==0)==1 ); blob_init(&x, 0, 0); zDate = date_in_standard_format("now"); blob_appendf(&x, "D %s\n", zDate); fossil_free(zDate); zG = db_text(0, "SELECT uuid FROM blob, forumpost" " WHERE blob.rid==forumpost.froot" " AND forumpost.fpid=%d", iBasis); if( zG ){ blob_appendf(&x, "G %s\n", zG); fossil_free(zG); } if( zTitle ){ blob_appendf(&x, "H %F\n", zTitle); } zI = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", iInReplyTo); if( zI ){ blob_appendf(&x, "I %s\n", zI); fossil_free(zI); } if( fossil_strcmp(zMimetype,"text/x-fossil-wiki")!=0 ){ blob_appendf(&x, "N %s\n", zMimetype); } if( iEdit>0 ){ char *zP = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", iEdit); if( zP==0 ) webpage_error("missing edit artifact %d", iEdit); blob_appendf(&x, "P %s\n", zP); fossil_free(zP); } if( zUser==0 ){ if( login_is_nobody() ){ zUser = "anonymous"; }else{ zUser = login_name(); } } blob_appendf(&x, "U %F\n", zUser); blob_appendf(&x, "W %d\n%s\n", strlen(zContent), zContent); md5sum_blob(&x, &cksum); blob_appendf(&x, "Z %b\n", &cksum); blob_reset(&cksum); /* Verify that the artifact we are creating is well-formed */ blob_init(&formatCheck, 0, 0); blob_init(&errMsg, 0, 0); blob_copy(&formatCheck, &x); pPost = manifest_parse(&formatCheck, 0, &errMsg); if( pPost==0 ){ webpage_error("malformed forum post artifact - %s", blob_str(&errMsg)); } webpage_assert( pPost->type==CFTYPE_FORUM ); manifest_destroy(pPost); if( P("dryrun") ){ @ <div class='debug'> @ This is the artifact that would have been generated: @ <pre>%h(blob_str(&x))</pre> @ </div> blob_reset(&x); return 0; }else{ int nrid = wiki_put(&x, 0, forum_need_moderation()); cgi_redirectf("%R/forumpost/%S", rid_to_uuid(nrid)); return 1; } } /* ** Paint the form elements for entering a Forum post */ static void forum_entry_widget( const char *zTitle, const char *zMimetype, const char *zContent ){ if( zTitle ){ @ Title: <input type="input" name="title" value="%h(zTitle)" size="50"><br> } @ Markup style: mimetype_option_menu(zMimetype); @ <br><textarea name="content" class="wikiedit" cols="80" \ @ rows="25" wrap="virtual">%h(zContent)</textarea><br> } /* ** WEBPAGE: forumnew ** WEBPAGE: forumedit ** ** Start a new thread on the forum or reply to an existing thread. ** But first prompt to see if the user would like to log in. */ void forum_page_init(void){ int isEdit; char *zGoto; login_check_credentials(); if( !g.perm.WrForum ){ login_needed(g.anon.WrForum); return; } if( sqlite3_strglob("*edit*", g.zPath)==0 ){ zGoto = mprintf("%R/forume2?fpid=%S",PD("fpid","")); isEdit = 1; }else{ zGoto = mprintf("%R/forume1"); isEdit = 0; } if( login_is_individual() ){ if( isEdit ){ forumedit_page(); }else{ forumnew_page(); } return; } style_header("%h As Anonymous?", isEdit ? "Reply" : "Post"); @ <p>You are not logged in. @ <p><table border="0" cellpadding="10"> @ <tr><td> @ <form action="%s(zGoto)" method="POST"> @ <input type="submit" value="Remain Anonymous"> @ </form> @ <td>Post to the forum anonymously if( login_self_register_available(0) ){ @ <tr><td> @ <form action="%R/register" method="POST"> @ <input type="hidden" name="g" value="%s(zGoto)"> @ <input type="submit" value="Create An Account"> @ </form> @ <td>Create a new account and post using that new account } @ <tr><td> @ <form action="%R/login" method="POST"> @ <input type="hidden" name="g" value="%s(zGoto)"> @ <input type="hidden" name="noanon" value="1"> @ <input type="submit" value="Login"> @ </form> @ <td>Log into an existing account @ </table> style_footer(); fossil_free(zGoto); } /* ** Write the "From: USER" line on the webpage. */ static void forum_from_line(void){ if( login_is_nobody() ){ @ From: anonymous<br> }else{ @ From: %h(login_name())<br> } } /* ** WEBPAGE: forume1 ** ** Start a new forum thread. */ void forumnew_page(void){ const char *zTitle = PDT("title",""); const char *zMimetype = PD("mimetype",DEFAULT_FORUM_MIMETYPE); const char *zContent = PDT("content",""); login_check_credentials(); if( !g.perm.WrForum ){ login_needed(g.anon.WrForum); return; } if( P("submit") ){ if( forum_post(zTitle, 0, 0, 0, zMimetype, zContent) ) return; } if( P("preview") ){ @ <h1>Preview:</h1> forum_render(zTitle, zMimetype, zContent, "forumEdit"); } style_header("New Forum Thread"); @ <form action="%R/forume1" method="POST"> @ <h1>New Message:</h1> forum_from_line(); forum_entry_widget(zTitle, zMimetype, zContent); @ <input type="submit" name="preview" value="Preview"> if( P("preview") ){ @ <input type="submit" name="submit" value="Submit"> }else{ @ <input type="submit" name="submit" value="Submit" disabled> } if( g.perm.Debug ){ /* For the test-forumnew page add these extra debugging controls */ @ <div class="debug"> @ <label><input type="checkbox" name="dryrun" %s(PCK("dryrun"))> \ @ Dry run</label> @ <br><label><input type="checkbox" name="domod" %s(PCK("domod"))> \ @ Require moderator approval</label> @ <br><label><input type="checkbox" name="showqp" %s(PCK("showqp"))> \ @ Show query parameters</label> @ </div> } @ </form> style_footer(); } /* ** WEBPAGE: forume2 ** ** Edit an existing forum message. ** Query parameters: ** ** fpid=X Hash of the post to be editted. REQUIRED */ void forumedit_page(void){ int fpid; Manifest *pPost; const char *zMimetype = 0; const char *zContent = 0; const char *zTitle = 0; int isCsrfSafe; int isDelete = 0; login_check_credentials(); if( !g.perm.WrForum ){ login_needed(g.anon.WrForum); return; } fpid = symbolic_name_to_rid(PD("fpid",""), "f"); if( fpid<=0 || (pPost = manifest_get(fpid, CFTYPE_FORUM, 0))==0 ){ webpage_error("Missing or invalid fpid query parameter"); } if( P("cancel") ){ cgi_redirectf("%R/forumpost/%S",P("fpid")); return; } isCsrfSafe = cgi_csrf_safe(1); if( g.perm.ModForum && isCsrfSafe ){ if( P("approve") ){ moderation_approve(fpid); cgi_redirectf("%R/forumpost/%S",P("fpid")); return; } if( P("reject") ){ char *zParent = db_text(0, "SELECT uuid FROM forumpost, blob" " WHERE forumpost.fpid=%d AND blob.rid=forumpost.firt", fpid ); moderation_disapprove(fpid); if( zParent ){ cgi_redirectf("%R/forumpost/%S",zParent); }else{ cgi_redirectf("%R/forum"); } return; } } isDelete = P("nullout")!=0; if( P("submit") && isCsrfSafe ){ int done = 1; const char *zMimetype = PD("mimetype",DEFAULT_FORUM_MIMETYPE); const char *zContent = PDT("content",""); if( P("reply") ){ done = forum_post(0, fpid, 0, 0, zMimetype, zContent); }else if( P("edit") || isDelete ){ done = forum_post(P("title"), 0, fpid, 0, zMimetype, zContent); }else{ webpage_error("Missing 'reply' query parameter"); } if( done ) return; } if( isDelete ){ zMimetype = "text/x-fossil-wiki"; zContent = ""; if( pPost->zThreadTitle ) zTitle = ""; style_header("Delete %s", zTitle ? "Post" : "Reply"); @ <h1>Original Post:</h1> forum_render(pPost->zThreadTitle, pPost->zMimetype, pPost->zWiki, "forumEdit"); @ <h1>Change Into:</h1> forum_render(zTitle, zMimetype, zContent,"forumEdit"); @ <form action="%R/forume2" method="POST"> @ <input type="hidden" name="fpid" value="%h(P("fpid"))"> @ <input type="hidden" name="nullout" value="1"> @ <input type="hidden" name="mimetype" value="%h(zMimetype)"> @ <input type="hidden" name="content" value="%h(zContent)"> if( zTitle ){ @ <input type="hidden" name="title" value="%h(zTitle)"> } }else if( P("edit") ){ /* Provide an edit to the fpid post */ zMimetype = P("mimetype"); zContent = PT("content"); zTitle = P("title"); if( zContent==0 ) zContent = fossil_strdup(pPost->zWiki); if( zMimetype==0 ) zMimetype = fossil_strdup(pPost->zMimetype); if( zTitle==0 && pPost->zThreadTitle!=0 ){ zTitle = fossil_strdup(pPost->zThreadTitle); } style_header("Edit %s", zTitle ? "Post" : "Reply"); @ <h1>Original Post:</h1> forum_render(pPost->zThreadTitle, pPost->zMimetype, pPost->zWiki, "forumEdit"); if( P("preview") ){ @ <h1>Preview Of Editted Post:</h1> forum_render(zTitle, zMimetype, zContent,"forumEdit"); } @ <h1>Revised Message:</h1> @ <form action="%R/forume2" method="POST"> @ <input type="hidden" name="fpid" value="%h(P("fpid"))"> @ <input type="hidden" name="edit" value="1"> forum_from_line(); forum_entry_widget(zTitle, zMimetype, zContent); }else{ /* Reply */ zMimetype = PD("mimetype",DEFAULT_FORUM_MIMETYPE); zContent = PDT("content",""); style_header("Reply"); @ <h1>Replying To:</h1> forum_render(0, pPost->zMimetype, pPost->zWiki, "forumEdit"); if( P("preview") ){ @ <h1>Preview:</h1> forum_render(0, zMimetype,zContent, "forumEdit"); } @ <h1>Enter Reply:</h1> @ <form action="%R/forume2" method="POST"> @ <input type="hidden" name="fpid" value="%h(P("fpid"))"> @ <input type="hidden" name="reply" value="1"> forum_from_line(); forum_entry_widget(0, zMimetype, zContent); } if( !isDelete ){ @ <input type="submit" name="preview" value="Preview"> } @ <input type="submit" name="cancel" value="Cancel"> if( P("preview") || isDelete ){ @ <input type="submit" name="submit" value="Submit"> } if( g.perm.Debug ){ /* For the test-forumnew page add these extra debugging controls */ @ <div class="debug"> @ <label><input type="checkbox" name="dryrun" %s(PCK("dryrun"))> \ @ Dry run</label> @ <br><label><input type="checkbox" name="domod" %s(PCK("domod"))> \ @ Require moderator approval</label> @ <br><label><input type="checkbox" name="showqp" %s(PCK("showqp"))> \ @ Show query parameters</label> @ </div> } @ </form> style_footer(); } /* ** WEBPAGE: forum ** ** The main page for the forum feature. Show a list of recent forum ** threads. Also show a search box at the top if search is enabled, ** and a button for creating a new thread, if enabled. ** ** Query parameters: ** ** n=N The number of threads to show on each page ** x=X Skip the first X threads */ void forum_main_page(void){ Stmt q; int iLimit, iOfst, iCnt; login_check_credentials(); if( !g.perm.RdForum ){ login_needed(g.anon.RdForum); return; } style_header("Forum"); if( g.perm.WrForum ){ style_submenu_element("New Message","%R/forumnew"); } if( g.perm.ModForum && moderation_needed() ){ style_submenu_element("Moderation Requests", "%R/modreq"); } if( search_screen(SRCH_FORUM, 0) ){ style_submenu_element("Recent Threads","%R/forum"); style_footer(); return; } iLimit = atoi(PD("n","25")); iOfst = atoi(PD("x","0")); iCnt = 0; if( db_table_exists("repository","forumpost") ){ db_prepare(&q, "WITH thread(age,duration,cnt,root,last) AS (" " SELECT" " julianday('now') - max(fmtime)," " max(fmtime) - min(fmtime)," " sum(fprev IS NULL)," " froot," " (SELECT fpid FROM forumpost AS y" " WHERE y.froot=x.froot %s" " ORDER BY y.fmtime DESC LIMIT 1)" " FROM forumpost AS x" " WHERE %s" " GROUP BY froot" " ORDER BY 1 LIMIT %d OFFSET %d" ")" "SELECT" " thread.age," /* 0 */ " thread.duration," /* 1 */ " thread.cnt," /* 2 */ " blob.uuid," /* 3 */ " substr(event.comment,instr(event.comment,':')+1)," /* 4 */ " thread.last" /* 5 */ " FROM thread, blob, event" " WHERE blob.rid=thread.last" " AND event.objid=thread.last" " ORDER BY 1;", g.perm.ModForum ? "" : "AND y.fpid NOT IN private" /*safe-for-%s*/, g.perm.ModForum ? "true" : "fpid NOT IN private" /*safe-for-%s*/, iLimit+1, iOfst ); while( db_step(&q)==SQLITE_ROW ){ char *zAge = human_readable_age(db_column_double(&q,0)); int nMsg = db_column_int(&q, 2); const char *zUuid = db_column_text(&q, 3); const char *zTitle = db_column_text(&q, 4); if( iCnt==0 ){ if( iOfst>0 ){ @ <h1>Threads at least %s(zAge) old</h1> }else{ @ <h1>Most recent threads</h1> } @ <div class='fileage'><table width="100%%"> if( iOfst>0 ){ if( iOfst>iLimit ){ @ <tr><td colspan="3">\ @ %z(href("%R/forum?x=%d&n=%d",iOfst-iLimit,iLimit))\ @ ↑ Newer...</a></td></tr> }else{ @ <tr><td colspan="3">%z(href("%R/forum?n=%d",iLimit))\ @ ↑ Newer...</a></td></tr> } } } iCnt++; if( iCnt>iLimit ){ @ <tr><td colspan="3">\ @ %z(href("%R/forum?x=%d&n=%d",iOfst+iLimit,iLimit))\ @ ↓ Older...</a></td></tr> fossil_free(zAge); break; } @ <tr><td>%h(zAge) ago</td> @ <td>%z(href("%R/forumpost/%S",zUuid))%h(zTitle)</a></td> @ <td>\ if( g.perm.ModForum && moderation_pending(db_column_int(&q,5)) ){ @ <span class="modpending">\ @ Awaiting Moderator Approval</span><br> } if( nMsg<2 ){ @ no replies</td> }else{ char *zDuration = human_readable_age(db_column_double(&q,1)); @ %d(nMsg) posts spanning %h(zDuration)</td> fossil_free(zDuration); } @ </tr> fossil_free(zAge); } db_finalize(&q); } if( iCnt>0 ){ @ </table></div> }else{ @ <h1>No forum posts found</h1> } style_footer(); } |
Added src/forum.js.
> > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | (function(){ function absoluteY(obj){ var top = 0; if( obj.offsetParent ){ do{ top += obj.offsetTop; }while( obj = obj.offsetParent ); } return top; } var x = document.getElementsByClassName('forumSel'); if(x[0]){ var w = window.innerHeight; var h = x[0].scrollHeight; var y = absoluteY(x[0]); if( w>h ) y = y + (h-w)/2; if( y>0 ) window.scrollTo(0, y); } }()) |
Changes to src/graph.c.
︙ | ︙ | |||
532 533 534 535 536 537 538 | } } mask = BIT(pRow->iRail); pRow->railInUse |= mask; if( pRow->pChild ){ assignChildrenToRail(pRow); }else if( !omitDescenders && count_nonbranch_children(pRow->rid)!=0 ){ | | | 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 | } } mask = BIT(pRow->iRail); pRow->railInUse |= mask; if( pRow->pChild ){ assignChildrenToRail(pRow); }else if( !omitDescenders && count_nonbranch_children(pRow->rid)!=0 ){ if( !pRow->timeWarp ) riser_to_top(pRow); } if( pParent ){ for(pLoop=pParent->pPrev; pLoop && pLoop!=pRow; pLoop=pLoop->pPrev){ pLoop->railInUse |= mask; } } } |
︙ | ︙ |
Changes to src/graph.js.
︙ | ︙ | |||
105 106 107 108 109 110 111 | line = elems.line; mArrow = elems.arrow_merge_r; mLine = elems.line_merge; wArrow = elems.arrow_warp; wLine = elems.line_warp; var minRailPitch = Math.ceil((node.w+line.w)/2 + mArrow.w + 1); | > > > | | | | | | | > | 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 | line = elems.line; mArrow = elems.arrow_merge_r; mLine = elems.line_merge; wArrow = elems.arrow_warp; wLine = elems.line_warp; var minRailPitch = Math.ceil((node.w+line.w)/2 + mArrow.w + 1); if( window.innerWidth<400 ){ railPitch = minRailPitch; }else{ if( tx.iRailPitch>0 ){ railPitch = tx.iRailPitch; }else{ railPitch = elems.rail.w; railPitch -= Math.floor((tx.nrail-1)*(railPitch-minRailPitch)/21); } railPitch = Math.max(railPitch, minRailPitch); } if( tx.nomo ){ mergeOffset = 0; }else{ mergeOffset = railPitch-minRailPitch-mLine.w; mergeOffset = Math.min(mergeOffset, elems.mergeoffset.w); mergeOffset = mergeOffset>0 ? mergeOffset + line.w/2 : 0; |
︙ | ︙ | |||
373 374 375 376 377 378 379 380 381 382 383 384 385 386 | var i; for(i=0; i<lx.length; i++){ if( lx[i].hasAttribute('data-id') ) lx[i].onclick = toggleDetail; } lx = topObj.getElementsByClassName('timelineCompactComment'); for(i=0; i<lx.length; i++){ if( lx[i].hasAttribute('data-id') ) lx[i].onclick = toggleDetail; } } /* Look for all timeline-data-NN objects. Load each one and draw ** a graph for each one. */ (function(){ | > > > > > > > > > > > > | 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 | var i; for(i=0; i<lx.length; i++){ if( lx[i].hasAttribute('data-id') ) lx[i].onclick = toggleDetail; } lx = topObj.getElementsByClassName('timelineCompactComment'); for(i=0; i<lx.length; i++){ if( lx[i].hasAttribute('data-id') ) lx[i].onclick = toggleDetail; } if( window.innerWidth<400 ){ /* On narrow displays, shift the date from the first column to the ** third column, to make the first column narrower */ lx = topObj.getElementsByClassName('timelineDateRow'); for(i=0; i<lx.length; i++){ var rx = lx[i]; if( rx.getAttribute('data-reordered') ) break; rx.setAttribute('data-reordered',1); rx.appendChild(rx.firstChild); rx.insertBefore(rx.childNodes[1],rx.firstChild); } } } /* Look for all timeline-data-NN objects. Load each one and draw ** a graph for each one. */ (function(){ |
︙ | ︙ |
Changes to src/http_socket.c.
︙ | ︙ | |||
22 23 24 25 26 27 28 29 30 31 32 33 34 35 | ** at a time. State information is stored in static variables. The identity ** of the server is held in global variables that are set by url_parse(). ** ** Low-level sockets are abstracted out into this module because they ** are handled different on Unix and windows. */ #if defined(_WIN32) # define _WIN32_WINNT 0x501 #endif #ifndef __EXTENSIONS__ # define __EXTENSIONS__ 1 /* IPv6 won't compile on Solaris without this */ #endif #include "config.h" #include "http_socket.h" | > > > | 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 | ** at a time. State information is stored in static variables. The identity ** of the server is held in global variables that are set by url_parse(). ** ** Low-level sockets are abstracted out into this module because they ** are handled different on Unix and windows. */ #if defined(_WIN32) # if defined(_WIN32_WINNT) # undef _WIN32_WINNT # endif # define _WIN32_WINNT 0x501 #endif #ifndef __EXTENSIONS__ # define __EXTENSIONS__ 1 /* IPv6 won't compile on Solaris without this */ #endif #include "config.h" #include "http_socket.h" |
︙ | ︙ | |||
77 78 79 80 81 82 83 | socketErrMsg = vmprintf(zFormat, ap); va_end(ap); } /* ** Return the current socket error message */ | | > | > | 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 | socketErrMsg = vmprintf(zFormat, ap); va_end(ap); } /* ** Return the current socket error message */ char *socket_errmsg(void){ char *zResult = socketErrMsg; socketErrMsg = 0; return zResult; } /* ** Call this routine once before any other use of the socket interface. ** This routine does initial configuration of the socket module. */ void socket_global_init(void){ |
︙ | ︙ | |||
130 131 132 133 134 135 136 | } } /* ** Open a socket connection. The identify of the server is determined ** by pUrlData ** | | | | 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 | } } /* ** Open a socket connection. The identify of the server is determined ** by pUrlData ** ** pUrlData->name Name of the server. Ex: www.fossil-scm.org ** pUrlData->port TCP/IP port to use. Ex: 80 ** ** Return the number of errors. */ int socket_open(UrlData *pUrlData){ int rc = 0; struct addrinfo *ai = 0; struct addrinfo *p; |
︙ | ︙ | |||
188 189 190 191 192 193 194 | if( ai ) freeaddrinfo(ai); return rc; } /* ** Send content out over the open socket connection. */ | | > > > > | > > > > | | 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 | if( ai ) freeaddrinfo(ai); return rc; } /* ** Send content out over the open socket connection. */ size_t socket_send(void *NotUsed, const void *pContent, size_t N){ size_t sent; size_t total = 0; while( N>0 ){ sent = send(iSocket, pContent, N, 0); if( sent<=0 ) break; total += sent; N -= sent; pContent = (void*)&((char*)pContent)[sent]; } return total; } /* ** Receive content back from the open socket connection. ** Return the number of bytes read. ** ** When bDontBlock is false, this function blocks until all N bytes ** have been read. */ size_t socket_receive(void *NotUsed, void *pContent, size_t N, int bDontBlock){ ssize_t got; size_t total = 0; int flags = 0; #ifdef MSG_DONTWAIT if( bDontBlock ) flags |= MSG_DONTWAIT; #endif while( N>0 ){ /* WinXP fails for large values of N. So limit it to 64KiB. */ got = recv(iSocket, pContent, N>65536 ? 65536 : N, flags); if( got<=0 ) break; total += (size_t)got; N -= (size_t)got; pContent = (void*)&((char*)pContent)[got]; } return total; } |
︙ | ︙ |
Changes to src/http_ssl.c.
︙ | ︙ | |||
109 110 111 112 113 114 115 | if( zCaSetting==0 || zCaSetting[0]=='\0' ){ /* CA location not specified, use platform's default certificate store */ X509_STORE_set_default_paths(SSL_CTX_get_cert_store(sslCtx)); }else{ /* User has specified a CA location, make sure it exists and use it */ switch( file_isdir(zCaSetting, ExtFILE) ){ case 0: { /* doesn't exist */ | | | | | 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 | if( zCaSetting==0 || zCaSetting[0]=='\0' ){ /* CA location not specified, use platform's default certificate store */ X509_STORE_set_default_paths(SSL_CTX_get_cert_store(sslCtx)); }else{ /* User has specified a CA location, make sure it exists and use it */ switch( file_isdir(zCaSetting, ExtFILE) ){ case 0: { /* doesn't exist */ fossil_panic("ssl-ca-location is set to '%s', " "but is not a file or directory", zCaSetting); break; } case 1: { /* directory */ zCaDirectory = zCaSetting; break; } case 2: { /* file */ zCaFile = zCaSetting; break; } } if( SSL_CTX_load_verify_locations(sslCtx, zCaFile, zCaDirectory)==0 ){ fossil_panic("Failed to use CA root certificates from " "ssl-ca-location '%s'", zCaSetting); } } /* Load client SSL identity, preferring the filename specified on the ** command line */ if( g.zSSLIdentity!=0 ){ identityFile = g.zSSLIdentity; }else{ identityFile = db_get("ssl-identity", 0); } if( identityFile!=0 && identityFile[0]!='\0' ){ if( SSL_CTX_use_certificate_file(sslCtx,identityFile,SSL_FILETYPE_PEM)!=1 || SSL_CTX_use_PrivateKey_file(sslCtx,identityFile,SSL_FILETYPE_PEM)!=1 ){ fossil_panic("Could not load SSL identity from %s", identityFile); } } /* Register a callback to tell the user what to do when the server asks ** for a cert */ SSL_CTX_set_client_cert_cb(sslCtx, ssl_client_cert_callback); sslIsInit = 1; |
︙ | ︙ |
Changes to src/http_transport.c.
︙ | ︙ | |||
125 126 127 128 129 130 131 | zHost = mprintf("%s@%s", pUrlData->user, pUrlData->name); blob_append_escaped_arg(&zCmd, zHost); fossil_free(zHost); }else{ blob_append_escaped_arg(&zCmd, pUrlData->name); } if( !is_safe_fossil_command(pUrlData->fossil) ){ | | | | 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 | zHost = mprintf("%s@%s", pUrlData->user, pUrlData->name); blob_append_escaped_arg(&zCmd, zHost); fossil_free(zHost); }else{ blob_append_escaped_arg(&zCmd, pUrlData->name); } if( !is_safe_fossil_command(pUrlData->fossil) ){ fossil_panic("the ssh:// URL is asking to run an unsafe command [%s] on " "the server.", pUrlData->fossil); } blob_append_escaped_arg(&zCmd, pUrlData->fossil); blob_append(&zCmd, " test-http", 10); if( pUrlData->path && pUrlData->path[0] ){ blob_append_escaped_arg(&zCmd, pUrlData->path); }else{ fossil_panic("ssh:// URI does not specify a path to the repository"); } if( g.fSshTrace ){ fossil_print("%s\n", blob_str(&zCmd)); /* Show the whole SSH command */ } popen2(blob_str(&zCmd), &sshIn, &sshOut, &sshPid); if( sshPid==0 ){ socket_set_errmsg("cannot start ssh tunnel using [%b]", &zCmd); |
︙ | ︙ | |||
179 180 181 182 183 184 185 | sqlite3_randomness(sizeof(iRandId), &iRandId); transport.zOutFile = mprintf("%s-%llu-out.http", g.zRepositoryName, iRandId); transport.zInFile = mprintf("%s-%llu-in.http", g.zRepositoryName, iRandId); transport.pFile = fossil_fopen(transport.zOutFile, "wb"); if( transport.pFile==0 ){ | | | 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 | sqlite3_randomness(sizeof(iRandId), &iRandId); transport.zOutFile = mprintf("%s-%llu-out.http", g.zRepositoryName, iRandId); transport.zInFile = mprintf("%s-%llu-in.http", g.zRepositoryName, iRandId); transport.pFile = fossil_fopen(transport.zOutFile, "wb"); if( transport.pFile==0 ){ fossil_panic("cannot output temporary file: %s", transport.zOutFile); } transport.isOpen = 1; }else{ rc = socket_open(pUrlData); if( rc==0 ) transport.isOpen = 1; } } |
︙ | ︙ | |||
267 268 269 270 271 272 273 | ** This routine is called when the outbound message is complete and ** it is time to being receiving a reply. */ void transport_flip(UrlData *pUrlData){ if( pUrlData->isFile ){ char *zCmd; fclose(transport.pFile); | > | | 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 | ** This routine is called when the outbound message is complete and ** it is time to being receiving a reply. */ void transport_flip(UrlData *pUrlData){ if( pUrlData->isFile ){ char *zCmd; fclose(transport.pFile); zCmd = mprintf("\"%s\" http --in \"%s\" --out \"%s\" --ipaddr 127.0.0.1" " \"%s\" --localauth", g.nameOfExe, transport.zOutFile, transport.zInFile, pUrlData->name ); fossil_system(zCmd); free(zCmd); transport.pFile = fossil_fopen(transport.zInFile, "rb"); } } |
︙ | ︙ | |||
323 324 325 326 327 328 329 | got = ssl_receive(0, zBuf, N); #else got = 0; #endif }else if( pUrlData->isFile ){ got = fread(zBuf, 1, N, transport.pFile); }else{ | | | 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 | got = ssl_receive(0, zBuf, N); #else got = 0; #endif }else if( pUrlData->isFile ){ got = fread(zBuf, 1, N, transport.pFile); }else{ got = socket_receive(0, zBuf, N, 0); } /* printf("received %d of %d bytes\n", got, N); fflush(stdout); */ if( transport.pLog ){ fwrite(zBuf, 1, got, transport.pLog); fflush(transport.pLog); } return got; |
︙ | ︙ |
Changes to src/import.c.
︙ | ︙ | |||
604 605 606 607 608 609 610 | fossil_free(gg.aData); gg.aData = 0; gg.nData = atoi(&zLine[5]); if( gg.nData ){ int got; gg.aData = fossil_malloc( gg.nData+1 ); got = fread(gg.aData, 1, gg.nData, pIn); if( got!=gg.nData ){ | | | 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 | fossil_free(gg.aData); gg.aData = 0; gg.nData = atoi(&zLine[5]); if( gg.nData ){ int got; gg.aData = fossil_malloc( gg.nData+1 ); got = fread(gg.aData, 1, gg.nData, pIn); if( got!=gg.nData ){ fossil_panic("short read: got %d of %d bytes", got, gg.nData); } gg.aData[got] = '\0'; if( gg.zComment==0 && (gg.xFinish==finish_commit || gg.xFinish==finish_tag) ){ /* Strip trailing newline, it's appended to the comment. */ if( gg.aData[got-1] == '\n' ) gg.aData[got-1] = '\0'; |
︙ | ︙ |
Changes to src/info.c.
︙ | ︙ | |||
933 934 935 936 937 938 939 | @ <div class="section">Overview</div> @ <p><table class="label-value"> @ <tr><th>Artifact ID:</th> @ <td>%z(href("%R/artifact/%!S",zUuid))%s(zUuid)</a> if( g.perm.Setup ){ @ (%d(rid)) } | | < < < | 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 | @ <div class="section">Overview</div> @ <p><table class="label-value"> @ <tr><th>Artifact ID:</th> @ <td>%z(href("%R/artifact/%!S",zUuid))%s(zUuid)</a> if( g.perm.Setup ){ @ (%d(rid)) } modPending = moderation_pending_www(rid); @ </td></tr> @ <tr><th>Page Name:</th><td>%h(pWiki->zWikiTitle)</td></tr> @ <tr><th>Date:</th><td> hyperlink_to_date(zDate, "</td></tr>"); @ <tr><th>Original User:</th><td> hyperlink_to_user(pWiki->zUser, zDate, "</td></tr>"); if( pWiki->zMimetype ){ |
︙ | ︙ | |||
976 977 978 979 980 981 982 | @ <div class="section">Content</div> blob_init(&wiki, pWiki->zWiki, -1); wiki_render_by_mimetype(&wiki, pWiki->zMimetype); blob_reset(&wiki); manifest_destroy(pWiki); | < < < < < < < < < < < < < < < | 973 974 975 976 977 978 979 980 981 982 983 984 985 986 | @ <div class="section">Content</div> blob_init(&wiki, pWiki->zWiki, -1); wiki_render_by_mimetype(&wiki, pWiki->zMimetype); blob_reset(&wiki); manifest_destroy(pWiki); style_footer(); } /* ** Find an check-in based on query parameter zParam and parse its ** manifest. Return the number of errors. */ |
︙ | ︙ | |||
1244 1245 1246 1247 1248 1249 1250 1251 1252 1253 1254 1255 1256 1257 | #define OBJTYPE_WIKI 0x0004 #define OBJTYPE_TICKET 0x0008 #define OBJTYPE_ATTACHMENT 0x0010 #define OBJTYPE_EVENT 0x0020 #define OBJTYPE_TAG 0x0040 #define OBJTYPE_SYMLINK 0x0080 #define OBJTYPE_EXE 0x0100 /* ** Possible flags for the second parameter to ** object_description() */ #define OBJDESC_DETAIL 0x0001 /* more detail */ #endif | > | 1226 1227 1228 1229 1230 1231 1232 1233 1234 1235 1236 1237 1238 1239 1240 | #define OBJTYPE_WIKI 0x0004 #define OBJTYPE_TICKET 0x0008 #define OBJTYPE_ATTACHMENT 0x0010 #define OBJTYPE_EVENT 0x0020 #define OBJTYPE_TAG 0x0040 #define OBJTYPE_SYMLINK 0x0080 #define OBJTYPE_EXE 0x0100 #define OBJTYPE_FORUM 0x0200 /* ** Possible flags for the second parameter to ** object_description() */ #define OBJDESC_DETAIL 0x0001 /* more detail */ #endif |
︙ | ︙ | |||
1436 1437 1438 1439 1440 1441 1442 1443 1444 1445 1446 1447 1448 1449 | if( eventTagId != 0) { @ Instance of technote objType |= OBJTYPE_EVENT; hyperlink_to_event_tagid(db_column_int(&q, 5)); }else{ @ Attachment to technote } }else{ @ Tag referencing } if( zType[0]!='e' || eventTagId == 0){ hyperlink_to_uuid(zUuid); } @ - %!W(zCom) by | > > > | 1419 1420 1421 1422 1423 1424 1425 1426 1427 1428 1429 1430 1431 1432 1433 1434 1435 | if( eventTagId != 0) { @ Instance of technote objType |= OBJTYPE_EVENT; hyperlink_to_event_tagid(db_column_int(&q, 5)); }else{ @ Attachment to technote } }else if( zType[0]=='f' ){ objType |= OBJTYPE_FORUM; @ Forum post }else{ @ Tag referencing } if( zType[0]!='e' || eventTagId == 0){ hyperlink_to_uuid(zUuid); } @ - %!W(zCom) by |
︙ | ︙ | |||
1905 1906 1907 1908 1909 1910 1911 | if( iEnd<iStart ) iEnd = iStart; db_multi_exec( "INSERT OR REPLACE INTO lnos VALUES(%d,%d)", iStart, iEnd ); iStart = iEnd = atoi(&zLn[i++]); }while( zLn[i] && iStart && iEnd ); } | | | 1891 1892 1893 1894 1895 1896 1897 1898 1899 1900 1901 1902 1903 1904 1905 | if( iEnd<iStart ) iEnd = iStart; db_multi_exec( "INSERT OR REPLACE INTO lnos VALUES(%d,%d)", iStart, iEnd ); iStart = iEnd = atoi(&zLn[i++]); }while( zLn[i] && iStart && iEnd ); } db_prepare(&q, "SELECT min(iStart), max(iEnd) FROM lnos"); if( db_step(&q)==SQLITE_ROW ){ iStart = db_column_int(&q, 0); iEnd = db_column_int(&q, 1); iTop = iStart - 15 + (iEnd-iStart)/4; if( iTop>iStart - 2 ) iTop = iStart-2; } db_finalize(&q); |
︙ | ︙ | |||
2246 2247 2248 2249 2250 2251 2252 | @ <div class="section">Overview</div> @ <p><table class="label-value"> @ <tr><th>Artifact ID:</th> @ <td>%z(href("%R/artifact/%!S",zUuid))%s(zUuid)</a> if( g.perm.Setup ){ @ (%d(rid)) } | | < < < | 2232 2233 2234 2235 2236 2237 2238 2239 2240 2241 2242 2243 2244 2245 2246 | @ <div class="section">Overview</div> @ <p><table class="label-value"> @ <tr><th>Artifact ID:</th> @ <td>%z(href("%R/artifact/%!S",zUuid))%s(zUuid)</a> if( g.perm.Setup ){ @ (%d(rid)) } modPending = moderation_pending_www(rid); @ <tr><th>Ticket:</th> @ <td>%z(href("%R/tktview/%s",zTktName))%s(zTktName)</a> if( zTktTitle ){ @<br />%h(zTktTitle) } @</td></tr> @ <tr><th>User & Date:</th><td> |
︙ | ︙ | |||
2362 2363 2364 2365 2366 2367 2368 2369 2370 2371 2372 2373 2374 2375 | ci_page(); }else if( db_exists("SELECT 1 FROM plink WHERE pid=%d", rid) ){ ci_page(); }else if( db_exists("SELECT 1 FROM attachment WHERE attachid=%d", rid) ){ ainfo_page(); }else { artifact_page(); } } /* | > > > > > | 2345 2346 2347 2348 2349 2350 2351 2352 2353 2354 2355 2356 2357 2358 2359 2360 2361 2362 2363 | ci_page(); }else if( db_exists("SELECT 1 FROM plink WHERE pid=%d", rid) ){ ci_page(); }else if( db_exists("SELECT 1 FROM attachment WHERE attachid=%d", rid) ){ ainfo_page(); }else if( db_table_exists("repository","forumpost") && db_exists("SELECT 1 FROM forumpost WHERE fpid=%d", rid) ){ forumthread_page(); }else { artifact_page(); } } /* |
︙ | ︙ |
Changes to src/json.c.
︙ | ︙ | |||
994 995 996 997 998 999 1000 | break; } inFile = (0==strcmp("-",jfile)) ? stdin : fossil_fopen(jfile,"rb"); if(!inFile){ g.json.resultCode = FSL_JSON_E_FILE_OPEN_FAILED; | | | 994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 | break; } inFile = (0==strcmp("-",jfile)) ? stdin : fossil_fopen(jfile,"rb"); if(!inFile){ g.json.resultCode = FSL_JSON_E_FILE_OPEN_FAILED; fossil_panic("Could not open JSON file [%s].",jfile) /* Does not return. */ ; } cgi_parse_POST_JSON(inFile, 0); if( stdin != inFile ){ fclose(inFile); } |
︙ | ︙ |
Changes to src/json_branch.c.
︙ | ︙ | |||
286 287 288 289 290 291 292 | blob_appendf(&branch, "U %F\n", g.zLogin); md5sum_blob(&branch, &mcksum); blob_appendf(&branch, "Z %b\n", &mcksum); brid = content_put(&branch); if( brid==0 ){ | | | | 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 | blob_appendf(&branch, "U %F\n", g.zLogin); md5sum_blob(&branch, &mcksum); blob_appendf(&branch, "Z %b\n", &mcksum); brid = content_put(&branch); if( brid==0 ){ fossil_panic("Problem committing manifest: %s", g.zErrMsg); } db_multi_exec("INSERT OR IGNORE INTO unsent VALUES(%d)", brid); if( manifest_crosslink(brid, &branch, MC_PERMIT_HOOKS)==0 ){ fossil_panic("%s", g.zErrMsg); } assert( blob_is_reset(&branch) ); content_deltify(rootid, &brid, 1, 0); if( zNewRid ){ *zNewRid = brid; } |
︙ | ︙ |
Changes to src/json_login.c.
︙ | ︙ | |||
122 123 124 125 126 127 128 | } } #if 0 { /* only for debugging the PD()-incorrect-result problem */ cson_object * o = NULL; | | | | 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 | } } #if 0 { /* only for debugging the PD()-incorrect-result problem */ cson_object * o = NULL; uid = login_search_uid( &name, pw ); payload = cson_value_new_object(); o = cson_value_get_object(payload); cson_object_set( o, "n", cson_value_new_string(name,strlen(name))); cson_object_set( o, "p", cson_value_new_string(pw,strlen(pw))); return payload; } #endif uid = anonSeed ? login_is_valid_anonymous(name, pw, anonSeed) : login_search_uid(&name, pw) ; if( !uid ){ g.json.resultCode = preciseErrors ? FSL_JSON_E_LOGIN_FAILED_NOTFOUND : FSL_JSON_E_LOGIN_FAILED; return NULL; }else{ |
︙ | ︙ |
Changes to src/json_status.c.
︙ | ︙ | |||
166 167 168 169 170 171 172 | case -4: zLabel = "INTEGRATE "; break; } blob_append(report, zPrefix, nPrefix); blob_appendf(report, "%s %s\n", zLabel, db_column_text(&q, 0)); } db_finalize(&q); if( nErr ){ | | | 166 167 168 169 170 171 172 173 174 175 176 177 178 179 | case -4: zLabel = "INTEGRATE "; break; } blob_append(report, zPrefix, nPrefix); blob_appendf(report, "%s %s\n", zLabel, db_column_text(&q, 0)); } db_finalize(&q); if( nErr ){ fossil_panic("aborting due to prior errors"); } #endif return cson_object_value( oPay ); } #endif /* FOSSIL_ENABLE_JSON */ |
Changes to src/json_timeline.c.
︙ | ︙ | |||
33 34 35 36 37 38 39 40 41 42 43 44 45 46 | static const JsonPageDef JsonPageDefs_Timeline[] = { /* the short forms are only enabled in CLI mode, to avoid that we end up with HTTP clients using 3 different names for the same requests. */ {"branch", json_timeline_branch, 0}, {"checkin", json_timeline_ci, 0}, {"ticket", json_timeline_ticket, 0}, {"wiki", json_timeline_wiki, 0}, /* Last entry MUST have a NULL name. */ {NULL,NULL,0} }; | > | 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 | static const JsonPageDef JsonPageDefs_Timeline[] = { /* the short forms are only enabled in CLI mode, to avoid that we end up with HTTP clients using 3 different names for the same requests. */ {"branch", json_timeline_branch, 0}, {"checkin", json_timeline_ci, 0}, {"event", json_timeline_event, 0}, {"ticket", json_timeline_ticket, 0}, {"wiki", json_timeline_wiki, 0}, /* Last entry MUST have a NULL name. */ {NULL,NULL,0} }; |
︙ | ︙ | |||
316 317 318 319 320 321 322 | ** or 0 for defaults. */ cson_value * json_get_changed_files(int rid, int flags){ cson_value * rowsV = NULL; cson_array * rows = NULL; Stmt q = empty_Stmt; db_prepare(&q, | | | | | | | | > | | | | | | 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 | ** or 0 for defaults. */ cson_value * json_get_changed_files(int rid, int flags){ cson_value * rowsV = NULL; cson_array * rows = NULL; Stmt q = empty_Stmt; db_prepare(&q, "SELECT (pid<=0) AS isnew," " (fid==0) AS isdel," " (SELECT name FROM filename WHERE fnid=mlink.fnid) AS name," " (SELECT uuid FROM blob WHERE rid=fid) as uuid," " (SELECT uuid FROM blob WHERE rid=pid) as parent," " blob.size as size" " FROM mlink" " LEFT JOIN blob ON blob.rid=fid" " WHERE mid=%d AND pid!=fid" " AND NOT mlink.isaux" " ORDER BY name /*sort*/", rid ); while( (SQLITE_ROW == db_step(&q)) ){ cson_value * rowV = cson_value_new_object(); cson_object * row = cson_value_get_object(rowV); int const isNew = db_column_int(&q,0); int const isDel = db_column_int(&q,1); char * zDownload = NULL; if(!rowsV){ |
︙ | ︙ | |||
516 517 518 519 520 521 522 523 524 525 526 527 528 529 | assert( 0 != g.json.resultCode ); cson_value_free(payV); payV = NULL; ok: db_finalize(&q); return payV; } /* ** Implementation of /json/timeline/wiki. ** */ cson_value * json_timeline_wiki(){ /* This code is 95% the same as json_timeline_ci(), by the way. */ | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 | assert( 0 != g.json.resultCode ); cson_value_free(payV); payV = NULL; ok: db_finalize(&q); return payV; } /* ** Implementation of /json/timeline/event. ** */ cson_value * json_timeline_event(){ /* This code is 95% the same as json_timeline_ci(), by the way. */ cson_value * payV = NULL; cson_object * pay = NULL; cson_array * list = NULL; int check = 0; Stmt q = empty_Stmt; Blob sql = empty_blob; if( !g.perm.RdWiki ){ json_set_err( FSL_JSON_E_DENIED, "Event timeline requires 'j' access."); return NULL; } payV = cson_value_new_object(); pay = cson_value_get_object(payV); check = json_timeline_setup_sql( "e", &sql, pay ); if(check){ json_set_err(check, "Query initialization failed."); goto error; } #if 0 /* only for testing! */ cson_object_set(pay, "timelineSql", cson_value_new_string(blob_buffer(&sql),strlen(blob_buffer(&sql)))); #endif db_multi_exec("%s", blob_buffer(&sql) /*safe-for-%s*/); blob_reset(&sql); db_prepare(&q, "SELECT" /* For events, the name is generally more useful than the uuid, but the uuid is unambiguous and can be used with commands like 'artifact'. */ " substr((SELECT tagname FROM tag AS tn WHERE tn.tagid=json_timeline.tagId AND tagname LIKE 'event-%%'),7) AS name," " uuid as uuid," " mtime AS timestamp," " comment AS comment, " " user AS user," " eventType AS eventType" " FROM json_timeline" " ORDER BY rowid"); list = cson_new_array(); json_stmt_to_array_of_obj(&q, list); cson_object_set(pay, "timeline", cson_array_value(list)); goto ok; error: assert( 0 != g.json.resultCode ); cson_value_free(payV); payV = NULL; ok: db_finalize(&q); blob_reset(&sql); return payV; } /* ** Implementation of /json/timeline/wiki. ** */ cson_value * json_timeline_wiki(){ /* This code is 95% the same as json_timeline_ci(), by the way. */ |
︙ | ︙ |
Changes to src/login.c.
︙ | ︙ | |||
204 205 206 207 208 209 210 211 212 213 | } /* ** Searches for the user ID matching the given name and password. ** On success it returns a positive value. On error it returns 0. ** On serious (DB-level) error it will probably exit. ** ** zPassword may be either the plain-text form or the encrypted ** form of the user's password. */ | > > > > > > > > | | < | | | | | | | | | | > > > > > > > > > > > > > > > > > > > > > > > | 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 | } /* ** Searches for the user ID matching the given name and password. ** On success it returns a positive value. On error it returns 0. ** On serious (DB-level) error it will probably exit. ** ** zUsername uses double indirection because we may re-point *zUsername ** at a C string allocated with fossil_strdup() if you pass an email ** address instead and we find that address in the user table's info ** field, which is expected to contain a string of the form "Human Name ** <human@example.com>". In that case, *zUsername will point to that ** user's actual login name on return, causing a leak unless the caller ** is diligent enough to check whether its pointer was re-pointed. ** ** zPassword may be either the plain-text form or the encrypted ** form of the user's password. */ int login_search_uid(const char **pzUsername, const char *zPasswd){ char *zSha1Pw = sha1_shared_secret(zPasswd, *pzUsername, 0); int uid = db_int(0, "SELECT uid FROM user" " WHERE login=%Q" " AND length(cap)>0 AND length(pw)>0" " AND login NOT IN ('anonymous','nobody','developer','reader')" " AND (pw=%Q OR (length(pw)<>40 AND pw=%Q))" " AND (info NOT LIKE '%%expires 20%%'" " OR substr(info,instr(lower(info),'expires')+8,10)>datetime('now'))", *pzUsername, zSha1Pw, zPasswd ); /* If we did not find a login on the first attempt, and the username ** looks like an email address, then perhaps the user entered their ** email address instead of their login. Try again to match the user ** against email addresses contained in the "info" field. */ if( uid==0 && strchr(*pzUsername,'@')!=0 ){ Stmt q; db_prepare(&q, "SELECT login FROM user" " WHERE find_emailaddr(info)=%Q" " AND instr(login,'@')==0", *pzUsername ); while( db_step(&q)==SQLITE_ROW ){ const char *zLogin = db_column_text(&q,0); if( (uid = login_search_uid(&zLogin, zPasswd) ) != 0 ){ *pzUsername = fossil_strdup(zLogin); break; } } db_finalize(&q); } free(zSha1Pw); return uid; } /* ** Generates a login cookie value for a non-anonymous user. ** |
︙ | ︙ | |||
468 469 470 471 472 473 474 475 476 477 478 479 480 481 | int rc; if( zReferer==0 ) return 0; zPattern = mprintf("%s/login*", g.zBaseURL); rc = sqlite3_strglob(zPattern, zReferer)==0; fossil_free(zPattern); return rc; } /* ** There used to be a page named "my" that was designed to show information ** about a specific user. The "my" page was linked from the "Logged in as USER" ** line on the title bar. The "my" page was never completed so it is now ** removed. Use this page as a placeholder in older installations. ** | > > > > > > > > > > > > > > > > > > | 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 | int rc; if( zReferer==0 ) return 0; zPattern = mprintf("%s/login*", g.zBaseURL); rc = sqlite3_strglob(zPattern, zReferer)==0; fossil_free(zPattern); return rc; } /* ** Return TRUE if self-registration is available. If the zNeeded ** argument is not NULL, then only return true if self-registration is ** available and any of the capabilities named in zNeeded are available ** to self-registered users. */ int login_self_register_available(const char *zNeeded){ CapabilityString *pCap; int rc; if( !db_get_boolean("self-register",0) ) return 0; if( zNeeded==0 ) return 1; pCap = capability_add(0, db_get("default-perms","")); capability_expand(pCap); rc = capability_has_any(pCap, zNeeded); capability_free(pCap); return rc; } /* ** There used to be a page named "my" that was designed to show information ** about a specific user. The "my" page was linked from the "Logged in as USER" ** line on the title bar. The "my" page was never completed so it is now ** removed. Use this page as a placeholder in older installations. ** |
︙ | ︙ | |||
496 497 498 499 500 501 502 503 504 505 506 507 508 509 | const char *zGoto = P("g"); int anonFlag; /* Login as "anonymous" would be useful */ char *zErrMsg = ""; int uid; /* User id logged in user */ char *zSha1Pw; const char *zIpAddr; /* IP address of requestor */ const char *zReferer; login_check_credentials(); if( login_wants_https_redirect() ){ const char *zQS = P("QUERY_STRING"); if( P("redir")!=0 ){ style_header("Insecure Connection"); @ <h1>Unable To Establish An Encrypted Connection</h1> | > | 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 | const char *zGoto = P("g"); int anonFlag; /* Login as "anonymous" would be useful */ char *zErrMsg = ""; int uid; /* User id logged in user */ char *zSha1Pw; const char *zIpAddr; /* IP address of requestor */ const char *zReferer; int noAnon = P("noanon")!=0; login_check_credentials(); if( login_wants_https_redirect() ){ const char *zQS = P("QUERY_STRING"); if( P("redir")!=0 ){ style_header("Insecure Connection"); @ <h1>Unable To Establish An Encrypted Connection</h1> |
︙ | ︙ | |||
519 520 521 522 523 524 525 | return; } if( zQS==0 ){ zQS = "?redir=1"; }else if( zQS[0]!=0 ){ zQS = mprintf("?%s&redir=1", zQS); } | | > > > > > > | 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 | return; } if( zQS==0 ){ zQS = "?redir=1"; }else if( zQS[0]!=0 ){ zQS = mprintf("?%s&redir=1", zQS); } cgi_redirectf("%s%T%s", g.zHttpsURL, P("PATH_INFO"), zQS); return; } sqlite3_create_function(g.db, "constant_time_cmp", 2, SQLITE_UTF8, 0, constant_time_cmp_function, 0, 0); zUsername = P("u"); zPasswd = P("p"); anonFlag = g.zLogin==0 && PB("anon"); /* Handle log-out requests */ if( P("out") ){ login_clear_login_data(); redirect_to_g(); return; } /* Redirect for create-new-account requests */ if( P("self") ){ cgi_redirectf("%R/register"); return; } /* Deal with password-change requests */ if( g.perm.Password && zPasswd && (zNew1 = P("n1"))!=0 && (zNew2 = P("n2"))!=0 ){ /* If there is not a "real" login, we cannot change any password. */ if( g.zLogin ){ |
︙ | ︙ | |||
605 606 607 608 609 610 611 | login_set_anon_cookie(zIpAddr, NULL); record_login_attempt("anonymous", zIpAddr, 1); redirect_to_g(); } if( zUsername!=0 && zPasswd!=0 && zPasswd[0]!=0 ){ /* Attempting to log in as a user other than anonymous. */ | | | 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 | login_set_anon_cookie(zIpAddr, NULL); record_login_attempt("anonymous", zIpAddr, 1); redirect_to_g(); } if( zUsername!=0 && zPasswd!=0 && zPasswd[0]!=0 ){ /* Attempting to log in as a user other than anonymous. */ uid = login_search_uid(&zUsername, zPasswd); if( uid<=0 ){ sleep(1); zErrMsg = @ <p><span class="loginError"> @ You entered an unknown user or an incorrect password. @ </span></p> ; |
︙ | ︙ | |||
629 630 631 632 633 634 635 | login_set_user_cookie(zUsername, uid, NULL); redirect_to_g(); } } style_header("Login/Logout"); style_adunit_config(ADUNIT_OFF); @ %s(zErrMsg) | | | 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 | login_set_user_cookie(zUsername, uid, NULL); redirect_to_g(); } } style_header("Login/Logout"); style_adunit_config(ADUNIT_OFF); @ %s(zErrMsg) if( zGoto && !noAnon ){ char *zAbbrev = fossil_strdup(zGoto); int i; for(i=0; zAbbrev[i] && zAbbrev[i]!='?'; i++){} zAbbrev[i] = 0; if( g.zLogin ){ @ <p>Use a different login with greater privilege than <b>%h(g.zLogin)</b> @ to access <b>%h(zAbbrev)</b>. |
︙ | ︙ | |||
663 664 665 666 667 668 669 | } if( anonFlag ){ @ <input type="hidden" name="anon" value="1" /> } if( g.zLogin ){ @ <p>Currently logged in as <b>%h(g.zLogin)</b>. @ <input type="submit" name="out" value="Logout"></p> | < < | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | > > | > | < < > > > | | | > | | | | | | | | | | | | | | | | | | | | | > | > > > > > | | | | 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 | } if( anonFlag ){ @ <input type="hidden" name="anon" value="1" /> } if( g.zLogin ){ @ <p>Currently logged in as <b>%h(g.zLogin)</b>. @ <input type="submit" name="out" value="Logout"></p> }else{ @ <table class="login_out"> @ <tr> @ <td class="form_label">User ID:</td> if( anonFlag ){ @ <td><input type="text" id="u" name="u" value="anonymous" size="30"></td> }else{ @ <td><input type="text" id="u" name="u" value="" size="30" /></td> } if( P("HTTPS")==0 ){ @ <td width="15"><td rowspan="2"> @ <p class='securityWarning'> @ Warning: Your password will be sent in the clear over an @ unencrypted connection. if( g.sslNotAvailable ){ @ No encrypted connection is available on this server. }else{ @ Consider logging in at @ <a href='%s(g.zHttpsURL)'>%h(g.zHttpsURL)</a> instead. } @ </p> } @ </tr> @ <tr> @ <td class="form_label">Password:</td> @ <td><input type="password" id="p" name="p" value="" size="30" /></td> @ </tr> if( g.zLogin==0 && (anonFlag || zGoto==0) ){ zAnonPw = db_text(0, "SELECT pw FROM user" " WHERE login='anonymous'" " AND cap!=''"); } @ <tr> @ <td></td> @ <td><input type="submit" name="in" value="Login"></td> @ <td colspan="2">← Pressing this button grants\ @ permission to store a cookie @ </tr> if( !noAnon && login_self_register_available(0) ){ @ <tr> @ <td></td> @ <td><input type="submit" name="self" value="Create A New Account"> @ <td colspan="2"> \ @ ← Don't have a login? Click this button to create one. @ </tr> } @ </table> if( zAnonPw && !noAnon ){ unsigned int uSeed = captcha_seed(); const char *zDecoded = captcha_decode(uSeed); int bAutoCaptcha = db_get_boolean("auto-captcha", 0); char *zCaptcha = captcha_render(zDecoded); @ <p><input type="hidden" name="cs" value="%u(uSeed)" /> @ Visitors may enter <b>anonymous</b> as the user-ID with @ the 8-character hexadecimal password shown below:</p> @ <div class="captcha"><table class="captcha"><tr><td><pre> @ %h(zCaptcha) @ </pre></td></tr></table> if( bAutoCaptcha ) { @ <input type="button" value="Fill out captcha" id='autofillButton' \ @ data-af='%s(zDecoded)' /> style_load_one_js_file("login.js"); } @ </div> free(zCaptcha); } @ </form> } if( login_is_individual() && g.perm.Password ){ if( email_enabled() ){ @ <hr> @ <p>Configure <a href="%R/alerts">Email Alerts</a> @ for user <b>%h(g.zLogin)</b></p> } @ <hr /> @ <p>Change Password for user <b>%h(g.zLogin)</b>:</p> form_begin(0, "%R/login"); @ <table> @ <tr><td class="form_label">Old Password:</td> @ <td><input type="password" name="p" size="30" /></td></tr> @ <tr><td class="form_label">New Password:</td> @ <td><input type="password" name="n1" size="30" /></td></tr> @ <tr><td class="form_label">Repeat New Password:</td> @ <td><input type="password" name="n2" size="30" /></td></tr> @ <tr><td></td> @ <td><input type="submit" value="Change Password" /></td></tr> @ </table> @ </form> } style_footer(); |
︙ | ︙ | |||
897 898 899 900 901 902 903 | zDecode[i] = 0; zUsername = zDecode; zPasswd = &zDecode[i+1]; /* Attempting to log in as the user provided by HTTP ** basic auth */ | | | 961 962 963 964 965 966 967 968 969 970 971 972 973 974 975 | zDecode[i] = 0; zUsername = zDecode; zPasswd = &zDecode[i+1]; /* Attempting to log in as the user provided by HTTP ** basic auth */ uid = login_search_uid(&zUsername, zPasswd); if( uid>0 ){ record_login_attempt(zUsername, zIpAddr, 1); }else{ record_login_attempt(zUsername, zIpAddr, 0); /* The user attempted to login specifically with HTTP basic ** auth, but provided invalid credentials. Inform them of |
︙ | ︙ | |||
1194 1195 1196 1197 1198 1199 1200 1201 1202 1203 1204 1205 1206 1207 | case 's': p->Setup = 1; /* Fall thru into Admin */ case 'a': p->Admin = p->RdTkt = p->WrTkt = p->Zip = p->RdWiki = p->WrWiki = p->NewWiki = p->ApndWiki = p->Hyperlink = p->Clone = p->NewTkt = p->Password = p->RdAddr = p->TktFmt = p->Attach = p->ApndTkt = p->ModWiki = p->ModTkt = p->Delete = p->WrUnver = p->Private = 1; /* Fall thru into Read/Write */ case 'i': p->Read = p->Write = 1; break; case 'o': p->Read = 1; break; case 'z': p->Zip = 1; break; case 'd': p->Delete = 1; break; | > > > | 1258 1259 1260 1261 1262 1263 1264 1265 1266 1267 1268 1269 1270 1271 1272 1273 1274 | case 's': p->Setup = 1; /* Fall thru into Admin */ case 'a': p->Admin = p->RdTkt = p->WrTkt = p->Zip = p->RdWiki = p->WrWiki = p->NewWiki = p->ApndWiki = p->Hyperlink = p->Clone = p->NewTkt = p->Password = p->RdAddr = p->TktFmt = p->Attach = p->ApndTkt = p->ModWiki = p->ModTkt = p->Delete = p->RdForum = p->WrForum = p->ModForum = p->WrTForum = p->AdminForum = p->EmailAlert = p->Announce = p->WrUnver = p->Private = 1; /* Fall thru into Read/Write */ case 'i': p->Read = p->Write = 1; break; case 'o': p->Read = 1; break; case 'z': p->Zip = 1; break; case 'd': p->Delete = 1; break; |
︙ | ︙ | |||
1223 1224 1225 1226 1227 1228 1229 | case 'c': p->ApndTkt = 1; break; case 'q': p->ModTkt = 1; break; case 't': p->TktFmt = 1; break; case 'b': p->Attach = 1; break; case 'x': p->Private = 1; break; case 'y': p->WrUnver = 1; break; | > > > > > > > > > > | | > | | | > | | 1290 1291 1292 1293 1294 1295 1296 1297 1298 1299 1300 1301 1302 1303 1304 1305 1306 1307 1308 1309 1310 1311 1312 1313 1314 1315 1316 1317 1318 1319 1320 1321 1322 1323 1324 1325 1326 1327 1328 1329 1330 1331 1332 1333 | case 'c': p->ApndTkt = 1; break; case 'q': p->ModTkt = 1; break; case 't': p->TktFmt = 1; break; case 'b': p->Attach = 1; break; case 'x': p->Private = 1; break; case 'y': p->WrUnver = 1; break; case '6': p->AdminForum = 1; case '5': p->ModForum = 1; case '4': p->WrTForum = 1; case '3': p->WrForum = 1; case '2': p->RdForum = 1; break; case '7': p->EmailAlert = 1; break; case 'A': p->Announce = 1; break; case 'D': p->Debug = 1; break; /* The "u" privilege recursively ** inherits all privileges of the user named "reader" */ case 'u': { if( p->XReader==0 ){ const char *zUser; p->XReader = 1; zUser = db_text("", "SELECT cap FROM user WHERE login='reader'"); login_set_capabilities(zUser, flags); } break; } /* The "v" privilege recursively ** inherits all privileges of the user named "developer" */ case 'v': { if( p->XDeveloper==0 ){ const char *zDev; p->XDeveloper = 1; zDev = db_text("", "SELECT cap FROM user WHERE login='developer'"); login_set_capabilities(zDev, flags); } break; } } } } |
︙ | ︙ | |||
1295 1296 1297 1298 1299 1300 1301 1302 1303 1304 1305 1306 1307 1308 | case 't': rc = p->TktFmt; break; /* case 'u': READER */ /* case 'v': DEVELOPER */ case 'w': rc = p->WrTkt; break; case 'x': rc = p->Private; break; case 'y': rc = p->WrUnver; break; case 'z': rc = p->Zip; break; default: rc = 0; break; } } return rc; } /* | > > > > > > > > | 1374 1375 1376 1377 1378 1379 1380 1381 1382 1383 1384 1385 1386 1387 1388 1389 1390 1391 1392 1393 1394 1395 | case 't': rc = p->TktFmt; break; /* case 'u': READER */ /* case 'v': DEVELOPER */ case 'w': rc = p->WrTkt; break; case 'x': rc = p->Private; break; case 'y': rc = p->WrUnver; break; case 'z': rc = p->Zip; break; case '2': rc = p->RdForum; break; case '3': rc = p->WrForum; break; case '4': rc = p->WrTForum; break; case '5': rc = p->ModForum; break; case '6': rc = p->AdminForum;break; case '7': rc = p->EmailAlert;break; case 'A': rc = p->Announce; break; case 'D': rc = p->Debug; break; default: rc = 0; break; } } return rc; } /* |
︙ | ︙ | |||
1336 1337 1338 1339 1340 1341 1342 1343 1344 1345 1346 1347 1348 1349 | /* ** Return true if the user is "nobody" */ int login_is_nobody(void){ return g.zLogin==0 || g.zLogin[0]==0 || fossil_strcmp(g.zLogin,"nobody")==0; } /* ** Return the login name. If no login name is specified, return "nobody". */ const char *login_name(void){ return (g.zLogin && g.zLogin[0]) ? g.zLogin : "nobody"; } | > > > > > > > > > | 1423 1424 1425 1426 1427 1428 1429 1430 1431 1432 1433 1434 1435 1436 1437 1438 1439 1440 1441 1442 1443 1444 1445 | /* ** Return true if the user is "nobody" */ int login_is_nobody(void){ return g.zLogin==0 || g.zLogin[0]==0 || fossil_strcmp(g.zLogin,"nobody")==0; } /* ** Return true if the user is a specific individual, not "nobody" or ** "anonymous". */ int login_is_individual(void){ return g.zLogin!=0 && g.zLogin[0]!=0 && fossil_strcmp(g.zLogin,"nobody")!=0 && fossil_strcmp(g.zLogin,"anonymous")!=0; } /* ** Return the login name. If no login name is specified, return "nobody". */ const char *login_name(void){ return (g.zLogin && g.zLogin[0]) ? g.zLogin : "nobody"; } |
︙ | ︙ | |||
1423 1424 1425 1426 1427 1428 1429 | /* ** WEBPAGE: register ** ** Page to allow users to self-register. The "self-register" setting ** must be enabled for this page to operate. */ void register_page(void){ | > | > > > > > > > > > > > > | | | | | | | > > > > > > > > > > > > | | | > | | < | | | > | | | < | | | < | | < < > | > > > > | < > > | > > > > > > > > | | | > > > > | | | < < < | < | > > > > > | | > > > > > > > > | | > | < > > > > > > > > | < | > > > | < < > | > > > > > > > > > > > > > > > > > > > > > > > > > > | > > > > > | | | > > > | | > > > > > > > > > > > > > > > > > > > | | > > > > > | | > > > | | > > > | 1519 1520 1521 1522 1523 1524 1525 1526 1527 1528 1529 1530 1531 1532 1533 1534 1535 1536 1537 1538 1539 1540 1541 1542 1543 1544 1545 1546 1547 1548 1549 1550 1551 1552 1553 1554 1555 1556 1557 1558 1559 1560 1561 1562 1563 1564 1565 1566 1567 1568 1569 1570 1571 1572 1573 1574 1575 1576 1577 1578 1579 1580 1581 1582 1583 1584 1585 1586 1587 1588 1589 1590 1591 1592 1593 1594 1595 1596 1597 1598 1599 1600 1601 1602 1603 1604 1605 1606 1607 1608 1609 1610 1611 1612 1613 1614 1615 1616 1617 1618 1619 1620 1621 1622 1623 1624 1625 1626 1627 1628 1629 1630 1631 1632 1633 1634 1635 1636 1637 1638 1639 1640 1641 1642 1643 1644 1645 1646 1647 1648 1649 1650 1651 1652 1653 1654 1655 1656 1657 1658 1659 1660 1661 1662 1663 1664 1665 1666 1667 1668 1669 1670 1671 1672 1673 1674 1675 1676 1677 1678 1679 1680 1681 1682 1683 1684 1685 1686 1687 1688 1689 1690 1691 1692 1693 1694 1695 1696 1697 1698 1699 1700 1701 1702 1703 1704 1705 1706 1707 1708 1709 1710 1711 1712 1713 1714 1715 1716 1717 1718 1719 1720 1721 1722 1723 1724 1725 1726 1727 1728 1729 1730 1731 1732 1733 1734 1735 1736 1737 1738 1739 1740 1741 1742 1743 1744 1745 1746 1747 1748 1749 1750 1751 1752 1753 1754 1755 1756 1757 1758 1759 | /* ** WEBPAGE: register ** ** Page to allow users to self-register. The "self-register" setting ** must be enabled for this page to operate. */ void register_page(void){ const char *zUserID, *zPasswd, *zConfirm, *zEAddr; const char *zDName; unsigned int uSeed; const char *zDecoded; char *zCaptcha; int iErrLine = -1; const char *zErr = 0; char *zPerms; /* Permissions for the default user */ int canDoAlerts = 0; /* True if receiving email alerts is possible */ int doAlerts = 0; /* True if subscription is wanted too */ if( !db_get_boolean("self-register", 0) ){ style_header("Registration not possible"); @ <p>This project does not allow user self-registration. Please contact the @ project administrator to obtain an account.</p> style_footer(); return; } zPerms = db_get("default-perms","u"); /* Prompt the user for email alerts if this repository is configured for ** email alerts and if the default permissions include "7" */ canDoAlerts = email_tables_exist() && db_int(0, "SELECT fullcap(%Q) GLOB '*7*'", zPerms ); doAlerts = canDoAlerts && atoi(PD("alerts","1"))!=0; zUserID = PDT("u",""); zPasswd = PDT("p",""); zConfirm = PDT("cp",""); zEAddr = PDT("ea",""); zDName = PDT("dn",""); /* Verify user imputs */ if( P("new")==0 || !cgi_csrf_safe(1) ){ /* This is not a valid form submission. Fall through into ** the form display */ }else if( !captcha_is_correct(1) ){ iErrLine = 6; zErr = "Incorrect CAPTCHA"; }else if( strlen(zUserID)<3 ){ iErrLine = 1; zErr = "User ID too short. Must be at least 3 characters."; }else if( sqlite3_strglob("*[^-a-zA-Z0-9_.]*",zUserID)==0 ){ iErrLine = 1; zErr = "User ID may not contain spaces or special characters."; }else if( zDName[0]==0 ){ iErrLine = 2; zErr = "Required"; }else if( zEAddr[0]==0 ){ iErrLine = 3; zErr = "Required"; }else if( email_copy_addr(zEAddr,0)==0 ){ iErrLine = 3; zErr = "Not a valid email address"; }else if( strlen(zPasswd)<6 ){ iErrLine = 4; zErr = "Password must be at least 6 characters long"; }else if( fossil_strcmp(zPasswd,zConfirm)!=0 ){ iErrLine = 5; zErr = "Passwords do not match"; }else if( db_exists("SELECT 1 FROM user WHERE login=%Q", zUserID) ){ iErrLine = 1; zErr = "This User ID is already taken. Choose something different."; }else if( /* If the email is found anywhere in USER.INFO... */ db_exists("SELECT 1 FROM user WHERE info LIKE '%%%q%%'", zEAddr) || /* Or if the email is a verify subscriber email with an associated ** user... */ db_exists( "SELECT 1 FROM subscriber WHERE semail=%Q AND suname IS NOT NULL" " AND sverified",zEAddr) ){ iErrLine = 3; zErr = "This email address is already claimed by another user"; }else{ /* If all of the tests above have passed, that means that the submitted ** form contains valid data and we can proceed to create the new login */ Blob sql; int uid; char *zPass = sha1_shared_secret(zPasswd, zUserID, 0); blob_init(&sql, 0, 0); blob_append_sql(&sql, "INSERT INTO user(login,pw,cap,info,mtime)\n" "VALUES(%Q,%Q,%Q," "'%q <%q>\nself-register from ip %q on '||datetime('now'),now())", zUserID, zPass, zPerms, zDName, zEAddr, g.zIpAddr); fossil_free(zPass); db_multi_exec("%s", blob_sql_text(&sql)); uid = db_int(0, "SELECT uid FROM user WHERE login=%Q", zUserID); login_set_user_cookie(zUserID, uid, NULL); if( doAlerts ){ /* Also make the new user a subscriber. */ Blob hdr, body; EmailSender *pSender; sqlite3_int64 id; /* New subscriber Id */ const char *zCode; /* New subscriber code (in hex) */ const char *zGoto = P("g"); int nsub = 0; char ssub[20]; ssub[nsub++] = 'a'; if( g.perm.Read ) ssub[nsub++] = 'c'; if( g.perm.RdForum ) ssub[nsub++] = 'f'; if( g.perm.RdTkt ) ssub[nsub++] = 't'; if( g.perm.RdWiki ) ssub[nsub++] = 'w'; ssub[nsub] = 0; /* Also add the user to the subscriber table. */ db_multi_exec( "INSERT INTO subscriber(semail,suname," " sverified,sdonotcall,sdigest,ssub,sctime,mtime,smip)" " VALUES(%Q,%Q,%d,0,%d,%Q,now(),now(),%Q)" " ON CONFLICT(semail) DO UPDATE" " SET suname=excluded.suname", /* semail */ zEAddr, /* suname */ zUserID, /* sverified */ 0, /* sdigest */ 0, /* ssub */ ssub, /* smip */ g.zIpAddr ); id = db_last_insert_rowid(); if( db_exists("SELECT 1 FROM subscriber WHERE semail=%Q" " AND sverified", zEAddr) ){ /* This the case where the user was formerly a verified subscriber ** and here they have also registered as a user as well. It is ** not necessary to repeat the verfication step */ redirect_to_g(); } zCode = db_text(0, "SELECT hex(subscriberCode) FROM subscriber WHERE subscriberId=%lld", id); /* A verification email */ pSender = email_sender_new(0,0); blob_init(&hdr,0,0); blob_init(&body,0,0); blob_appendf(&hdr, "To: <%s>\n", zEAddr); blob_appendf(&hdr, "Subject: Subscription verification\n"); email_append_confirmation_message(&body, zCode); email_send(pSender, &hdr, &body, 0); style_header("Email Verification"); if( pSender->zErr ){ @ <h1>Internal Error</h1> @ <p>The following internal error was encountered while trying @ to send the confirmation email: @ <blockquote><pre> @ %h(pSender->zErr) @ </pre></blockquote> }else{ @ <p>An email has been sent to "%h(zEAddr)". That email contains a @ hyperlink that you must click on in order to activate your @ subscription.</p> } email_sender_free(pSender); if( zGoto ){ @ <p><a href='%h(zGoto)'>Continue</a> } style_footer(); return; } redirect_to_g(); } /* Prepare the captcha. */ uSeed = captcha_seed(); zDecoded = captcha_decode(uSeed); zCaptcha = captcha_render(zDecoded); style_header("Register"); /* Print out the registration form. */ form_begin(0, "%R/register"); if( P("g") ){ @ <input type="hidden" name="g" value="%h(P("g"))" /> } @ <p><input type="hidden" name="captchaseed" value="%u(uSeed)" /> @ <table class="login_out"> @ <tr> @ <td class="form_label" align="right">User ID:</td> @ <td><input type="text" name="u" value="%h(zUserID)" size="30"></td> if( iErrLine==1 ){ @ <td><span class='loginError'>← %h(zErr)</span></td> } @ </tr> @ <tr> @ <td class="form_label" align="right">Display Name:</td> @ <td><input type="text" name="dn" value="%h(zDName)" size="30"></td> if( iErrLine==2 ){ @ <td><span class='loginError'>← %h(zErr)</span></td> } @ </tr> @ <tr> @ <td class="form_label" align="right">Email Address:</td> @ <td><input type="text" name="ea" value="%h(zEAddr)" size="30"></td> if( iErrLine==3 ){ @ <td><span class='loginError'>← %h(zErr)</span></td> } @ </tr> if( canDoAlerts ){ int a = atoi(PD("alerts","1")); @ <tr> @ <td class="form_label" align="right">Receive Email Alerts?</td> @ <td><select size='1' name='alerts'> @ <option value="1" %s(a?"selected":"")>Yes</option> @ <option value="0" %s(!a?"selected":"")>No</option> @ </select></td></tr> } @ <tr> @ <td class="form_label" align="right">Password:</td> @ <td><input type="password" name="p" value="%h(zPasswd)" size="30"></td> if( iErrLine==4 ){ @ <td><span class='loginError'>← %h(zErr)</span></td> }else{ @ <td>← Must be at least 6 characters</td> } @ </tr> @ <tr> @ <td class="form_label" align="right">Confirm password:</td> @ <td><input type="password" name="cp" value="%h(zConfirm)" size="30"></td> if( iErrLine==5 ){ @ <td><span class='loginError'>← %h(zErr)</span></td> } @ </tr> @ <tr> @ <td class="form_label" align="right">Captcha text (below):</td> @ <td><input type="text" name="captcha" value="" size="30"></td> if( iErrLine==6 ){ @ <td><span class='loginError'>← %h(zErr)</span></td> } @ </tr> @ <tr><td></td> @ <td><input type="submit" name="new" value="Register" /></td></tr> @ </table> @ <div class="captcha"><table class="captcha"><tr><td><pre> @ %h(zCaptcha) @ </pre></td></tr></table></div> |
︙ | ︙ |
Changes to src/main.c.
︙ | ︙ | |||
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 | ** This module codes the main() procedure that runs first when the ** program is invoked. */ #include "VERSION.h" #include "config.h" #if defined(_WIN32) # include <windows.h> #endif #include "main.h" #include <string.h> #include <time.h> #include <fcntl.h> #include <sys/types.h> #include <sys/stat.h> #include <stdlib.h> /* atexit() */ #if !defined(_WIN32) # include <errno.h> /* errno global */ #endif #ifdef FOSSIL_ENABLE_SSL # include "openssl/crypto.h" #endif #if defined(FOSSIL_ENABLE_MINIZ) # define MINIZ_HEADER_FILE_ONLY # include "miniz.c" #else # include <zlib.h> #endif #if INTERFACE #ifdef FOSSIL_ENABLE_TCL # include "tcl.h" #endif #ifdef FOSSIL_ENABLE_JSON # include "cson_amalgamation.h" /* JSON API. */ # include "json_detail.h" #endif /* ** Maximum number of auxiliary parameters on reports */ #define MX_AUX 5 /* | > > > > > > > > > | 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 | ** This module codes the main() procedure that runs first when the ** program is invoked. */ #include "VERSION.h" #include "config.h" #if defined(_WIN32) # include <windows.h> # include <io.h> # define isatty(h) _isatty(h) # define GETPID (int)GetCurrentProcessId #endif #include "main.h" #include <string.h> #include <time.h> #include <fcntl.h> #include <sys/types.h> #include <sys/stat.h> #include <stdlib.h> /* atexit() */ #if !defined(_WIN32) # include <errno.h> /* errno global */ # include <unistd.h> # include <signal.h> # define GETPID getpid #endif #ifdef FOSSIL_ENABLE_SSL # include "openssl/crypto.h" #endif #if defined(FOSSIL_ENABLE_MINIZ) # define MINIZ_HEADER_FILE_ONLY # include "miniz.c" #else # include <zlib.h> #endif #if INTERFACE #ifdef FOSSIL_ENABLE_TCL # include "tcl.h" #endif #ifdef FOSSIL_ENABLE_JSON # include "cson_amalgamation.h" /* JSON API. */ # include "json_detail.h" #endif #ifdef HAVE_BACKTRACE # include <execinfo.h> #endif /* ** Maximum number of auxiliary parameters on reports */ #define MX_AUX 5 /* |
︙ | ︙ | |||
81 82 83 84 85 86 87 88 89 90 91 92 93 94 | char ModTkt; /* q: approve and publish ticket changes (Moderator) */ char Attach; /* b: add attachments */ char TktFmt; /* t: create new ticket report formats */ char RdAddr; /* e: read email addresses or other private data */ char Zip; /* z: download zipped artifact via /zip URL */ char Private; /* x: can send and receive private content */ char WrUnver; /* y: can push unversioned content */ }; #ifdef FOSSIL_ENABLE_TCL /* ** All Tcl related context information is in this structure. This structure ** definition has been copied from and should be kept in sync with the one in ** "th_tcl.c". | > > > > > > > > > > > | 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 | char ModTkt; /* q: approve and publish ticket changes (Moderator) */ char Attach; /* b: add attachments */ char TktFmt; /* t: create new ticket report formats */ char RdAddr; /* e: read email addresses or other private data */ char Zip; /* z: download zipped artifact via /zip URL */ char Private; /* x: can send and receive private content */ char WrUnver; /* y: can push unversioned content */ char RdForum; /* 2: Read forum posts */ char WrForum; /* 3: Create new forum posts */ char WrTForum; /* 4: Post to forums not subject to moderation */ char ModForum; /* 5: Moderate (approve or reject) forum posts */ char AdminForum; /* 6: Set or remove capability 4 on other users */ char EmailAlert; /* 7: Sign up for email notifications */ char Announce; /* A: Send announcements */ char Debug; /* D: show extra Fossil debugging features */ /* These last two are included to block infinite recursion */ char XReader; /* u: Inherit all privileges of "reader" */ char XDeveloper; /* v: Inherit all privileges of "developer" */ }; #ifdef FOSSIL_ENABLE_TCL /* ** All Tcl related context information is in this structure. This structure ** definition has been copied from and should be kept in sync with the one in ** "th_tcl.c". |
︙ | ︙ | |||
121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 | sqlite3 *db; /* The connection to the databases */ sqlite3 *dbConfig; /* Separate connection for global_config table */ char *zAuxSchema; /* Main repository aux-schema */ int dbIgnoreErrors; /* Ignore database errors if true */ const char *zConfigDbName;/* Path of the config database. NULL if not open */ sqlite3_int64 now; /* Seconds since 1970 */ int repositoryOpen; /* True if the main repository database is open */ char *zRepositoryOption; /* Most recent cached repository option value */ char *zRepositoryName; /* Name of the repository database file */ char *zLocalDbName; /* Name of the local database file */ char *zOpenRevision; /* Check-in version to use during database open */ int localOpen; /* True if the local database is open */ char *zLocalRoot; /* The directory holding the local database */ int minPrefix; /* Number of digits needed for a distinct UUID */ int eHashPolicy; /* Current hash policy. One of HPOLICY_* */ int fSqlTrace; /* True if --sqltrace flag is present */ int fSqlStats; /* True if --sqltrace or --sqlstats are present */ | > | > > | | 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 | sqlite3 *db; /* The connection to the databases */ sqlite3 *dbConfig; /* Separate connection for global_config table */ char *zAuxSchema; /* Main repository aux-schema */ int dbIgnoreErrors; /* Ignore database errors if true */ const char *zConfigDbName;/* Path of the config database. NULL if not open */ sqlite3_int64 now; /* Seconds since 1970 */ int repositoryOpen; /* True if the main repository database is open */ unsigned iRepoDataVers; /* Initial data version for repository database */ char *zRepositoryOption; /* Most recent cached repository option value */ char *zRepositoryName; /* Name of the repository database file */ char *zLocalDbName; /* Name of the local database file */ char *zOpenRevision; /* Check-in version to use during database open */ int localOpen; /* True if the local database is open */ char *zLocalRoot; /* The directory holding the local database */ int minPrefix; /* Number of digits needed for a distinct UUID */ int eHashPolicy; /* Current hash policy. One of HPOLICY_* */ int fSqlTrace; /* True if --sqltrace flag is present */ int fSqlStats; /* True if --sqltrace or --sqlstats are present */ int fSqlPrint; /* True if --sqlprint flag is present */ int fCgiTrace; /* True if --cgitrace is enabled */ int fQuiet; /* True if -quiet flag is present */ int fJail; /* True if running with a chroot jail */ int fHttpTrace; /* Trace outbound HTTP requests */ int fAnyTrace; /* Any kind of tracing */ char *zHttpAuth; /* HTTP Authorization user:pass information */ int fSystemTrace; /* Trace calls to fossil_system(), --systemtrace */ int fSshTrace; /* Trace the SSH setup traffic */ int fSshClient; /* HTTP client flags for SSH client */ int fNoHttpCompress; /* Do not compress HTTP traffic (for debugging) */ char *zSshCmd; /* SSH command string */ int fNoSync; /* Do not do an autosync ever. --nosync */ int fIPv4; /* Use only IPv4, not IPv6. --ipv4 */ char *zPath; /* Name of webpage being served */ char *zExtra; /* Extra path information past the webpage name */ char *zBaseURL; /* Full text of the URL being served */ char *zHttpsURL; /* zBaseURL translated to https: */ char *zTop; /* Parent directory of zPath */ const char *zContentType; /* The content type of the input HTTP request */ int iErrPriority; /* Priority of current error message */ char *zErrMsg; /* Text of an error message */ int sslNotAvailable; /* SSL is not available. Do not redirect to https: */ Blob cgiIn; /* Input to an xfer www method */ int cgiOutput; /* 0: command-line 1: CGI. 2: after CGI */ int xferPanic; /* Write error messages in XFER protocol */ int fullHttpReply; /* True for full HTTP reply. False for CGI reply */ Th_Interp *interp; /* The TH1 interpreter */ char *th1Setup; /* The TH1 post-creation setup script, if any */ int th1Flags; /* The TH1 integration state flags */ FILE *httpIn; /* Accept HTTP input from here */ FILE *httpOut; /* Send HTTP output here */ |
︙ | ︙ | |||
499 500 501 502 503 504 505 506 507 | } } return zCode; } /* Error logs from SQLite */ static void fossil_sqlite_log(void *notUsed, int iCode, const char *zErrmsg){ #ifdef __APPLE__ /* Disable the file alias warning on apple products because Time Machine | > > | > > | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | | 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 | } } return zCode; } /* Error logs from SQLite */ static void fossil_sqlite_log(void *notUsed, int iCode, const char *zErrmsg){ sqlite3_stmt *p; Blob msg; #ifdef __APPLE__ /* Disable the file alias warning on apple products because Time Machine ** creates lots of aliases and the warnings alarm people. */ if( iCode==SQLITE_WARNING ) return; #endif #ifndef FOSSIL_DEBUG /* Disable the automatic index warning except in FOSSIL_DEBUG builds. */ if( iCode==SQLITE_WARNING_AUTOINDEX ) return; #endif if( iCode==SQLITE_SCHEMA ) return; if( g.dbIgnoreErrors ) return; #ifdef SQLITE_READONLY_DIRECTORY if( iCode==SQLITE_READONLY_DIRECTORY ){ zErrmsg = "database is in a read-only directory"; } #endif blob_init(&msg, 0, 0); blob_appendf(&msg, "%s(%d): %s", fossil_sqlite_return_code_name(iCode), iCode, zErrmsg); if( g.db ){ for(p=sqlite3_next_stmt(g.db, 0); p; p=sqlite3_next_stmt(g.db,p)){ const char *zSql; if( !sqlite3_stmt_busy(p) ) continue; zSql = sqlite3_sql(p); if( zSql==0 ) continue; blob_appendf(&msg, "\nSQL: %s", zSql); } } fossil_warning("%s", blob_str(&msg)); blob_reset(&msg); } /* ** This function attempts to find command line options known to contain ** bitwise flags and initializes the associated global variables. After ** this function executes, all global variables (i.e. in the "g" struct) ** containing option-settable bitwise flag fields must be initialized. */ static void fossil_init_flags_from_options(void){ const char *zValue = find_option("comfmtflags", 0, 1); if( zValue ){ g.comFmtFlags = atoi(zValue); }else{ g.comFmtFlags = COMMENT_PRINT_DEFAULT; } } /* ** Check to see if the Fossil binary contains an appended repository ** file using the appendvfs extension. If so, change command-line arguments ** to cause Fossil to launch with "fossil ui" on that repo. */ static int fossilExeHasAppendedRepo(void){ extern int deduceDatabaseType(const char*,int); if( 2==deduceDatabaseType(g.nameOfExe,0) ){ static char *azAltArgv[] = { 0, "ui", 0, 0 }; azAltArgv[0] = g.nameOfExe; azAltArgv[2] = g.nameOfExe; g.argv = azAltArgv; g.argc = 3; return 1; }else{ return 0; } } /* ** This procedure runs first. */ #if defined(_WIN32) && !defined(BROKEN_MINGW_CMDLINE) int _dowildcard = -1; /* This turns on command-line globbing in MinGW-w64 */ int wmain(int argc, wchar_t **argv) #else #if defined(_WIN32) int _CRT_glob = 0x0001; /* See MinGW bug #2062 */ #endif int main(int argc, char **argv) #endif { const char *zCmdName = "unknown"; const CmdOrPage *pCmd = 0; int rc; #if !defined(_WIN32_WCE) if( fossil_getenv("FOSSIL_BREAK") ){ if( isatty(0) && isatty(2) ){ fprintf(stderr, "attach debugger to process %d and press any key to continue.\n", GETPID()); fgetc(stdin); }else{ #if defined(_WIN32) || defined(WIN32) DebugBreak(); #elif defined(SIGTRAP) raise(SIGTRAP); #endif } } #endif fossil_limit_memory(1); if( sqlite3_libversion_number()<3014000 ){ fossil_panic("Unsuitable SQLite version %s, must be at least 3.14.0", sqlite3_libversion()); } sqlite3_config(SQLITE_CONFIG_MULTITHREAD); sqlite3_config(SQLITE_CONFIG_LOG, fossil_sqlite_log, 0); memset(&g, 0, sizeof(g)); g.now = time(0); g.httpHeader = empty_blob; |
︙ | ︙ | |||
596 597 598 599 600 601 602 | }else{ fossil_fatal("no such VFS: \"%s\"", g.zVfsName); } } if( fossil_getenv("GATEWAY_INTERFACE")!=0 && !find_option("nocgi", 0, 0)){ zCmdName = "cgi"; g.isHTTP = 1; | | | 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 | }else{ fossil_fatal("no such VFS: \"%s\"", g.zVfsName); } } if( fossil_getenv("GATEWAY_INTERFACE")!=0 && !find_option("nocgi", 0, 0)){ zCmdName = "cgi"; g.isHTTP = 1; }else if( g.argc<2 && !fossilExeHasAppendedRepo() ){ fossil_print( "Usage: %s COMMAND ...\n" " or: %s help -- for a list of common commands\n" " or: %s help COMMAND -- for help with the named command\n", g.argv[0], g.argv[0], g.argv[0]); fossil_print( "\nCommands and filenames may be passed on to fossil from a file\n" |
︙ | ︙ | |||
623 624 625 626 627 628 629 630 631 632 633 634 635 636 | g.isHTTP = 0; g.rcvid = 0; g.fQuiet = find_option("quiet", 0, 0)!=0; g.fSqlTrace = find_option("sqltrace", 0, 0)!=0; g.fSqlStats = find_option("sqlstats", 0, 0)!=0; g.fSystemTrace = find_option("systemtrace", 0, 0)!=0; g.fSshTrace = find_option("sshtrace", 0, 0)!=0; g.fSshClient = 0; g.zSshCmd = 0; if( g.fSqlTrace ) g.fSqlStats = 1; g.fHttpTrace = find_option("httptrace", 0, 0)!=0; #ifdef FOSSIL_ENABLE_TH1_HOOKS g.fNoThHook = find_option("no-th-hook", 0, 0)!=0; #endif | > | > | 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 | g.isHTTP = 0; g.rcvid = 0; g.fQuiet = find_option("quiet", 0, 0)!=0; g.fSqlTrace = find_option("sqltrace", 0, 0)!=0; g.fSqlStats = find_option("sqlstats", 0, 0)!=0; g.fSystemTrace = find_option("systemtrace", 0, 0)!=0; g.fSshTrace = find_option("sshtrace", 0, 0)!=0; g.fCgiTrace = find_option("cgitrace", 0, 0)!=0; g.fSshClient = 0; g.zSshCmd = 0; if( g.fSqlTrace ) g.fSqlStats = 1; g.fHttpTrace = find_option("httptrace", 0, 0)!=0; #ifdef FOSSIL_ENABLE_TH1_HOOKS g.fNoThHook = find_option("no-th-hook", 0, 0)!=0; #endif g.fAnyTrace = g.fSqlTrace|g.fSystemTrace|g.fSshTrace| g.fHttpTrace|g.fCgiTrace; g.zHttpAuth = 0; g.zLogin = find_option("user", "U", 1); g.zSSLIdentity = find_option("ssl-identity", 0, 1); g.zErrlog = find_option("errorlog", 0, 1); fossil_init_flags_from_options(); if( find_option("utc",0,0) ) g.fTimeFormat = 1; if( find_option("localtime",0,0) ) g.fTimeFormat = 2; |
︙ | ︙ | |||
688 689 690 691 692 693 694 | if( fd>=2 ) break; if( fd<0 ) x = errno; }while( nTry++ < 2 ); if( fd<2 ){ g.cgiOutput = 1; g.httpOut = stdout; g.fullHttpReply = !g.isHTTP; | | | 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 | if( fd>=2 ) break; if( fd<0 ) x = errno; }while( nTry++ < 2 ); if( fd<2 ){ g.cgiOutput = 1; g.httpOut = stdout; g.fullHttpReply = !g.isHTTP; fossil_panic("file descriptor 2 is not open. (fd=%d, errno=%d)", fd, x); } } #endif rc = dispatch_name_search(zCmdName, CMDFLAG_COMMAND|CMDFLAG_PREFIX, &pCmd); if( rc==1 ){ #ifdef FOSSIL_ENABLE_TH1_HOOKS |
︙ | ︙ | |||
1174 1175 1176 1177 1178 1179 1180 | ** Assume the user-id and group-id of the repository, or if zRepo ** is a directory, of that directory. ** ** The noJail flag means that the chroot jail is not entered. But ** privileges are still lowered to that of the user-id and group-id ** of the repository file. */ | | | | | | 1250 1251 1252 1253 1254 1255 1256 1257 1258 1259 1260 1261 1262 1263 1264 1265 1266 1267 1268 1269 1270 1271 1272 1273 1274 1275 1276 1277 1278 1279 1280 1281 1282 1283 1284 1285 1286 1287 1288 1289 1290 | ** Assume the user-id and group-id of the repository, or if zRepo ** is a directory, of that directory. ** ** The noJail flag means that the chroot jail is not entered. But ** privileges are still lowered to that of the user-id and group-id ** of the repository file. */ char *enter_chroot_jail(char *zRepo, int noJail){ #if !defined(_WIN32) if( getuid()==0 ){ int i; struct stat sStat; Blob dir; char *zDir; if( g.db!=0 ){ db_close(1); } file_canonical_name(zRepo, &dir, 0); zDir = blob_str(&dir); if( !noJail ){ if( file_isdir(zDir, ExtFILE)==1 ){ if( file_chdir(zDir, 1) ){ fossil_panic("unable to chroot into %s", zDir); } g.fJail = 1; zRepo = "/"; }else{ for(i=strlen(zDir)-1; i>0 && zDir[i]!='/'; i--){} if( zDir[i]!='/' ) fossil_panic("bad repository name: %s", zRepo); if( i>0 ){ zDir[i] = 0; if( file_chdir(zDir, 1) ){ fossil_panic("unable to chroot into %s", zDir); } zDir[i] = '/'; } zRepo = &zDir[i]; } } if( stat(zRepo, &sStat)!=0 ){ |
︙ | ︙ | |||
1277 1278 1279 1280 1281 1282 1283 1284 1285 1286 1287 1288 1289 1290 | vfile_scan(&base, blob_size(&base), 0, 0, 0); db_multi_exec("DELETE FROM sfile WHERE pathname NOT GLOB '*[^/].fossil'"); allRepo = 0; } @ <html> @ <head> @ <base href="%s(g.zBaseURL)/" /> @ <title>Repository List</title> @ </head> @ <body> n = db_int(0, "SELECT count(*) FROM sfile"); if( n>0 ){ Stmt q; sqlite3_int64 iNow, iMTime; | > | 1353 1354 1355 1356 1357 1358 1359 1360 1361 1362 1363 1364 1365 1366 1367 | vfile_scan(&base, blob_size(&base), 0, 0, 0); db_multi_exec("DELETE FROM sfile WHERE pathname NOT GLOB '*[^/].fossil'"); allRepo = 0; } @ <html> @ <head> @ <base href="%s(g.zBaseURL)/" /> @ <meta name="viewport" content="width=device-width, initial-scale=1.0"> @ <title>Repository List</title> @ </head> @ <body> n = db_int(0, "SELECT count(*) FROM sfile"); if( n>0 ){ Stmt q; sqlite3_int64 iNow, iMTime; |
︙ | ︙ | |||
1365 1366 1367 1368 1369 1370 1371 1372 1373 1374 1375 1376 1377 1378 | g.zRepositoryName = "/"; }else{ g.zRepositoryName = g.argv[2]; } g.httpOut = stdout; repo_list_page(); } /* ** Preconditions: ** ** * Environment variables are set up according to the CGI standard. ** ** If the repository is known, it has already been opened. If unknown, | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1442 1443 1444 1445 1446 1447 1448 1449 1450 1451 1452 1453 1454 1455 1456 1457 1458 1459 1460 1461 1462 1463 1464 1465 1466 1467 1468 1469 1470 1471 1472 1473 1474 1475 1476 1477 1478 1479 1480 1481 1482 1483 1484 1485 1486 1487 1488 1489 1490 1491 1492 1493 | g.zRepositoryName = "/"; }else{ g.zRepositoryName = g.argv[2]; } g.httpOut = stdout; repo_list_page(); } /* ** Called whenever a crash is encountered while processing a webpage. */ void sigsegv_handler(int x){ #if HAVE_BACKTRACE void *array[20]; size_t size; char **strings; size_t i; Blob out; size = backtrace(array, sizeof(array)/sizeof(array[0])); strings = backtrace_symbols(array, size); blob_init(&out, 0, 0); blob_appendf(&out, "Segfault"); for(i=0; i<size; i++){ blob_appendf(&out, "\n(%d) %s", i, strings[i]); } fossil_panic("%s", blob_str(&out)); #else fossil_panic("Segfault"); #endif exit(1); } /* ** Called if a server gets a SIGPIPE. This often happens when a client ** webbrowser opens a connection but never sends the HTTP request */ void sigpipe_handler(int x){ #ifndef _WIN32 if( g.fAnyTrace ){ fprintf(stderr,"/***** sigpipe received by subprocess %d ****\n", getpid()); } #endif db_panic_close(); exit(1); } /* ** Preconditions: ** ** * Environment variables are set up according to the CGI standard. ** ** If the repository is known, it has already been opened. If unknown, |
︙ | ︙ | |||
1398 1399 1400 1401 1402 1403 1404 1405 1406 1407 1408 1409 1410 1411 | int allowRepoList /* Send repo list for "/" URL */ ){ const char *zPathInfo = PD("PATH_INFO", ""); char *zPath = NULL; int i; const CmdOrPage *pCmd = 0; const char *zBase = g.zRepositoryName; /* Handle universal query parameters */ if( PB("utc") ){ g.fTimeFormat = 1; }else if( PB("localtime") ){ g.fTimeFormat = 2; } | > > > > | 1513 1514 1515 1516 1517 1518 1519 1520 1521 1522 1523 1524 1525 1526 1527 1528 1529 1530 | int allowRepoList /* Send repo list for "/" URL */ ){ const char *zPathInfo = PD("PATH_INFO", ""); char *zPath = NULL; int i; const CmdOrPage *pCmd = 0; const char *zBase = g.zRepositoryName; #if !defined(_WIN32) signal(SIGSEGV, sigsegv_handler); #endif /* Handle universal query parameters */ if( PB("utc") ){ g.fTimeFormat = 1; }else if( PB("localtime") ){ g.fTimeFormat = 2; } |
︙ | ︙ | |||
1554 1555 1556 1557 1558 1559 1560 1561 1562 1563 1564 1565 1566 1567 1568 | }else{ #ifdef FOSSIL_ENABLE_JSON if(g.json.isJsonMode){ json_err(FSL_JSON_E_RESOURCE_NOT_FOUND,NULL,1); return; } #endif @ <h1>Not Found</h1> cgi_set_status(404, "not found"); cgi_reply(); } return; } break; } | > > > > > | 1673 1674 1675 1676 1677 1678 1679 1680 1681 1682 1683 1684 1685 1686 1687 1688 1689 1690 1691 1692 | }else{ #ifdef FOSSIL_ENABLE_JSON if(g.json.isJsonMode){ json_err(FSL_JSON_E_RESOURCE_NOT_FOUND,NULL,1); return; } #endif @ <html><head> @ <meta name="viewport" \ @ content="width=device-width, initial-scale=1.0"> @ </head><body> @ <h1>Not Found</h1> @ </body> cgi_set_status(404, "not found"); cgi_reply(); } return; } break; } |
︙ | ︙ | |||
1607 1608 1609 1610 1611 1612 1613 | if( zPathInfo && strncmp(zPathInfo,"/draft",6)==0 && zPathInfo[6]>='1' && zPathInfo[6]<='9' && (zPathInfo[7]=='/' || zPathInfo[7]==0) ){ int iSkin = zPathInfo[6] - '0'; char *zNewScript; skin_use_draft(iSkin); | | | 1731 1732 1733 1734 1735 1736 1737 1738 1739 1740 1741 1742 1743 1744 1745 | if( zPathInfo && strncmp(zPathInfo,"/draft",6)==0 && zPathInfo[6]>='1' && zPathInfo[6]<='9' && (zPathInfo[7]=='/' || zPathInfo[7]==0) ){ int iSkin = zPathInfo[6] - '0'; char *zNewScript; skin_use_draft(iSkin); zNewScript = mprintf("%T/draft%d", P("SCRIPT_NAME"), iSkin); if( g.zTop ) g.zTop = mprintf("%s/draft%d", g.zTop, iSkin); if( g.zBaseURL ) g.zBaseURL = mprintf("%s/draft%d", g.zBaseURL, iSkin); zPathInfo += 7; cgi_replace_parameter("PATH_INFO", zPathInfo); cgi_replace_parameter("SCRIPT_NAME", zNewScript); } |
︙ | ︙ | |||
1739 1740 1741 1742 1743 1744 1745 1746 | #endif { @ <h1>Server Configuration Error</h1> @ <p>The database schema on the server is out-of-date. Please ask @ the administrator to run <b>fossil rebuild</b>.</p> } }else{ #ifdef FOSSIL_ENABLE_TH1_HOOKS | > > > > > | | | | | | | | | | | | | | | | | | | | | | | | | | | | | > | 1863 1864 1865 1866 1867 1868 1869 1870 1871 1872 1873 1874 1875 1876 1877 1878 1879 1880 1881 1882 1883 1884 1885 1886 1887 1888 1889 1890 1891 1892 1893 1894 1895 1896 1897 1898 1899 1900 1901 1902 1903 1904 1905 1906 1907 1908 1909 1910 1911 1912 1913 1914 | #endif { @ <h1>Server Configuration Error</h1> @ <p>The database schema on the server is out-of-date. Please ask @ the administrator to run <b>fossil rebuild</b>.</p> } }else{ if( g.fCgiTrace ){ fossil_trace("######## Calling %s #########\n", pCmd->zName); cgi_print_all(1, 1); } #ifdef FOSSIL_ENABLE_TH1_HOOKS { /* ** The TH1 return codes from the hook will be handled as follows: ** ** TH_OK: The xFunc() and the TH1 notification will both be executed. ** ** TH_ERROR: The xFunc() will be skipped, the TH1 notification will be ** skipped. If the xFunc() is being hooked, the error message ** will be emitted. ** ** TH_BREAK: The xFunc() and the TH1 notification will both be skipped. ** ** TH_RETURN: The xFunc() will be executed, the TH1 notification will be ** skipped. ** ** TH_CONTINUE: The xFunc() will be skipped, the TH1 notification will be ** executed. */ int rc; if( !g.fNoThHook ){ rc = Th_WebpageHook(pCmd->zName+1, pCmd->eCmdFlags); }else{ rc = TH_OK; } if( rc==TH_OK || rc==TH_RETURN || rc==TH_CONTINUE ){ if( rc==TH_OK || rc==TH_RETURN ){ #endif pCmd->xFunc(); #ifdef FOSSIL_ENABLE_TH1_HOOKS } if( !g.fNoThHook && (rc==TH_OK || rc==TH_CONTINUE) ){ Th_WebpageNotify(pCmd->zName+1, pCmd->eCmdFlags); } } } #endif } /* Return the result. */ |
︙ | ︙ | |||
2157 2158 2159 2160 2161 2162 2163 | }else{ fossil_fatal("failed to parse pid key"); } } #endif /* | < < < < < < | 2287 2288 2289 2290 2291 2292 2293 2294 2295 2296 2297 2298 2299 2300 | }else{ fossil_fatal("failed to parse pid key"); } } #endif /* ** COMMAND: http* ** ** Usage: %fossil http ?REPOSITORY? ?OPTIONS? ** ** Handle a single HTTP request appearing on stdin. The resulting webpage ** is delivered on stdout. This method is used to launch an HTTP request ** handler from inetd, for example. The argument is the name of the |
︙ | ︙ | |||
2200 2201 2202 2203 2204 2205 2206 2207 2208 2209 2210 2211 2212 2213 2214 2215 2216 2217 2218 2219 2220 2221 2222 2223 2224 2225 2226 2227 2228 2229 2230 2231 | ** ** Options: ** --baseurl URL base URL (useful with reverse proxies) ** --files GLOB comma-separate glob patterns for static file to serve ** --localauth enable automatic login for local connections ** --host NAME specify hostname of the server ** --https signal a request coming in via https ** --nojail drop root privilege but do not enter the chroot jail ** --nossl signal that no SSL connections are available ** --notfound URL use URL as "HTTP 404, object not found" page. ** --repolist If REPOSITORY is directory, URL "/" lists all repos ** --scgi Interpret input as SCGI rather than HTTP ** --skin LABEL Use override skin LABEL ** --th-trace trace TH1 execution (for debugging purposes) ** --usepidkey Use saved encryption key from parent process. This is ** only necessary when using SEE on Windows. ** ** See also: cgi, server, winsrv */ void cmd_http(void){ const char *zIpAddr = 0; const char *zNotFound; const char *zHost; const char *zAltBase; const char *zFileGlob; int useSCGI; int noJail; int allowRepoList; #if defined(_WIN32) && USE_SEE const char *zPidKey; #endif | > > > > > > > | 2324 2325 2326 2327 2328 2329 2330 2331 2332 2333 2334 2335 2336 2337 2338 2339 2340 2341 2342 2343 2344 2345 2346 2347 2348 2349 2350 2351 2352 2353 2354 2355 2356 2357 2358 2359 2360 2361 2362 | ** ** Options: ** --baseurl URL base URL (useful with reverse proxies) ** --files GLOB comma-separate glob patterns for static file to serve ** --localauth enable automatic login for local connections ** --host NAME specify hostname of the server ** --https signal a request coming in via https ** --in FILE Take input from FILE instead of standard input ** --ipaddr ADDR Assume the request comes from the given IP address ** --nocompress do not compress HTTP replies ** --nodelay omit backoffice processing if it would delay process exit ** --nojail drop root privilege but do not enter the chroot jail ** --nossl signal that no SSL connections are available ** --notfound URL use URL as "HTTP 404, object not found" page. ** --out FILE write results to FILE instead of to standard output ** --repolist If REPOSITORY is directory, URL "/" lists all repos ** --scgi Interpret input as SCGI rather than HTTP ** --skin LABEL Use override skin LABEL ** --th-trace trace TH1 execution (for debugging purposes) ** --usepidkey Use saved encryption key from parent process. This is ** only necessary when using SEE on Windows. ** ** See also: cgi, server, winsrv */ void cmd_http(void){ const char *zIpAddr = 0; const char *zNotFound; const char *zHost; const char *zAltBase; const char *zFileGlob; const char *zInFile; const char *zOutFile; int useSCGI; int noJail; int allowRepoList; #if defined(_WIN32) && USE_SEE const char *zPidKey; #endif |
︙ | ︙ | |||
2245 2246 2247 2248 2249 2250 2251 2252 2253 2254 2255 2256 2257 2258 2259 2260 | } skin_override(); zNotFound = find_option("notfound", 0, 1); noJail = find_option("nojail",0,0)!=0; allowRepoList = find_option("repolist",0,0)!=0; g.useLocalauth = find_option("localauth", 0, 0)!=0; g.sslNotAvailable = find_option("nossl", 0, 0)!=0; useSCGI = find_option("scgi", 0, 0)!=0; zAltBase = find_option("baseurl", 0, 1); if( zAltBase ) set_base_url(zAltBase); if( find_option("https",0,0)!=0 ){ zIpAddr = fossil_getenv("REMOTE_HOST"); /* From stunnel */ cgi_replace_parameter("HTTPS","on"); } zHost = find_option("host", 0, 1); if( zHost ) cgi_replace_parameter("HTTP_HOST",zHost); | > > > > > > > > > > > > > > > > > | 2376 2377 2378 2379 2380 2381 2382 2383 2384 2385 2386 2387 2388 2389 2390 2391 2392 2393 2394 2395 2396 2397 2398 2399 2400 2401 2402 2403 2404 2405 2406 2407 2408 | } skin_override(); zNotFound = find_option("notfound", 0, 1); noJail = find_option("nojail",0,0)!=0; allowRepoList = find_option("repolist",0,0)!=0; g.useLocalauth = find_option("localauth", 0, 0)!=0; g.sslNotAvailable = find_option("nossl", 0, 0)!=0; g.fNoHttpCompress = find_option("nocompress",0,0)!=0; zInFile = find_option("in",0,1); if( zInFile ){ g.httpIn = fossil_fopen(zInFile, "rb"); if( g.httpIn==0 ) fossil_fatal("cannot open \"%s\" for reading", zInFile); }else{ g.httpIn = stdin; } zOutFile = find_option("out",0,1); if( zOutFile ){ g.httpOut = fossil_fopen(zOutFile, "wb"); if( g.httpOut==0 ) fossil_fatal("cannot open \"%s\" for writing", zOutFile); }else{ g.httpOut = stdout; } zIpAddr = find_option("ipaddr",0,1); useSCGI = find_option("scgi", 0, 0)!=0; zAltBase = find_option("baseurl", 0, 1); if( find_option("nodelay",0,0)!=0 ) backoffice_no_delay(); if( zAltBase ) set_base_url(zAltBase); if( find_option("https",0,0)!=0 ){ zIpAddr = fossil_getenv("REMOTE_HOST"); /* From stunnel */ cgi_replace_parameter("HTTPS","on"); } zHost = find_option("host", 0, 1); if( zHost ) cgi_replace_parameter("HTTP_HOST",zHost); |
︙ | ︙ | |||
2269 2270 2271 2272 2273 2274 2275 | db_read_saved_encryption_key_from_process(processId, pAddress, nSize); } #endif /* We should be done with options.. */ verify_all_options(); | | < < < < < < < < < < | < | 2417 2418 2419 2420 2421 2422 2423 2424 2425 2426 2427 2428 2429 2430 2431 2432 2433 2434 | db_read_saved_encryption_key_from_process(processId, pAddress, nSize); } #endif /* We should be done with options.. */ verify_all_options(); if( g.argc!=2 && g.argc!=3 ) usage("?REPOSITORY?"); g.cgiOutput = 1; g.fullHttpReply = 1; find_server_repository(2, 0); if( zIpAddr==0 ){ zIpAddr = cgi_ssh_remote_addr(0); if( zIpAddr && zIpAddr[0] ){ g.fSshClient |= CGI_SSH_CLIENT; } } g.zRepositoryName = enter_chroot_jail(g.zRepositoryName, noJail); |
︙ | ︙ | |||
2335 2336 2337 2338 2339 2340 2341 2342 2343 2344 2345 2346 2347 2348 | Th_InitTraceLog(); login_set_capabilities("sx", 0); g.useLocalauth = 1; g.httpIn = stdin; g.httpOut = stdout; find_server_repository(2, 0); g.cgiOutput = 1; g.fullHttpReply = 1; zIpAddr = cgi_ssh_remote_addr(0); if( zIpAddr && zIpAddr[0] ){ g.fSshClient |= CGI_SSH_CLIENT; ssh_request_loop(zIpAddr, 0); }else{ cgi_set_parameter("REMOTE_ADDR", "127.0.0.1"); | > | 2472 2473 2474 2475 2476 2477 2478 2479 2480 2481 2482 2483 2484 2485 2486 | Th_InitTraceLog(); login_set_capabilities("sx", 0); g.useLocalauth = 1; g.httpIn = stdin; g.httpOut = stdout; find_server_repository(2, 0); g.cgiOutput = 1; g.fNoHttpCompress = 1; g.fullHttpReply = 1; zIpAddr = cgi_ssh_remote_addr(0); if( zIpAddr && zIpAddr[0] ){ g.fSshClient |= CGI_SSH_CLIENT; ssh_request_loop(zIpAddr, 0); }else{ cgi_set_parameter("REMOTE_ADDR", "127.0.0.1"); |
︙ | ︙ | |||
2376 2377 2378 2379 2380 2381 2382 | #endif #endif /* ** Send a time-out reply */ void sigalrm_handler(int x){ | | < < | 2514 2515 2516 2517 2518 2519 2520 2521 2522 2523 2524 2525 2526 2527 2528 | #endif #endif /* ** Send a time-out reply */ void sigalrm_handler(int x){ fossil_panic("TIMEOUT"); } /* ** COMMAND: server* ** COMMAND: ui ** ** Usage: %fossil server ?OPTIONS? ?REPOSITORY? |
︙ | ︙ | |||
2434 2435 2436 2437 2438 2439 2440 2441 2442 2443 2444 2445 2446 2447 | ** --page PAGE Start "ui" on PAGE. ex: --page "timeline?y=ci" ** --files GLOBLIST Comma-separated list of glob patterns for static files ** --localauth enable automatic login for requests from localhost ** --localhost listen on 127.0.0.1 only (always true for "ui") ** --https signal a request coming in via https ** --max-latency N Do not let any single HTTP request run for more than N ** seconds (only works on unix) ** --nojail Drop root privileges but do not enter the chroot jail ** --nossl signal that no SSL connections are available ** --notfound URL Redirect ** -P|--port TCPPORT listen to request on port TCPPORT ** --th-trace trace TH1 execution (for debugging purposes) ** --repolist If REPOSITORY is dir, URL "/" lists repos. ** --scgi Accept SCGI rather than HTTP | > | 2570 2571 2572 2573 2574 2575 2576 2577 2578 2579 2580 2581 2582 2583 2584 | ** --page PAGE Start "ui" on PAGE. ex: --page "timeline?y=ci" ** --files GLOBLIST Comma-separated list of glob patterns for static files ** --localauth enable automatic login for requests from localhost ** --localhost listen on 127.0.0.1 only (always true for "ui") ** --https signal a request coming in via https ** --max-latency N Do not let any single HTTP request run for more than N ** seconds (only works on unix) ** --nocompress Do not compress HTTP replies ** --nojail Drop root privileges but do not enter the chroot jail ** --nossl signal that no SSL connections are available ** --notfound URL Redirect ** -P|--port TCPPORT listen to request on port TCPPORT ** --th-trace trace TH1 execution (for debugging purposes) ** --repolist If REPOSITORY is dir, URL "/" lists repos. ** --scgi Accept SCGI rather than HTTP |
︙ | ︙ | |||
2474 2475 2476 2477 2478 2479 2480 2481 2482 2483 2484 2485 2486 2487 | #endif #if defined(_WIN32) const char *zStopperFile; /* Name of file used to terminate server */ zStopperFile = find_option("stopper", 0, 1); #endif zFileGlob = find_option("files-urlenc",0,1); if( zFileGlob ){ char *z = mprintf("%s", zFileGlob); dehttpize(z); zFileGlob = z; }else{ zFileGlob = find_option("files",0,1); | > > > | 2611 2612 2613 2614 2615 2616 2617 2618 2619 2620 2621 2622 2623 2624 2625 2626 2627 | #endif #if defined(_WIN32) const char *zStopperFile; /* Name of file used to terminate server */ zStopperFile = find_option("stopper", 0, 1); #endif if( g.zErrlog==0 ){ g.zErrlog = "-"; } zFileGlob = find_option("files-urlenc",0,1); if( zFileGlob ){ char *z = mprintf("%s", zFileGlob); dehttpize(z); zFileGlob = z; }else{ zFileGlob = find_option("files",0,1); |
︙ | ︙ | |||
2496 2497 2498 2499 2500 2501 2502 2503 2504 2505 2506 2507 2508 2509 | zPort = find_option("port", "P", 1); isUiCmd = g.argv[1][0]=='u'; if( isUiCmd ){ zInitPage = find_option("page", 0, 1); } zNotFound = find_option("notfound", 0, 1); allowRepoList = find_option("repolist",0,0)!=0; zAltBase = find_option("baseurl", 0, 1); fCreate = find_option("create",0,0)!=0; if( find_option("scgi", 0, 0)!=0 ) flags |= HTTP_SERVER_SCGI; if( zAltBase ){ set_base_url(zAltBase); } g.sslNotAvailable = find_option("nossl", 0, 0)!=0; | > | 2636 2637 2638 2639 2640 2641 2642 2643 2644 2645 2646 2647 2648 2649 2650 | zPort = find_option("port", "P", 1); isUiCmd = g.argv[1][0]=='u'; if( isUiCmd ){ zInitPage = find_option("page", 0, 1); } zNotFound = find_option("notfound", 0, 1); allowRepoList = find_option("repolist",0,0)!=0; if( find_option("nocompress",0,0)!=0 ) g.fNoHttpCompress = 1; zAltBase = find_option("baseurl", 0, 1); fCreate = find_option("create",0,0)!=0; if( find_option("scgi", 0, 0)!=0 ) flags |= HTTP_SERVER_SCGI; if( zAltBase ){ set_base_url(zAltBase); } g.sslNotAvailable = find_option("nossl", 0, 0)!=0; |
︙ | ︙ | |||
2599 2600 2601 2602 2603 2604 2605 | } if( zMaxLatency ){ signal(SIGALRM, sigalrm_handler); alarm(atoi(zMaxLatency)); } g.httpIn = stdin; g.httpOut = stdout; | | > > > > | > > > > > > | 2740 2741 2742 2743 2744 2745 2746 2747 2748 2749 2750 2751 2752 2753 2754 2755 2756 2757 2758 2759 2760 2761 2762 2763 2764 2765 2766 2767 2768 2769 2770 2771 2772 2773 2774 2775 2776 2777 2778 2779 | } if( zMaxLatency ){ signal(SIGALRM, sigalrm_handler); alarm(atoi(zMaxLatency)); } g.httpIn = stdin; g.httpOut = stdout; #if !defined(_WIN32) signal(SIGSEGV, sigsegv_handler); signal(SIGPIPE, sigpipe_handler); #endif if( g.fAnyTrace ){ fprintf(stderr, "/***** Subprocess %d *****/\n", getpid()); } g.cgiOutput = 1; find_server_repository(2, 0); if( fossil_strcmp(g.zRepositoryName,"/")==0 ){ allowRepoList = 1; }else{ g.zRepositoryName = enter_chroot_jail(g.zRepositoryName, noJail); } if( flags & HTTP_SERVER_SCGI ){ cgi_handle_scgi_request(); }else{ cgi_handle_http_request(0); } process_one_web_page(zNotFound, glob_create(zFileGlob), allowRepoList); if( g.fAnyTrace ){ fprintf(stderr, "/***** Webpage finished in subprocess %d *****/\n", getpid()); } #else /* Win32 implementation */ if( isUiCmd ){ zBrowser = db_get("web-browser", "start"); if( zIpAddr==0 ){ zBrowserCmd = mprintf("%s http://localhost:%%d/%s &", zBrowser, zInitPage); |
︙ | ︙ | |||
2673 2674 2675 2676 2677 2678 2679 | for(j=0; (c = z[j])!=0; j++){ fossil_print("%02x", c); } fossil_print("]\n"); } } } | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 2824 2825 2826 2827 2828 2829 2830 2831 2832 2833 2834 2835 2836 2837 2838 2839 2840 2841 2842 2843 2844 2845 2846 2847 2848 2849 2850 2851 2852 2853 2854 2855 2856 2857 2858 2859 2860 2861 2862 2863 2864 2865 2866 2867 2868 2869 2870 2871 2872 2873 2874 2875 2876 2877 2878 2879 2880 2881 2882 2883 2884 2885 2886 2887 2888 2889 2890 2891 2892 2893 2894 2895 2896 2897 2898 2899 2900 2901 2902 2903 | for(j=0; (c = z[j])!=0; j++){ fossil_print("%02x", c); } fossil_print("]\n"); } } } /* ** WEBPAGE: test-warning ** ** Test error and warning log operation. This webpage is accessible to ** the administrator only. ** ** case=1 Issue a fossil_warning() while generating the page. ** case=2 Extra db_begin_transaction() ** case=3 Extra db_end_transaction() ** case=4 Error during SQL processing ** case=5 Call the segfault handler ** case=6 Call webpage_assert() ** case=7 Call webpage_error() */ void test_warning_page(void){ int iCase = atoi(PD("case","0")); int i; login_check_credentials(); if( !g.perm.Setup && !g.perm.Admin ){ login_needed(0); return; } style_header("Warning Test Page"); style_submenu_element("Error Log","%R/errorlog"); if( iCase<1 || iCase>4 ){ @ <p>Generate a message to the <a href="%R/errorlog">error log</a> @ by clicking on one of the following cases: }else{ @ <p>This is the test page for case=%d(iCase). All possible cases: } for(i=1; i<=7; i++){ @ <a href='./test-warning?case=%d(i)'>[%d(i)]</a> } @ </p> @ <p><ol> @ <li value='1'> Call fossil_warning() if( iCase==1 ){ fossil_warning("Test warning message from /test-warning"); } @ <li value='2'> Call db_begin_transaction() if( iCase==2 ){ db_begin_transaction(); } @ <li value='3'> Call db_end_transaction() if( iCase==3 ){ db_end_transaction(0); } @ <li value='4'> warning during SQL if( iCase==4 ){ Stmt q; db_prepare(&q, "SELECT uuid FROM blob LIMIT 5"); db_step(&q); sqlite3_log(SQLITE_ERROR, "Test warning message during SQL"); db_finalize(&q); } @ <li value='5'> simulate segfault handling if( iCase==5 ){ sigsegv_handler(0); } @ <li value='6'> call webpage_assert(0) if( iCase==6 ){ webpage_assert( 5==7 ); } @ <li value='7'> call webpage_error()" if( iCase==7 ){ cgi_reset_content(); webpage_error("Case 7 from /test-warning"); } @ </ol> @ <p>End of test</p> style_footer(); } |
Changes to src/main.mk.
1 2 3 4 5 6 7 8 9 10 11 12 | # ############################################################################## # WARNING: DO NOT EDIT, AUTOMATICALLY GENERATED FILE (SEE "src/makemake.tcl") ############################################################################## # # This file is automatically generated. Instead of editing this # file, edit "makemake.tcl" then run "tclsh makemake.tcl" # to regenerate this file. # # This file is included by primary Makefile. # | | | > > > > | 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 | # ############################################################################## # WARNING: DO NOT EDIT, AUTOMATICALLY GENERATED FILE (SEE "src/makemake.tcl") ############################################################################## # # This file is automatically generated. Instead of editing this # file, edit "makemake.tcl" then run "tclsh makemake.tcl" # to regenerate this file. # # This file is included by primary Makefile. # XBCC = $(BCC) $(BCCFLAGS) XTCC = $(TCC) -I. -I$(SRCDIR) -I$(OBJDIR) $(TCCFLAGS) SRC = \ $(SRCDIR)/add.c \ $(SRCDIR)/allrepo.c \ $(SRCDIR)/attach.c \ $(SRCDIR)/backoffice.c \ $(SRCDIR)/bag.c \ $(SRCDIR)/bisect.c \ $(SRCDIR)/blob.c \ $(SRCDIR)/branch.c \ $(SRCDIR)/browse.c \ $(SRCDIR)/builtin.c \ $(SRCDIR)/bundle.c \ $(SRCDIR)/cache.c \ $(SRCDIR)/capabilities.c \ $(SRCDIR)/captcha.c \ $(SRCDIR)/cgi.c \ $(SRCDIR)/checkin.c \ $(SRCDIR)/checkout.c \ $(SRCDIR)/clearsign.c \ $(SRCDIR)/clone.c \ $(SRCDIR)/comformat.c \ $(SRCDIR)/configure.c \ $(SRCDIR)/content.c \ $(SRCDIR)/cookies.c \ $(SRCDIR)/db.c \ $(SRCDIR)/delta.c \ $(SRCDIR)/deltacmd.c \ $(SRCDIR)/descendants.c \ $(SRCDIR)/diff.c \ $(SRCDIR)/diffcmd.c \ $(SRCDIR)/dispatch.c \ $(SRCDIR)/doc.c \ $(SRCDIR)/email.c \ $(SRCDIR)/encode.c \ $(SRCDIR)/etag.c \ $(SRCDIR)/event.c \ $(SRCDIR)/export.c \ $(SRCDIR)/file.c \ $(SRCDIR)/finfo.c \ $(SRCDIR)/foci.c \ $(SRCDIR)/forum.c \ $(SRCDIR)/fshell.c \ $(SRCDIR)/fusefs.c \ $(SRCDIR)/glob.c \ $(SRCDIR)/graph.c \ $(SRCDIR)/gzip.c \ $(SRCDIR)/hname.c \ $(SRCDIR)/http.c \ |
︙ | ︙ | |||
103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 | $(SRCDIR)/regexp.c \ $(SRCDIR)/report.c \ $(SRCDIR)/rss.c \ $(SRCDIR)/schema.c \ $(SRCDIR)/search.c \ $(SRCDIR)/security_audit.c \ $(SRCDIR)/setup.c \ $(SRCDIR)/sha1.c \ $(SRCDIR)/sha1hard.c \ $(SRCDIR)/sha3.c \ $(SRCDIR)/shun.c \ $(SRCDIR)/sitemap.c \ $(SRCDIR)/skins.c \ $(SRCDIR)/sqlcmd.c \ $(SRCDIR)/stash.c \ $(SRCDIR)/stat.c \ $(SRCDIR)/statrep.c \ $(SRCDIR)/style.c \ $(SRCDIR)/sync.c \ $(SRCDIR)/tag.c \ | > > | 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 | $(SRCDIR)/regexp.c \ $(SRCDIR)/report.c \ $(SRCDIR)/rss.c \ $(SRCDIR)/schema.c \ $(SRCDIR)/search.c \ $(SRCDIR)/security_audit.c \ $(SRCDIR)/setup.c \ $(SRCDIR)/setupuser.c \ $(SRCDIR)/sha1.c \ $(SRCDIR)/sha1hard.c \ $(SRCDIR)/sha3.c \ $(SRCDIR)/shun.c \ $(SRCDIR)/sitemap.c \ $(SRCDIR)/skins.c \ $(SRCDIR)/smtp.c \ $(SRCDIR)/sqlcmd.c \ $(SRCDIR)/stash.c \ $(SRCDIR)/stat.c \ $(SRCDIR)/statrep.c \ $(SRCDIR)/style.c \ $(SRCDIR)/sync.c \ $(SRCDIR)/tag.c \ |
︙ | ︙ | |||
131 132 133 134 135 136 137 138 139 140 141 142 143 144 | $(SRCDIR)/update.c \ $(SRCDIR)/url.c \ $(SRCDIR)/user.c \ $(SRCDIR)/utf8.c \ $(SRCDIR)/util.c \ $(SRCDIR)/verify.c \ $(SRCDIR)/vfile.c \ $(SRCDIR)/wiki.c \ $(SRCDIR)/wikiformat.c \ $(SRCDIR)/winfile.c \ $(SRCDIR)/winhttp.c \ $(SRCDIR)/wysiwyg.c \ $(SRCDIR)/xfer.c \ $(SRCDIR)/xfersetup.c \ | > | 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 | $(SRCDIR)/update.c \ $(SRCDIR)/url.c \ $(SRCDIR)/user.c \ $(SRCDIR)/utf8.c \ $(SRCDIR)/util.c \ $(SRCDIR)/verify.c \ $(SRCDIR)/vfile.c \ $(SRCDIR)/webmail.c \ $(SRCDIR)/wiki.c \ $(SRCDIR)/wikiformat.c \ $(SRCDIR)/winfile.c \ $(SRCDIR)/winhttp.c \ $(SRCDIR)/wysiwyg.c \ $(SRCDIR)/xfer.c \ $(SRCDIR)/xfersetup.c \ |
︙ | ︙ | |||
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 | $(SRCDIR)/../skins/rounded1/header.txt \ $(SRCDIR)/../skins/xekri/css.txt \ $(SRCDIR)/../skins/xekri/details.txt \ $(SRCDIR)/../skins/xekri/footer.txt \ $(SRCDIR)/../skins/xekri/header.txt \ $(SRCDIR)/ci_edit.js \ $(SRCDIR)/diff.tcl \ $(SRCDIR)/graph.js \ $(SRCDIR)/href.js \ $(SRCDIR)/login.js \ $(SRCDIR)/markdown.md \ $(SRCDIR)/menu.js \ $(SRCDIR)/sbsdiff.js \ $(SRCDIR)/scroll.js \ $(SRCDIR)/skin.js \ $(SRCDIR)/sorttable.js \ $(SRCDIR)/tree.js \ $(SRCDIR)/useredit.js \ $(SRCDIR)/wiki.wiki TRANS_SRC = \ $(OBJDIR)/add_.c \ $(OBJDIR)/allrepo_.c \ $(OBJDIR)/attach_.c \ $(OBJDIR)/bag_.c \ $(OBJDIR)/bisect_.c \ $(OBJDIR)/blob_.c \ $(OBJDIR)/branch_.c \ $(OBJDIR)/browse_.c \ $(OBJDIR)/builtin_.c \ $(OBJDIR)/bundle_.c \ $(OBJDIR)/cache_.c \ $(OBJDIR)/captcha_.c \ $(OBJDIR)/cgi_.c \ $(OBJDIR)/checkin_.c \ $(OBJDIR)/checkout_.c \ $(OBJDIR)/clearsign_.c \ $(OBJDIR)/clone_.c \ $(OBJDIR)/comformat_.c \ $(OBJDIR)/configure_.c \ $(OBJDIR)/content_.c \ $(OBJDIR)/cookies_.c \ $(OBJDIR)/db_.c \ $(OBJDIR)/delta_.c \ $(OBJDIR)/deltacmd_.c \ $(OBJDIR)/descendants_.c \ $(OBJDIR)/diff_.c \ $(OBJDIR)/diffcmd_.c \ $(OBJDIR)/dispatch_.c \ $(OBJDIR)/doc_.c \ $(OBJDIR)/encode_.c \ $(OBJDIR)/etag_.c \ $(OBJDIR)/event_.c \ $(OBJDIR)/export_.c \ $(OBJDIR)/file_.c \ $(OBJDIR)/finfo_.c \ $(OBJDIR)/foci_.c \ $(OBJDIR)/fshell_.c \ $(OBJDIR)/fusefs_.c \ $(OBJDIR)/glob_.c \ $(OBJDIR)/graph_.c \ $(OBJDIR)/gzip_.c \ $(OBJDIR)/hname_.c \ $(OBJDIR)/http_.c \ | > > > > > | 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 | $(SRCDIR)/../skins/rounded1/header.txt \ $(SRCDIR)/../skins/xekri/css.txt \ $(SRCDIR)/../skins/xekri/details.txt \ $(SRCDIR)/../skins/xekri/footer.txt \ $(SRCDIR)/../skins/xekri/header.txt \ $(SRCDIR)/ci_edit.js \ $(SRCDIR)/diff.tcl \ $(SRCDIR)/forum.js \ $(SRCDIR)/graph.js \ $(SRCDIR)/href.js \ $(SRCDIR)/login.js \ $(SRCDIR)/markdown.md \ $(SRCDIR)/menu.js \ $(SRCDIR)/sbsdiff.js \ $(SRCDIR)/scroll.js \ $(SRCDIR)/skin.js \ $(SRCDIR)/sorttable.js \ $(SRCDIR)/tree.js \ $(SRCDIR)/useredit.js \ $(SRCDIR)/wiki.wiki TRANS_SRC = \ $(OBJDIR)/add_.c \ $(OBJDIR)/allrepo_.c \ $(OBJDIR)/attach_.c \ $(OBJDIR)/backoffice_.c \ $(OBJDIR)/bag_.c \ $(OBJDIR)/bisect_.c \ $(OBJDIR)/blob_.c \ $(OBJDIR)/branch_.c \ $(OBJDIR)/browse_.c \ $(OBJDIR)/builtin_.c \ $(OBJDIR)/bundle_.c \ $(OBJDIR)/cache_.c \ $(OBJDIR)/capabilities_.c \ $(OBJDIR)/captcha_.c \ $(OBJDIR)/cgi_.c \ $(OBJDIR)/checkin_.c \ $(OBJDIR)/checkout_.c \ $(OBJDIR)/clearsign_.c \ $(OBJDIR)/clone_.c \ $(OBJDIR)/comformat_.c \ $(OBJDIR)/configure_.c \ $(OBJDIR)/content_.c \ $(OBJDIR)/cookies_.c \ $(OBJDIR)/db_.c \ $(OBJDIR)/delta_.c \ $(OBJDIR)/deltacmd_.c \ $(OBJDIR)/descendants_.c \ $(OBJDIR)/diff_.c \ $(OBJDIR)/diffcmd_.c \ $(OBJDIR)/dispatch_.c \ $(OBJDIR)/doc_.c \ $(OBJDIR)/email_.c \ $(OBJDIR)/encode_.c \ $(OBJDIR)/etag_.c \ $(OBJDIR)/event_.c \ $(OBJDIR)/export_.c \ $(OBJDIR)/file_.c \ $(OBJDIR)/finfo_.c \ $(OBJDIR)/foci_.c \ $(OBJDIR)/forum_.c \ $(OBJDIR)/fshell_.c \ $(OBJDIR)/fusefs_.c \ $(OBJDIR)/glob_.c \ $(OBJDIR)/graph_.c \ $(OBJDIR)/gzip_.c \ $(OBJDIR)/hname_.c \ $(OBJDIR)/http_.c \ |
︙ | ︙ | |||
304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 | $(OBJDIR)/regexp_.c \ $(OBJDIR)/report_.c \ $(OBJDIR)/rss_.c \ $(OBJDIR)/schema_.c \ $(OBJDIR)/search_.c \ $(OBJDIR)/security_audit_.c \ $(OBJDIR)/setup_.c \ $(OBJDIR)/sha1_.c \ $(OBJDIR)/sha1hard_.c \ $(OBJDIR)/sha3_.c \ $(OBJDIR)/shun_.c \ $(OBJDIR)/sitemap_.c \ $(OBJDIR)/skins_.c \ $(OBJDIR)/sqlcmd_.c \ $(OBJDIR)/stash_.c \ $(OBJDIR)/stat_.c \ $(OBJDIR)/statrep_.c \ $(OBJDIR)/style_.c \ $(OBJDIR)/sync_.c \ $(OBJDIR)/tag_.c \ | > > | 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 | $(OBJDIR)/regexp_.c \ $(OBJDIR)/report_.c \ $(OBJDIR)/rss_.c \ $(OBJDIR)/schema_.c \ $(OBJDIR)/search_.c \ $(OBJDIR)/security_audit_.c \ $(OBJDIR)/setup_.c \ $(OBJDIR)/setupuser_.c \ $(OBJDIR)/sha1_.c \ $(OBJDIR)/sha1hard_.c \ $(OBJDIR)/sha3_.c \ $(OBJDIR)/shun_.c \ $(OBJDIR)/sitemap_.c \ $(OBJDIR)/skins_.c \ $(OBJDIR)/smtp_.c \ $(OBJDIR)/sqlcmd_.c \ $(OBJDIR)/stash_.c \ $(OBJDIR)/stat_.c \ $(OBJDIR)/statrep_.c \ $(OBJDIR)/style_.c \ $(OBJDIR)/sync_.c \ $(OBJDIR)/tag_.c \ |
︙ | ︙ | |||
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 | $(OBJDIR)/update_.c \ $(OBJDIR)/url_.c \ $(OBJDIR)/user_.c \ $(OBJDIR)/utf8_.c \ $(OBJDIR)/util_.c \ $(OBJDIR)/verify_.c \ $(OBJDIR)/vfile_.c \ $(OBJDIR)/wiki_.c \ $(OBJDIR)/wikiformat_.c \ $(OBJDIR)/winfile_.c \ $(OBJDIR)/winhttp_.c \ $(OBJDIR)/wysiwyg_.c \ $(OBJDIR)/xfer_.c \ $(OBJDIR)/xfersetup_.c \ $(OBJDIR)/zip_.c OBJ = \ $(OBJDIR)/add.o \ $(OBJDIR)/allrepo.o \ $(OBJDIR)/attach.o \ $(OBJDIR)/bag.o \ $(OBJDIR)/bisect.o \ $(OBJDIR)/blob.o \ $(OBJDIR)/branch.o \ $(OBJDIR)/browse.o \ $(OBJDIR)/builtin.o \ $(OBJDIR)/bundle.o \ $(OBJDIR)/cache.o \ $(OBJDIR)/captcha.o \ $(OBJDIR)/cgi.o \ $(OBJDIR)/checkin.o \ $(OBJDIR)/checkout.o \ $(OBJDIR)/clearsign.o \ $(OBJDIR)/clone.o \ $(OBJDIR)/comformat.o \ $(OBJDIR)/configure.o \ $(OBJDIR)/content.o \ $(OBJDIR)/cookies.o \ $(OBJDIR)/db.o \ $(OBJDIR)/delta.o \ $(OBJDIR)/deltacmd.o \ $(OBJDIR)/descendants.o \ $(OBJDIR)/diff.o \ $(OBJDIR)/diffcmd.o \ $(OBJDIR)/dispatch.o \ $(OBJDIR)/doc.o \ $(OBJDIR)/encode.o \ $(OBJDIR)/etag.o \ $(OBJDIR)/event.o \ $(OBJDIR)/export.o \ $(OBJDIR)/file.o \ $(OBJDIR)/finfo.o \ $(OBJDIR)/foci.o \ $(OBJDIR)/fshell.o \ $(OBJDIR)/fusefs.o \ $(OBJDIR)/glob.o \ $(OBJDIR)/graph.o \ $(OBJDIR)/gzip.o \ $(OBJDIR)/hname.o \ $(OBJDIR)/http.o \ | > > > > > | 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 | $(OBJDIR)/update_.c \ $(OBJDIR)/url_.c \ $(OBJDIR)/user_.c \ $(OBJDIR)/utf8_.c \ $(OBJDIR)/util_.c \ $(OBJDIR)/verify_.c \ $(OBJDIR)/vfile_.c \ $(OBJDIR)/webmail_.c \ $(OBJDIR)/wiki_.c \ $(OBJDIR)/wikiformat_.c \ $(OBJDIR)/winfile_.c \ $(OBJDIR)/winhttp_.c \ $(OBJDIR)/wysiwyg_.c \ $(OBJDIR)/xfer_.c \ $(OBJDIR)/xfersetup_.c \ $(OBJDIR)/zip_.c OBJ = \ $(OBJDIR)/add.o \ $(OBJDIR)/allrepo.o \ $(OBJDIR)/attach.o \ $(OBJDIR)/backoffice.o \ $(OBJDIR)/bag.o \ $(OBJDIR)/bisect.o \ $(OBJDIR)/blob.o \ $(OBJDIR)/branch.o \ $(OBJDIR)/browse.o \ $(OBJDIR)/builtin.o \ $(OBJDIR)/bundle.o \ $(OBJDIR)/cache.o \ $(OBJDIR)/capabilities.o \ $(OBJDIR)/captcha.o \ $(OBJDIR)/cgi.o \ $(OBJDIR)/checkin.o \ $(OBJDIR)/checkout.o \ $(OBJDIR)/clearsign.o \ $(OBJDIR)/clone.o \ $(OBJDIR)/comformat.o \ $(OBJDIR)/configure.o \ $(OBJDIR)/content.o \ $(OBJDIR)/cookies.o \ $(OBJDIR)/db.o \ $(OBJDIR)/delta.o \ $(OBJDIR)/deltacmd.o \ $(OBJDIR)/descendants.o \ $(OBJDIR)/diff.o \ $(OBJDIR)/diffcmd.o \ $(OBJDIR)/dispatch.o \ $(OBJDIR)/doc.o \ $(OBJDIR)/email.o \ $(OBJDIR)/encode.o \ $(OBJDIR)/etag.o \ $(OBJDIR)/event.o \ $(OBJDIR)/export.o \ $(OBJDIR)/file.o \ $(OBJDIR)/finfo.o \ $(OBJDIR)/foci.o \ $(OBJDIR)/forum.o \ $(OBJDIR)/fshell.o \ $(OBJDIR)/fusefs.o \ $(OBJDIR)/glob.o \ $(OBJDIR)/graph.o \ $(OBJDIR)/gzip.o \ $(OBJDIR)/hname.o \ $(OBJDIR)/http.o \ |
︙ | ︙ | |||
434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 | $(OBJDIR)/regexp.o \ $(OBJDIR)/report.o \ $(OBJDIR)/rss.o \ $(OBJDIR)/schema.o \ $(OBJDIR)/search.o \ $(OBJDIR)/security_audit.o \ $(OBJDIR)/setup.o \ $(OBJDIR)/sha1.o \ $(OBJDIR)/sha1hard.o \ $(OBJDIR)/sha3.o \ $(OBJDIR)/shun.o \ $(OBJDIR)/sitemap.o \ $(OBJDIR)/skins.o \ $(OBJDIR)/sqlcmd.o \ $(OBJDIR)/stash.o \ $(OBJDIR)/stat.o \ $(OBJDIR)/statrep.o \ $(OBJDIR)/style.o \ $(OBJDIR)/sync.o \ $(OBJDIR)/tag.o \ | > > | 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 | $(OBJDIR)/regexp.o \ $(OBJDIR)/report.o \ $(OBJDIR)/rss.o \ $(OBJDIR)/schema.o \ $(OBJDIR)/search.o \ $(OBJDIR)/security_audit.o \ $(OBJDIR)/setup.o \ $(OBJDIR)/setupuser.o \ $(OBJDIR)/sha1.o \ $(OBJDIR)/sha1hard.o \ $(OBJDIR)/sha3.o \ $(OBJDIR)/shun.o \ $(OBJDIR)/sitemap.o \ $(OBJDIR)/skins.o \ $(OBJDIR)/smtp.o \ $(OBJDIR)/sqlcmd.o \ $(OBJDIR)/stash.o \ $(OBJDIR)/stat.o \ $(OBJDIR)/statrep.o \ $(OBJDIR)/style.o \ $(OBJDIR)/sync.o \ $(OBJDIR)/tag.o \ |
︙ | ︙ | |||
462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 | $(OBJDIR)/update.o \ $(OBJDIR)/url.o \ $(OBJDIR)/user.o \ $(OBJDIR)/utf8.o \ $(OBJDIR)/util.o \ $(OBJDIR)/verify.o \ $(OBJDIR)/vfile.o \ $(OBJDIR)/wiki.o \ $(OBJDIR)/wikiformat.o \ $(OBJDIR)/winfile.o \ $(OBJDIR)/winhttp.o \ $(OBJDIR)/wysiwyg.o \ $(OBJDIR)/xfer.o \ $(OBJDIR)/xfersetup.o \ $(OBJDIR)/zip.o APPNAME = fossil$(E) all: $(OBJDIR) $(APPNAME) install: $(APPNAME) mkdir -p $(INSTALLDIR) | > | | 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 | $(OBJDIR)/update.o \ $(OBJDIR)/url.o \ $(OBJDIR)/user.o \ $(OBJDIR)/utf8.o \ $(OBJDIR)/util.o \ $(OBJDIR)/verify.o \ $(OBJDIR)/vfile.o \ $(OBJDIR)/webmail.o \ $(OBJDIR)/wiki.o \ $(OBJDIR)/wikiformat.o \ $(OBJDIR)/winfile.o \ $(OBJDIR)/winhttp.o \ $(OBJDIR)/wysiwyg.o \ $(OBJDIR)/xfer.o \ $(OBJDIR)/xfersetup.o \ $(OBJDIR)/zip.o APPNAME = fossil$(E) all: $(OBJDIR) $(APPNAME) install: $(APPNAME) mkdir -p $(INSTALLDIR) cp $(APPNAME) $(INSTALLDIR) codecheck: $(TRANS_SRC) $(OBJDIR)/codecheck1 $(OBJDIR)/codecheck1 $(TRANS_SRC) $(OBJDIR): -mkdir $(OBJDIR) |
︙ | ︙ | |||
587 588 589 590 591 592 593 | -DSQLITE_HAVE_ZLIB \ -DSQLITE_INTROSPECTION_PRAGMAS \ -DSQLITE_ENABLE_DBPAGE_VTAB \ -Dmain=sqlite3_shell \ -DSQLITE_SHELL_IS_UTF8=1 \ -DSQLITE_OMIT_LOAD_EXTENSION=1 \ -DUSE_SYSTEM_SQLITE=$(USE_SYSTEM_SQLITE) \ | | > | 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 | -DSQLITE_HAVE_ZLIB \ -DSQLITE_INTROSPECTION_PRAGMAS \ -DSQLITE_ENABLE_DBPAGE_VTAB \ -Dmain=sqlite3_shell \ -DSQLITE_SHELL_IS_UTF8=1 \ -DSQLITE_OMIT_LOAD_EXTENSION=1 \ -DUSE_SYSTEM_SQLITE=$(USE_SYSTEM_SQLITE) \ -DSQLITE_SHELL_DBNAME_PROC=sqlcmd_get_dbname \ -DSQLITE_SHELL_INIT_PROC=sqlcmd_init_proc # Setup the options used to compile the included miniz library. MINIZ_OPTIONS = -DMINIZ_NO_STDIO \ -DMINIZ_NO_TIME \ -DMINIZ_NO_ARCHIVE_APIS # The USE_SYSTEM_SQLITE variable may be undefined, set to 0, or set |
︙ | ︙ | |||
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 | $(OBJDIR)/builtin_data.h: $(OBJDIR)/mkbuiltin $(EXTRA_FILES) $(OBJDIR)/mkbuiltin --prefix $(SRCDIR)/ $(EXTRA_FILES) >$@ $(OBJDIR)/headers: $(OBJDIR)/page_index.h $(OBJDIR)/builtin_data.h $(OBJDIR)/default_css.h $(OBJDIR)/makeheaders $(OBJDIR)/VERSION.h $(OBJDIR)/makeheaders $(OBJDIR)/add_.c:$(OBJDIR)/add.h \ $(OBJDIR)/allrepo_.c:$(OBJDIR)/allrepo.h \ $(OBJDIR)/attach_.c:$(OBJDIR)/attach.h \ $(OBJDIR)/bag_.c:$(OBJDIR)/bag.h \ $(OBJDIR)/bisect_.c:$(OBJDIR)/bisect.h \ $(OBJDIR)/blob_.c:$(OBJDIR)/blob.h \ $(OBJDIR)/branch_.c:$(OBJDIR)/branch.h \ $(OBJDIR)/browse_.c:$(OBJDIR)/browse.h \ $(OBJDIR)/builtin_.c:$(OBJDIR)/builtin.h \ $(OBJDIR)/bundle_.c:$(OBJDIR)/bundle.h \ $(OBJDIR)/cache_.c:$(OBJDIR)/cache.h \ $(OBJDIR)/captcha_.c:$(OBJDIR)/captcha.h \ $(OBJDIR)/cgi_.c:$(OBJDIR)/cgi.h \ $(OBJDIR)/checkin_.c:$(OBJDIR)/checkin.h \ $(OBJDIR)/checkout_.c:$(OBJDIR)/checkout.h \ $(OBJDIR)/clearsign_.c:$(OBJDIR)/clearsign.h \ $(OBJDIR)/clone_.c:$(OBJDIR)/clone.h \ $(OBJDIR)/comformat_.c:$(OBJDIR)/comformat.h \ $(OBJDIR)/configure_.c:$(OBJDIR)/configure.h \ $(OBJDIR)/content_.c:$(OBJDIR)/content.h \ $(OBJDIR)/cookies_.c:$(OBJDIR)/cookies.h \ $(OBJDIR)/db_.c:$(OBJDIR)/db.h \ $(OBJDIR)/delta_.c:$(OBJDIR)/delta.h \ $(OBJDIR)/deltacmd_.c:$(OBJDIR)/deltacmd.h \ $(OBJDIR)/descendants_.c:$(OBJDIR)/descendants.h \ $(OBJDIR)/diff_.c:$(OBJDIR)/diff.h \ $(OBJDIR)/diffcmd_.c:$(OBJDIR)/diffcmd.h \ $(OBJDIR)/dispatch_.c:$(OBJDIR)/dispatch.h \ $(OBJDIR)/doc_.c:$(OBJDIR)/doc.h \ $(OBJDIR)/encode_.c:$(OBJDIR)/encode.h \ $(OBJDIR)/etag_.c:$(OBJDIR)/etag.h \ $(OBJDIR)/event_.c:$(OBJDIR)/event.h \ $(OBJDIR)/export_.c:$(OBJDIR)/export.h \ $(OBJDIR)/file_.c:$(OBJDIR)/file.h \ $(OBJDIR)/finfo_.c:$(OBJDIR)/finfo.h \ $(OBJDIR)/foci_.c:$(OBJDIR)/foci.h \ $(OBJDIR)/fshell_.c:$(OBJDIR)/fshell.h \ $(OBJDIR)/fusefs_.c:$(OBJDIR)/fusefs.h \ $(OBJDIR)/glob_.c:$(OBJDIR)/glob.h \ $(OBJDIR)/graph_.c:$(OBJDIR)/graph.h \ $(OBJDIR)/gzip_.c:$(OBJDIR)/gzip.h \ $(OBJDIR)/hname_.c:$(OBJDIR)/hname.h \ $(OBJDIR)/http_.c:$(OBJDIR)/http.h \ | > > > > | 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 741 742 743 744 745 | $(OBJDIR)/builtin_data.h: $(OBJDIR)/mkbuiltin $(EXTRA_FILES) $(OBJDIR)/mkbuiltin --prefix $(SRCDIR)/ $(EXTRA_FILES) >$@ $(OBJDIR)/headers: $(OBJDIR)/page_index.h $(OBJDIR)/builtin_data.h $(OBJDIR)/default_css.h $(OBJDIR)/makeheaders $(OBJDIR)/VERSION.h $(OBJDIR)/makeheaders $(OBJDIR)/add_.c:$(OBJDIR)/add.h \ $(OBJDIR)/allrepo_.c:$(OBJDIR)/allrepo.h \ $(OBJDIR)/attach_.c:$(OBJDIR)/attach.h \ $(OBJDIR)/backoffice_.c:$(OBJDIR)/backoffice.h \ $(OBJDIR)/bag_.c:$(OBJDIR)/bag.h \ $(OBJDIR)/bisect_.c:$(OBJDIR)/bisect.h \ $(OBJDIR)/blob_.c:$(OBJDIR)/blob.h \ $(OBJDIR)/branch_.c:$(OBJDIR)/branch.h \ $(OBJDIR)/browse_.c:$(OBJDIR)/browse.h \ $(OBJDIR)/builtin_.c:$(OBJDIR)/builtin.h \ $(OBJDIR)/bundle_.c:$(OBJDIR)/bundle.h \ $(OBJDIR)/cache_.c:$(OBJDIR)/cache.h \ $(OBJDIR)/capabilities_.c:$(OBJDIR)/capabilities.h \ $(OBJDIR)/captcha_.c:$(OBJDIR)/captcha.h \ $(OBJDIR)/cgi_.c:$(OBJDIR)/cgi.h \ $(OBJDIR)/checkin_.c:$(OBJDIR)/checkin.h \ $(OBJDIR)/checkout_.c:$(OBJDIR)/checkout.h \ $(OBJDIR)/clearsign_.c:$(OBJDIR)/clearsign.h \ $(OBJDIR)/clone_.c:$(OBJDIR)/clone.h \ $(OBJDIR)/comformat_.c:$(OBJDIR)/comformat.h \ $(OBJDIR)/configure_.c:$(OBJDIR)/configure.h \ $(OBJDIR)/content_.c:$(OBJDIR)/content.h \ $(OBJDIR)/cookies_.c:$(OBJDIR)/cookies.h \ $(OBJDIR)/db_.c:$(OBJDIR)/db.h \ $(OBJDIR)/delta_.c:$(OBJDIR)/delta.h \ $(OBJDIR)/deltacmd_.c:$(OBJDIR)/deltacmd.h \ $(OBJDIR)/descendants_.c:$(OBJDIR)/descendants.h \ $(OBJDIR)/diff_.c:$(OBJDIR)/diff.h \ $(OBJDIR)/diffcmd_.c:$(OBJDIR)/diffcmd.h \ $(OBJDIR)/dispatch_.c:$(OBJDIR)/dispatch.h \ $(OBJDIR)/doc_.c:$(OBJDIR)/doc.h \ $(OBJDIR)/email_.c:$(OBJDIR)/email.h \ $(OBJDIR)/encode_.c:$(OBJDIR)/encode.h \ $(OBJDIR)/etag_.c:$(OBJDIR)/etag.h \ $(OBJDIR)/event_.c:$(OBJDIR)/event.h \ $(OBJDIR)/export_.c:$(OBJDIR)/export.h \ $(OBJDIR)/file_.c:$(OBJDIR)/file.h \ $(OBJDIR)/finfo_.c:$(OBJDIR)/finfo.h \ $(OBJDIR)/foci_.c:$(OBJDIR)/foci.h \ $(OBJDIR)/forum_.c:$(OBJDIR)/forum.h \ $(OBJDIR)/fshell_.c:$(OBJDIR)/fshell.h \ $(OBJDIR)/fusefs_.c:$(OBJDIR)/fusefs.h \ $(OBJDIR)/glob_.c:$(OBJDIR)/glob.h \ $(OBJDIR)/graph_.c:$(OBJDIR)/graph.h \ $(OBJDIR)/gzip_.c:$(OBJDIR)/gzip.h \ $(OBJDIR)/hname_.c:$(OBJDIR)/hname.h \ $(OBJDIR)/http_.c:$(OBJDIR)/http.h \ |
︙ | ︙ | |||
761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 | $(OBJDIR)/regexp_.c:$(OBJDIR)/regexp.h \ $(OBJDIR)/report_.c:$(OBJDIR)/report.h \ $(OBJDIR)/rss_.c:$(OBJDIR)/rss.h \ $(OBJDIR)/schema_.c:$(OBJDIR)/schema.h \ $(OBJDIR)/search_.c:$(OBJDIR)/search.h \ $(OBJDIR)/security_audit_.c:$(OBJDIR)/security_audit.h \ $(OBJDIR)/setup_.c:$(OBJDIR)/setup.h \ $(OBJDIR)/sha1_.c:$(OBJDIR)/sha1.h \ $(OBJDIR)/sha1hard_.c:$(OBJDIR)/sha1hard.h \ $(OBJDIR)/sha3_.c:$(OBJDIR)/sha3.h \ $(OBJDIR)/shun_.c:$(OBJDIR)/shun.h \ $(OBJDIR)/sitemap_.c:$(OBJDIR)/sitemap.h \ $(OBJDIR)/skins_.c:$(OBJDIR)/skins.h \ $(OBJDIR)/sqlcmd_.c:$(OBJDIR)/sqlcmd.h \ $(OBJDIR)/stash_.c:$(OBJDIR)/stash.h \ $(OBJDIR)/stat_.c:$(OBJDIR)/stat.h \ $(OBJDIR)/statrep_.c:$(OBJDIR)/statrep.h \ $(OBJDIR)/style_.c:$(OBJDIR)/style.h \ $(OBJDIR)/sync_.c:$(OBJDIR)/sync.h \ $(OBJDIR)/tag_.c:$(OBJDIR)/tag.h \ | > > | 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 | $(OBJDIR)/regexp_.c:$(OBJDIR)/regexp.h \ $(OBJDIR)/report_.c:$(OBJDIR)/report.h \ $(OBJDIR)/rss_.c:$(OBJDIR)/rss.h \ $(OBJDIR)/schema_.c:$(OBJDIR)/schema.h \ $(OBJDIR)/search_.c:$(OBJDIR)/search.h \ $(OBJDIR)/security_audit_.c:$(OBJDIR)/security_audit.h \ $(OBJDIR)/setup_.c:$(OBJDIR)/setup.h \ $(OBJDIR)/setupuser_.c:$(OBJDIR)/setupuser.h \ $(OBJDIR)/sha1_.c:$(OBJDIR)/sha1.h \ $(OBJDIR)/sha1hard_.c:$(OBJDIR)/sha1hard.h \ $(OBJDIR)/sha3_.c:$(OBJDIR)/sha3.h \ $(OBJDIR)/shun_.c:$(OBJDIR)/shun.h \ $(OBJDIR)/sitemap_.c:$(OBJDIR)/sitemap.h \ $(OBJDIR)/skins_.c:$(OBJDIR)/skins.h \ $(OBJDIR)/smtp_.c:$(OBJDIR)/smtp.h \ $(OBJDIR)/sqlcmd_.c:$(OBJDIR)/sqlcmd.h \ $(OBJDIR)/stash_.c:$(OBJDIR)/stash.h \ $(OBJDIR)/stat_.c:$(OBJDIR)/stat.h \ $(OBJDIR)/statrep_.c:$(OBJDIR)/statrep.h \ $(OBJDIR)/style_.c:$(OBJDIR)/style.h \ $(OBJDIR)/sync_.c:$(OBJDIR)/sync.h \ $(OBJDIR)/tag_.c:$(OBJDIR)/tag.h \ |
︙ | ︙ | |||
789 790 791 792 793 794 795 796 797 798 799 800 801 802 | $(OBJDIR)/update_.c:$(OBJDIR)/update.h \ $(OBJDIR)/url_.c:$(OBJDIR)/url.h \ $(OBJDIR)/user_.c:$(OBJDIR)/user.h \ $(OBJDIR)/utf8_.c:$(OBJDIR)/utf8.h \ $(OBJDIR)/util_.c:$(OBJDIR)/util.h \ $(OBJDIR)/verify_.c:$(OBJDIR)/verify.h \ $(OBJDIR)/vfile_.c:$(OBJDIR)/vfile.h \ $(OBJDIR)/wiki_.c:$(OBJDIR)/wiki.h \ $(OBJDIR)/wikiformat_.c:$(OBJDIR)/wikiformat.h \ $(OBJDIR)/winfile_.c:$(OBJDIR)/winfile.h \ $(OBJDIR)/winhttp_.c:$(OBJDIR)/winhttp.h \ $(OBJDIR)/wysiwyg_.c:$(OBJDIR)/wysiwyg.h \ $(OBJDIR)/xfer_.c:$(OBJDIR)/xfer.h \ $(OBJDIR)/xfersetup_.c:$(OBJDIR)/xfersetup.h \ | > | 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 | $(OBJDIR)/update_.c:$(OBJDIR)/update.h \ $(OBJDIR)/url_.c:$(OBJDIR)/url.h \ $(OBJDIR)/user_.c:$(OBJDIR)/user.h \ $(OBJDIR)/utf8_.c:$(OBJDIR)/utf8.h \ $(OBJDIR)/util_.c:$(OBJDIR)/util.h \ $(OBJDIR)/verify_.c:$(OBJDIR)/verify.h \ $(OBJDIR)/vfile_.c:$(OBJDIR)/vfile.h \ $(OBJDIR)/webmail_.c:$(OBJDIR)/webmail.h \ $(OBJDIR)/wiki_.c:$(OBJDIR)/wiki.h \ $(OBJDIR)/wikiformat_.c:$(OBJDIR)/wikiformat.h \ $(OBJDIR)/winfile_.c:$(OBJDIR)/winfile.h \ $(OBJDIR)/winhttp_.c:$(OBJDIR)/winhttp.h \ $(OBJDIR)/wysiwyg_.c:$(OBJDIR)/wysiwyg.h \ $(OBJDIR)/xfer_.c:$(OBJDIR)/xfer.h \ $(OBJDIR)/xfersetup_.c:$(OBJDIR)/xfersetup.h \ |
︙ | ︙ | |||
827 828 829 830 831 832 833 834 835 836 837 838 839 840 | $(OBJDIR)/attach_.c: $(SRCDIR)/attach.c $(OBJDIR)/translate $(OBJDIR)/translate $(SRCDIR)/attach.c >$@ $(OBJDIR)/attach.o: $(OBJDIR)/attach_.c $(OBJDIR)/attach.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/attach.o -c $(OBJDIR)/attach_.c $(OBJDIR)/attach.h: $(OBJDIR)/headers $(OBJDIR)/bag_.c: $(SRCDIR)/bag.c $(OBJDIR)/translate $(OBJDIR)/translate $(SRCDIR)/bag.c >$@ $(OBJDIR)/bag.o: $(OBJDIR)/bag_.c $(OBJDIR)/bag.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/bag.o -c $(OBJDIR)/bag_.c | > > > > > > > > | 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 | $(OBJDIR)/attach_.c: $(SRCDIR)/attach.c $(OBJDIR)/translate $(OBJDIR)/translate $(SRCDIR)/attach.c >$@ $(OBJDIR)/attach.o: $(OBJDIR)/attach_.c $(OBJDIR)/attach.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/attach.o -c $(OBJDIR)/attach_.c $(OBJDIR)/attach.h: $(OBJDIR)/headers $(OBJDIR)/backoffice_.c: $(SRCDIR)/backoffice.c $(OBJDIR)/translate $(OBJDIR)/translate $(SRCDIR)/backoffice.c >$@ $(OBJDIR)/backoffice.o: $(OBJDIR)/backoffice_.c $(OBJDIR)/backoffice.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/backoffice.o -c $(OBJDIR)/backoffice_.c $(OBJDIR)/backoffice.h: $(OBJDIR)/headers $(OBJDIR)/bag_.c: $(SRCDIR)/bag.c $(OBJDIR)/translate $(OBJDIR)/translate $(SRCDIR)/bag.c >$@ $(OBJDIR)/bag.o: $(OBJDIR)/bag_.c $(OBJDIR)/bag.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/bag.o -c $(OBJDIR)/bag_.c |
︙ | ︙ | |||
891 892 893 894 895 896 897 898 899 900 901 902 903 904 | $(OBJDIR)/cache_.c: $(SRCDIR)/cache.c $(OBJDIR)/translate $(OBJDIR)/translate $(SRCDIR)/cache.c >$@ $(OBJDIR)/cache.o: $(OBJDIR)/cache_.c $(OBJDIR)/cache.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/cache.o -c $(OBJDIR)/cache_.c $(OBJDIR)/cache.h: $(OBJDIR)/headers $(OBJDIR)/captcha_.c: $(SRCDIR)/captcha.c $(OBJDIR)/translate $(OBJDIR)/translate $(SRCDIR)/captcha.c >$@ $(OBJDIR)/captcha.o: $(OBJDIR)/captcha_.c $(OBJDIR)/captcha.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/captcha.o -c $(OBJDIR)/captcha_.c | > > > > > > > > | 929 930 931 932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 | $(OBJDIR)/cache_.c: $(SRCDIR)/cache.c $(OBJDIR)/translate $(OBJDIR)/translate $(SRCDIR)/cache.c >$@ $(OBJDIR)/cache.o: $(OBJDIR)/cache_.c $(OBJDIR)/cache.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/cache.o -c $(OBJDIR)/cache_.c $(OBJDIR)/cache.h: $(OBJDIR)/headers $(OBJDIR)/capabilities_.c: $(SRCDIR)/capabilities.c $(OBJDIR)/translate $(OBJDIR)/translate $(SRCDIR)/capabilities.c >$@ $(OBJDIR)/capabilities.o: $(OBJDIR)/capabilities_.c $(OBJDIR)/capabilities.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/capabilities.o -c $(OBJDIR)/capabilities_.c $(OBJDIR)/capabilities.h: $(OBJDIR)/headers $(OBJDIR)/captcha_.c: $(SRCDIR)/captcha.c $(OBJDIR)/translate $(OBJDIR)/translate $(SRCDIR)/captcha.c >$@ $(OBJDIR)/captcha.o: $(OBJDIR)/captcha_.c $(OBJDIR)/captcha.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/captcha.o -c $(OBJDIR)/captcha_.c |
︙ | ︙ | |||
1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 1046 1047 1048 | $(OBJDIR)/doc_.c: $(SRCDIR)/doc.c $(OBJDIR)/translate $(OBJDIR)/translate $(SRCDIR)/doc.c >$@ $(OBJDIR)/doc.o: $(OBJDIR)/doc_.c $(OBJDIR)/doc.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/doc.o -c $(OBJDIR)/doc_.c $(OBJDIR)/doc.h: $(OBJDIR)/headers $(OBJDIR)/encode_.c: $(SRCDIR)/encode.c $(OBJDIR)/translate $(OBJDIR)/translate $(SRCDIR)/encode.c >$@ $(OBJDIR)/encode.o: $(OBJDIR)/encode_.c $(OBJDIR)/encode.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/encode.o -c $(OBJDIR)/encode_.c | > > > > > > > > | 1081 1082 1083 1084 1085 1086 1087 1088 1089 1090 1091 1092 1093 1094 1095 1096 1097 1098 1099 1100 1101 1102 | $(OBJDIR)/doc_.c: $(SRCDIR)/doc.c $(OBJDIR)/translate $(OBJDIR)/translate $(SRCDIR)/doc.c >$@ $(OBJDIR)/doc.o: $(OBJDIR)/doc_.c $(OBJDIR)/doc.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/doc.o -c $(OBJDIR)/doc_.c $(OBJDIR)/doc.h: $(OBJDIR)/headers $(OBJDIR)/email_.c: $(SRCDIR)/email.c $(OBJDIR)/translate $(OBJDIR)/translate $(SRCDIR)/email.c >$@ $(OBJDIR)/email.o: $(OBJDIR)/email_.c $(OBJDIR)/email.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/email.o -c $(OBJDIR)/email_.c $(OBJDIR)/email.h: $(OBJDIR)/headers $(OBJDIR)/encode_.c: $(SRCDIR)/encode.c $(OBJDIR)/translate $(OBJDIR)/translate $(SRCDIR)/encode.c >$@ $(OBJDIR)/encode.o: $(OBJDIR)/encode_.c $(OBJDIR)/encode.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/encode.o -c $(OBJDIR)/encode_.c |
︙ | ︙ | |||
1091 1092 1093 1094 1095 1096 1097 1098 1099 1100 1101 1102 1103 1104 | $(OBJDIR)/foci_.c: $(SRCDIR)/foci.c $(OBJDIR)/translate $(OBJDIR)/translate $(SRCDIR)/foci.c >$@ $(OBJDIR)/foci.o: $(OBJDIR)/foci_.c $(OBJDIR)/foci.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/foci.o -c $(OBJDIR)/foci_.c $(OBJDIR)/foci.h: $(OBJDIR)/headers $(OBJDIR)/fshell_.c: $(SRCDIR)/fshell.c $(OBJDIR)/translate $(OBJDIR)/translate $(SRCDIR)/fshell.c >$@ $(OBJDIR)/fshell.o: $(OBJDIR)/fshell_.c $(OBJDIR)/fshell.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/fshell.o -c $(OBJDIR)/fshell_.c | > > > > > > > > | 1145 1146 1147 1148 1149 1150 1151 1152 1153 1154 1155 1156 1157 1158 1159 1160 1161 1162 1163 1164 1165 1166 | $(OBJDIR)/foci_.c: $(SRCDIR)/foci.c $(OBJDIR)/translate $(OBJDIR)/translate $(SRCDIR)/foci.c >$@ $(OBJDIR)/foci.o: $(OBJDIR)/foci_.c $(OBJDIR)/foci.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/foci.o -c $(OBJDIR)/foci_.c $(OBJDIR)/foci.h: $(OBJDIR)/headers $(OBJDIR)/forum_.c: $(SRCDIR)/forum.c $(OBJDIR)/translate $(OBJDIR)/translate $(SRCDIR)/forum.c >$@ $(OBJDIR)/forum.o: $(OBJDIR)/forum_.c $(OBJDIR)/forum.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/forum.o -c $(OBJDIR)/forum_.c $(OBJDIR)/forum.h: $(OBJDIR)/headers $(OBJDIR)/fshell_.c: $(SRCDIR)/fshell.c $(OBJDIR)/translate $(OBJDIR)/translate $(SRCDIR)/fshell.c >$@ $(OBJDIR)/fshell.o: $(OBJDIR)/fshell_.c $(OBJDIR)/fshell.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/fshell.o -c $(OBJDIR)/fshell_.c |
︙ | ︙ | |||
1539 1540 1541 1542 1543 1544 1545 1546 1547 1548 1549 1550 1551 1552 | $(OBJDIR)/setup_.c: $(SRCDIR)/setup.c $(OBJDIR)/translate $(OBJDIR)/translate $(SRCDIR)/setup.c >$@ $(OBJDIR)/setup.o: $(OBJDIR)/setup_.c $(OBJDIR)/setup.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/setup.o -c $(OBJDIR)/setup_.c $(OBJDIR)/setup.h: $(OBJDIR)/headers $(OBJDIR)/sha1_.c: $(SRCDIR)/sha1.c $(OBJDIR)/translate $(OBJDIR)/translate $(SRCDIR)/sha1.c >$@ $(OBJDIR)/sha1.o: $(OBJDIR)/sha1_.c $(OBJDIR)/sha1.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/sha1.o -c $(OBJDIR)/sha1_.c | > > > > > > > > | 1601 1602 1603 1604 1605 1606 1607 1608 1609 1610 1611 1612 1613 1614 1615 1616 1617 1618 1619 1620 1621 1622 | $(OBJDIR)/setup_.c: $(SRCDIR)/setup.c $(OBJDIR)/translate $(OBJDIR)/translate $(SRCDIR)/setup.c >$@ $(OBJDIR)/setup.o: $(OBJDIR)/setup_.c $(OBJDIR)/setup.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/setup.o -c $(OBJDIR)/setup_.c $(OBJDIR)/setup.h: $(OBJDIR)/headers $(OBJDIR)/setupuser_.c: $(SRCDIR)/setupuser.c $(OBJDIR)/translate $(OBJDIR)/translate $(SRCDIR)/setupuser.c >$@ $(OBJDIR)/setupuser.o: $(OBJDIR)/setupuser_.c $(OBJDIR)/setupuser.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/setupuser.o -c $(OBJDIR)/setupuser_.c $(OBJDIR)/setupuser.h: $(OBJDIR)/headers $(OBJDIR)/sha1_.c: $(SRCDIR)/sha1.c $(OBJDIR)/translate $(OBJDIR)/translate $(SRCDIR)/sha1.c >$@ $(OBJDIR)/sha1.o: $(OBJDIR)/sha1_.c $(OBJDIR)/sha1.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/sha1.o -c $(OBJDIR)/sha1_.c |
︙ | ︙ | |||
1587 1588 1589 1590 1591 1592 1593 1594 1595 1596 1597 1598 1599 1600 | $(OBJDIR)/skins_.c: $(SRCDIR)/skins.c $(OBJDIR)/translate $(OBJDIR)/translate $(SRCDIR)/skins.c >$@ $(OBJDIR)/skins.o: $(OBJDIR)/skins_.c $(OBJDIR)/skins.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/skins.o -c $(OBJDIR)/skins_.c $(OBJDIR)/skins.h: $(OBJDIR)/headers $(OBJDIR)/sqlcmd_.c: $(SRCDIR)/sqlcmd.c $(OBJDIR)/translate $(OBJDIR)/translate $(SRCDIR)/sqlcmd.c >$@ $(OBJDIR)/sqlcmd.o: $(OBJDIR)/sqlcmd_.c $(OBJDIR)/sqlcmd.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/sqlcmd.o -c $(OBJDIR)/sqlcmd_.c | > > > > > > > > | 1657 1658 1659 1660 1661 1662 1663 1664 1665 1666 1667 1668 1669 1670 1671 1672 1673 1674 1675 1676 1677 1678 | $(OBJDIR)/skins_.c: $(SRCDIR)/skins.c $(OBJDIR)/translate $(OBJDIR)/translate $(SRCDIR)/skins.c >$@ $(OBJDIR)/skins.o: $(OBJDIR)/skins_.c $(OBJDIR)/skins.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/skins.o -c $(OBJDIR)/skins_.c $(OBJDIR)/skins.h: $(OBJDIR)/headers $(OBJDIR)/smtp_.c: $(SRCDIR)/smtp.c $(OBJDIR)/translate $(OBJDIR)/translate $(SRCDIR)/smtp.c >$@ $(OBJDIR)/smtp.o: $(OBJDIR)/smtp_.c $(OBJDIR)/smtp.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/smtp.o -c $(OBJDIR)/smtp_.c $(OBJDIR)/smtp.h: $(OBJDIR)/headers $(OBJDIR)/sqlcmd_.c: $(SRCDIR)/sqlcmd.c $(OBJDIR)/translate $(OBJDIR)/translate $(SRCDIR)/sqlcmd.c >$@ $(OBJDIR)/sqlcmd.o: $(OBJDIR)/sqlcmd_.c $(OBJDIR)/sqlcmd.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/sqlcmd.o -c $(OBJDIR)/sqlcmd_.c |
︙ | ︙ | |||
1763 1764 1765 1766 1767 1768 1769 1770 1771 1772 1773 1774 1775 1776 | $(OBJDIR)/vfile_.c: $(SRCDIR)/vfile.c $(OBJDIR)/translate $(OBJDIR)/translate $(SRCDIR)/vfile.c >$@ $(OBJDIR)/vfile.o: $(OBJDIR)/vfile_.c $(OBJDIR)/vfile.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/vfile.o -c $(OBJDIR)/vfile_.c $(OBJDIR)/vfile.h: $(OBJDIR)/headers $(OBJDIR)/wiki_.c: $(SRCDIR)/wiki.c $(OBJDIR)/translate $(OBJDIR)/translate $(SRCDIR)/wiki.c >$@ $(OBJDIR)/wiki.o: $(OBJDIR)/wiki_.c $(OBJDIR)/wiki.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/wiki.o -c $(OBJDIR)/wiki_.c | > > > > > > > > | 1841 1842 1843 1844 1845 1846 1847 1848 1849 1850 1851 1852 1853 1854 1855 1856 1857 1858 1859 1860 1861 1862 | $(OBJDIR)/vfile_.c: $(SRCDIR)/vfile.c $(OBJDIR)/translate $(OBJDIR)/translate $(SRCDIR)/vfile.c >$@ $(OBJDIR)/vfile.o: $(OBJDIR)/vfile_.c $(OBJDIR)/vfile.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/vfile.o -c $(OBJDIR)/vfile_.c $(OBJDIR)/vfile.h: $(OBJDIR)/headers $(OBJDIR)/webmail_.c: $(SRCDIR)/webmail.c $(OBJDIR)/translate $(OBJDIR)/translate $(SRCDIR)/webmail.c >$@ $(OBJDIR)/webmail.o: $(OBJDIR)/webmail_.c $(OBJDIR)/webmail.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/webmail.o -c $(OBJDIR)/webmail_.c $(OBJDIR)/webmail.h: $(OBJDIR)/headers $(OBJDIR)/wiki_.c: $(SRCDIR)/wiki.c $(OBJDIR)/translate $(OBJDIR)/translate $(SRCDIR)/wiki.c >$@ $(OBJDIR)/wiki.o: $(OBJDIR)/wiki_.c $(OBJDIR)/wiki.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/wiki.o -c $(OBJDIR)/wiki_.c |
︙ | ︙ |
Changes to src/makeheaders.c.
︙ | ︙ | |||
1470 1471 1472 1473 1474 1475 1476 1477 1478 1479 1480 1481 1482 1483 | *pReset = ';'; return 0; } for(pEnd=pName->pNext; pEnd && pEnd->eType!=TT_Braces; pEnd=pEnd->pNext){ switch( pEnd->zText[0] ){ case '(': case '*': case '[': case '=': case ';': return 0; } } | > | 1470 1471 1472 1473 1474 1475 1476 1477 1478 1479 1480 1481 1482 1483 1484 | *pReset = ';'; return 0; } for(pEnd=pName->pNext; pEnd && pEnd->eType!=TT_Braces; pEnd=pEnd->pNext){ switch( pEnd->zText[0] ){ case '(': case ')': case '*': case '[': case '=': case ';': return 0; } } |
︙ | ︙ |
Changes to src/makemake.tcl.
︙ | ︙ | |||
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 | # Set the separate extra_files variable further down for how to add non-C # files, such as string and BLOB resources. # set src { add allrepo attach bag bisect blob branch browse builtin bundle cache captcha cgi checkin checkout clearsign clone comformat configure content cookies db delta deltacmd descendants diff diffcmd dispatch doc encode etag event export file finfo foci fshell fusefs glob graph gzip hname http | > > > > | 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 | # Set the separate extra_files variable further down for how to add non-C # files, such as string and BLOB resources. # set src { add allrepo attach backoffice bag bisect blob branch browse builtin bundle cache capabilities captcha cgi checkin checkout clearsign clone comformat configure content cookies db delta deltacmd descendants diff diffcmd dispatch doc email encode etag event export file finfo foci forum fshell fusefs glob graph gzip hname http |
︙ | ︙ | |||
114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 | regexp report rss schema search security_audit setup sha1 sha1hard sha3 shun sitemap skins sqlcmd stash stat statrep style sync tag | > > | 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 | regexp report rss schema search security_audit setup setupuser sha1 sha1hard sha3 shun sitemap skins smtp sqlcmd stash stat statrep style sync tag |
︙ | ︙ | |||
142 143 144 145 146 147 148 149 150 151 152 153 154 155 | update url user utf8 util verify vfile wiki wikiformat winfile winhttp wysiwyg xfer xfersetup | > | 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 | update url user utf8 util verify vfile webmail wiki wikiformat winfile winhttp wysiwyg xfer xfersetup |
︙ | ︙ | |||
204 205 206 207 208 209 210 | # Options used to compile the included SQLite shell. # set SHELL_OPTIONS [concat $SQLITE_OPTIONS { -Dmain=sqlite3_shell -DSQLITE_SHELL_IS_UTF8=1 -DSQLITE_OMIT_LOAD_EXTENSION=1 -DUSE_SYSTEM_SQLITE=$(USE_SYSTEM_SQLITE) | | > | 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 | # Options used to compile the included SQLite shell. # set SHELL_OPTIONS [concat $SQLITE_OPTIONS { -Dmain=sqlite3_shell -DSQLITE_SHELL_IS_UTF8=1 -DSQLITE_OMIT_LOAD_EXTENSION=1 -DUSE_SYSTEM_SQLITE=$(USE_SYSTEM_SQLITE) -DSQLITE_SHELL_DBNAME_PROC=sqlcmd_get_dbname -DSQLITE_SHELL_INIT_PROC=sqlcmd_init_proc }] # miniz (libz drop-in alternative) precompiler flags. # set MINIZ_OPTIONS { -DMINIZ_NO_STDIO -DMINIZ_NO_TIME |
︙ | ︙ | |||
273 274 275 276 277 278 279 | # This file is automatically generated. Instead of editing this # file, edit "makemake.tcl" then run "tclsh makemake.tcl" # to regenerate this file. # # This file is included by primary Makefile. # | | | | 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 | # This file is automatically generated. Instead of editing this # file, edit "makemake.tcl" then run "tclsh makemake.tcl" # to regenerate this file. # # This file is included by primary Makefile. # XBCC = $(BCC) $(BCCFLAGS) XTCC = $(TCC) -I. -I$(SRCDIR) -I$(OBJDIR) $(TCCFLAGS) } writeln -nonewline "SRC =" foreach s [lsort $src] { writeln -nonewline " \\\n \$(SRCDIR)/$s.c" } writeln "\n" |
︙ | ︙ | |||
308 309 310 311 312 313 314 | <<<SQLITE_OPTIONS>>> [join $SQLITE_OPTIONS " \\\n "] \ <<<SHELL_OPTIONS>>> [join $SHELL_OPTIONS " \\\n "] \ <<<MINIZ_OPTIONS>>> [join $MINIZ_OPTIONS " \\\n "]] { all: $(OBJDIR) $(APPNAME) install: $(APPNAME) mkdir -p $(INSTALLDIR) | | | 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 | <<<SQLITE_OPTIONS>>> [join $SQLITE_OPTIONS " \\\n "] \ <<<SHELL_OPTIONS>>> [join $SHELL_OPTIONS " \\\n "] \ <<<MINIZ_OPTIONS>>> [join $MINIZ_OPTIONS " \\\n "]] { all: $(OBJDIR) $(APPNAME) install: $(APPNAME) mkdir -p $(INSTALLDIR) cp $(APPNAME) $(INSTALLDIR) codecheck: $(TRANS_SRC) $(OBJDIR)/codecheck1 $(OBJDIR)/codecheck1 $(TRANS_SRC) $(OBJDIR): -mkdir $(OBJDIR) |
︙ | ︙ | |||
596 597 598 599 600 601 602 | #### Enable relative paths in external diff/gdiff # # FOSSIL_ENABLE_EXEC_REL_PATHS = 1 #### Enable legacy treatment of mv/rm (skip checkout files) # | | | 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 | #### Enable relative paths in external diff/gdiff # # FOSSIL_ENABLE_EXEC_REL_PATHS = 1 #### Enable legacy treatment of mv/rm (skip checkout files) # FOSSIL_ENABLE_LEGACY_MV_RM = 1 #### Enable TH1 scripts in embedded documentation files # # FOSSIL_ENABLE_TH1_DOCS = 1 #### Enable hooks for commands and web pages via TH1 # |
︙ | ︙ | |||
699 700 701 702 703 704 705 | endif #### The directories where the OpenSSL include and library files are located. # The recommended usage here is to use the Sysinternals junction tool # to create a hard link between an "openssl-1.x" sub-directory of the # Fossil source code directory and the target OpenSSL source directory. # | | | 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 | endif #### The directories where the OpenSSL include and library files are located. # The recommended usage here is to use the Sysinternals junction tool # to create a hard link between an "openssl-1.x" sub-directory of the # Fossil source code directory and the target OpenSSL source directory. # OPENSSLDIR = $(SRCDIR)/../compat/openssl-1.1.1-pre9 OPENSSLINCDIR = $(OPENSSLDIR)/include OPENSSLLIBDIR = $(OPENSSLDIR) #### Either the directory where the Tcl library is installed or the Tcl # source code directory resides (depending on the value of the macro # FOSSIL_TCL_SOURCE). If this points to the Tcl install directory, # this directory must have "include" and "lib" sub-directories. If |
︙ | ︙ | |||
932 933 934 935 936 937 938 939 940 941 942 943 944 945 | LIB += -lkernel32 -lws2_32 else LIB += -lnetapi32 -lkernel32 -luser32 -ladvapi32 -lws2_32 endif else LIB += -lkernel32 -lws2_32 endif #### Tcl shell for use in running the fossil test suite. This is only # used for testing. # TCLSH = tclsh #### Nullsoft installer MakeNSIS location | > > > > | 940 941 942 943 944 945 946 947 948 949 950 951 952 953 954 955 956 957 | LIB += -lkernel32 -lws2_32 else LIB += -lnetapi32 -lkernel32 -luser32 -ladvapi32 -lws2_32 endif else LIB += -lkernel32 -lws2_32 endif #### Library required for DNS lookups. # LIB += -ldnsapi #### Tcl shell for use in running the fossil test suite. This is only # used for testing. # TCLSH = tclsh #### Nullsoft installer MakeNSIS location |
︙ | ︙ | |||
1038 1039 1040 1041 1042 1043 1044 | $(CP) $(SRCDIR)/../win/fossil.exe.manifest $(OBJDIR) endif $(RCC) $(OBJDIR)/fossil.rc -o $(OBJDIR)/fossil.o install: $(OBJDIR) $(APPNAME) ifdef USE_WINDOWS $(MKDIR) $(subst /,\,$(INSTALLDIR)) | | | | 1050 1051 1052 1053 1054 1055 1056 1057 1058 1059 1060 1061 1062 1063 1064 1065 1066 1067 | $(CP) $(SRCDIR)/../win/fossil.exe.manifest $(OBJDIR) endif $(RCC) $(OBJDIR)/fossil.rc -o $(OBJDIR)/fossil.o install: $(OBJDIR) $(APPNAME) ifdef USE_WINDOWS $(MKDIR) $(subst /,\,$(INSTALLDIR)) $(CP) $(subst /,\,$(APPNAME)) $(subst /,\,$(INSTALLDIR)) else $(MKDIR) $(INSTALLDIR) $(CP) $(APPNAME) $(INSTALLDIR) endif $(OBJDIR): ifdef USE_WINDOWS $(MKDIR) $(subst /,\,$(OBJDIR)) else $(MKDIR) $(OBJDIR) |
︙ | ︙ | |||
1306 1307 1308 1309 1310 1311 1312 | #SSL = -DFOSSIL_ENABLE_SSL=1 SSL = CFLAGS = -o BCC = $(DMDIR)\bin\dmc $(CFLAGS) TCC = $(DMDIR)\bin\dmc $(CFLAGS) $(DMCDEF) $(SSL) $(INCL) | | | 1318 1319 1320 1321 1322 1323 1324 1325 1326 1327 1328 1329 1330 1331 1332 | #SSL = -DFOSSIL_ENABLE_SSL=1 SSL = CFLAGS = -o BCC = $(DMDIR)\bin\dmc $(CFLAGS) TCC = $(DMDIR)\bin\dmc $(CFLAGS) $(DMCDEF) $(SSL) $(INCL) LIBS = $(DMDIR)\extra\lib\ zlib wsock32 advapi32 dnsapi } writeln "SQLITE_OPTIONS = [join $SQLITE_OPTIONS { }]\n" writeln "SHELL_OPTIONS = [join $SHELL_WIN32_OPTIONS { }]\n" writeln -nonewline "SRC =" foreach s [lsort $src] { writeln -nonewline " ${s}_.c" } |
︙ | ︙ | |||
1512 1513 1514 1515 1516 1517 1518 | # Enable the JSON API? !ifndef FOSSIL_ENABLE_JSON FOSSIL_ENABLE_JSON = 0 !endif # Enable legacy treatment of the mv/rm commands? !ifndef FOSSIL_ENABLE_LEGACY_MV_RM | | | 1524 1525 1526 1527 1528 1529 1530 1531 1532 1533 1534 1535 1536 1537 1538 | # Enable the JSON API? !ifndef FOSSIL_ENABLE_JSON FOSSIL_ENABLE_JSON = 0 !endif # Enable legacy treatment of the mv/rm commands? !ifndef FOSSIL_ENABLE_LEGACY_MV_RM FOSSIL_ENABLE_LEGACY_MV_RM = 1 !endif # Enable use of miniz instead of zlib? !ifndef FOSSIL_ENABLE_MINIZ FOSSIL_ENABLE_MINIZ = 0 !endif |
︙ | ︙ | |||
1551 1552 1553 1554 1555 1556 1557 | # Enable support for the SQLite Encryption Extension? !ifndef USE_SEE USE_SEE = 0 !endif !if $(FOSSIL_ENABLE_SSL)!=0 | | | 1563 1564 1565 1566 1567 1568 1569 1570 1571 1572 1573 1574 1575 1576 1577 | # Enable support for the SQLite Encryption Extension? !ifndef USE_SEE USE_SEE = 0 !endif !if $(FOSSIL_ENABLE_SSL)!=0 SSLDIR = $(B)\compat\openssl-1.1.1-pre9 SSLINCDIR = $(SSLDIR)\inc32 !if $(FOSSIL_DYNAMIC_BUILD)!=0 SSLLIBDIR = $(SSLDIR)\out32dll !else SSLLIBDIR = $(SSLDIR)\out32 !endif SSLLFLAGS = /nologo /opt:ref /debug |
︙ | ︙ | |||
1698 1699 1700 1701 1702 1703 1704 | CFLAGS = $(CFLAGS) $(CRTFLAGS) /O2 !endif BCC = $(CC) $(CFLAGS) TCC = $(CC) /c $(CFLAGS) $(MSCDEF) $(INCL) RCC = $(RC) /D_WIN32 /D_MSC_VER $(MSCDEF) $(INCL) MTC = mt | | | 1710 1711 1712 1713 1714 1715 1716 1717 1718 1719 1720 1721 1722 1723 1724 | CFLAGS = $(CFLAGS) $(CRTFLAGS) /O2 !endif BCC = $(CC) $(CFLAGS) TCC = $(CC) /c $(CFLAGS) $(MSCDEF) $(INCL) RCC = $(RC) /D_WIN32 /D_MSC_VER $(MSCDEF) $(INCL) MTC = mt LIBS = ws2_32.lib advapi32.lib dnsapi.lib LIBDIR = !if $(FOSSIL_DYNAMIC_BUILD)!=0 TCC = $(TCC) /DFOSSIL_DYNAMIC_BUILD=1 RCC = $(RCC) /DFOSSIL_DYNAMIC_BUILD=1 !endif |
︙ | ︙ | |||
1793 1794 1795 1796 1797 1798 1799 1800 1801 1802 1803 1804 1805 1806 | writeln -nonewline "EXTRA_FILES = " set i 0 foreach s [lsort $extra_files] { if {$i > 0} { writeln " \\" writeln -nonewline " " } writeln -nonewline "\$(SRCDIR)\\${s}"; incr i } writeln "\n" set AdditionalObj [list shell sqlite3 th th_lang th_tcl cson_amalgamation] writeln -nonewline "OBJ = " set i 0 foreach s [lsort [concat $src $AdditionalObj]] { | > | 1805 1806 1807 1808 1809 1810 1811 1812 1813 1814 1815 1816 1817 1818 1819 | writeln -nonewline "EXTRA_FILES = " set i 0 foreach s [lsort $extra_files] { if {$i > 0} { writeln " \\" writeln -nonewline " " } set s [regsub -all / $s \\] writeln -nonewline "\$(SRCDIR)\\${s}"; incr i } writeln "\n" set AdditionalObj [list shell sqlite3 th th_lang th_tcl cson_amalgamation] writeln -nonewline "OBJ = " set i 0 foreach s [lsort [concat $src $AdditionalObj]] { |
︙ | ︙ | |||
2091 2092 2093 2094 2095 2096 2097 | B=.. SRCDIR=$(B)/src/ WINDIR=$(B)/win/ ZLIBSRCDIR=../../zlib/ # define linker command and options LINK=$(PellesCDir)/bin/polink.exe | | | 2104 2105 2106 2107 2108 2109 2110 2111 2112 2113 2114 2115 2116 2117 2118 | B=.. SRCDIR=$(B)/src/ WINDIR=$(B)/win/ ZLIBSRCDIR=../../zlib/ # define linker command and options LINK=$(PellesCDir)/bin/polink.exe LINKFLAGS=-subsystem:console -machine:$(TARGETMACHINE_LN) /LIBPATH:$(PellesCDir)\lib\win$(TARGETEXTEND) /LIBPATH:$(PellesCDir)\lib kernel32.lib advapi32.lib delayimp$(TARGETEXTEND).lib Wsock32.lib dnsapi.lib Crtmt$(TARGETEXTEND).lib # define standard C-compiler and flags, used to compile # the fossil binary. Some special definitions follow for # special files follow CC=$(PellesCDir)\bin\pocc.exe DEFINES=-D_pgmptr=g.argv[0] CCFLAGS=-T$(TARGETMACHINE_CC)-coff -Ot -W2 -Gd -Go -Ze -MT $(DEFINES) |
︙ | ︙ |
Changes to src/manifest.c.
︙ | ︙ | |||
32 33 34 35 36 37 38 39 40 41 42 43 44 45 | #define CFTYPE_MANIFEST 1 #define CFTYPE_CLUSTER 2 #define CFTYPE_CONTROL 3 #define CFTYPE_WIKI 4 #define CFTYPE_TICKET 5 #define CFTYPE_ATTACHMENT 6 #define CFTYPE_EVENT 7 /* ** File permissions used by Fossil internally. */ #define PERM_REG 0 /* regular file */ #define PERM_EXE 1 /* executable */ #define PERM_LNK 2 /* symlink */ | > | 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 | #define CFTYPE_MANIFEST 1 #define CFTYPE_CLUSTER 2 #define CFTYPE_CONTROL 3 #define CFTYPE_WIKI 4 #define CFTYPE_TICKET 5 #define CFTYPE_ATTACHMENT 6 #define CFTYPE_EVENT 7 #define CFTYPE_FORUM 8 /* ** File permissions used by Fossil internally. */ #define PERM_REG 0 /* regular file */ #define PERM_EXE 1 /* executable */ #define PERM_LNK 2 /* symlink */ |
︙ | ︙ | |||
74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 | char *zComment; /* Decoded comment. The C card. */ double rDate; /* Date and time from D card. 0.0 if no D card. */ char *zUser; /* Name of the user from the U card. */ char *zRepoCksum; /* MD5 checksum of the baseline content. R card. */ char *zWiki; /* Text of the wiki page. W card. */ char *zWikiTitle; /* Name of the wiki page. L card. */ char *zMimetype; /* Mime type of wiki or comment text. N card. */ double rEventDate; /* Date of an event. E card. */ char *zEventId; /* Artifact hash for an event. E card. */ char *zTicketUuid; /* UUID for a ticket. K card. */ char *zAttachName; /* Filename of an attachment. A card. */ char *zAttachSrc; /* Artifact hash for document being attached. A card. */ char *zAttachTarget; /* Ticket or wiki that attachment applies to. A card */ int nFile; /* Number of F cards */ int nFileAlloc; /* Slots allocated in aFile[] */ int iFile; /* Index of current file in iterator */ ManifestFile *aFile; /* One entry for each F-card */ int nParent; /* Number of parents. */ int nParentAlloc; /* Slots allocated in azParent[] */ char **azParent; /* Hashes of parents. One for each P card argument */ | > > > | 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 | char *zComment; /* Decoded comment. The C card. */ double rDate; /* Date and time from D card. 0.0 if no D card. */ char *zUser; /* Name of the user from the U card. */ char *zRepoCksum; /* MD5 checksum of the baseline content. R card. */ char *zWiki; /* Text of the wiki page. W card. */ char *zWikiTitle; /* Name of the wiki page. L card. */ char *zMimetype; /* Mime type of wiki or comment text. N card. */ char *zThreadTitle; /* The forum thread title. H card */ double rEventDate; /* Date of an event. E card. */ char *zEventId; /* Artifact hash for an event. E card. */ char *zTicketUuid; /* UUID for a ticket. K card. */ char *zAttachName; /* Filename of an attachment. A card. */ char *zAttachSrc; /* Artifact hash for document being attached. A card. */ char *zAttachTarget; /* Ticket or wiki that attachment applies to. A card */ char *zThreadRoot; /* Thread root artifact. G card */ char *zInReplyTo; /* Forum in-reply-to artifact. I card */ int nFile; /* Number of F cards */ int nFileAlloc; /* Slots allocated in aFile[] */ int iFile; /* Index of current file in iterator */ ManifestFile *aFile; /* One entry for each F-card */ int nParent; /* Number of parents. */ int nParentAlloc; /* Slots allocated in azParent[] */ char **azParent; /* Hashes of parents. One for each P card argument */ |
︙ | ︙ | |||
110 111 112 113 114 115 116 117 118 119 120 121 122 123 | int nFieldAlloc; /* Slots allocated in aField[] */ struct { char *zName; /* Key or field name */ char *zValue; /* Value of the field */ } *aField; /* One for each J card */ }; #endif /* ** A cache of parsed manifests. This reduces the number of ** calls to manifest_parse() when doing a rebuild. */ #define MX_MANIFEST_CACHE 6 static struct { | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 | int nFieldAlloc; /* Slots allocated in aField[] */ struct { char *zName; /* Key or field name */ char *zValue; /* Value of the field */ } *aField; /* One for each J card */ }; #endif /* ** Allowed and required card types in each style of artifact */ static struct { const char *zAllowed; /* Allowed cards. Human-readable */ const char *zRequired; /* Required cards. Human-readable */ } manifestCardTypes[] = { /* Allowed Required */ /* CFTYPE_MANIFEST 1 */ { "BCDFNPQRTUZ", "DZ" }, /* Wants to be "CDUZ" ----^^^^ ** but we must limit for historical compatibility */ /* CFTYPE_CLUSTER 2 */ { "MZ", "MZ" }, /* CFTYPE_CONTROL 3 */ { "DTUZ", "DTUZ" }, /* CFTYPE_WIKI 4 */ { "DLNPUWZ", "DLUWZ" }, /* CFTYPE_TICKET 5 */ { "DJKUZ", "DJKUZ" }, /* CFTYPE_ATTACHMENT 6 */ { "ACDNUZ", "ADZ" }, /* CFTYPE_EVENT 7 */ { "CDENPTUWZ", "DEWZ" }, /* CFTYPE_FORUM 8 */ { "DGHINPUWZ", "DUWZ" }, }; /* ** Names of manifest types */ static const char *azNameOfMType[] = { "manifest", "cluster", "tag", "wiki", "ticket", "attachment", "technote", "forum post" }; /* ** A cache of parsed manifests. This reduces the number of ** calls to manifest_parse() when doing a rebuild. */ #define MX_MANIFEST_CACHE 6 static struct { |
︙ | ︙ | |||
145 146 147 148 149 150 151 152 153 154 155 156 157 158 | fossil_free(p->aField); fossil_free(p->aCherrypick); if( p->pBaseline ) manifest_destroy(p->pBaseline); memset(p, 0, sizeof(*p)); fossil_free(p); } } /* ** Add an element to the manifest cache using LRU replacement. */ void manifest_cache_insert(Manifest *p){ while( p ){ int i; | > > > > > > > > > > > > > > > > > > > > > > > > > | 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 | fossil_free(p->aField); fossil_free(p->aCherrypick); if( p->pBaseline ) manifest_destroy(p->pBaseline); memset(p, 0, sizeof(*p)); fossil_free(p); } } /* ** Given a string of upper-case letters, compute a mask of the letters ** present. For example, "ABC" computes 0x0007. "DE" gives 0x0018". */ static unsigned int manifest_card_mask(const char *z){ unsigned int m = 0; char c; while( (c = *(z++))>='A' && c<='Z' ){ m |= 1 << (c - 'A'); } return m; } /* ** Given an integer mask representing letters A-Z, return the ** letter which is the first bit set in the mask. Example: ** 0x03520 gives 'F' since the F-bit is the lowest. */ static char maskToType(unsigned int x){ char c = 'A'; if( x==0 ) return '?'; while( (x&1)==0 ){ x >>= 1; c++; } return c; } /* ** Add an element to the manifest cache using LRU replacement. */ void manifest_cache_insert(Manifest *p){ while( p ){ int i; |
︙ | ︙ | |||
350 351 352 353 354 355 356 | ** Each card is divided into tokens by a single space character. ** The first token is a single upper-case letter which is the card type. ** The card type determines the other parameters to the card. ** Cards must occur in lexicographical order. */ Manifest *manifest_parse(Blob *pContent, int rid, Blob *pErr){ Manifest *p; | < | > > > > > | 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 | ** Each card is divided into tokens by a single space character. ** The first token is a single upper-case letter which is the card type. ** The card type determines the other parameters to the card. ** Cards must occur in lexicographical order. */ Manifest *manifest_parse(Blob *pContent, int rid, Blob *pErr){ Manifest *p; int i, lineNo=0; ManifestText x; char cPrevType = 0; char cType; char *z; int n; char *zUuid; int sz = 0; int isRepeat; int nSelfTag = 0; /* Number of T cards referring to this manifest */ int nSimpleTag = 0; /* Number of T cards with "+" prefix */ static Bag seen; const char *zErr = 0; unsigned int m; unsigned int seenCard = 0; /* Which card types have been seen */ char zErrBuf[100]; /* Write error messages here */ if( rid==0 ){ isRepeat = 1; }else if( bag_find(&seen, rid) ){ isRepeat = 1; }else{ isRepeat = 0; |
︙ | ︙ | |||
420 421 422 423 424 425 426 427 428 429 430 431 432 433 | /* Begin parsing, card by card. */ x.z = z; x.zEnd = &z[n]; x.atEol = 1; while( (cType = next_card(&x))!=0 && cType>=cPrevType ){ lineNo++; switch( cType ){ /* ** A <filename> <target> ?<source>? ** ** Identifies an attachment to either a wiki page or a ticket. ** <source> is the artifact that is the attachment. <source> ** is omitted to delete an attachment. <target> is the name of | > > | 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 | /* Begin parsing, card by card. */ x.z = z; x.zEnd = &z[n]; x.atEol = 1; while( (cType = next_card(&x))!=0 && cType>=cPrevType ){ lineNo++; if( cType<'A' || cType>'Z' ) SYNTAX("bad card type"); seenCard |= 1 << (cType-'A'); switch( cType ){ /* ** A <filename> <target> ?<source>? ** ** Identifies an attachment to either a wiki page or a ticket. ** <source> is the artifact that is the attachment. <source> ** is omitted to delete an attachment. <target> is the name of |
︙ | ︙ | |||
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 | } if( zSrc && !hname_validate(zSrc,nSrc) ){ SYNTAX("invalid source on A-card"); } p->zAttachName = (char*)file_tail(zName); p->zAttachSrc = zSrc; p->zAttachTarget = zTarget; break; } /* ** B <uuid> ** ** A B-line gives the artifact hash for the baseline of a delta-manifest. */ case 'B': { if( p->zBaseline ) SYNTAX("more than one B-card"); p->zBaseline = next_token(&x, &sz); if( p->zBaseline==0 ) SYNTAX("missing hash on B-card"); if( !hname_validate(p->zBaseline,sz) ){ SYNTAX("invalid hash on B-card"); } break; } /* ** C <comment> ** | > > | 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 | } if( zSrc && !hname_validate(zSrc,nSrc) ){ SYNTAX("invalid source on A-card"); } p->zAttachName = (char*)file_tail(zName); p->zAttachSrc = zSrc; p->zAttachTarget = zTarget; p->type = CFTYPE_ATTACHMENT; break; } /* ** B <uuid> ** ** A B-line gives the artifact hash for the baseline of a delta-manifest. */ case 'B': { if( p->zBaseline ) SYNTAX("more than one B-card"); p->zBaseline = next_token(&x, &sz); if( p->zBaseline==0 ) SYNTAX("missing hash on B-card"); if( !hname_validate(p->zBaseline,sz) ){ SYNTAX("invalid hash on B-card"); } p->type = CFTYPE_MANIFEST; break; } /* ** C <comment> ** |
︙ | ︙ | |||
518 519 520 521 522 523 524 525 526 527 528 529 530 531 | if( p->rEventDate>0.0 ) SYNTAX("more than one E-card"); p->rEventDate = db_double(0.0,"SELECT julianday(%Q)", next_token(&x,0)); if( p->rEventDate<=0.0 ) SYNTAX("malformed date on E-card"); p->zEventId = next_token(&x, &sz); if( !hname_validate(p->zEventId, sz) ){ SYNTAX("malformed hash on E-card"); } break; } /* ** F <filename> ?<uuid>? ?<permissions>? ?<old-name>? ** ** Identifies a file in a manifest. Multiple F lines are | > | 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 | if( p->rEventDate>0.0 ) SYNTAX("more than one E-card"); p->rEventDate = db_double(0.0,"SELECT julianday(%Q)", next_token(&x,0)); if( p->rEventDate<=0.0 ) SYNTAX("malformed date on E-card"); p->zEventId = next_token(&x, &sz); if( !hname_validate(p->zEventId, sz) ){ SYNTAX("malformed hash on E-card"); } p->type = CFTYPE_EVENT; break; } /* ** F <filename> ?<uuid>? ?<permissions>? ?<old-name>? ** ** Identifies a file in a manifest. Multiple F lines are |
︙ | ︙ | |||
563 564 565 566 567 568 569 570 571 572 573 574 575 576 | p->aFile[i].zName = zName; p->aFile[i].zUuid = zUuid; p->aFile[i].zPerm = zPerm; p->aFile[i].zPrior = zPriorName; if( i>0 && fossil_strcmp(p->aFile[i-1].zName, zName)>=0 ){ SYNTAX("incorrect F-card sort order"); } break; } /* ** J <name> ?<value>? ** ** Specifies a name value pair for ticket. If the first character | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 | p->aFile[i].zName = zName; p->aFile[i].zUuid = zUuid; p->aFile[i].zPerm = zPerm; p->aFile[i].zPrior = zPriorName; if( i>0 && fossil_strcmp(p->aFile[i-1].zName, zName)>=0 ){ SYNTAX("incorrect F-card sort order"); } p->type = CFTYPE_MANIFEST; break; } /* ** G <hash> ** ** A G-card identifies the initial root forum post for the thread ** of which this post is a part. Forum posts only. */ case 'G': { if( p->zThreadRoot!=0 ) SYNTAX("more than one G-card"); p->zThreadRoot = next_token(&x, &sz); if( p->zThreadRoot==0 ) SYNTAX("missing hash on G-card"); if( !hname_validate(p->zThreadRoot,sz) ){ SYNTAX("Invalid hash on G-card"); } p->type = CFTYPE_FORUM; break; } /* ** H <threadtitle> ** ** The title for a forum thread. */ case 'H': { if( p->zThreadTitle!=0 ) SYNTAX("more than one H-card"); p->zThreadTitle = next_token(&x,0); if( p->zThreadTitle==0 ) SYNTAX("missing title on H-card"); defossilize(p->zThreadTitle); p->type = CFTYPE_FORUM; break; } /* ** I <hash> ** ** A I-card identifies another forum post that the current forum post ** is in reply to. */ case 'I': { if( p->zInReplyTo!=0 ) SYNTAX("more than one I-card"); p->zInReplyTo = next_token(&x, &sz); if( p->zInReplyTo==0 ) SYNTAX("missing hash on I-card"); if( !hname_validate(p->zInReplyTo,sz) ){ SYNTAX("Invalid hash on I-card"); } p->type = CFTYPE_FORUM; break; } /* ** J <name> ?<value>? ** ** Specifies a name value pair for ticket. If the first character |
︙ | ︙ | |||
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 | } i = p->nField++; p->aField[i].zName = zName; p->aField[i].zValue = zValue; if( i>0 && fossil_strcmp(p->aField[i-1].zName, zName)>=0 ){ SYNTAX("incorrect J-card sort order"); } break; } /* ** K <uuid> ** ** A K-line gives the UUID for the ticket which this control file ** is amending. */ case 'K': { if( p->zTicketUuid!=0 ) SYNTAX("more than one K-card"); p->zTicketUuid = next_token(&x, &sz); if( sz!=HNAME_LEN_SHA1 ) SYNTAX("K-card UUID is the wrong size"); if( !validate16(p->zTicketUuid, sz) ){ SYNTAX("invalid K-card UUID"); } break; } /* ** L <wikititle> ** ** The wiki page title is fossil-encoded. There may be no more than ** one L line. */ case 'L': { if( p->zWikiTitle!=0 ) SYNTAX("more than one L-card"); p->zWikiTitle = next_token(&x,0); if( p->zWikiTitle==0 ) SYNTAX("missing title on L-card"); defossilize(p->zWikiTitle); if( !wiki_name_is_wellformed((const unsigned char *)p->zWikiTitle) ){ SYNTAX("L-card has malformed wiki name"); } break; } /* ** M <hash> ** ** An M-line identifies another artifact by its hash. M-lines | > > > | 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 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 | } i = p->nField++; p->aField[i].zName = zName; p->aField[i].zValue = zValue; if( i>0 && fossil_strcmp(p->aField[i-1].zName, zName)>=0 ){ SYNTAX("incorrect J-card sort order"); } p->type = CFTYPE_TICKET; break; } /* ** K <uuid> ** ** A K-line gives the UUID for the ticket which this control file ** is amending. */ case 'K': { if( p->zTicketUuid!=0 ) SYNTAX("more than one K-card"); p->zTicketUuid = next_token(&x, &sz); if( sz!=HNAME_LEN_SHA1 ) SYNTAX("K-card UUID is the wrong size"); if( !validate16(p->zTicketUuid, sz) ){ SYNTAX("invalid K-card UUID"); } p->type = CFTYPE_TICKET; break; } /* ** L <wikititle> ** ** The wiki page title is fossil-encoded. There may be no more than ** one L line. */ case 'L': { if( p->zWikiTitle!=0 ) SYNTAX("more than one L-card"); p->zWikiTitle = next_token(&x,0); if( p->zWikiTitle==0 ) SYNTAX("missing title on L-card"); defossilize(p->zWikiTitle); if( !wiki_name_is_wellformed((const unsigned char *)p->zWikiTitle) ){ SYNTAX("L-card has malformed wiki name"); } p->type = CFTYPE_WIKI; break; } /* ** M <hash> ** ** An M-line identifies another artifact by its hash. M-lines |
︙ | ︙ | |||
651 652 653 654 655 656 657 658 659 660 661 662 663 664 | , p->nCChildAlloc*sizeof(p->azCChild[0]) ); } i = p->nCChild++; p->azCChild[i] = zUuid; if( i>0 && fossil_strcmp(p->azCChild[i-1], zUuid)>=0 ){ SYNTAX("M-card in the wrong order"); } break; } /* ** N <uuid> ** ** An N-line identifies the mimetype of wiki or comment text. | > | 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 | , p->nCChildAlloc*sizeof(p->azCChild[0]) ); } i = p->nCChild++; p->azCChild[i] = zUuid; if( i>0 && fossil_strcmp(p->azCChild[i-1], zUuid)>=0 ){ SYNTAX("M-card in the wrong order"); } p->type = CFTYPE_CLUSTER; break; } /* ** N <uuid> ** ** An N-line identifies the mimetype of wiki or comment text. |
︙ | ︙ | |||
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 741 742 | p->aCherrypick = fossil_realloc(p->aCherrypick, p->nCherrypick*sizeof(p->aCherrypick[0])); p->aCherrypick[n].zCPTarget = zUuid; p->aCherrypick[n].zCPBase = zUuid = next_token(&x, &sz); if( zUuid && !hname_validate(zUuid,sz) ){ SYNTAX("invalid second hash on Q-card"); } break; } /* ** R <md5sum> ** ** Specify the MD5 checksum over the name and content of all files ** in the manifest. */ case 'R': { if( p->zRepoCksum!=0 ) SYNTAX("more than one R-card"); p->zRepoCksum = next_token(&x, &sz); if( sz!=32 ) SYNTAX("wrong size cksum on R-card"); if( !validate16(p->zRepoCksum, 32) ) SYNTAX("malformed R-card cksum"); break; } /* ** T (+|*|-)<tagname> <uuid> ?<value>? ** ** Create or cancel a tag or property. The tagname is fossil-encoded. | > > | 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 | p->aCherrypick = fossil_realloc(p->aCherrypick, p->nCherrypick*sizeof(p->aCherrypick[0])); p->aCherrypick[n].zCPTarget = zUuid; p->aCherrypick[n].zCPBase = zUuid = next_token(&x, &sz); if( zUuid && !hname_validate(zUuid,sz) ){ SYNTAX("invalid second hash on Q-card"); } p->type = CFTYPE_MANIFEST; break; } /* ** R <md5sum> ** ** Specify the MD5 checksum over the name and content of all files ** in the manifest. */ case 'R': { if( p->zRepoCksum!=0 ) SYNTAX("more than one R-card"); p->zRepoCksum = next_token(&x, &sz); if( sz!=32 ) SYNTAX("wrong size cksum on R-card"); if( !validate16(p->zRepoCksum, 32) ) SYNTAX("malformed R-card cksum"); p->type = CFTYPE_MANIFEST; break; } /* ** T (+|*|-)<tagname> <uuid> ?<value>? ** ** Create or cancel a tag or property. The tagname is fossil-encoded. |
︙ | ︙ | |||
757 758 759 760 761 762 763 | if( zName==0 ) SYNTAX("missing name on T-card"); zUuid = next_token(&x, &sz); if( zUuid==0 ) SYNTAX("missing artifact hash on T-card"); zValue = next_token(&x, 0); if( zValue ) defossilize(zValue); if( hname_validate(zUuid, sz) ){ /* A valid artifact hash */ | < | < < < > | 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 | if( zName==0 ) SYNTAX("missing name on T-card"); zUuid = next_token(&x, &sz); if( zUuid==0 ) SYNTAX("missing artifact hash on T-card"); zValue = next_token(&x, 0); if( zValue ) defossilize(zValue); if( hname_validate(zUuid, sz) ){ /* A valid artifact hash */ }else if( sz==1 && zUuid[0]=='*' ){ zUuid = 0; nSelfTag++; }else{ SYNTAX("malformed artifact hash on T-card"); } defossilize(zName); if( zName[0]!='-' && zName[0]!='+' && zName[0]!='*' ){ SYNTAX("T-card name does not begin with '-', '+', or '*'"); } if( zName[0]=='+' ) nSimpleTag++; if( validate16(&zName[1], strlen(&zName[1])) ){ /* Do not allow tags whose names look like a hash */ SYNTAX("T-card name looks like a hexadecimal hash"); } if( p->nTag>=p->nTagAlloc ){ p->nTagAlloc = p->nTagAlloc*2 + 10; p->aTag = fossil_realloc(p->aTag, p->nTagAlloc*sizeof(p->aTag[0]) ); |
︙ | ︙ | |||
856 857 858 859 860 861 862 | ** Manifest. It is not required for manifest only for historical ** compatibility reasons. */ case 'Z': { zUuid = next_token(&x, &sz); if( sz!=32 ) SYNTAX("wrong size for Z-card cksum"); if( !validate16(zUuid, 32) ) SYNTAX("malformed Z-card cksum"); | < | > | < | | < < | | > > | | | | | | | | < < > | | > | | < < | | < < < < < < | < < < > > > | | < < < < | < < | < < | < < < > > | | | < > | < < | < < > > > | | | > | < < | < < < < | > > | | 980 981 982 983 984 985 986 987 988 989 990 991 992 993 994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 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 1064 1065 | ** Manifest. It is not required for manifest only for historical ** compatibility reasons. */ case 'Z': { zUuid = next_token(&x, &sz); if( sz!=32 ) SYNTAX("wrong size for Z-card cksum"); if( !validate16(zUuid, 32) ) SYNTAX("malformed Z-card cksum"); break; } default: { SYNTAX("unrecognized card"); } } } if( x.z<x.zEnd ) SYNTAX("extra characters at end of card"); /* If the artifact type has not yet been determined, then compute ** it now. */ if( p->type==0 ){ if( p->zComment!=0 || p->nFile>0 || p->nParent>0 ){ p->type = CFTYPE_MANIFEST; }else{ p->type = CFTYPE_CONTROL; } } /* Verify that no disallowed cards are present for this artifact type */ m = manifest_card_mask(manifestCardTypes[p->type-1].zAllowed); if( seenCard & ~m ){ sqlite3_snprintf(sizeof(zErrBuf), zErrBuf, "%c-card in %s", maskToType(seenCard & ~m), azNameOfMType[p->type-1]); zErr = zErrBuf; goto manifest_syntax_error; } /* Verify that all required cards are present for this artifact type */ m = manifest_card_mask(manifestCardTypes[p->type-1].zRequired); if( ~seenCard & m ){ sqlite3_snprintf(sizeof(zErrBuf), zErrBuf, "%c-card missing in %s", maskToType(~seenCard & m), azNameOfMType[p->type-1]); zErr = zErrBuf; goto manifest_syntax_error; } /* Additional checks based on artifact type */ switch( p->type ){ case CFTYPE_CONTROL: { if( nSelfTag ) SYNTAX("self-referential T-card in control artifact"); break; } case CFTYPE_EVENT: { if( p->nTag!=nSelfTag ){ SYNTAX("non-self-referential T-card in technote"); } if( p->nTag!=nSimpleTag ){ SYNTAX("T-card with '*' or '-' in technote"); } break; } case CFTYPE_FORUM: { if( p->zThreadTitle && p->zInReplyTo ){ SYNTAX("cannot have I-card and H-card in a forum post"); } if( p->nParent>1 ) SYNTAX("too many arguments to P-card"); break; } } md5sum_init(); if( !isRepeat ) g.parseCnt[p->type]++; return p; manifest_syntax_error: { char *zUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", rid); if( zUuid ){ blob_appendf(pErr, "artifact [%s] ", zUuid); fossil_free(zUuid); } } if( zErr ){ blob_appendf(pErr, "line %d: %s", lineNo, zErr); }else{ blob_appendf(pErr, "unknown error on line %d", lineNo); |
︙ | ︙ | |||
1013 1014 1015 1016 1017 1018 1019 | } /* ** COMMAND: test-parse-manifest ** ** Usage: %fossil test-parse-manifest FILENAME ?N? ** | | > | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1115 1116 1117 1118 1119 1120 1121 1122 1123 1124 1125 1126 1127 1128 1129 1130 1131 1132 1133 1134 1135 1136 1137 1138 1139 1140 1141 1142 1143 1144 1145 1146 1147 1148 1149 1150 1151 1152 1153 1154 1155 1156 1157 1158 1159 1160 1161 1162 1163 1164 1165 1166 1167 1168 1169 1170 1171 1172 1173 1174 1175 1176 1177 1178 1179 1180 1181 1182 1183 1184 1185 1186 1187 1188 1189 1190 1191 | } /* ** COMMAND: test-parse-manifest ** ** Usage: %fossil test-parse-manifest FILENAME ?N? ** ** Parse the manifest(s) given on the command-line and report any ** errors. If the N argument is given, run the parsing N times. */ void manifest_test_parse_cmd(void){ Manifest *p; Blob b; int i; int n = 1; db_find_and_open_repository(0,0); verify_all_options(); if( g.argc!=3 && g.argc!=4 ){ usage("FILENAME"); } blob_read_from_file(&b, g.argv[2], ExtFILE); if( g.argc>3 ) n = atoi(g.argv[3]); for(i=0; i<n; i++){ Blob b2; Blob err; blob_copy(&b2, &b); blob_zero(&err); p = manifest_parse(&b2, 0, &err); if( p==0 ) fossil_print("ERROR: %s\n", blob_str(&err)); blob_reset(&err); manifest_destroy(p); } } /* ** COMMAND: test-parse-all-blobs ** ** Usage: %fossil test-parse-all-blobs ** ** Parse all entries in the BLOB table that are believed to be non-data ** artifacts and report any errors. Run this test command on historical ** repositories after making any changes to the manifest_parse() ** implementation to confirm that the changes did not break anything. */ void manifest_test_parse_all_blobs_cmd(void){ Manifest *p; Blob err; Stmt q; int nTest = 0; int nErr = 0; db_find_and_open_repository(0, 0); verify_all_options(); db_prepare(&q, "SELECT DISTINCT objid FROM EVENT"); while( db_step(&q)==SQLITE_ROW ){ int id = db_column_int(&q,0); fossil_print("Checking %d \r", id); nTest++; fflush(stdout); blob_init(&err, 0, 0); p = manifest_get(id, CFTYPE_ANY, &err); if( p==0 ){ fossil_print("%d ERROR: %s\n", id, blob_str(&err)); nErr++; } blob_reset(&err); manifest_destroy(p); } db_finalize(&q); fossil_print("%d tests with %d errors\n", nTest, nErr); } /* ** Fetch the baseline associated with the delta-manifest p. ** Return 0 on success. If unable to parse the baseline, ** throw an error. If the baseline is a manifest, throw an ** error if throwError is true, or record that p is an orphan ** and return 1 if throwError is false. |
︙ | ︙ | |||
1752 1753 1754 1755 1756 1757 1758 | db_reset(&u); } db_finalize(&q); db_finalize(&u); if( db_exists("SELECT 1 FROM time_fudge") ){ db_multi_exec( "UPDATE event SET mtime=(SELECT m1 FROM time_fudge WHERE mid=objid)" | | > | 1893 1894 1895 1896 1897 1898 1899 1900 1901 1902 1903 1904 1905 1906 1907 1908 | db_reset(&u); } db_finalize(&q); db_finalize(&u); if( db_exists("SELECT 1 FROM time_fudge") ){ db_multi_exec( "UPDATE event SET mtime=(SELECT m1 FROM time_fudge WHERE mid=objid)" " WHERE objid IN (SELECT mid FROM time_fudge)" " AND (mtime=omtime OR omtime IS NULL)" ); } db_multi_exec("DROP TABLE time_fudge;"); db_end_transaction(0); manifest_crosslink_busy = 0; return ( rc!=TH_ERROR ); |
︙ | ︙ | |||
1927 1928 1929 1930 1931 1932 1933 1934 1935 1936 1937 1938 1939 1940 | int i, rc = TH_OK; Manifest *p; int parentid = 0; int permitHooks = (flags & MC_PERMIT_HOOKS); const char *zScript = 0; const char *zUuid = 0; if( (p = manifest_cache_find(rid))!=0 ){ blob_reset(pContent); }else if( (p = manifest_parse(pContent, rid, 0))==0 ){ assert( blob_is_reset(pContent) || pContent==0 ); if( (flags & MC_NO_ERRORS)==0 ){ fossil_error(1, "syntax error in manifest [%S]", db_text(0, "SELECT uuid FROM blob WHERE rid=%d",rid)); | > > > | 2069 2070 2071 2072 2073 2074 2075 2076 2077 2078 2079 2080 2081 2082 2083 2084 2085 | int i, rc = TH_OK; Manifest *p; int parentid = 0; int permitHooks = (flags & MC_PERMIT_HOOKS); const char *zScript = 0; const char *zUuid = 0; if( g.fSqlTrace ){ fossil_trace("-- manifest_crosslink(%d)\n", rid); } if( (p = manifest_cache_find(rid))!=0 ){ blob_reset(pContent); }else if( (p = manifest_parse(pContent, rid, 0))==0 ){ assert( blob_is_reset(pContent) || pContent==0 ); if( (flags & MC_NO_ERRORS)==0 ){ fossil_error(1, "syntax error in manifest [%S]", db_text(0, "SELECT uuid FROM blob WHERE rid=%d",rid)); |
︙ | ︙ | |||
2372 2373 2374 2375 2376 2377 2378 2379 2380 2381 2382 2383 2384 2385 | if( blob_size(&comment)==0 ) blob_append(&comment, " ", 1); db_multi_exec( "REPLACE INTO event(type,mtime,objid,user,comment)" "VALUES('g',%.17g,%d,%Q,%Q)", p->rDate, rid, p->zUser, blob_str(&comment)+1 ); blob_reset(&comment); } db_end_transaction(0); if( permitHooks ){ rc = xfer_run_common_script(); if( rc==TH_OK ){ rc = xfer_run_script(zScript, zUuid, 0); } | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 2517 2518 2519 2520 2521 2522 2523 2524 2525 2526 2527 2528 2529 2530 2531 2532 2533 2534 2535 2536 2537 2538 2539 2540 2541 2542 2543 2544 2545 2546 2547 2548 2549 2550 2551 2552 2553 2554 2555 2556 2557 2558 2559 2560 2561 2562 2563 2564 2565 2566 2567 2568 2569 2570 2571 2572 2573 2574 2575 2576 2577 2578 2579 2580 2581 2582 2583 2584 2585 2586 2587 2588 2589 2590 2591 | if( blob_size(&comment)==0 ) blob_append(&comment, " ", 1); db_multi_exec( "REPLACE INTO event(type,mtime,objid,user,comment)" "VALUES('g',%.17g,%d,%Q,%Q)", p->rDate, rid, p->zUser, blob_str(&comment)+1 ); blob_reset(&comment); } if( p->type==CFTYPE_FORUM ){ int froot, fprev, firt; char *zFType; char *zTitle; schema_forum(); froot = p->zThreadRoot ? uuid_to_rid(p->zThreadRoot, 1) : rid; fprev = p->nParent ? uuid_to_rid(p->azParent[0],1) : 0; firt = p->zInReplyTo ? uuid_to_rid(p->zInReplyTo,1) : 0; db_multi_exec( "INSERT INTO forumpost(fpid,froot,fprev,firt,fmtime)" "VALUES(%d,%d,nullif(%d,0),nullif(%d,0),%.17g)", p->rid, froot, fprev, firt, p->rDate ); if( firt==0 ){ /* This is the start of a new thread, either the initial entry ** or an edit of the initial entry. */ zTitle = p->zThreadTitle; if( zTitle==0 || zTitle[0]==0 ){ zTitle = "(Deleted)"; } zFType = fprev ? "Edit" : "Post"; db_multi_exec( "REPLACE INTO event(type,mtime,objid,user,comment)" "VALUES('f',%.17g,%d,%Q,'%q: %q')", p->rDate, rid, p->zUser, zFType, zTitle ); /* ** If this edit is the most recent, then make it the title for ** all other entries for the same thread */ if( !db_exists("SELECT 1 FROM forumpost WHERE froot=%d AND firt=0" " AND fpid!=%d AND fmtime>%.17g", froot, rid, p->rDate) ){ /* This entry establishes a new title for all entries on the thread */ db_multi_exec( "UPDATE event" " SET comment=substr(comment,1,instr(comment,':')) || ' %q'" " WHERE objid IN (SELECT fpid FROM forumpost WHERE froot=%d)", zTitle, froot ); } }else{ /* This is a reply to a prior post. Take the title from the root. */ zTitle = db_text(0, "SELECT substr(comment,instr(comment,':')+2)" " FROM event WHERE objid=%d", froot); if( zTitle==0 ) zTitle = fossil_strdup("<i>Unknown</i>"); if( p->zWiki[0]==0 ){ zFType = "Delete reply"; }else if( fprev ){ zFType = "Edit reply"; }else{ zFType = "Reply"; } db_multi_exec( "REPLACE INTO event(type,mtime,objid,user,comment)" "VALUES('f',%.17g,%d,%Q,'%q: %q')", p->rDate, rid, p->zUser, zFType, zTitle ); fossil_free(zTitle); } } db_end_transaction(0); if( permitHooks ){ rc = xfer_run_common_script(); if( rc==TH_OK ){ rc = xfer_run_script(zScript, zUuid, 0); } |
︙ | ︙ |
Changes to src/merge.c.
︙ | ︙ | |||
285 286 287 288 289 290 291 | ** leaf that is (1) not the version currently checked out and (2) ** has not already been merged into the current checkout and (3) ** the leaf is not closed and (4) the leaf is in the same branch ** as the current checkout. */ Stmt q; if( pickFlag || backoutFlag || integrateFlag){ | | > | 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 | ** leaf that is (1) not the version currently checked out and (2) ** has not already been merged into the current checkout and (3) ** the leaf is not closed and (4) the leaf is in the same branch ** as the current checkout. */ Stmt q; if( pickFlag || backoutFlag || integrateFlag){ fossil_fatal("cannot use --backout, --cherrypick or --integrate " "with a fork merge"); } mid = fossil_find_nearest_fork(vid, db_open_local(0)); if( mid==0 ){ fossil_fatal("no unmerged forks of branch \"%s\"", db_text(0, "SELECT value FROM tagxref" " WHERE tagid=%d AND rid=%d AND tagtype>0", TAG_BRANCH, vid) |
︙ | ︙ | |||
323 324 325 326 327 328 329 | if( zPivot ){ pid = name_to_typed_rid(zPivot, "ci"); if( pid==0 || !is_a_version(pid) ){ fossil_fatal("not a version: %s", zPivot); } if( pickFlag ){ | | | > | 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 | if( zPivot ){ pid = name_to_typed_rid(zPivot, "ci"); if( pid==0 || !is_a_version(pid) ){ fossil_fatal("not a version: %s", zPivot); } if( pickFlag ){ fossil_fatal("incompatible options: --cherrypick and --baseline"); } } if( pickFlag || backoutFlag ){ if( integrateFlag ){ fossil_fatal("incompatible options: --integrate and --cherrypick " "with --backout"); } pid = db_int(0, "SELECT pid FROM plink WHERE cid=%d AND isprim", mid); if( pid<=0 ){ fossil_fatal("cannot find an ancestor for %s", g.argv[2]); } }else{ if( !zPivot ){ |
︙ | ︙ | |||
377 378 379 380 381 382 383 | return; } if( integrateFlag && !is_a_leaf(mid)){ fossil_warning("ignoring --integrate: %s is not a leaf", g.argv[2]); integrateFlag = 0; } if( verboseFlag ){ | | > | 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 | return; } if( integrateFlag && !is_a_leaf(mid)){ fossil_warning("ignoring --integrate: %s is not a leaf", g.argv[2]); integrateFlag = 0; } if( verboseFlag ){ print_checkin_description(mid, 12, integrateFlag ? "integrate:" : "merge-from:"); print_checkin_description(pid, 12, "baseline:"); } vfile_check_signature(vid, CKSIG_ENOTFILE); db_begin_transaction(); if( !dryRunFlag ) undo_begin(); if( load_vfile_from_rid(mid) && !forceMissingFlag ){ fossil_fatal("missing content, unable to merge"); |
︙ | ︙ |
Changes to src/mkbuiltin.c.
︙ | ︙ | |||
58 59 60 61 62 63 64 | } /* ** There is an instance of the following for each file translated. */ typedef struct Resource Resource; struct Resource { | | | 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 | } /* ** There is an instance of the following for each file translated. */ typedef struct Resource Resource; struct Resource { char *zName; int nByte; int idx; }; /* ** Compare two Resource objects for sorting purposes. They sort ** in zName order so that Fossil can search for resources using |
︙ | ︙ | |||
145 146 147 148 149 150 151 | printf("struct BuiltinFileTable {\n"); printf(" const char *zName;\n"); printf(" const unsigned char *pData;\n"); printf(" int nByte;\n"); printf("};\n"); printf("static const BuiltinFileTable aBuiltinFiles[] = {\n"); for(i=0; i<nRes; i++){ | | | > > > > | 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 | printf("struct BuiltinFileTable {\n"); printf(" const char *zName;\n"); printf(" const unsigned char *pData;\n"); printf(" int nByte;\n"); printf("};\n"); printf("static const BuiltinFileTable aBuiltinFiles[] = {\n"); for(i=0; i<nRes; i++){ char *z = aRes[i].zName; if( strlen(z)>=nPrefix ) z += nPrefix; while( z[0]=='.' || z[0]=='/' || z[0]=='\\' ){ z++; } aRes[i].zName = z; while( z[0] ){ if( z[0]=='\\' ) z[0] = '/'; z++; } } qsort(aRes, nRes, sizeof(aRes[0]), compareResource); for(i=0; i<nRes; i++){ printf(" { \"%s\", bidata%d, %d },\n", aRes[i].zName, aRes[i].idx, aRes[i].nByte); } printf("};\n"); return nErr; } |
Changes to src/moderate.c.
1 2 3 4 5 6 | /* ** Copyright (c) 2012 D. Richard Hipp ** ** This program is free software; you can redistribute it and/or ** modify it under the terms of the Simplified BSD License (also ** known as the "2-Clause License" or "FreeBSD License".) | | | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 | /* ** Copyright (c) 2012 D. Richard Hipp ** ** This program is free software; you can redistribute it and/or ** modify it under the terms of the Simplified BSD License (also ** known as the "2-Clause License" or "FreeBSD License".) ** ** This program is distributed in the hope that it will be useful, ** but without any warranty; without even the implied warranty of ** merchantability or fitness for a particular purpose. ** ** Author contact information: ** drh@hwaci.com ** http://www.hwaci.com/drh/ |
︙ | ︙ | |||
52 53 54 55 56 57 58 59 60 61 62 63 64 65 | if( rid==0 || !moderation_table_exists() ) return 0; db_static_prepare(&q, "SELECT 1 FROM modreq WHERE objid=:objid"); db_bind_int(&q, ":objid", rid); rc = db_step(&q)==SQLITE_ROW; db_reset(&q); return rc; } /* ** Check to see if the object identified by RID is used for anything. */ static int object_used(int rid){ static const char *const aTabField[] = { "modreq", "attachRid", | > > > > > > > > > > > > > > > > > > > > > > > > | 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 | if( rid==0 || !moderation_table_exists() ) return 0; db_static_prepare(&q, "SELECT 1 FROM modreq WHERE objid=:objid"); db_bind_int(&q, ":objid", rid); rc = db_step(&q)==SQLITE_ROW; db_reset(&q); return rc; } /* ** If the rid object is being held for moderation, write out ** an "awaiting moderation" message and return true. ** ** If the object is not being held for moderation, simply return ** false without generating any output. */ int moderation_pending_www(int rid){ int pending = moderation_pending(rid); if( pending ){ @ <span class="modpending">(Awaiting Moderator Approval)</span> } return pending; } /* ** Return TRUE if there any pending moderation requests. */ int moderation_needed(void){ if( !moderation_table_exists() ) return 0; return db_exists("SELECT 1 FROM modreq"); } /* ** Check to see if the object identified by RID is used for anything. */ static int object_used(int rid){ static const char *const aTabField[] = { "modreq", "attachRid", |
︙ | ︙ | |||
99 100 101 102 103 104 105 106 107 108 109 110 111 112 | "DELETE FROM delta WHERE rid=%d;" "DELETE FROM event WHERE objid=%d;" "DELETE FROM tagxref WHERE rid=%d;" "DELETE FROM private WHERE rid=%d;" "DELETE FROM attachment WHERE attachid=%d;", rid, rid, rid, rid, rid, rid ); zTktid = db_text(0, "SELECT tktid FROM modreq WHERE objid=%d", rid); if( zTktid && zTktid[0] ){ ticket_rebuild_entry(zTktid); fossil_free(zTktid); } attachRid = db_int(0, "SELECT attachRid FROM modreq WHERE objid=%d", rid); if( rid==objid ){ | > > > | 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 | "DELETE FROM delta WHERE rid=%d;" "DELETE FROM event WHERE objid=%d;" "DELETE FROM tagxref WHERE rid=%d;" "DELETE FROM private WHERE rid=%d;" "DELETE FROM attachment WHERE attachid=%d;", rid, rid, rid, rid, rid, rid ); if( db_table_exists("repository","forumpost") ){ db_multi_exec("DELETE FROM forumpost WHERE fpid=%d", rid); } zTktid = db_text(0, "SELECT tktid FROM modreq WHERE objid=%d", rid); if( zTktid && zTktid[0] ){ ticket_rebuild_entry(zTktid); fossil_free(zTktid); } attachRid = db_int(0, "SELECT attachRid FROM modreq WHERE objid=%d", rid); if( rid==objid ){ |
︙ | ︙ | |||
142 143 144 145 146 147 148 | ** Show all pending moderation request */ void modreq_page(void){ Blob sql; Stmt q; login_check_credentials(); | | | | 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 | ** Show all pending moderation request */ void modreq_page(void){ Blob sql; Stmt q; login_check_credentials(); if( !g.perm.ModWiki && !g.perm.ModTkt && !g.perm.ModForum ){ login_needed(g.anon.ModWiki && g.anon.ModTkt && g.anon.ModForum); return; } style_header("Pending Moderation Requests"); @ <h2>All Pending Moderation Requests</h2> if( moderation_table_exists() ){ blob_init(&sql, timeline_query_for_www(), -1); blob_append_sql(&sql, |
︙ | ︙ |
Changes to src/name.c.
︙ | ︙ | |||
92 93 94 95 96 97 98 | ** * "current" ** * "prev" or "previous" ** * "next" ** ** Return the RID of the matching artifact. Or return 0 if the name does not ** match any known object. Or return -1 if the name is ambiguous. ** | | | | 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 | ** * "current" ** * "prev" or "previous" ** * "next" ** ** Return the RID of the matching artifact. Or return 0 if the name does not ** match any known object. Or return -1 if the name is ambiguous. ** ** The zType parameter specifies the type of artifact: ci, t, w, e, g, f. ** If zType is NULL or "" or "*" then any type of artifact will serve. ** If zType is "br" then find the first check-in of the named branch ** rather than the last. ** zType is "ci" in most use cases since we are usually searching for ** a check-in. ** ** Note that the input zTag for types "t" and "e" is the artifact hash of ** the ticket-change or technote-change artifact, not the randomly generated ** hexadecimal identifier assigned to tickets and events. Those identifiers ** live in a separate namespace. */ int symbolic_name_to_rid(const char *zTag, const char *zType){ int vid; int rid = 0; int nTag; |
︙ | ︙ | |||
601 602 603 604 605 606 607 | " coalesce(euser,user), coalesce(ecomment,comment)" " FROM event WHERE objid=%d", rid); if( db_step(&q)==SQLITE_ROW ){ const char *zType; switch( db_column_text(&q,0)[0] ){ case 'c': zType = "Check-in"; break; case 'w': zType = "Wiki-edit"; break; | | > | 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 | " coalesce(euser,user), coalesce(ecomment,comment)" " FROM event WHERE objid=%d", rid); if( db_step(&q)==SQLITE_ROW ){ const char *zType; switch( db_column_text(&q,0)[0] ){ case 'c': zType = "Check-in"; break; case 'w': zType = "Wiki-edit"; break; case 'e': zType = "Technote"; break; case 'f': zType = "Forum-post"; break; case 't': zType = "Ticket-change"; break; case 'g': zType = "Tag-change"; break; default: zType = "Unknown"; break; } fossil_print("type: %s by %s on %s\n", zType, db_column_text(&q,2), db_column_text(&q, 1)); fossil_print("comment: "); |
︙ | ︙ | |||
924 925 926 927 928 929 930 931 932 933 934 935 936 937 | " 'attachment '||attachment.filename\n" " FROM attachment, blob\n" " WHERE (blob.rid %s)\n" " AND blob.rid NOT IN (SELECT rid FROM description)\n" " AND blob.uuid=attachment.src", zWhere /*safe-for-%s*/ ); /* Everything else */ db_multi_exec( "INSERT OR IGNORE INTO description(rid,uuid,type,summary)\n" "SELECT blob.rid, blob.uuid," " CASE WHEN blob.size<0 THEN 'phantom' ELSE '' END,\n" " 'unknown'\n" | > > > > > > > > > > > > > > > | 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952 953 | " 'attachment '||attachment.filename\n" " FROM attachment, blob\n" " WHERE (blob.rid %s)\n" " AND blob.rid NOT IN (SELECT rid FROM description)\n" " AND blob.uuid=attachment.src", zWhere /*safe-for-%s*/ ); /* Forum posts */ if( db_table_exists("repository","forumpost") ){ db_multi_exec( "INSERT OR IGNORE INTO description(rid,uuid,ctime,type,summary)\n" "SELECT postblob.rid, postblob.uuid, forumpost.fmtime, 'forumpost',\n" " CASE WHEN fpid=froot THEN 'forum-post '\n" " ELSE 'forum-reply-to ' END || substr(rootblob.uuid,1,14)\n" " FROM forumpost, blob AS postblob, blob AS rootblob\n" " WHERE (forumpost.fpid %s)\n" " AND postblob.rid=forumpost.fpid" " AND rootblob.rid=forumpost.froot", zWhere /*safe-for-%s*/ ); } /* Everything else */ db_multi_exec( "INSERT OR IGNORE INTO description(rid,uuid,type,summary)\n" "SELECT blob.rid, blob.uuid," " CASE WHEN blob.size<0 THEN 'phantom' ELSE '' END,\n" " 'unknown'\n" |
︙ | ︙ |
Changes to src/path.c.
︙ | ︙ | |||
545 546 547 548 549 550 551 552 | g.argv += 2; g.argc -= 2; } } /* Query to extract all rename operations */ static const char zRenameQuery[] = @ SELECT | > | | > > > > > > > > > > > > > > > > > | > > > | < > > > > > > > > > > > > > | | | < | > | 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 | g.argv += 2; g.argc -= 2; } } /* Query to extract all rename operations */ static const char zRenameQuery[] = @ CREATE TEMP TABLE renames AS @ SELECT @ datetime(event.mtime) AS date, @ F.name AS old_name, @ T.name AS new_name, @ blob.uuid AS checkin @ FROM mlink, filename F, filename T, event, blob @ WHERE coalesce(mlink.pfnid,0)!=0 AND mlink.pfnid!=mlink.fnid @ AND F.fnid=mlink.pfnid @ AND T.fnid=mlink.fnid @ AND event.objid=mlink.mid @ AND event.type='ci' @ AND blob.rid=mlink.mid; ; /* Query to extract distinct rename operations */ static const char zDistinctRenameQuery[] = @ CREATE TEMP TABLE renames AS @ SELECT @ min(datetime(event.mtime)) AS date, @ F.name AS old_name, @ T.name AS new_name, @ blob.uuid AS checkin @ FROM mlink, filename F, filename T, event, blob @ WHERE coalesce(mlink.pfnid,0)!=0 AND mlink.pfnid!=mlink.fnid @ AND F.fnid=mlink.pfnid @ AND T.fnid=mlink.fnid @ AND event.objid=mlink.mid @ AND event.type='ci' @ AND blob.rid=mlink.mid @ GROUP BY 2, 3; ; /* ** WEBPAGE: test-rename-list ** ** Print a list of all file rename operations throughout history. ** This page is intended for for testing purposes only and may change ** or be discontinued without notice. */ void test_rename_list_page(void){ Stmt q; int nRename; int nCheckin; login_check_credentials(); if( !g.perm.Read ){ login_needed(g.anon.Read); return; } if( P("all")!=0 ){ style_header("List Of All Filename Changes"); db_multi_exec("%s", zRenameQuery/*safe-for-%s*/); style_submenu_element("Distinct", "%R/test-rename-list"); }else{ style_header("List Of Distinct Filename Changes"); db_multi_exec("%s", zDistinctRenameQuery/*safe-for-%s*/); style_submenu_element("All", "%R/test-rename-list?all"); } nRename = db_int(0, "SELECT count(*) FROM renames;"); nCheckin = db_int(0, "SELECT count(DISTINCT checkin) FROM renames;"); db_prepare(&q, "SELECT date, old_name, new_name, checkin FROM renames" " ORDER BY date DESC, old_name ASC"); @ <h1>%d(nRename) filename changes in %d(nCheckin) check-ins</h1> @ <table class='sortable' data-column-types='tttt' data-init-sort='1'\ @ border="1" cellpadding="2" cellspacing="0"> @ <thead><tr><th>Date & Time</th> @ <th>Old Name</th> @ <th>New Name</th> @ <th>Check-in</th></tr></thead><tbody> while( db_step(&q)==SQLITE_ROW ){ const char *zDate = db_column_text(&q, 0); const char *zOld = db_column_text(&q, 1); const char *zNew = db_column_text(&q, 2); const char *zUuid = db_column_text(&q, 3); @ <tr> @ <td>%z(href("%R/timeline?c=%t",zDate))%s(zDate)</a></td> @ <td>%z(href("%R/finfo?name=%t",zOld))%h(zOld)</a></td> @ <td>%z(href("%R/finfo?name=%t",zNew))%h(zNew)</a></td> @ <td>%z(href("%R/info/%!S",zUuid))%S(zUuid)</a></td></tr> } @ </tbody></table> db_finalize(&q); style_table_sorter(); style_footer(); } |
Changes to src/pivot.c.
︙ | ︙ | |||
36 37 38 39 40 41 42 | ** ** The act of setting the primary resets the pivot-finding algorithm. */ void pivot_set_primary(int rid){ /* Set up table used to do the search */ db_multi_exec( "CREATE TEMP TABLE IF NOT EXISTS aqueue(" | | | > | | 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 | ** ** The act of setting the primary resets the pivot-finding algorithm. */ void pivot_set_primary(int rid){ /* Set up table used to do the search */ db_multi_exec( "CREATE TEMP TABLE IF NOT EXISTS aqueue(" " rid INTEGER," /* The record id for this version */ " mtime REAL," /* Time when this version was created */ " pending BOOLEAN," /* True if we have not check this one yet */ " src BOOLEAN," /* 1 for primary. 0 for others */ " PRIMARY KEY(rid,src)" ") WITHOUT ROWID;" "DELETE FROM aqueue;" "CREATE INDEX IF NOT EXISTS aqueue_idx1 ON aqueue(pending, mtime);" ); /* Insert the primary record */ db_multi_exec( "INSERT INTO aqueue(rid, mtime, pending, src)" |
︙ | ︙ | |||
116 117 118 119 120 121 122 | db_prepare(&u1, "UPDATE aqueue SET pending=0 WHERE rid=:rid" ); /* Add to the queue all ancestors of :rid. */ db_prepare(&i1, | | | | 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 | db_prepare(&u1, "UPDATE aqueue SET pending=0 WHERE rid=:rid" ); /* Add to the queue all ancestors of :rid. */ db_prepare(&i1, "REPLACE INTO aqueue " "SELECT plink.pid," " coalesce((SELECT mtime FROM event X WHERE X.objid=plink.pid), 0.0)," " 1," " aqueue.src " " FROM plink, aqueue" " WHERE plink.cid=:rid" " AND aqueue.rid=:rid %s", ignoreMerges ? "AND plink.isprim" : "" ); |
︙ | ︙ |
Changes to src/printf.c.
︙ | ︙ | |||
979 980 981 982 983 984 985 | va_end(ap); } /* ** Write a message to the error log, if the error log filename is ** defined. */ | | | > > > > | | > | | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | < < < < | < > | < < < < < < < < | < | < < < > < < < < < < < < < < < < < < < < < | < < < | | 979 980 981 982 983 984 985 986 987 988 989 990 991 992 993 994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 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 1064 1065 1066 1067 1068 1069 1070 1071 1072 1073 1074 1075 1076 1077 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 1105 1106 1107 1108 1109 1110 1111 1112 1113 1114 | va_end(ap); } /* ** Write a message to the error log, if the error log filename is ** defined. */ void fossil_errorlog(const char *zFormat, ...){ struct tm *pNow; time_t now; FILE *out; const char *z; int i; va_list ap; static const char *const azEnv[] = { "HTTP_HOST", "HTTP_REFERER", "HTTP_USER_AGENT", "PATH_INFO", "QUERY_STRING", "REMOTE_ADDR", "REQUEST_METHOD", "REQUEST_URI", "SCRIPT_NAME" }; if( g.zErrlog==0 ) return; if( g.zErrlog[0]=='-' && g.zErrlog[1]==0 ){ out = stderr; }else{ out = fossil_fopen(g.zErrlog, "a"); if( out==0 ) return; } now = time(0); pNow = gmtime(&now); fprintf(out, "------------- %04d-%02d-%02d %02d:%02d:%02d UTC ------------\n", pNow->tm_year+1900, pNow->tm_mon+1, pNow->tm_mday+1, pNow->tm_hour, pNow->tm_min, pNow->tm_sec); va_start(ap, zFormat); vfprintf(out, zFormat, ap); fprintf(out, "\n"); va_end(ap); for(i=0; i<count(azEnv); i++){ char *p; if( (p = fossil_getenv(azEnv[i]))!=0 && p[0]!=0 ){ fprintf(out, "%s=%s\n", azEnv[i], p); fossil_path_free(p); }else if( (z = P(azEnv[i]))!=0 && z[0]!=0 ){ fprintf(out, "%s=%s\n", azEnv[i], z); } } fclose(out); } /* ** The following variable becomes true while processing a fatal error ** or a panic. If additional "recursive-fatal" errors occur while ** shutting down, the recursive errors are silently ignored. */ static int mainInFatalError = 0; /* ** Write error message output */ static int fossil_print_error(int rc, const char *z){ #ifdef FOSSIL_ENABLE_JSON if( g.json.isJsonMode ){ json_err( 0, z, 1 ); if( g.isHTTP ){ rc = 0 /* avoid HTTP 500 */; } } else #endif if( g.cgiOutput==1 && g.db ){ g.cgiOutput = 2; cgi_reset_content(); cgi_set_content_type("text/html"); style_header("Bad Request"); @ <p class="generalError">%h(z)</p> cgi_set_status(400, "Bad Request"); style_footer(); cgi_reply(); }else if( !g.fQuiet ){ fossil_force_newline(); fossil_trace("%s\n", z); } return rc; } /* ** Print an error message, rollback all databases, and quit. These ** routines never return. ** ** The only different between fossil_fatal() and fossil_panic() is that ** fossil_panic() makes an entry in the error log whereas fossil_fatal() ** does not. If there is not error log, then both routines work the ** same. Hence, the routines are interchangable for commands and only ** make a difference with processing web pages. ** ** Use fossil_fatal() for malformed inputs that should be reported back ** to the user, but which do not represent a configuration problem or bug. ** ** Use fossil_panic() for any kind of error that should be brought to the ** attention of the system administrator. */ NORETURN void fossil_panic(const char *zFormat, ...){ va_list ap; int rc = 1; char z[1000]; static int once = 0; if( once ) exit(1); once = 1; mainInFatalError = 1; /* db_force_rollback(); */ va_start(ap, zFormat); sqlite3_vsnprintf(sizeof(z),z,zFormat, ap); va_end(ap); if( g.fAnyTrace ){ fprintf(stderr, "/***** panic on %d *****/\n", getpid()); } fossil_errorlog("panic: %s", z); rc = fossil_print_error(rc, z); abort(); exit(rc); } NORETURN void fossil_fatal(const char *zFormat, ...){ char *z; int rc = 1; va_list ap; mainInFatalError = 1; va_start(ap, zFormat); z = vmprintf(zFormat, ap); va_end(ap); rc = fossil_print_error(rc, z); fossil_free(z); db_force_rollback(); fossil_exit(rc); } /* This routine works like fossil_fatal() except that if called ** recursively, the recursive call is a no-op. ** |
︙ | ︙ | |||
1112 1113 1114 1115 1116 1117 1118 | va_list ap; int rc = 1; if( mainInFatalError ) return; mainInFatalError = 1; va_start(ap, zFormat); z = vmprintf(zFormat, ap); va_end(ap); | < < < < < < < < < < < < < < < | < < < | > > > > > > > | | 1123 1124 1125 1126 1127 1128 1129 1130 1131 1132 1133 1134 1135 1136 1137 1138 1139 1140 1141 1142 1143 1144 1145 1146 1147 1148 1149 1150 1151 1152 1153 1154 1155 1156 1157 1158 1159 1160 1161 1162 1163 1164 | va_list ap; int rc = 1; if( mainInFatalError ) return; mainInFatalError = 1; va_start(ap, zFormat); z = vmprintf(zFormat, ap); va_end(ap); rc = fossil_print_error(rc, z); db_force_rollback(); fossil_exit(rc); } /* Print a warning message. ** ** Unlike fossil_fatal() and fossil_panic(), this routine does return ** and processing attempts to continue. A message is written to the ** error log, however, so this routine should only be used for situations ** that require administrator or developer attention. Minor problems ** in user inputs should not use this routine. */ void fossil_warning(const char *zFormat, ...){ char *z; va_list ap; va_start(ap, zFormat); z = vmprintf(zFormat, ap); va_end(ap); fossil_errorlog("warning: %s", z); #ifdef FOSSIL_ENABLE_JSON if(g.json.isJsonMode){ json_warn( FSL_JSON_W_UNKNOWN, "%s", z ); }else #endif { if( g.cgiOutput==1 ){ cgi_printf("<p class=\"generalError\">\n%h\n</p>\n", z); }else{ fossil_force_newline(); fossil_trace("%s\n", z); } } free(z); |
︙ | ︙ |
Changes to src/purge.c.
︙ | ︙ | |||
120 121 122 123 124 125 126 | return 0; } /* Make sure we are not removing a manifest that is the baseline of some ** manifest that is being left behind. This step is not strictly necessary. ** is is just a safety check. */ if( purge_baseline_out_from_under_delta(zTab) ){ | | | 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 | return 0; } /* Make sure we are not removing a manifest that is the baseline of some ** manifest that is being left behind. This step is not strictly necessary. ** is is just a safety check. */ if( purge_baseline_out_from_under_delta(zTab) ){ fossil_panic("attempt to purge a baseline manifest without also purging " "all of its deltas"); } /* Make sure that no delta that is left behind requires a purged artifact ** as its basis. If such artifacts exist, go ahead and undelta them now. */ db_prepare(&q, "SELECT rid FROM delta WHERE srcid IN \"%w\"" |
︙ | ︙ |
Changes to src/rebuild.c.
︙ | ︙ | |||
142 143 144 145 146 147 148 | /* ** Update the repository schema for Fossil version 2.0. (2017-02-28) ** (1) Change the CHECK constraint on BLOB.UUID so that the length ** is greater than or equal to 40, not exactly equal to 40. */ void rebuild_schema_update_2_0(void){ | | > | 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 | /* ** Update the repository schema for Fossil version 2.0. (2017-02-28) ** (1) Change the CHECK constraint on BLOB.UUID so that the length ** is greater than or equal to 40, not exactly equal to 40. */ void rebuild_schema_update_2_0(void){ char *z = db_text(0, "SELECT sql FROM repository.sqlite_master" " WHERE name='blob'"); if( z ){ /* Search for: length(uuid)==40 ** 0123456789 12345 */ int i; for(i=10; z[i]; i++){ if( z[i]=='=' && strncmp(&z[i-6],"(uuid)==40",10)==0 ){ z[i] = '>'; |
︙ | ︙ | |||
355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 | bag_init(&bagDone); ttyOutput = doOut; processCnt = 0; if (ttyOutput && !g.fQuiet) { percent_complete(0); } rebuild_update_schema(); blob_init(&sql, 0, 0); db_prepare(&q, "SELECT name FROM sqlite_master /*scan*/" " WHERE type='table'" " AND name NOT IN ('admin_log', 'blob','delta','rcvfrom','user','alias'," "'config','shun','private','reportfmt'," "'concealed','accesslog','modreq'," | > | > | 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 | bag_init(&bagDone); ttyOutput = doOut; processCnt = 0; if (ttyOutput && !g.fQuiet) { percent_complete(0); } email_triggers_disable(); rebuild_update_schema(); blob_init(&sql, 0, 0); db_prepare(&q, "SELECT name FROM sqlite_master /*scan*/" " WHERE type='table'" " AND name NOT IN ('admin_log', 'blob','delta','rcvfrom','user','alias'," "'config','shun','private','reportfmt'," "'concealed','accesslog','modreq'," "'purgeevent','purgeitem','unversioned'," "'subscriber','pending_alert','email_bounce')" " AND name NOT GLOB 'sqlite_*'" " AND name NOT GLOB 'fx_*'" ); while( db_step(&q)==SQLITE_ROW ){ blob_appendf(&sql, "DROP TABLE IF EXISTS \"%w\";\n", db_column_text(&q,0)); } db_finalize(&q); |
︙ | ︙ | |||
444 445 446 447 448 449 450 451 452 453 454 455 456 457 | percent_complete((processCnt*1000)/totalSize); } if( doClustering ) create_cluster(); if( ttyOutput && !g.fQuiet && totalSize>0 ){ processCnt += incrSize; percent_complete((processCnt*1000)/totalSize); } if(!g.fQuiet && ttyOutput ){ percent_complete(1000); fossil_print("\n"); } return errCnt; } | > | 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 | percent_complete((processCnt*1000)/totalSize); } if( doClustering ) create_cluster(); if( ttyOutput && !g.fQuiet && totalSize>0 ){ processCnt += incrSize; percent_complete((processCnt*1000)/totalSize); } email_triggers_enable(); if(!g.fQuiet && ttyOutput ){ percent_complete(1000); fossil_print("\n"); } return errCnt; } |
︙ | ︙ |
Changes to src/regexp.c.
︙ | ︙ | |||
734 735 736 737 738 739 740 | */ int re_add_sql_func(sqlite3 *db){ return sqlite3_create_function(db, "regexp", 2, SQLITE_UTF8, 0, re_sql_func, 0, 0); } /* | | | | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 | */ int re_add_sql_func(sqlite3 *db){ return sqlite3_create_function(db, "regexp", 2, SQLITE_UTF8, 0, re_sql_func, 0, 0); } /* ** Run a "grep" over a single file read from disk. */ static void grep_file(ReCompiled *pRe, const char *zFile, FILE *in){ int ln = 0; int n; char zLine[2000]; while( fgets(zLine, sizeof(zLine), in) ){ ln++; n = (int)strlen(zLine); while( n && (zLine[n-1]=='\n' || zLine[n-1]=='\r') ) n--; if( re_match(pRe, (const unsigned char*)zLine, n) ){ fossil_print("%s:%d:%.*s\n", zFile, ln, n, zLine); } } } /* ** Flags for grep_buffer() */ #define GREP_EXISTS 0x001 /* If any match, print only the name and stop */ /* ** Run a "grep" over a text file */ static int grep_buffer( ReCompiled *pRe, const char *zName, const char *z, u32 flags ){ int i, j, n, ln, cnt; for(i=j=ln=cnt=0; z[i]; i=j+1){ for(j=i; z[j] && z[j]!='\n'; j++){} n = j - i; if( z[j]=='\n' ) j++; ln++; if( re_match(pRe, (const unsigned char*)(z+i), j-i) ){ cnt++; if( flags & GREP_EXISTS ){ fossil_print("%s\n", zName); break; } fossil_print("%s:%d:%.*s\n", zName, ln, n, z+i); } } return cnt; } /* ** COMMAND: test-grep ** ** Usage: %fossil test-grep REGEXP [FILE...] ** |
︙ | ︙ | |||
772 773 774 775 776 777 778 | int ignoreCase = find_option("ignore-case","i",0)!=0; if( g.argc<3 ){ usage("REGEXP [FILE...]"); } zErr = re_compile(&pRe, g.argv[2], ignoreCase); if( zErr ) fossil_fatal("%s", zErr); if( g.argc==3 ){ | | | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 | int ignoreCase = find_option("ignore-case","i",0)!=0; if( g.argc<3 ){ usage("REGEXP [FILE...]"); } zErr = re_compile(&pRe, g.argv[2], ignoreCase); if( zErr ) fossil_fatal("%s", zErr); if( g.argc==3 ){ grep_file(pRe, "-", stdin); }else{ int i; for(i=3; i<g.argc; i++){ FILE *in = fossil_fopen(g.argv[i], "rb"); if( in==0 ){ fossil_warning("cannot open \"%s\"", g.argv[i]); }else{ grep_file(pRe, g.argv[i], in); fclose(in); } } } re_free(pRe); } /* ** COMMAND: grep ** ** Usage: %fossil grep [OPTIONS] PATTERN FILENAME ** ** Run grep over all historic version of FILENAME ** ** Options: ** ** -i|--ignore-case Ignore case ** -l|--files-with-matches Print only filenames that match ** -v|--verbose Show each file as it is analyzed */ void re_grep_cmd(void){ u32 flags = 0; int bVerbose = 0; ReCompiled *pRe; const char *zErr; int ignoreCase = 0; Blob fullName; if( find_option("ignore-case","i",0)!=0 ) ignoreCase = 1; if( find_option("files-with-matches","l",0)!=0 ) flags |= GREP_EXISTS; if( find_option("verbose","v",0)!=0 ) bVerbose = 1; db_find_and_open_repository(0, 0); verify_all_options(); if( g.argc<3 ){ usage("REGEXP FILENAME"); } zErr = re_compile(&pRe, g.argv[2], ignoreCase); if( zErr ) fossil_fatal("%s", zErr); if( file_tree_name(g.argv[3], &fullName, 0, 0) ){ int fnid = db_int(0, "SELECT fnid FROM filename WHERE name=%Q", blob_str(&fullName)); if( fnid ){ Stmt q; add_content_sql_commands(g.db); db_prepare(&q, "SELECT content(ux), substr(ux,1,10) FROM (" " SELECT blob.uuid AS ux, min(event.mtime) AS mx" " FROM mlink, blob, event" " WHERE mlink.mid=event.objid" " AND mlink.fid=blob.rid" " AND mlink.fnid=%d" " GROUP BY blob.uuid" ") ORDER BY mx DESC;", fnid ); while( db_step(&q)==SQLITE_ROW ){ if( bVerbose ) fossil_print("%s:\n", db_column_text(&q,1)); grep_buffer(pRe, db_column_text(&q,1), db_column_text(&q,0), flags); } db_finalize(&q); } } } |
Changes to src/schema.c.
︙ | ︙ | |||
288 289 290 291 292 293 294 | @ -- between the plink and tagxref tables, but it is a slower join for @ -- very large repositories (repositories with 100,000 or more check-ins) @ -- and so it makes sense to precompute the set of leaves. There is @ -- one entry in the following table for each leaf. @ -- @ CREATE TABLE leaf(rid INTEGER PRIMARY KEY); @ | | > > > > > > | | 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 | @ -- between the plink and tagxref tables, but it is a slower join for @ -- very large repositories (repositories with 100,000 or more check-ins) @ -- and so it makes sense to precompute the set of leaves. There is @ -- one entry in the following table for each leaf. @ -- @ CREATE TABLE leaf(rid INTEGER PRIMARY KEY); @ @ -- Events used to generate a timeline. Type meanings: @ -- ci Check-ins @ -- e Technotes @ -- f Forum posts @ -- g Tags @ -- t Ticket changes @ -- w Wiki page edit @ -- @ CREATE TABLE event( @ type TEXT, -- Type of event: ci, e, f, g, t, w @ mtime DATETIME, -- Time of occurrence. Julian day. @ objid INTEGER PRIMARY KEY, -- Associated record ID @ tagid INTEGER, -- Associated ticket or wiki name tag @ uid INTEGER REFERENCES user, -- User who caused the event @ bgcolor TEXT, -- Color set by 'bgcolor' property @ euser TEXT, -- User set by 'user' property @ user TEXT, -- Name of the user |
︙ | ︙ | |||
540 541 542 543 544 545 546 | @ UNIQUE(id, merge) @ ); @ @ -- Identifier for this file type. @ -- The integer is the same as 'FSLC'. @ PRAGMA application_id=252006674; ; | > > > > > > > > > > > > > > > > > > > > > > > | 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 | @ UNIQUE(id, merge) @ ); @ @ -- Identifier for this file type. @ -- The integer is the same as 'FSLC'. @ PRAGMA application_id=252006674; ; /* ** The following table holds information about forum posts. It ** is created on-demand whenever the manifest parser encounters ** a forum-post artifact. */ static const char zForumSchema[] = @ CREATE TABLE repository.forumpost( @ fpid INTEGER PRIMARY KEY, -- BLOB.rid for the artifact @ froot INT, -- fpid of the thread root @ fprev INT, -- Previous version of this same post @ firt INT, -- This post is in-reply-to @ fmtime REAL -- When posted. Julian day @ ); @ CREATE INDEX repository.forumthread ON forumpost(froot,fmtime); ; /* Create the forum-post schema if it does not already exist */ void schema_forum(void){ if( !db_table_exists("repository","forumpost") ){ db_multi_exec("%s",zForumSchema/*safe-for-%s*/); } } |
Changes to src/search.c.
︙ | ︙ | |||
636 637 638 639 640 641 642 | #if INTERFACE /* What to search for */ #define SRCH_CKIN 0x0001 /* Search over check-in comments */ #define SRCH_DOC 0x0002 /* Search over embedded documents */ #define SRCH_TKT 0x0004 /* Search over tickets */ #define SRCH_WIKI 0x0008 /* Search over wiki */ #define SRCH_TECHNOTE 0x0010 /* Search over tech notes */ | > | > > | 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 | #if INTERFACE /* What to search for */ #define SRCH_CKIN 0x0001 /* Search over check-in comments */ #define SRCH_DOC 0x0002 /* Search over embedded documents */ #define SRCH_TKT 0x0004 /* Search over tickets */ #define SRCH_WIKI 0x0008 /* Search over wiki */ #define SRCH_TECHNOTE 0x0010 /* Search over tech notes */ #define SRCH_FORUM 0x0020 /* Search over forum messages */ #define SRCH_ALL 0x003f /* Search over everything */ #endif /* ** Remove bits from srchFlags which are disallowed by either the ** current server configuration or by user permissions. */ unsigned int search_restrict(unsigned int srchFlags){ static unsigned int knownGood = 0; static unsigned int knownBad = 0; static const struct { unsigned m; const char *zKey; } aSetng[] = { { SRCH_CKIN, "search-ci" }, { SRCH_DOC, "search-doc" }, { SRCH_TKT, "search-tkt" }, { SRCH_WIKI, "search-wiki" }, { SRCH_TECHNOTE, "search-technote" }, { SRCH_FORUM, "search-forum" }, }; int i; if( g.perm.Read==0 ) srchFlags &= ~(SRCH_CKIN|SRCH_DOC|SRCH_TECHNOTE); if( g.perm.RdTkt==0 ) srchFlags &= ~(SRCH_TKT); if( g.perm.RdWiki==0 ) srchFlags &= ~(SRCH_WIKI); if( g.perm.RdForum==0) srchFlags &= ~(SRCH_FORUM); for(i=0; i<count(aSetng); i++){ unsigned int m = aSetng[i].m; if( (srchFlags & m)==0 ) continue; if( ((knownGood|knownBad) & m)!=0 ) continue; if( db_get_boolean(aSetng[i].zKey,0) ){ knownGood |= m; }else{ |
︙ | ︙ | |||
791 792 793 794 795 796 797 798 799 800 801 802 803 804 | " 'e'||rid," " datetime(mtime)," " search_snippet()" " FROM technote" " WHERE search_match('',body('e',rid,NULL));" ); } } /* ** Number of significant bits in a u32 */ static int nbits(u32 x){ int n = 0; | > > > > > > > > > > > > > | 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 | " 'e'||rid," " datetime(mtime)," " search_snippet()" " FROM technote" " WHERE search_match('',body('e',rid,NULL));" ); } if( (srchFlags & SRCH_FORUM)!=0 ){ db_multi_exec( "INSERT INTO x(label,url,score,id,date,snip)" " SELECT 'Forum '||comment," " '/forumpost/'||uuid," " search_score()," " 'f'||rid," " datetime(event.mtime)," " search_snippet()" " FROM event JOIN blob on event.objid=blob.rid" " WHERE search_match('',body('f',rid,NULL));" ); } } /* ** Number of significant bits in a u32 */ static int nbits(u32 x){ int n = 0; |
︙ | ︙ | |||
911 912 913 914 915 916 917 918 919 920 921 922 923 924 | const char *zSep = " AND ("; static const struct { unsigned m; char c; } aMask[] = { { SRCH_CKIN, 'c' }, { SRCH_DOC, 'd' }, { SRCH_TKT, 't' }, { SRCH_WIKI, 'w' }, { SRCH_TECHNOTE, 'e' }, }; int i; for(i=0; i<count(aMask); i++){ if( srchFlags & aMask[i].m ){ blob_appendf(&sql, "%sftsdocs.type='%c'", zSep, aMask[i].c); zSep = " OR "; } | > | 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941 | const char *zSep = " AND ("; static const struct { unsigned m; char c; } aMask[] = { { SRCH_CKIN, 'c' }, { SRCH_DOC, 'd' }, { SRCH_TKT, 't' }, { SRCH_WIKI, 'w' }, { SRCH_TECHNOTE, 'e' }, { SRCH_FORUM, 'f' }, }; int i; for(i=0; i<count(aMask); i++){ if( srchFlags & aMask[i].m ){ blob_appendf(&sql, "%sftsdocs.type='%c'", zSep, aMask[i].c); zSep = " OR "; } |
︙ | ︙ | |||
1047 1048 1049 1050 1051 1052 1053 | ** categories. Any srchFlags with two or more bits set ** is treated like SRCH_ALL for display purposes. ** ** This routine automatically restricts srchFlag according to user ** permissions and the server configuration. The entry box is shown ** disabled if srchFlags is 0 after these restrictions are applied. ** | > > | | > > > > | > > > | > | 1064 1065 1066 1067 1068 1069 1070 1071 1072 1073 1074 1075 1076 1077 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 1105 1106 1107 1108 1109 1110 1111 1112 1113 1114 1115 1116 1117 1118 1119 1120 1121 1122 1123 1124 1125 1126 1127 1128 1129 | ** categories. Any srchFlags with two or more bits set ** is treated like SRCH_ALL for display purposes. ** ** This routine automatically restricts srchFlag according to user ** permissions and the server configuration. The entry box is shown ** disabled if srchFlags is 0 after these restrictions are applied. ** ** The mFlags value controls options: ** ** 0x01 If the y= query parameter is present, use it as an addition ** restriction what to search. ** ** 0x02 Show nothing if search is disabled. ** ** Return true if there are search results. */ int search_screen(unsigned srchFlags, int mFlags){ const char *zType = 0; const char *zClass = 0; const char *zDisable1; const char *zDisable2; const char *zPattern; int fDebug = PB("debug"); int haveResult = 0; srchFlags = search_restrict(srchFlags); switch( srchFlags ){ case SRCH_CKIN: zType = " Check-ins"; zClass = "Ckin"; break; case SRCH_DOC: zType = " Docs"; zClass = "Doc"; break; case SRCH_TKT: zType = " Tickets"; zClass = "Tkt"; break; case SRCH_WIKI: zType = " Wiki"; zClass = "Wiki"; break; case SRCH_TECHNOTE: zType = " Tech Notes"; zClass = "Note"; break; case SRCH_FORUM: zType = " Forum"; zClass = "Frm"; break; } if( srchFlags==0 ){ if( mFlags & 0x02 ) return 0; zDisable1 = " disabled"; zDisable2 = " disabled"; zPattern = ""; }else{ zDisable1 = ""; /* Was: " autofocus" */ zDisable2 = ""; zPattern = PD("s",""); } @ <form method='GET' action='%R/%T(g.zPath)'> if( zClass ){ @ <div class='searchForm searchForm%s(zClass)'> }else{ @ <div class='searchForm'> } @ <input type="text" name="s" size="40" value="%h(zPattern)"%s(zDisable1)> if( (mFlags & 0x01)!=0 && (srchFlags & (srchFlags-1))!=0 ){ static const struct { char *z; char *zNm; unsigned m; } aY[] = { { "all", "All", SRCH_ALL }, { "c", "Check-ins", SRCH_CKIN }, { "d", "Docs", SRCH_DOC }, { "t", "Tickets", SRCH_TKT }, { "w", "Wiki", SRCH_WIKI }, { "e", "Tech Notes", SRCH_TECHNOTE }, { "f", "Forum", SRCH_FORUM }, }; const char *zY = PD("y","all"); unsigned newFlags = srchFlags; int i; @ <select size='1' name='y'> for(i=0; i<count(aY); i++){ if( (aY[i].m & srchFlags)==0 ) continue; |
︙ | ︙ | |||
1125 1126 1127 1128 1129 1130 1131 1132 1133 1134 1135 1136 1137 1138 1139 1140 1141 1142 1143 1144 1145 1146 1147 1148 1149 1150 1151 1152 1153 1154 | }else{ @ <div class='searchResult'> } if( search_run_and_output(zPattern, srchFlags, fDebug)==0 ){ @ <p class='searchEmpty'>No matches for: <span>%h(zPattern)</span></p> } @ </div> } } /* ** WEBPAGE: search ** ** Search for check-in comments, documents, tickets, or wiki that ** match a user-supplied pattern. ** ** s=PATTERN Specify the full-text pattern to search for ** y=TYPE What to search. ** c -> check-ins ** d -> documentation ** t -> tickets ** w -> wiki ** e -> tech notes ** all -> everything */ void search_page(void){ login_check_credentials(); style_header("Search"); search_screen(SRCH_ALL, 1); style_footer(); | > > > | 1152 1153 1154 1155 1156 1157 1158 1159 1160 1161 1162 1163 1164 1165 1166 1167 1168 1169 1170 1171 1172 1173 1174 1175 1176 1177 1178 1179 1180 1181 1182 1183 1184 | }else{ @ <div class='searchResult'> } if( search_run_and_output(zPattern, srchFlags, fDebug)==0 ){ @ <p class='searchEmpty'>No matches for: <span>%h(zPattern)</span></p> } @ </div> haveResult = 1; } return haveResult; } /* ** WEBPAGE: search ** ** Search for check-in comments, documents, tickets, or wiki that ** match a user-supplied pattern. ** ** s=PATTERN Specify the full-text pattern to search for ** y=TYPE What to search. ** c -> check-ins ** d -> documentation ** t -> tickets ** w -> wiki ** e -> tech notes ** f -> forum ** all -> everything */ void search_page(void){ login_check_credentials(); style_header("Search"); search_screen(SRCH_ALL, 1); style_footer(); |
︙ | ︙ | |||
1248 1249 1250 1251 1252 1253 1254 1255 1256 1257 1258 1259 1260 1261 | ** full text search and/or for constructing a search result snippet. ** ** cType: d Embedded documentation ** w Wiki page ** c Check-in comment ** t Ticket text ** e Tech note ** ** rid The RID of an artifact that defines the object ** being searched. ** ** zName Name of the object being searched. This is used ** only to help figure out the mimetype (text/plain, ** test/html, test/x-fossil-wiki, or text/x-markdown) | > | 1278 1279 1280 1281 1282 1283 1284 1285 1286 1287 1288 1289 1290 1291 1292 | ** full text search and/or for constructing a search result snippet. ** ** cType: d Embedded documentation ** w Wiki page ** c Check-in comment ** t Ticket text ** e Tech note ** f Forum ** ** rid The RID of an artifact that defines the object ** being searched. ** ** zName Name of the object being searched. This is used ** only to help figure out the mimetype (text/plain, ** test/html, test/x-fossil-wiki, or text/x-markdown) |
︙ | ︙ | |||
1273 1274 1275 1276 1277 1278 1279 1280 1281 1282 | Blob doc; content_get(rid, &doc); blob_to_utf8_no_bom(&doc, 0); get_stext_by_mimetype(&doc, mimetype_from_name(zName), pOut); blob_reset(&doc); break; } case 'e': /* Tech Notes */ case 'w': { /* Wiki */ Manifest *pWiki = manifest_get(rid, | > | > > > > > > > > | > | 1304 1305 1306 1307 1308 1309 1310 1311 1312 1313 1314 1315 1316 1317 1318 1319 1320 1321 1322 1323 1324 1325 1326 1327 1328 1329 1330 1331 1332 1333 1334 | Blob doc; content_get(rid, &doc); blob_to_utf8_no_bom(&doc, 0); get_stext_by_mimetype(&doc, mimetype_from_name(zName), pOut); blob_reset(&doc); break; } case 'f': /* Forum messages */ case 'e': /* Tech Notes */ case 'w': { /* Wiki */ Manifest *pWiki = manifest_get(rid, cType == 'e' ? CFTYPE_EVENT : cType == 'f' ? CFTYPE_FORUM : CFTYPE_WIKI, 0); Blob wiki; if( pWiki==0 ) break; if( cType=='f' ){ blob_init(&wiki, 0, 0); if( pWiki->zThreadTitle ){ blob_appendf(&wiki, "<h1>%h</h1>\n", pWiki->zThreadTitle); } blob_appendf(&wiki, "From %s:\n\n%s", pWiki->zUser, pWiki->zWiki); }else{ blob_init(&wiki, pWiki->zWiki, -1); } get_stext_by_mimetype(&wiki, wiki_filter_mimetypes(pWiki->zMimetype), pOut); blob_reset(&wiki); manifest_destroy(pWiki); break; } case 'c': { /* Check-in Comments */ |
︙ | ︙ | |||
1514 1515 1516 1517 1518 1519 1520 | ); db_multi_exec( "INSERT OR IGNORE INTO ftsdocs(type,rid,idxed)" " SELECT 't', tkt_id, 0 FROM ticket;" ); db_multi_exec( "INSERT OR IGNORE INTO ftsdocs(type,rid,name,idxed)" | | | 1555 1556 1557 1558 1559 1560 1561 1562 1563 1564 1565 1566 1567 1568 1569 | ); db_multi_exec( "INSERT OR IGNORE INTO ftsdocs(type,rid,idxed)" " SELECT 't', tkt_id, 0 FROM ticket;" ); db_multi_exec( "INSERT OR IGNORE INTO ftsdocs(type,rid,name,idxed)" " SELECT type, objid, comment, 0 FROM event WHERE type IN ('e','f');" ); } /* ** The document described by cType,rid,zName is about to be added or ** updated. If the document has already been indexed, then unindex it ** now while we still have access to the old content. Add the document |
︙ | ︙ | |||
1551 1552 1553 1554 1555 1556 1557 1558 1559 1560 1561 1562 1563 1564 | cType, zName ); db_multi_exec( "DELETE FROM ftsdocs WHERE type='%c' AND name=%Q AND rid!=%d", cType, zName, rid ); } } } /* ** If the doc-glob and doc-br settings are valid for document search ** and if the latest check-in on doc-br is in the unindexed set of ** check-ins, then update all 'd' entries in FTSDOCS that have | > | 1592 1593 1594 1595 1596 1597 1598 1599 1600 1601 1602 1603 1604 1605 1606 | cType, zName ); db_multi_exec( "DELETE FROM ftsdocs WHERE type='%c' AND name=%Q AND rid!=%d", cType, zName, rid ); } /* All forum posts are always indexed */ } } /* ** If the doc-glob and doc-br settings are valid for document search ** and if the latest check-in on doc-br is in the unindexed set of ** check-ins, then update all 'd' entries in FTSDOCS that have |
︙ | ︙ | |||
1679 1680 1681 1682 1683 1684 1685 1686 1687 1688 1689 1690 1691 1692 | " 'Wiki: '||ftsdocs.name," " '/wiki?name='||urlencode(ftsdocs.name)," " tagxref.mtime" " FROM tagxref WHERE tagxref.rid=ftsdocs.rid)" " WHERE ftsdocs.type='w' AND NOT ftsdocs.idxed" ); } /* ** Deal with all of the unindexed 'e' terms in FTSDOCS */ static void search_update_technote_index(void){ db_multi_exec( "INSERT INTO ftsidx(docid,title,body)" | > > > > > > > > > > > > > > > > > > > > > > > | 1721 1722 1723 1724 1725 1726 1727 1728 1729 1730 1731 1732 1733 1734 1735 1736 1737 1738 1739 1740 1741 1742 1743 1744 1745 1746 1747 1748 1749 1750 1751 1752 1753 1754 1755 1756 1757 | " 'Wiki: '||ftsdocs.name," " '/wiki?name='||urlencode(ftsdocs.name)," " tagxref.mtime" " FROM tagxref WHERE tagxref.rid=ftsdocs.rid)" " WHERE ftsdocs.type='w' AND NOT ftsdocs.idxed" ); } /* ** Deal with all of the unindexed 'f' terms in FTSDOCS */ static void search_update_forum_index(void){ db_multi_exec( "INSERT INTO ftsidx(docid,title,body)" " SELECT rowid, title('f',rid,NULL),body('f',rid,NULL) FROM ftsdocs" " WHERE type='f' AND NOT idxed;" ); if( db_changes()==0 ) return; db_multi_exec( "UPDATE ftsdocs SET idxed=1, name=NULL," " (label,url,mtime) = " " (SELECT 'Forum '||event.comment," " '/forumpost/'||blob.uuid," " event.mtime" " FROM event, blob" " WHERE event.objid=ftsdocs.rid" " AND blob.rid=ftsdocs.rid)" "WHERE ftsdocs.type='f' AND NOT ftsdocs.idxed" ); } /* ** Deal with all of the unindexed 'e' terms in FTSDOCS */ static void search_update_technote_index(void){ db_multi_exec( "INSERT INTO ftsidx(docid,title,body)" |
︙ | ︙ | |||
1726 1727 1728 1729 1730 1731 1732 1733 1734 1735 1736 1737 1738 1739 | } if( srchFlags & SRCH_WIKI ){ search_update_wiki_index(); } if( srchFlags & SRCH_TECHNOTE ){ search_update_technote_index(); } } /* ** Construct, prepopulate, and then update the full-text index. */ void search_rebuild_index(void){ fossil_print("rebuilding the search index..."); | > > > | 1791 1792 1793 1794 1795 1796 1797 1798 1799 1800 1801 1802 1803 1804 1805 1806 1807 | } if( srchFlags & SRCH_WIKI ){ search_update_wiki_index(); } if( srchFlags & SRCH_TECHNOTE ){ search_update_technote_index(); } if( srchFlags & SRCH_FORUM ){ search_update_forum_index(); } } /* ** Construct, prepopulate, and then update the full-text index. */ void search_rebuild_index(void){ fossil_print("rebuilding the search index..."); |
︙ | ︙ | |||
1778 1779 1780 1781 1782 1783 1784 1785 1786 1787 1788 1789 1790 1791 | }; static const struct { char *zSetting; char *zName; char *zSw; } aSetng[] = { { "search-ci", "check-in search:", "c" }, { "search-doc", "document search:", "d" }, { "search-tkt", "ticket search:", "t" }, { "search-wiki", "wiki search:", "w" }, { "search-technote", "tech note search:", "e" }, }; char *zSubCmd = 0; int i, j, n; int iCmd = 0; int iAction = 0; db_find_and_open_repository(0, 0); if( g.argc>2 ){ | > | 1846 1847 1848 1849 1850 1851 1852 1853 1854 1855 1856 1857 1858 1859 1860 | }; static const struct { char *zSetting; char *zName; char *zSw; } aSetng[] = { { "search-ci", "check-in search:", "c" }, { "search-doc", "document search:", "d" }, { "search-tkt", "ticket search:", "t" }, { "search-wiki", "wiki search:", "w" }, { "search-technote", "tech note search:", "e" }, { "search-forum", "forum search:", "f" }, }; char *zSubCmd = 0; int i, j, n; int iCmd = 0; int iAction = 0; db_find_and_open_repository(0, 0); if( g.argc>2 ){ |
︙ | ︙ |
Changes to src/security_audit.c.
︙ | ︙ | |||
56 57 58 59 60 61 62 | @ <ol> /* Step 1: Determine if the repository is public or private. "Public" ** means that any anonymous user on the internet can access all content. ** "Private" repos require (non-anonymous) login to access all content, ** though some content may be accessible anonymously. */ | | < | > > > > | 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 | @ <ol> /* Step 1: Determine if the repository is public or private. "Public" ** means that any anonymous user on the internet can access all content. ** "Private" repos require (non-anonymous) login to access all content, ** though some content may be accessible anonymously. */ zAnonCap = db_text("", "SELECT fullcap(NULL)"); zPubPages = db_get("public-pages",0); if( hasAnyCap(zAnonCap,"as") ){ @ <li><p>This repository is <big><b>Wildly INSECURE</b></big> because @ it grants administrator privileges to anonymous users. You @ should <a href="takeitprivate">take this repository private</a> @ immediately! Or, at least remove the Setup and Admin privileges @ for users "anonymous" and "login" on the @ <a href="setup_ulist">User Configuration</a> page. }else if( hasAnyCap(zAnonCap,"y") ){ @ <li><p>This repository is <big><b>INSECURE</b></big> because @ it allows anonymous users to push unversioned files. @ <p>Fix this by <a href="takeitprivate">taking the repository private</a> @ or by removing the "y" permission from users "anonymous" and @ "nobody" on the <a href="setup_ulist">User Configuration</a> page. }else if( hasAnyCap(zAnonCap,"goz") ){ @ <li><p>This repository is <big><b>PUBLIC</b></big>. All @ checked-in content can be accessed by anonymous users. @ <a href="takeitprivate">Take it private</a>.<p> }else if( !hasAnyCap(zAnonCap, "jrwy234567") && (zPubPages==0 || zPubPages[0]==0) ){ @ <li><p>This repository is <big><b>Completely PRIVATE</b></big>. @ A valid login and password is required to access any content. }else{ @ <li><p>This repository is <big><b>Mostly PRIVATE</b></big>. @ A valid login and password is usually required, however some @ content can be accessed anonymously: @ <ul> if( hasAnyCap(zAnonCap,"j") ){ @ <li> Wiki pages } if( hasAnyCap(zAnonCap,"r") ){ @ <li> Tickets } if( hasAnyCap(zAnonCap,"234567") ){ @ <li> Forum posts } if( zPubPages && zPubPages[0] ){ Glob *pGlob = glob_create(zPubPages); int i; @ <li> URLs that match any of these GLOB patterns: @ <ul> for(i=0; i<pGlob->nPattern; i++){ |
︙ | ︙ | |||
124 125 126 127 128 129 130 | /* Anonymous users should not be able to harvest email addresses ** from tickets. */ if( hasAnyCap(zAnonCap, "e") ){ @ <li><p><b>WARNING:</b> @ Anonymous users can view email addresses and other personally @ identifiable information on tickets. | | > | > | | | | > | | | 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 | /* Anonymous users should not be able to harvest email addresses ** from tickets. */ if( hasAnyCap(zAnonCap, "e") ){ @ <li><p><b>WARNING:</b> @ Anonymous users can view email addresses and other personally @ identifiable information on tickets. @ <p>Fix this by removing the "Email" privilege @ (<a href="setup_ucap_list">capability "e"</a>) from users @ "anonymous" and "nobody" on the @ <a href="setup_ulist">User Configuration</a> page. } /* Anonymous users probably should not be allowed to push content ** to the repository. */ if( hasAnyCap(zAnonCap, "i") ){ @ <li><p><b>WARNING:</b> @ Anonymous users can push new check-ins into the repository. @ <p>Fix this by removing the "Check-in" privilege @ (<a href="setup_ucap_list">capability</a> "i") from users @ "anonymous" and "nobody" on the @ <a href="setup_ulist">User Configuration</a> page. } /* Anonymous users probably should not be allowed act as moderators ** for wiki or tickets. */ if( hasAnyCap(zAnonCap, "lq5") ){ @ <li><p><b>WARNING:</b> @ Anonymous users can act as moderators for wiki, tickets, or @ forum posts. This defeats the whole purpose of moderation. @ <p>Fix this by removing the "Mod-Wiki", "Mod-Tkt", and "Mod-Forum" @ privileges (<a href="%R/setup_ucap_list">capabilities</a> "fq5") @ from users "anonymous" and "nobody" @ on the <a href="setup_ulist">User Configuration</a> page. } /* Anonymous users probably should not be allowed to delete ** wiki or tickets. */ if( hasAnyCap(zAnonCap, "d") ){ @ <li><p><b>WARNING:</b> |
︙ | ︙ | |||
172 173 174 175 176 177 178 | */ if( hasAnyCap(zAnonCap, "fk") ){ if( db_get_boolean("modreq-wiki",0)==0 ){ @ <li><p><b>WARNING:</b> @ Anonymous users can create or edit wiki without moderation. @ This can result in robots inserting lots of wiki spam into @ repository. | | > > > > > > > > > > > > > > > > > > > > > > > > > > | > > > > | | > | | > | | | | | | | < | | 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 | */ if( hasAnyCap(zAnonCap, "fk") ){ if( db_get_boolean("modreq-wiki",0)==0 ){ @ <li><p><b>WARNING:</b> @ Anonymous users can create or edit wiki without moderation. @ This can result in robots inserting lots of wiki spam into @ repository. @ Fix this by removing the "New-Wiki" and "Write-Wiki" @ privileges from users "anonymous" and "nobody" on the @ <a href="setup_ulist">User Configuration</a> page or @ by enabling wiki moderation on the @ <a href="setup_modreq">Moderation Setup</a> page. }else{ @ <li><p> @ Anonymous users can create or edit wiki, but moderator @ approval is required before the edits become permanent. } } /* Anonymous users should not be able to create trusted forum ** posts. */ if( hasAnyCap(zAnonCap, "456") ){ @ <li><p><b>WARNING:</b> @ Anonymous users can create forum posts that are @ accepted into the permanent record without moderation. @ This can result in robots generating spam on forum posts. @ Fix this by removing the "WriteTrusted-Forum" privilege @ (<a href="setup_ucap_list">capabilities</a> "456") from @ users "anonymous" and "nobody" on the @ <a href="setup_ulist">User Configuration</a> page or } /* Anonymous users should not be able to send announcements. */ if( hasAnyCap(zAnonCap, "A") ){ @ <li><p><b>WARNING:</b> @ Anonymous users can send announcements to anybody who is signed @ up to receive announcements. This can result in spam. @ Fix this by removing the "Announce" privilege @ (<a href="setup_ucap_list">capability</a> "A") from @ users "anonymous" and "nobody" on the @ <a href="setup_ulist">User Configuration</a> page or } /* Administrative privilege should only be provided to ** specific individuals, not to entire classes of people. ** And not too many people should have administrator privilege. */ z = db_text(0, "SELECT group_concat(" "printf('<a href=''setup_uedit?id=%%d''>%%s</a>',uid,login)," "' and ')" " FROM user" " WHERE cap GLOB '*[as]*'" " AND login in ('anonymous','nobody','reader','developer')" ); if( z && z[0] ){ @ <li><p><b>WARNING:</b> @ Administrative privilege ('a' or 's') @ is granted to an entire class of users: %s(z). @ Administrative privilege should only be @ granted to specific individuals. } n = db_int(0,"SELECT count(*) FROM user WHERE fullcap(cap) GLOB '*[as]*'"); if( n==0 ){ @ <li><p> @ No users have administrator privilege. }else{ z = db_text(0, "SELECT group_concat(" "printf('<a href=''setup_uedit?id=%%d''>%%s</a>',uid,login)," "', ')" " FROM user" " WHERE fullcap(cap) GLOB '*[as]*'" ); @ <li><p> @ Users with administrator privilege are: %s(z) fossil_free(z); if( n>3 ){ @ <li><p><b>WARNING:</b> @ Administrator privilege is granted to @ <a href='setup_ulist?with=as'>%d(n) users</a>. @ Ideally, administator privilege ('s' or 'a') should only @ be granted to one or two users. } } /* The push-unversioned privilege should only be provided to ** specific individuals, not to entire classes of people. ** And no too many people should have this privilege. */ z = db_text(0, "SELECT group_concat(" "printf('<a href=''setup_uedit?id=%%d''>%%s</a>',uid,login)," "' and ')" " FROM user" " WHERE cap GLOB '*y*'" " AND login in ('anonymous','nobody','reader','developer')" ); if( z && z[0] ){ @ <li><p><b>WARNING:</b> @ The "Write-Unver" privilege is granted to an entire class of users: %s(z). @ The Write-Unver privilege should only be granted to specific individuals. fossil_free(z); } n = db_int(0,"SELECT count(*) FROM user WHERE cap GLOB '*y*'"); if( n>0 ){ z = db_text(0, "SELECT group_concat(" "printf('<a href=''setup_uedit?id=%%d''>%%s</a>',uid,login),', ')" " FROM user WHERE fullcap(cap) GLOB '*y*'" ); @ <li><p> @ Users with "Write-Unver" privilege: %s(z) fossil_free(z); if( n>3 ){ @ <p><b>Caution:</b> @ The "Write-Unver" privilege ('y') is granted to an excessive |
︙ | ︙ | |||
323 324 325 326 327 328 329 330 331 332 333 334 335 336 | @ The "Server Load Average Limit" on the @ <a href="setup_access">Access Control</a> page is set to %g(r), @ which seems high. Is this server really a %d((int)r)-core machine? } } #endif @ </ol> style_footer(); } /* ** WEBPAGE: takeitprivate | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 | @ The "Server Load Average Limit" on the @ <a href="setup_access">Access Control</a> page is set to %g(r), @ which seems high. Is this server really a %d((int)r)-core machine? } } #endif if( g.zErrlog==0 || fossil_strcmp(g.zErrlog,"-")==0 ){ @ <li><p> @ The server error log is disabled. @ To set up an error log: @ <ul> @ <li>If running from CGI, make an entry "errorlog: <i>FILENAME</i>" @ in the CGI script. @ <li>If running the "fossil server" or "fossil http" commands, @ add the "--errorlog <i>FILENAME</i>" command-line option. @ </ul> }else{ FILE *pTest = fossil_fopen(g.zErrlog,"a"); if( pTest==0 ){ @ <li><p> @ <b>Error:</b> @ There is an error log at "%h(g.zErrlog)" but that file is not @ writable and so no logging will occur. }else{ fclose(pTest); @ <li><p> @ The error log at "<a href='%R/errorlog'>%h(g.zErrlog)</a>" that is @ %,lld(file_size(g.zErrlog, ExtFILE)) bytes in size. } } @ <li><p> User capability summary: capability_summary(); if( email_enabled() ){ @ <li><p> Email alert configuration summary: @ <table class="label-value"> stats_for_email(); @ </table> }else{ @ <li><p> Email alerts are disabled } @ </ol> style_footer(); } /* ** WEBPAGE: takeitprivate |
︙ | ︙ | |||
366 367 368 369 370 371 372 | @ <form action="%s(g.zPath)" method="post"> @ <input type="submit" name="apply" value="Make It Private"> @ <input type="submit" name="cancel" value="Cancel"> @ </form> style_footer(); } | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 | @ <form action="%s(g.zPath)" method="post"> @ <input type="submit" name="apply" value="Make It Private"> @ <input type="submit" name="cancel" value="Cancel"> @ </form> style_footer(); } /* ** The maximum number of bytes of log to show */ #define MXSHOWLOG 50000 /* ** WEBPAGE: errorlog ** ** Show the content of the error log. Only the administrator can view ** this page. */ void errorlog_page(void){ i64 szFile; FILE *in; char z[10000]; login_check_credentials(); if( !g.perm.Setup && !g.perm.Admin ){ login_needed(0); return; } style_header("Server Error Log"); style_submenu_element("Test", "%R/test-warning"); style_submenu_element("Refresh", "%R/errorlog"); if( g.zErrlog==0 || fossil_strcmp(g.zErrlog,"-")==0 ){ @ <p>To create a server error log: @ <ol> @ <li><p> @ If the server is running as CGI, then create a line in the CGI file @ like this: @ <blockquote><pre> @ errorlog: <i>FILENAME</i> @ </pre></blockquote> @ <li><p> @ If the server is running using one of @ the "fossil http" or "fossil server" commands then add @ a command-line option "--errorlog <i>FILENAME</i>" to that @ command. @ </ol> style_footer(); return; } if( P("truncate1") && cgi_csrf_safe(1) ){ fclose(fopen(g.zErrlog,"w")); } if( P("download") ){ Blob log; blob_read_from_file(&log, g.zErrlog, ExtFILE); cgi_set_content_type("text/plain"); cgi_set_content(&log); return; } szFile = file_size(g.zErrlog, ExtFILE); if( P("truncate") ){ @ <form action="%R/errorlog" method="POST"> @ <p>Confirm that you want to truncate the %,lld(szFile)-byte error log: @ <input type="submit" name="truncate1" value="Confirm"> @ <input type="submit" name="cancel" value="Cancel"> @ </form> style_footer(); return; } @ <p>The server error log at "%h(g.zErrlog)" is %,lld(szFile) bytes in size. style_submenu_element("Download", "%R/errorlog?download"); style_submenu_element("Truncate", "%R/errorlog?truncate"); in = fossil_fopen(g.zErrlog, "rb"); if( in==0 ){ @ <p class='generalError'>Unable top open that file for reading!</p> style_footer(); return; } if( szFile>MXSHOWLOG && P("all")==0 ){ @ <form action="%R/errorlog" method="POST"> @ <p>Only the last %,d(MXSHOWLOG) bytes are shown. @ <input type="submit" name="all" value="Show All"> @ </form> fseek(in, -MXSHOWLOG, SEEK_END); } @ <hr> @ <pre> while( fgets(z, sizeof(z), in) ){ @ %h(z)\ } fclose(in); @ </pre> style_footer(); } |
Changes to src/setup.c.
1 2 3 4 5 6 | /* ** Copyright (c) 2007 D. Richard Hipp ** ** This program is free software; you can redistribute it and/or ** modify it under the terms of the Simplified BSD License (also ** known as the "2-Clause License" or "FreeBSD License".) | | | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 | /* ** Copyright (c) 2007 D. Richard Hipp ** ** This program is free software; you can redistribute it and/or ** modify it under the terms of the Simplified BSD License (also ** known as the "2-Clause License" or "FreeBSD License".) ** ** This program is distributed in the hope that it will be useful, ** but without any warranty; without even the implied warranty of ** merchantability or fitness for a particular purpose. ** ** Author contact information: ** drh@hwaci.com ** http://www.hwaci.com/drh/ |
︙ | ︙ | |||
72 73 74 75 76 77 78 | style_header("Server Administration"); /* Make sure the header contains <base href="...">. Issue a warning ** if it does not. */ if( !cgi_header_contains("<base href=") ){ @ <p class="generalError"><b>Configuration Error:</b> Please add @ <tt><base href="$secureurl/$current_page"></tt> after | | > | 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 | style_header("Server Administration"); /* Make sure the header contains <base href="...">. Issue a warning ** if it does not. */ if( !cgi_header_contains("<base href=") ){ @ <p class="generalError"><b>Configuration Error:</b> Please add @ <tt><base href="$secureurl/$current_page"></tt> after @ <tt><head></tt> in the @ <a href="setup_skinedit?w=2">HTML header</a>!</p> } #if !defined(_WIN32) /* Check for /dev/null and /dev/urandom. We want both devices to be present, ** but they are sometimes omitted (by mistake) from chroot jails. */ if( access("/dev/null", R_OK|W_OK) ){ @ <p class="generalError">WARNING: Device "/dev/null" is not available |
︙ | ︙ | |||
111 112 113 114 115 116 117 118 119 120 121 122 123 124 | " on the same server"); setup_menu_entry("Tickets", "tktsetup", "Configure the trouble-ticketing system for this repository"); setup_menu_entry("Search","srchsetup", "Configure the built-in search engine"); setup_menu_entry("URL Aliases", "waliassetup", "Configure URL aliases"); setup_menu_entry("Transfers", "xfersetup", "Configure the transfer system for this repository"); setup_menu_entry("Skins", "setup_skin", "Select and/or modify the web interface \"skins\""); setup_menu_entry("Moderation", "setup_modreq", "Enable/Disable requiring moderator approval of Wiki and/or Ticket" " changes and attachments."); | > > > > | 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 | " on the same server"); setup_menu_entry("Tickets", "tktsetup", "Configure the trouble-ticketing system for this repository"); setup_menu_entry("Search","srchsetup", "Configure the built-in search engine"); setup_menu_entry("URL Aliases", "waliassetup", "Configure URL aliases"); setup_menu_entry("Notification", "setup_notification", "Automatic notifications of changes via outbound email"); setup_menu_entry("Email-Server", "setup_smtp", "Activate and configure the built-in email server"); setup_menu_entry("Transfers", "xfersetup", "Configure the transfer system for this repository"); setup_menu_entry("Skins", "setup_skin", "Select and/or modify the web interface \"skins\""); setup_menu_entry("Moderation", "setup_modreq", "Enable/Disable requiring moderator approval of Wiki and/or Ticket" " changes and attachments."); |
︙ | ︙ | |||
134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 | "Show artifacts that are shunned by this repository"); setup_menu_entry("Artifact Receipts Log", "rcvfromlist", "A record of received artifacts and their sources"); setup_menu_entry("User Log", "access_log", "A record of login attempts"); setup_menu_entry("Administrative Log", "admin_log", "View the admin_log entries"); setup_menu_entry("Unversioned Files", "uvlist?byage=1", "Show all unversioned files held"); setup_menu_entry("Stats", "stat", "Repository Status Reports"); setup_menu_entry("Sitemap", "sitemap", "Links to miscellaneous pages"); setup_menu_entry("SQL", "admin_sql", "Enter raw SQL commands"); setup_menu_entry("TH1", "admin_th1", "Enter raw TH1 commands"); @ </table> style_footer(); } | > > < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < | | 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 | "Show artifacts that are shunned by this repository"); setup_menu_entry("Artifact Receipts Log", "rcvfromlist", "A record of received artifacts and their sources"); setup_menu_entry("User Log", "access_log", "A record of login attempts"); setup_menu_entry("Administrative Log", "admin_log", "View the admin_log entries"); setup_menu_entry("Error Log", "errorlog", "View the Fossil server error log"); setup_menu_entry("Unversioned Files", "uvlist?byage=1", "Show all unversioned files held"); setup_menu_entry("Stats", "stat", "Repository Status Reports"); setup_menu_entry("Sitemap", "sitemap", "Links to miscellaneous pages"); setup_menu_entry("SQL", "admin_sql", "Enter raw SQL commands"); setup_menu_entry("TH1", "admin_th1", "Enter raw TH1 commands"); @ </table> style_footer(); } /* ** Generate a checkbox for an attribute. */ void onoff_attribute( const char *zLabel, /* The text label on the checkbox */ const char *zVar, /* The corresponding row in the VAR table */ const char *zQParm, /* The query parameter */ int dfltVal, /* Default value if VAR table entry does not exist */ int disabled /* 1 if disabled */ ){ const char *zQ = P(zQParm); |
︙ | ︙ | |||
971 972 973 974 975 976 977 | const int nZQ = (int)strlen(zQ); login_verify_csrf_secret(); db_set(zVar, zQ, 0); admin_log("Set entry_attribute %Q to: %.*s%s", zVar, 20, zQ, (nZQ>20 ? "..." : "")); zVal = zQ; } | | > | | 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 | const int nZQ = (int)strlen(zQ); login_verify_csrf_secret(); db_set(zVar, zQ, 0); admin_log("Set entry_attribute %Q to: %.*s%s", zVar, 20, zQ, (nZQ>20 ? "..." : "")); zVal = zQ; } @ <input type="text" id="%s(zQParm)" name="%s(zQParm)" value="%h(zVal)" \ @ size="%d(width)" \ if( disabled ){ @ disabled="disabled" \ } @ /> <b>%s(zLabel)</b> } /* ** Generate a text box for an attribute. */ |
︙ | ︙ | |||
1016 1017 1018 1019 1020 1021 1022 | } return z; } /* ** Generate a text box for an attribute. */ | | | 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 | } return z; } /* ** Generate a text box for an attribute. */ void multiple_choice_attribute( const char *zLabel, /* The text label on the menu */ const char *zVar, /* The corresponding row in the VAR table */ const char *zQP, /* The query parameter */ const char *zDflt, /* Default value if VAR table entry does not exist */ int nChoice, /* Number of choices */ const char *const *azChoice /* Choices. 2 per choice: (VAR value, Display) */ ){ |
︙ | ︙ | |||
1242 1243 1244 1245 1246 1247 1248 1249 1250 1251 1252 1253 1254 1255 | @ <hr /> entry_attribute("Default privileges", 10, "default-perms", "defaultperms", "u", 0); @ <p>Permissions given to users that... <ul><li>register themselves using @ the self-registration procedure (if enabled), or <li>access "public" @ pages identified by the public-pages glob pattern above, or <li> @ are users newly created by the administrator.</ul> @ (Property: "default-perms") @ </p> @ <hr /> onoff_attribute("Show javascript button to fill in CAPTCHA", "auto-captcha", "autocaptcha", 0, 0); @ <p>When enabled, a button appears on the login screen for user | > > | 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 | @ <hr /> entry_attribute("Default privileges", 10, "default-perms", "defaultperms", "u", 0); @ <p>Permissions given to users that... <ul><li>register themselves using @ the self-registration procedure (if enabled), or <li>access "public" @ pages identified by the public-pages glob pattern above, or <li> @ are users newly created by the administrator.</ul> @ <p>Recommended value: "u" for Reader. @ <a href="%R/setup_ucap_list">Capability Key</a>. @ (Property: "default-perms") @ </p> @ <hr /> onoff_attribute("Show javascript button to fill in CAPTCHA", "auto-captcha", "autocaptcha", 0, 0); @ <p>When enabled, a button appears on the login screen for user |
︙ | ︙ | |||
1362 1363 1364 1365 1366 1367 1368 | @ </form></p> @ <br />For best results, use the same number of <a href="setup_access#ipt"> @ IP octets</a> in the login cookie across all repositories in the @ same Login Group. @ <hr /><h2>Implementation Details</h2> @ <p>The following are fields from the CONFIG table related to login-groups, @ provided here for instructional and debugging purposes:</p> | | > | 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 | @ </form></p> @ <br />For best results, use the same number of <a href="setup_access#ipt"> @ IP octets</a> in the login cookie across all repositories in the @ same Login Group. @ <hr /><h2>Implementation Details</h2> @ <p>The following are fields from the CONFIG table related to login-groups, @ provided here for instructional and debugging purposes:</p> @ <table border='1' class='sortable' data-column-types='ttt' \ @ data-init-sort='1'> @ <thead><tr> @ <th>Config.Name<th>Config.Value<th>Config.mtime</tr> @ </thead><tbody> db_prepare(&q, "SELECT name, value, datetime(mtime,'unixepoch') FROM config" " WHERE name GLOB 'peer-*'" " OR name GLOB 'project-*'" " OR name GLOB 'login-group-*'" |
︙ | ︙ | |||
1456 1457 1458 1459 1460 1461 1462 | @ %s(zTmDiff) hours ahead of UTC.</p> } @ <p>(Property: "timeline-utc") @ <hr /> multiple_choice_attribute("Per-Item Time Format", "timeline-date-format", "tdf", "0", count(azTimeFormats)/2, azTimeFormats); @ <p>If the "HH:MM" or "HH:MM:SS" format is selected, then the date is shown | | | | | < < < < < < < < < | 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 | @ %s(zTmDiff) hours ahead of UTC.</p> } @ <p>(Property: "timeline-utc") @ <hr /> multiple_choice_attribute("Per-Item Time Format", "timeline-date-format", "tdf", "0", count(azTimeFormats)/2, azTimeFormats); @ <p>If the "HH:MM" or "HH:MM:SS" format is selected, then the date is shown @ in a separate box (using CSS class "timelineDate") whenever the date @ changes. With the "YYYY-MM-DD HH:MM" and "YYMMDD ..." formats, @ the complete date and time is shown on every timeline entry using the @ CSS class "timelineTime". (Property: "timeline-date-format")</p> @ <hr /> entry_attribute("Max timeline comment length", 6, "timeline-max-comment", "tmc", "0", 0); @ <p>The maximum length of a comment to be displayed in a timeline. @ "0" there is no length limit. @ (Property: "timeline-max-comment")</p> |
︙ | ︙ | |||
1604 1605 1606 1607 1608 1609 1610 | "project-description", "pd", "", 0); @ <p>Describe your project. This will be used in page headers for search @ engines as well as a short RSS description. @ (Property: "project-description")</p> @ <hr /> entry_attribute("Tarball and ZIP-archive Prefix", 20, "short-project-name", "spn", "", 0); | | | | | | | | 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 | "project-description", "pd", "", 0); @ <p>Describe your project. This will be used in page headers for search @ engines as well as a short RSS description. @ (Property: "project-description")</p> @ <hr /> entry_attribute("Tarball and ZIP-archive Prefix", 20, "short-project-name", "spn", "", 0); @ <p>This is used as a prefix on the names of generated tarballs and @ ZIP archive. For best results, keep this prefix brief and avoid special @ characters such as "/" and "\". @ If no tarball prefix is specified, then the full Project Name above is used. @ (Property: "short-project-name") @ </p> @ <hr /> entry_attribute("Download Tag", 20, "download-tag", "dlt", "trunk", 0); @ <p>The <a href='%R/download'>/download</a> page is designed to provide @ a convenient place for newbies @ to download a ZIP archive or a tarball of the project. By default, @ the latest trunk check-in is downloaded. Change this tag to something @ else (ex: release) to alter the behavior of the /download page. @ (Property: "download-tag") @ </p> @ <hr /> onoff_attribute("Enable WYSIWYG Wiki Editing", "wysiwyg-wiki", "wysiwyg-wiki", 0, 0); @ <p>Enable what-you-see-is-what-you-get (WYSIWYG) editing of wiki pages. @ The WYSIWYG editor generates HTML instead of markup, which makes |
︙ | ︙ | |||
1761 1762 1763 1764 1765 1766 1767 | @ <input type="submit" name="clear" value="Delete Ad-Unit" /> @ </div></form> @ <hr /> @ <b>Ad-Unit Notes:</b><ul> @ <li>Leave both Ad-Units blank to disable all advertising. @ <li>The "Banner Ad-Unit" is used for wide pages. @ <li>The "Right-Column Ad-Unit" is used on pages with tall, narrow content. | | > | 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 | @ <input type="submit" name="clear" value="Delete Ad-Unit" /> @ </div></form> @ <hr /> @ <b>Ad-Unit Notes:</b><ul> @ <li>Leave both Ad-Units blank to disable all advertising. @ <li>The "Banner Ad-Unit" is used for wide pages. @ <li>The "Right-Column Ad-Unit" is used on pages with tall, narrow content. @ <li>If the "Right-Column Ad-Unit" is blank, the "Banner Ad-Unit" is @ used on all pages. @ <li>Properties: "adunit", "adunit-right", "adunit-omit-if-admin", and @ "adunit-omit-if-user". @ <li>Suggested <a href="setup_skinedit?w=0">CSS</a> changes: @ <blockquote><pre> @ div.adunit_banner { @ margin: auto; @ width: 100%%; |
︙ | ︙ | |||
1874 1875 1876 1877 1878 1879 1880 | ); db_end_transaction(0); cgi_redirect("setup_logo"); } style_header("Edit Project Logo And Background"); @ <p>The current project logo has a MIME-Type of <b>%h(zLogoMime)</b> @ and looks like this:</p> | | > | 1111 1112 1113 1114 1115 1116 1117 1118 1119 1120 1121 1122 1123 1124 1125 1126 | ); db_end_transaction(0); cgi_redirect("setup_logo"); } style_header("Edit Project Logo And Background"); @ <p>The current project logo has a MIME-Type of <b>%h(zLogoMime)</b> @ and looks like this:</p> @ <blockquote><p><img src="%s(g.zTop)/logo/%z(zLogoMtime)" \ @ alt="logo" border="1" /> @ </p></blockquote> @ @ <form action="%s(g.zTop)/setup_logo" method="post" @ enctype="multipart/form-data"><div> @ <p>The logo is accessible to all users at this URL: @ <a href="%s(g.zBaseURL)/logo">%s(g.zBaseURL)/logo</a>. @ The logo may or may not appear on each |
︙ | ︙ | |||
1897 1898 1899 1900 1901 1902 1903 | @ <input type="submit" name="clrlogo" value="Revert To Default" /></p> @ <p>(Properties: "logo-image" and "logo-mimetype") @ </div></form> @ <hr /> @ @ <p>The current background image has a MIME-Type of <b>%h(zBgMime)</b> @ and looks like this:</p> | | > | 1135 1136 1137 1138 1139 1140 1141 1142 1143 1144 1145 1146 1147 1148 1149 1150 | @ <input type="submit" name="clrlogo" value="Revert To Default" /></p> @ <p>(Properties: "logo-image" and "logo-mimetype") @ </div></form> @ <hr /> @ @ <p>The current background image has a MIME-Type of <b>%h(zBgMime)</b> @ and looks like this:</p> @ <blockquote><p><img src="%s(g.zTop)/background/%z(zBgMtime)" \ @ alt="background" border=1 /> @ </p></blockquote> @ @ <form action="%s(g.zTop)/setup_logo" method="post" @ enctype="multipart/form-data"><div> @ <p>The background image is accessible to all users at this URL: @ <a href="%s(g.zBaseURL)/background">%s(g.zBaseURL)/background</a>. @ The background image may or may not appear on each |
︙ | ︙ | |||
1964 1965 1966 1967 1968 1969 1970 | int go = P("go")!=0; login_check_credentials(); if( !g.perm.Setup ){ login_needed(0); return; } add_content_sql_commands(g.db); | < | 1203 1204 1205 1206 1207 1208 1209 1210 1211 1212 1213 1214 1215 1216 | int go = P("go")!=0; login_check_credentials(); if( !g.perm.Setup ){ login_needed(0); return; } add_content_sql_commands(g.db); zQ = cgi_csrf_safe(1) ? P("q") : 0; style_header("Raw SQL Commands"); @ <p><b>Caution:</b> There are no restrictions on the SQL that can be @ run by this page. You can do serious and irrepairable damage to the @ repository. Proceed with extreme caution.</p> @ #if 0 |
︙ | ︙ | |||
1989 1990 1991 1992 1993 1994 1995 | @ </ul></p> #endif if( P("configtab") ){ /* If the user presses the "CONFIG Table Query" button, populate the ** query text with a pre-packaged query against the CONFIG table */ zQ = "SELECT\n" | | > | > | 1227 1228 1229 1230 1231 1232 1233 1234 1235 1236 1237 1238 1239 1240 1241 1242 1243 1244 1245 1246 1247 1248 1249 1250 1251 1252 1253 1254 1255 1256 1257 1258 1259 1260 1261 1262 1263 | @ </ul></p> #endif if( P("configtab") ){ /* If the user presses the "CONFIG Table Query" button, populate the ** query text with a pre-packaged query against the CONFIG table */ zQ = "SELECT\n" " CASE WHEN length(name)<50 THEN name ELSE printf('%.50s...',name)" " END AS name,\n" " CASE WHEN typeof(value)<>'blob' AND length(value)<80 THEN value\n" " ELSE '...' END AS value,\n" " datetime(mtime, 'unixepoch') AS mtime\n" "FROM config\n" "-- ORDER BY mtime DESC; -- optional"; go = 1; } @ @ <form method="post" action="%s(g.zTop)/admin_sql"> login_insert_csrf_secret(); @ SQL:<br /> @ <textarea name="q" rows="8" cols="80">%h(zQ)</textarea><br /> @ <input type="submit" name="go" value="Run SQL"> @ <input type="submit" name="schema" value="Show Schema"> @ <input type="submit" name="tablelist" value="List Tables"> @ <input type="submit" name="configtab" value="CONFIG Table Query"> @ </form> if( P("schema") ){ zQ = sqlite3_mprintf( "SELECT sql FROM repository.sqlite_master" " WHERE sql IS NOT NULL ORDER BY name"); go = 1; }else if( P("tablelist") ){ zQ = sqlite3_mprintf( "SELECT name FROM repository.sqlite_master WHERE type='table'" " ORDER BY name"); go = 1; } |
︙ | ︙ | |||
2189 2190 2191 2192 2193 2194 2195 2196 2197 2198 2199 2200 2201 2202 | @ <tr class="row%d(counter%2)"> @ <td class="adminTime">%s(zTime)</td> @ <td>%s(zUser)</td> @ <td>%s(zPage)</td> @ <td>%h(zMessage)</td> @ </tr> } @ </tbody></table> if( counter>ofst+limit ){ @ <p><a href="admin_log?n=%d(limit)&x=%d(limit+ofst)">[Older]</a></p> } style_footer(); } | > | 1429 1430 1431 1432 1433 1434 1435 1436 1437 1438 1439 1440 1441 1442 1443 | @ <tr class="row%d(counter%2)"> @ <td class="adminTime">%s(zTime)</td> @ <td>%s(zUser)</td> @ <td>%s(zPage)</td> @ <td>%h(zMessage)</td> @ </tr> } db_finalize(&stLog); @ </tbody></table> if( counter>ofst+limit ){ @ <p><a href="admin_log?n=%d(limit)&x=%d(limit+ofst)">[Older]</a></p> } style_footer(); } |
︙ | ︙ | |||
2245 2246 2247 2248 2249 2250 2251 2252 2253 2254 2255 2256 2257 2258 | onoff_attribute("Search Documents", "search-doc", "sd", 0, 0); @ <br /> onoff_attribute("Search Tickets", "search-tkt", "st", 0, 0); @ <br /> onoff_attribute("Search Wiki", "search-wiki", "sw", 0, 0); @ <br /> onoff_attribute("Search Tech Notes", "search-technote", "se", 0, 0); @ <hr /> @ <p><input type="submit" name="submit" value="Apply Changes" /></p> @ <hr /> if( P("fts0") ){ search_drop_index(); }else if( P("fts1") ){ search_drop_index(); | > > | 1486 1487 1488 1489 1490 1491 1492 1493 1494 1495 1496 1497 1498 1499 1500 1501 | onoff_attribute("Search Documents", "search-doc", "sd", 0, 0); @ <br /> onoff_attribute("Search Tickets", "search-tkt", "st", 0, 0); @ <br /> onoff_attribute("Search Wiki", "search-wiki", "sw", 0, 0); @ <br /> onoff_attribute("Search Tech Notes", "search-technote", "se", 0, 0); @ <br /> onoff_attribute("Search Forum", "search-forum", "sf", 0, 0); @ <hr /> @ <p><input type="submit" name="submit" value="Apply Changes" /></p> @ <hr /> if( P("fts0") ){ search_drop_index(); }else if( P("fts1") ){ search_drop_index(); |
︙ | ︙ | |||
2387 2388 2389 2390 2391 2392 2393 | db_finalize(&q); @ <tr><td> @ <input type='hidden' name='namelist' value='%h(blob_str(&namelist))'> @ <input type='submit' name='submit' value="Apply Changes"> @ </td><td></td></tr> @ </table></form> @ <hr> | | | | | | > | | > | | > > | > | | | | | 1630 1631 1632 1633 1634 1635 1636 1637 1638 1639 1640 1641 1642 1643 1644 1645 1646 1647 1648 1649 1650 1651 1652 1653 1654 1655 1656 1657 1658 1659 1660 1661 1662 1663 1664 1665 1666 1667 1668 1669 1670 1671 1672 1673 1674 1675 1676 1677 | db_finalize(&q); @ <tr><td> @ <input type='hidden' name='namelist' value='%h(blob_str(&namelist))'> @ <input type='submit' name='submit' value="Apply Changes"> @ </td><td></td></tr> @ </table></form> @ <hr> @ <p>When the first term of an incoming URL exactly matches one of @ the "Aliases" on the left-hand side (LHS) above, the URL is @ converted into the corresponding form on the right-hand side (RHS). @ <ul> @ <li><p> @ The LHS is compared against only the first term of the incoming URL. @ All LHS entries in the alias table should therefore begin with a @ single "/" followed by a single path element. @ <li><p> @ The RHS entries in the alias table should begin with a single "/" @ followed by a path element, and optionally followed by "?" and a @ list of query parameters. @ <li><p> @ Query parameters on the RHS are added to the set of query parameters @ in the incoming URL. @ <li><p> @ If the same query parameter appears in both the incoming URL and @ on the RHS of the alias, the RHS query parameter value overwrites @ the value on the incoming URL. @ <li><p> @ If a query parameter on the RHS of the alias is of the form "X!" @ (a name followed by "!") then the X query parameter is removed @ from the incoming URL if @ it exists. @ <li><p> @ Only a single alias operation occurs. It is not possible to nest aliases. @ The RHS entries must be built-in webpage names. @ <li><p> @ The alias table is only checked if no built-in webpage matches @ the incoming URL. @ Hence, it is not possible to override a built-in webpage using aliases. @ This is by design. @ </ul> @ @ <p>To delete an entry from the alias table, change its name or value to an @ empty string and press "Apply Changes". @ @ <p>To add a new alias, fill in the name and value in the bottom row @ of the table above and press "Apply Changes". style_footer(); } |
Added src/setupuser.c.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 | /* ** Copyright (c) 2007 D. Richard Hipp ** ** This program is free software; you can redistribute it and/or ** modify it under the terms of the Simplified BSD License (also ** known as the "2-Clause License" or "FreeBSD License".) ** ** This program is distributed in the hope that it will be useful, ** but without any warranty; without even the implied warranty of ** merchantability or fitness for a particular purpose. ** ** Author contact information: ** drh@hwaci.com ** http://www.hwaci.com/drh/ ** ******************************************************************************* ** ** Setup pages associated with user management. The code in this ** file was formerly part of the "setup.c" module, but has been broken ** out into its own module to improve maintainability. */ #include "config.h" #include <assert.h> #include "setupuser.h" /* ** WEBPAGE: setup_ulist ** ** Show a list of users. Clicking on any user jumps to the edit ** screen for that user. Requires Admin privileges. ** ** Query parameters: ** ** with=CAP Only show users that have one or more capabilities in CAP. */ void setup_ulist(void){ Stmt s; double rNow; const char *zWith = P("with"); login_check_credentials(); if( !g.perm.Admin ){ login_needed(0); return; } if( zWith==0 || zWith[0]==0 ){ style_submenu_element("Add", "setup_uedit"); style_submenu_element("Log", "access_log"); style_submenu_element("Help", "setup_ulist_notes"); style_header("User List"); @ <table border=1 cellpadding=2 cellspacing=0 class='userTable'> @ <thead><tr> @ <th>Category @ <th>Capabilities (<a href='%R/setup_ucap_list'>key</a>) @ <th>Info <th>Last Change</tr></thead> @ <tbody> db_prepare(&s, "SELECT uid, login, cap, date(mtime,'unixepoch')" " FROM user" " WHERE login IN ('anonymous','nobody','developer','reader')" " ORDER BY login" ); while( db_step(&s)==SQLITE_ROW ){ int uid = db_column_int(&s, 0); const char *zLogin = db_column_text(&s, 1); const char *zCap = db_column_text(&s, 2); const char *zDate = db_column_text(&s, 4); @ <tr> @ <td><a href='setup_uedit?id=%d(uid)'>%h(zLogin)</a> @ <td>%h(zCap) if( fossil_strcmp(zLogin,"anonymous")==0 ){ @ <td>All logged-in users }else if( fossil_strcmp(zLogin,"developer")==0 ){ @ <td>Users with '<b>v</b>' capability }else if( fossil_strcmp(zLogin,"nobody")==0 ){ @ <td>All users without login }else if( fossil_strcmp(zLogin,"reader")==0 ){ @ <td>Users with '<b>u</b>' capability }else{ @ <td> } if( zDate && zDate[0] ){ @ <td>%h(zDate) }else{ @ <td> } @ </tr> } db_finalize(&s); }else{ style_header("Users With Capabilities \"%h\"", zWith); } @ </tbody></table> @ <div class='section'>Users</div> @ <table border=1 cellpadding=2 cellspacing=0 class='userTable sortable' \ @ data-column-types='ktxTTK' data-init-sort='2'> @ <thead><tr> @ <th>Login Name<th>Caps<th>Info<th>Date<th>Expire<th>Last Login</tr></thead> @ <tbody> db_multi_exec( "CREATE TEMP TABLE lastAccess(uname TEXT PRIMARY KEY, atime REAL)" "WITHOUT ROWID;" ); if( db_table_exists("repository","accesslog") ){ db_multi_exec( "INSERT INTO lastAccess(uname, atime)" " SELECT uname, max(mtime) FROM (" " SELECT uname, mtime FROM accesslog WHERE success" " UNION ALL" " SELECT login AS uname, rcvfrom.mtime AS mtime" " FROM rcvfrom JOIN user USING(uid))" " GROUP BY 1;" ); } if( zWith && zWith[0] ){ zWith = mprintf(" AND fullcap(cap) GLOB '*[%q]*'", zWith); }else{ zWith = ""; } db_prepare(&s, "SELECT uid, login, cap, info, date(mtime,'unixepoch')," " lower(login) AS sortkey, " " CASE WHEN info LIKE '%%expires 20%%'" " THEN substr(info,instr(lower(info),'expires')+8,10)" " END AS exp," "atime" " FROM user LEFT JOIN lastAccess ON login=uname" " WHERE login NOT IN ('anonymous','nobody','developer','reader') %s" " ORDER BY sortkey", zWith/*safe-for-%s*/ ); rNow = db_double(0.0, "SELECT julianday('now');"); while( db_step(&s)==SQLITE_ROW ){ int uid = db_column_int(&s, 0); const char *zLogin = db_column_text(&s, 1); const char *zCap = db_column_text(&s, 2); const char *zInfo = db_column_text(&s, 3); const char *zDate = db_column_text(&s, 4); const char *zSortKey = db_column_text(&s,5); const char *zExp = db_column_text(&s,6); double rATime = db_column_double(&s,7); char *zAge = 0; if( rATime>0.0 ){ zAge = human_readable_age(rNow - rATime); } @ <tr> @ <td data-sortkey='%h(zSortKey)'>\ @ <a href='setup_uedit?id=%d(uid)'>%h(zLogin)</a> @ <td>%h(zCap) @ <td>%h(zInfo) @ <td>%h(zDate?zDate:"") @ <td>%h(zExp?zExp:"") @ <td data-sortkey='%f(rATime)' style='white-space:nowrap'>%s(zAge?zAge:"") @ </tr> fossil_free(zAge); } @ </tbody></table> db_finalize(&s); style_table_sorter(); style_footer(); } /* ** WEBPAGE: setup_ulist_notes ** ** A documentation page showing notes about user configuration. This ** information used to be a side-bar on the user list page, but has been ** factored out for improved presentation. */ void setup_ulist_notes(void){ style_header("User Configuration Notes"); @ <h1>User Configuration Notes:</h1> @ <ol> @ <li><p> @ Every user, logged in or not, inherits the privileges of @ <span class="usertype">nobody</span>. @ </p></li> @ @ <li><p> @ Any human can login as <span class="usertype">anonymous</span> since the @ password is clearly displayed on the login page for them to type. The @ purpose of requiring anonymous to log in is to prevent access by spiders. @ Every logged-in user inherits the combined privileges of @ <span class="usertype">anonymous</span> and @ <span class="usertype">nobody</span>. @ </p></li> @ @ <li><p> @ Users with privilege <span class="capability">u</span> inherit the combined @ privileges of <span class="usertype">reader</span>, @ <span class="usertype">anonymous</span>, and @ <span class="usertype">nobody</span>. @ </p></li> @ @ <li><p> @ Users with privilege <span class="capability">v</span> inherit the combined @ privileges of <span class="usertype">developer</span>, @ <span class="usertype">anonymous</span>, and @ <span class="usertype">nobody</span>. @ </p></li> @ @ <li><p>The permission flags are as follows:</p> capabilities_table(); @ </li> @ </ol> style_footer(); } /* ** WEBPAGE: setup_ucap_list ** ** A documentation page showing the meaning of the various user capabilities ** code letters. */ void setup_ucap_list(void){ style_header("User Capability Codes"); capabilities_table(); style_footer(); } /* ** Return true if zPw is a valid password string. A valid ** password string is: ** ** (1) A zero-length string, or ** (2) a string that contains a character other than '*'. */ static int isValidPwString(const char *zPw){ if( zPw==0 ) return 0; if( zPw[0]==0 ) return 1; while( zPw[0]=='*' ){ zPw++; } return zPw[0]!=0; } /* ** WEBPAGE: setup_uedit ** ** Edit information about a user or create a new user. ** Requires Admin privileges. */ void user_edit(void){ const char *zId, *zLogin, *zInfo, *zCap, *zPw; const char *zGroup; const char *zOldLogin; int doWrite; int uid, i; int higherUser = 0; /* True if user being edited is SETUP and the */ /* user doing the editing is ADMIN. Disallow editing */ const char *inherit[128]; int a[128]; const char *oa[128]; /* Must have ADMIN privileges to access this page */ login_check_credentials(); if( !g.perm.Admin ){ login_needed(0); return; } /* Check to see if an ADMIN user is trying to edit a SETUP account. ** Don't allow that. */ zId = PD("id", "0"); uid = atoi(zId); if( zId && !g.perm.Setup && uid>0 ){ char *zOldCaps; zOldCaps = db_text(0, "SELECT cap FROM user WHERE uid=%d",uid); higherUser = zOldCaps && strchr(zOldCaps,'s'); } if( P("can") ){ /* User pressed the cancel button */ cgi_redirect(cgi_referer("setup_ulist")); return; } /* If we have all the necessary information, write the new or ** modified user record. After writing the user record, redirect ** to the page that displays a list of users. */ doWrite = cgi_all("login","info","pw") && !higherUser && cgi_csrf_safe(1); if( doWrite ){ char c; char zCap[70], zNm[4]; zNm[0] = 'a'; zNm[2] = 0; for(i=0, c='a'; c<='z'; c++){ zNm[1] = c; a[c&0x7f] = (c!='s' || g.perm.Setup) && P(zNm)!=0; if( a[c&0x7f] ) zCap[i++] = c; } for(c='0'; c<='9'; c++){ zNm[1] = c; a[c&0x7f] = P(zNm)!=0; if( a[c&0x7f] ) zCap[i++] = c; } for(c='A'; c<='Z'; c++){ zNm[1] = c; a[c&0x7f] = P(zNm)!=0; if( a[c&0x7f] ) zCap[i++] = c; } zCap[i] = 0; zPw = P("pw"); zLogin = P("login"); if( strlen(zLogin)==0 ){ const char *zRef = cgi_referer("setup_ulist"); style_header("User Creation Error"); @ <span class="loginError">Empty login not allowed.</span> @ @ <p><a href="setup_uedit?id=%d(uid)&referer=%T(zRef)"> @ [Bummer]</a></p> style_footer(); return; } if( isValidPwString(zPw) ){ zPw = sha1_shared_secret(zPw, zLogin, 0); }else{ zPw = db_text(0, "SELECT pw FROM user WHERE uid=%d", uid); } zOldLogin = db_text(0, "SELECT login FROM user WHERE uid=%d", uid); if( db_exists("SELECT 1 FROM user WHERE login=%Q AND uid!=%d",zLogin,uid) ){ const char *zRef = cgi_referer("setup_ulist"); style_header("User Creation Error"); @ <span class="loginError">Login "%h(zLogin)" is already used by @ a different user.</span> @ @ <p><a href="setup_uedit?id=%d(uid)&referer=%T(zRef)"> @ [Bummer]</a></p> style_footer(); return; } login_verify_csrf_secret(); db_multi_exec( "REPLACE INTO user(uid,login,info,pw,cap,mtime) " "VALUES(nullif(%d,0),%Q,%Q,%Q,%Q,now())", uid, zLogin, P("info"), zPw, zCap ); setup_incr_cfgcnt(); admin_log( "Updated user [%q] with capabilities [%q].", zLogin, zCap ); if( atoi(PD("all","0"))>0 ){ Blob sql; char *zErr = 0; blob_zero(&sql); if( zOldLogin==0 ){ blob_appendf(&sql, "INSERT INTO user(login)" " SELECT %Q WHERE NOT EXISTS(SELECT 1 FROM user WHERE login=%Q);", zLogin, zLogin ); zOldLogin = zLogin; } blob_appendf(&sql, "UPDATE user SET login=%Q," " pw=coalesce(shared_secret(%Q,%Q," "(SELECT value FROM config WHERE name='project-code')),pw)," " info=%Q," " cap=%Q," " mtime=now()" " WHERE login=%Q;", zLogin, P("pw"), zLogin, P("info"), zCap, zOldLogin ); login_group_sql(blob_str(&sql), "<li> ", " </li>\n", &zErr); blob_reset(&sql); admin_log( "Updated user [%q] in all login groups " "with capabilities [%q].", zLogin, zCap ); if( zErr ){ const char *zRef = cgi_referer("setup_ulist"); style_header("User Change Error"); admin_log( "Error updating user '%q': %s'.", zLogin, zErr ); @ <span class="loginError">%h(zErr)</span> @ @ <p><a href="setup_uedit?id=%d(uid)&referer=%T(zRef)"> @ [Bummer]</a></p> style_footer(); return; } } cgi_redirect(cgi_referer("setup_ulist")); return; } /* Load the existing information about the user, if any */ zLogin = ""; zInfo = ""; zCap = ""; zPw = ""; for(i='a'; i<='z'; i++) oa[i] = ""; for(i='0'; i<='9'; i++) oa[i] = ""; for(i='A'; i<='Z'; i++) oa[i] = ""; if( uid ){ zLogin = db_text("", "SELECT login FROM user WHERE uid=%d", uid); zInfo = db_text("", "SELECT info FROM user WHERE uid=%d", uid); zCap = db_text("", "SELECT cap FROM user WHERE uid=%d", uid); zPw = db_text("", "SELECT pw FROM user WHERE uid=%d", uid); for(i=0; zCap[i]; i++){ char c = zCap[i]; if( (c>='a' && c<='z') || (c>='0' && c<='9') || (c>='A' && c<='Z') ){ oa[c&0x7f] = " checked=\"checked\""; } } } /* figure out inherited permissions */ memset((char *)inherit, 0, sizeof(inherit)); if( fossil_strcmp(zLogin, "developer") ){ char *z1, *z2; z1 = z2 = db_text(0,"SELECT cap FROM user WHERE login='developer'"); while( z1 && *z1 ){ inherit[0x7f & *(z1++)] = "<span class=\"ueditInheritDeveloper\"><sub>[D]</sub></span>"; } free(z2); } if( fossil_strcmp(zLogin, "reader") ){ char *z1, *z2; z1 = z2 = db_text(0,"SELECT cap FROM user WHERE login='reader'"); while( z1 && *z1 ){ inherit[0x7f & *(z1++)] = "<span class=\"ueditInheritReader\"><sub>[R]</sub></span>"; } free(z2); } if( fossil_strcmp(zLogin, "anonymous") ){ char *z1, *z2; z1 = z2 = db_text(0,"SELECT cap FROM user WHERE login='anonymous'"); while( z1 && *z1 ){ inherit[0x7f & *(z1++)] = "<span class=\"ueditInheritAnonymous\"><sub>[A]</sub></span>"; } free(z2); } if( fossil_strcmp(zLogin, "nobody") ){ char *z1, *z2; z1 = z2 = db_text(0,"SELECT cap FROM user WHERE login='nobody'"); while( z1 && *z1 ){ inherit[0x7f & *(z1++)] = "<span class=\"ueditInheritNobody\"><sub>[N]</sub></span>"; } free(z2); } /* Begin generating the page */ style_submenu_element("Cancel", "%s", cgi_referer("setup_ulist")); if( uid ){ style_header("Edit User %h", zLogin); style_submenu_element("Access Log", "%R/access_log?u=%t", zLogin); }else{ style_header("Add A New User"); } @ <div class="ueditCapBox"> @ <form action="%s(g.zPath)" method="post"><div> login_insert_csrf_secret(); if( login_is_special(zLogin) ){ @ <input type="hidden" name="login" value="%s(zLogin)"> @ <input type="hidden" name="info" value=""> @ <input type="hidden" name="pw" value="*"> } @ <input type="hidden" name="referer" value="%h(cgi_referer("setup_ulist"))"> @ <table width="100%%"> @ <tr> @ <td class="usetupEditLabel">User ID:</td> if( uid ){ @ <td>%d(uid) <input type="hidden" name="id" value="%d(uid)" /></td> }else{ @ <td>(new user)<input type="hidden" name="id" value="0" /></td> } @ </tr> @ <tr> @ <td class="usetupEditLabel">Login:</td> if( login_is_special(zLogin) ){ @ <td><b>%h(zLogin)</b></td> }else{ @ <td><input type="text" name="login" value="%h(zLogin)" /></td> @ </tr> @ <tr> @ <td class="usetupEditLabel">Contact Info:</td> @ <td><textarea name="info" cols="40" rows="2">%h(zInfo)</textarea></td> } @ </tr> @ <tr> @ <td class="usetupEditLabel">Capabilities:</td> @ <td width="100%%"> #define B(x) inherit[x] @ <div class="columns" style="column-width:13em;"> @ <ul style="list-style-type: none;"> if( g.perm.Setup ){ @ <li><label><input type="checkbox" name="as"%s(oa['s']) /> @ Setup%s(B('s'))</label> } @ <li><label><input type="checkbox" name="aa"%s(oa['a']) /> @ Admin%s(B('a'))</label> @ <li><label><input type="checkbox" name="au"%s(oa['u']) /> @ Reader%s(B('u'))</label> @ <li><label><input type="checkbox" name="av"%s(oa['v']) /> @ Developer%s(B('v'))</label> @ <li><label><input type="checkbox" name="ad"%s(oa['d']) /> @ Delete%s(B('d'))</label> @ <li><label><input type="checkbox" name="ae"%s(oa['e']) /> @ View-PII%s(B('e'))</label> @ <li><label><input type="checkbox" name="ap"%s(oa['p']) /> @ Password%s(B('p'))</label> @ <li><label><input type="checkbox" name="ai"%s(oa['i']) /> @ Check-In%s(B('i'))</label> @ <li><label><input type="checkbox" name="ao"%s(oa['o']) /> @ Check-Out%s(B('o'))</label> @ <li><label><input type="checkbox" name="ah"%s(oa['h']) /> @ Hyperlinks%s(B('h'))</label> @ <li><label><input type="checkbox" name="ab"%s(oa['b']) /> @ Attachments%s(B('b'))</label> @ <li><label><input type="checkbox" name="ag"%s(oa['g']) /> @ Clone%s(B('g'))</label> @ <li><label><input type="checkbox" name="aj"%s(oa['j']) /> @ Read Wiki%s(B('j'))</label> @ <li><label><input type="checkbox" name="af"%s(oa['f']) /> @ New Wiki%s(B('f'))</label> @ <li><label><input type="checkbox" name="am"%s(oa['m']) /> @ Append Wiki%s(B('m'))</label> @ <li><label><input type="checkbox" name="ak"%s(oa['k']) /> @ Write Wiki%s(B('k'))</label> @ <li><label><input type="checkbox" name="al"%s(oa['l']) /> @ Moderate Wiki%s(B('l'))</label> @ <li><label><input type="checkbox" name="ar"%s(oa['r']) /> @ Read Ticket%s(B('r'))</label> @ <li><label><input type="checkbox" name="an"%s(oa['n']) /> @ New Tickets%s(B('n'))</label> @ <li><label><input type="checkbox" name="ac"%s(oa['c']) /> @ Append To Ticket%s(B('c'))</label> @ <li><label><input type="checkbox" name="aw"%s(oa['w']) /> @ Write Tickets%s(B('w'))</label> @ <li><label><input type="checkbox" name="aq"%s(oa['q']) /> @ Moderate Tickets%s(B('q'))</label> @ <li><label><input type="checkbox" name="at"%s(oa['t']) /> @ Ticket Report%s(B('t'))</label> @ <li><label><input type="checkbox" name="ax"%s(oa['x']) /> @ Private%s(B('x'))</label> @ <li><label><input type="checkbox" name="ay"%s(oa['y']) /> @ Write Unversioned%s(B('y'))</label> @ <li><label><input type="checkbox" name="az"%s(oa['z']) /> @ Download Zip%s(B('z'))</label> @ <li><label><input type="checkbox" name="a2"%s(oa['2']) /> @ Read Forum%s(B('2'))</label> @ <li><label><input type="checkbox" name="a3"%s(oa['3']) /> @ Write Forum%s(B('3'))</label> @ <li><label><input type="checkbox" name="a4"%s(oa['4']) /> @ WriteTrusted Forum%s(B('4'))</label> @ <li><label><input type="checkbox" name="a5"%s(oa['5']) /> @ Moderate Forum%s(B('5'))</label> @ <li><label><input type="checkbox" name="a6"%s(oa['6']) /> @ Supervise Forum%s(B('6'))</label> @ <li><label><input type="checkbox" name="a7"%s(oa['7']) /> @ Email Alerts%s(B('7'))</label> @ <li><label><input type="checkbox" name="aA"%s(oa['A']) /> @ Send Announcements%s(B('A'))</label> @ <li><label><input type="checkbox" name="aD"%s(oa['D']) /> @ Enable Debug%s(B('D'))</label> @ </ul></div> @ </td> @ </tr> @ <tr> @ <td class="usetupEditLabel">Selected Cap.:</td> @ <td> @ <span id="usetupEditCapability">(missing JS?)</span> @ <a href="%R/setup_ucap_list">(key)</a> @ </td> @ </tr> if( !login_is_special(zLogin) ){ @ <tr> @ <td align="right">Password:</td> if( zPw[0] ){ /* Obscure the password for all users */ @ <td><input type="password" name="pw" value="**********" /></td> }else{ /* Show an empty password as an empty input field */ @ <td><input type="password" name="pw" value="" /></td> } @ </tr> } zGroup = login_group_name(); if( zGroup ){ @ <tr> @ <td valign="top" align="right">Scope:</td> @ <td valign="top"> @ <input type="radio" name="all" checked value="0"> @ Apply changes to this repository only.<br /> @ <input type="radio" name="all" value="1"> @ Apply changes to all repositories in the "<b>%h(zGroup)</b>" @ login group.</td></tr> } if( !higherUser ){ @ <tr> @ <td> </td> @ <td><input type="submit" name="submit" value="Apply Changes" /></td> @ </tr> } @ </table> @ </div></form> @ </div> style_load_one_js_file("useredit.js"); @ <hr> @ <h1>Notes On Privileges And Capabilities:</h1> @ <ul> if( higherUser ){ @ <li><p class="missingPriv"> @ User %h(zLogin) has Setup privileges and you only have Admin privileges @ so you are not permitted to make changes to %h(zLogin). @ </p></li> @ } @ <li><p> @ The <span class="capability">Setup</span> user can make arbitrary @ configuration changes. An <span class="usertype">Admin</span> user @ can add other users and change user privileges @ and reset user passwords. Both automatically get all other privileges @ listed below. Use these two settings with discretion. @ </p></li> @ @ <li><p> @ The "<span class="ueditInheritNobody"><sub>N</sub></span>" subscript suffix @ indicates the privileges of <span class="usertype">nobody</span> that @ are available to all users regardless of whether or not they are logged in. @ </p></li> @ @ <li><p> @ The "<span class="ueditInheritAnonymous"><sub>A</sub></span>" @ subscript suffix @ indicates the privileges of <span class="usertype">anonymous</span> that @ are inherited by all logged-in users. @ </p></li> @ @ <li><p> @ The "<span class="ueditInheritDeveloper"><sub>D</sub></span>" @ subscript suffix indicates the privileges of @ <span class="usertype">developer</span> that @ are inherited by all users with the @ <span class="capability">Developer</span> privilege. @ </p></li> @ @ <li><p> @ The "<span class="ueditInheritReader"><sub>R</sub></span>" subscript suffix @ indicates the privileges of <span class="usertype">reader</span> that @ are inherited by all users with the <span class="capability">Reader</span> @ privilege. @ </p></li> @ @ <li><p> @ The <span class="capability">Delete</span> privilege give the user the @ ability to erase wiki, tickets, and attachments that have been added @ by anonymous users. This capability is intended for deletion of spam. @ The delete capability is only in effect for 24 hours after the item @ is first posted. The <span class="usertype">Setup</span> user can @ delete anything at any time. @ </p></li> @ @ <li><p> @ The <span class="capability">Hyperlinks</span> privilege allows a user @ to see most hyperlinks. This is recommended ON for most logged-in users @ but OFF for user "nobody" to avoid problems with spiders trying to walk @ every diff and annotation of every historical check-in and file. @ </p></li> @ @ <li><p> @ The <span class="capability">Zip</span> privilege allows a user to @ see the "download as ZIP" @ hyperlink and permits access to the <tt>/zip</tt> page. This allows @ users to download ZIP archives without granting other rights like @ <span class="capability">Read</span> or @ <span class="capability">Hyperlink</span>. The "z" privilege is recommended @ for user <span class="usertype">nobody</span> so that automatic package @ downloaders can obtain the sources without going through the login @ procedure. @ </p></li> @ @ <li><p> @ The <span class="capability">Check-in</span> privilege allows remote @ users to "push". The <span class="capability">Check-out</span> privilege @ allows remote users to "pull". The <span class="capability">Clone</span> @ privilege allows remote users to "clone". @ </p></li> @ @ <li><p> @ The <span class="capability">Read Wiki</span>, @ <span class="capability">New Wiki</span>, @ <span class="capability">Append Wiki</span>, and @ <b>Write Wiki</b> privileges control access to wiki pages. The @ <span class="capability">Read Ticket</span>, @ <span class="capability">New Ticket</span>, @ <span class="capability">Append Ticket</span>, and @ <span class="capability">Write Ticket</span> privileges control access @ to trouble tickets. @ The <span class="capability">Ticket Report</span> privilege allows @ the user to create or edit ticket report formats. @ </p></li> @ @ <li><p> @ Users with the <span class="capability">Password</span> privilege @ are allowed to change their own password. Recommended ON for most @ users but OFF for special users <span class="usertype">developer</span>, @ <span class="usertype">anonymous</span>, @ and <span class="usertype">nobody</span>. @ </p></li> @ @ <li><p> @ The <span class="capability">View-PII</span> privilege allows the display @ of personally-identifiable information information such as the @ email address of users and contact @ information on tickets. Recommended OFF for @ <span class="usertype">anonymous</span> and for @ <span class="usertype">nobody</span> but ON for @ <span class="usertype">developer</span>. @ </p></li> @ @ <li><p> @ The <span class="capability">Attachment</span> privilege is needed in @ order to add attachments to tickets or wiki. Write privilege on the @ ticket or wiki is also required. @ </p></li> @ @ <li><p> @ Login is prohibited if the password is an empty string. @ </p></li> @ </ul> @ @ <h2>Special Logins</h2> @ @ <ul> @ <li><p> @ No login is required for user <span class="usertype">nobody</span>. The @ capabilities of the <span class="usertype">nobody</span> user are @ inherited by all users, regardless of whether or not they are logged in. @ To disable universal access to the repository, make sure that the @ <span class="usertype">nobody</span> user has no capabilities @ enabled. The password for <span class="usertype">nobody</span> is ignored. @ </p></li> @ @ <li><p> @ Login is required for user <span class="usertype">anonymous</span> but the @ password is displayed on the login screen beside the password entry box @ so anybody who can read should be able to login as anonymous. @ On the other hand, spiders and web-crawlers will typically not @ be able to login. Set the capabilities of the @ <span class="usertype">anonymous</span> @ user to things that you want any human to be able to do, but not any @ spider. Every other logged-in user inherits the privileges of @ <span class="usertype">anonymous</span>. @ </p></li> @ @ <li><p> @ The <span class="usertype">developer</span> user is intended as a template @ for trusted users with check-in privileges. When adding new trusted users, @ simply select the <span class="capability">developer</span> privilege to @ cause the new user to inherit all privileges of the @ <span class="usertype">developer</span> @ user. Similarly, the <span class="usertype">reader</span> user is a @ template for users who are allowed more access than @ <span class="usertype">anonymous</span>, @ but less than a <span class="usertype">developer</span>. @ </p></li> @ </ul> style_footer(); } |
Changes to src/shell.c.
︙ | ︙ | |||
93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 | # if !defined(__RTP__) && !defined(_WRS_KERNEL) # include <pwd.h> # endif #endif #if (!defined(_WIN32) && !defined(WIN32)) || defined(__MINGW32__) # include <unistd.h> # include <dirent.h> # if defined(__MINGW32__) # define DIRENT dirent # ifndef S_ISLNK # define S_ISLNK(mode) (0) # endif # endif #endif #include <sys/types.h> #include <sys/stat.h> #if HAVE_READLINE # include <readline/readline.h> # include <readline/history.h> | > > > | 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 | # if !defined(__RTP__) && !defined(_WRS_KERNEL) # include <pwd.h> # endif #endif #if (!defined(_WIN32) && !defined(WIN32)) || defined(__MINGW32__) # include <unistd.h> # include <dirent.h> # define GETPID getpid # if defined(__MINGW32__) # define DIRENT dirent # ifndef S_ISLNK # define S_ISLNK(mode) (0) # endif # endif #else # define GETPID (int)GetCurrentProcessId #endif #include <sys/types.h> #include <sys/stat.h> #if HAVE_READLINE # include <readline/readline.h> # include <readline/history.h> |
︙ | ︙ | |||
449 450 451 452 453 454 455 456 457 458 459 460 461 462 | /* ** Render output like fprintf(). This should not be used on anything that ** includes string formatting (e.g. "%s"). */ #if !defined(raw_printf) # define raw_printf fprintf #endif /* ** Write I/O traces to the following stream. */ #ifdef SQLITE_ENABLE_IOTRACE static FILE *iotrace = 0; #endif | > > > > > > | 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 | /* ** Render output like fprintf(). This should not be used on anything that ** includes string formatting (e.g. "%s"). */ #if !defined(raw_printf) # define raw_printf fprintf #endif /* Indicate out-of-memory and exit. */ static void shell_out_of_memory(void){ raw_printf(stderr,"Error: out of memory\n"); exit(1); } /* ** Write I/O traces to the following stream. */ #ifdef SQLITE_ENABLE_IOTRACE static FILE *iotrace = 0; #endif |
︙ | ︙ | |||
573 574 575 576 577 578 579 | int nLine = zLine==0 ? 0 : 100; int n = 0; while( 1 ){ if( n+100>nLine ){ nLine = nLine*2 + 100; zLine = realloc(zLine, nLine); | | | 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 | int nLine = zLine==0 ? 0 : 100; int n = 0; while( 1 ){ if( n+100>nLine ){ nLine = nLine*2 + 100; zLine = realloc(zLine, nLine); if( zLine==0 ) shell_out_of_memory(); } if( fgets(&zLine[n], nLine - n, in)==0 ){ if( n==0 ){ free(zLine); return 0; } zLine[n] = 0; |
︙ | ︙ | |||
600 601 602 603 604 605 606 | ** multi-byte characterset characters into UTF-8. */ if( stdin_is_interactive && in==stdin ){ char *zTrans = sqlite3_win32_mbcs_to_utf8_v2(zLine, 0); if( zTrans ){ int nTrans = strlen30(zTrans)+1; if( nTrans>nLine ){ zLine = realloc(zLine, nTrans); | | < < < | 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 | ** multi-byte characterset characters into UTF-8. */ if( stdin_is_interactive && in==stdin ){ char *zTrans = sqlite3_win32_mbcs_to_utf8_v2(zLine, 0); if( zTrans ){ int nTrans = strlen30(zTrans)+1; if( nTrans>nLine ){ zLine = realloc(zLine, nTrans); if( zLine==0 ) shell_out_of_memory(); } memcpy(zLine, zTrans, nTrans); sqlite3_free(zTrans); } } #endif /* defined(_WIN32) || defined(WIN32) */ return zLine; |
︙ | ︙ | |||
750 751 752 753 754 755 756 | if( zAppend[i]==quote ) len++; } } if( p->n+len>=p->nAlloc ){ p->nAlloc = p->nAlloc*2 + len + 20; p->z = realloc(p->z, p->nAlloc); | | < < < | 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 | if( zAppend[i]==quote ) len++; } } if( p->n+len>=p->nAlloc ){ p->nAlloc = p->nAlloc*2 + len + 20; p->z = realloc(p->z, p->nAlloc); if( p->z==0 ) shell_out_of_memory(); } if( quote ){ char *zCsr = p->z+p->n; *zCsr++ = quote; for(i=0; i<nAppend; i++){ *zCsr++ = zAppend[i]; |
︙ | ︙ | |||
782 783 784 785 786 787 788 | ** because it contains non-alphanumeric characters, or because it is an ** SQLite keyword. Be conservative in this estimate: When in doubt assume ** that quoting is required. ** ** Return '"' if quoting is required. Return 0 if no quoting is required. */ static char quoteChar(const char *zName){ | < < < < < < < < < < < < < < < < < < < < < | < < < < < < < < < < < < | | 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 | ** because it contains non-alphanumeric characters, or because it is an ** SQLite keyword. Be conservative in this estimate: When in doubt assume ** that quoting is required. ** ** Return '"' if quoting is required. Return 0 if no quoting is required. */ static char quoteChar(const char *zName){ int i; if( !isalpha((unsigned char)zName[0]) && zName[0]!='_' ) return '"'; for(i=0; zName[i]; i++){ if( !isalnum((unsigned char)zName[i]) && zName[i]!='_' ) return '"'; } return sqlite3_keyword_check(zName, i) ? '"' : 0; } /* ** Construct a fake object name and column list to describe the structure ** of the view, virtual table, or table valued function zSchema.zName. */ static char *shellFakeSchema( |
︙ | ︙ | |||
3038 3039 3040 3041 3042 3043 3044 3045 3046 3047 3048 3049 3050 3051 | struct completion_cursor { sqlite3_vtab_cursor base; /* Base class - must be first */ sqlite3 *db; /* Database connection for this cursor */ int nPrefix, nLine; /* Number of bytes in zPrefix and zLine */ char *zPrefix; /* The prefix for the word we want to complete */ char *zLine; /* The whole that we want to complete */ const char *zCurrentRow; /* Current output row */ sqlite3_stmt *pStmt; /* Current statement */ sqlite3_int64 iRowid; /* The rowid */ int ePhase; /* Current phase */ int j; /* inter-phase counter */ }; /* Values for ePhase: | > | 3008 3009 3010 3011 3012 3013 3014 3015 3016 3017 3018 3019 3020 3021 3022 | struct completion_cursor { sqlite3_vtab_cursor base; /* Base class - must be first */ sqlite3 *db; /* Database connection for this cursor */ int nPrefix, nLine; /* Number of bytes in zPrefix and zLine */ char *zPrefix; /* The prefix for the word we want to complete */ char *zLine; /* The whole that we want to complete */ const char *zCurrentRow; /* Current output row */ int szRow; /* Length of the zCurrentRow string */ sqlite3_stmt *pStmt; /* Current statement */ sqlite3_int64 iRowid; /* The rowid */ int ePhase; /* Current phase */ int j; /* inter-phase counter */ }; /* Values for ePhase: |
︙ | ︙ | |||
3150 3151 3152 3153 3154 3155 3156 | */ static int completionClose(sqlite3_vtab_cursor *cur){ completionCursorReset((completion_cursor*)cur); sqlite3_free(cur); return SQLITE_OK; } | < < < < < < < < < < < < < < < < < < < < < < < < < < | 3121 3122 3123 3124 3125 3126 3127 3128 3129 3130 3131 3132 3133 3134 | */ static int completionClose(sqlite3_vtab_cursor *cur){ completionCursorReset((completion_cursor*)cur); sqlite3_free(cur); return SQLITE_OK; } /* ** Advance a completion_cursor to its next row of output. ** ** The ->ePhase, ->j, and ->pStmt fields of the completion_cursor object ** record the current state of the scan. This routine sets ->zCurrentRow ** to the current row of output and then returns. If no more rows remain, ** then ->ePhase is set to COMPLETION_EOF which will signal the virtual |
︙ | ︙ | |||
3198 3199 3200 3201 3202 3203 3204 | completion_cursor *pCur = (completion_cursor*)cur; int eNextPhase = 0; /* Next phase to try if current phase reaches end */ int iCol = -1; /* If >=0, step pCur->pStmt and use the i-th column */ pCur->iRowid++; while( pCur->ePhase!=COMPLETION_EOF ){ switch( pCur->ePhase ){ case COMPLETION_KEYWORDS: { | | | | 3143 3144 3145 3146 3147 3148 3149 3150 3151 3152 3153 3154 3155 3156 3157 3158 3159 3160 3161 | completion_cursor *pCur = (completion_cursor*)cur; int eNextPhase = 0; /* Next phase to try if current phase reaches end */ int iCol = -1; /* If >=0, step pCur->pStmt and use the i-th column */ pCur->iRowid++; while( pCur->ePhase!=COMPLETION_EOF ){ switch( pCur->ePhase ){ case COMPLETION_KEYWORDS: { if( pCur->j >= sqlite3_keyword_count() ){ pCur->zCurrentRow = 0; pCur->ePhase = COMPLETION_DATABASES; }else{ sqlite3_keyword_name(pCur->j++, &pCur->zCurrentRow, &pCur->szRow); } iCol = -1; break; } case COMPLETION_DATABASES: { if( pCur->pStmt==0 ){ sqlite3_prepare_v2(pCur->db, "PRAGMA database_list", -1, |
︙ | ︙ | |||
3274 3275 3276 3277 3278 3279 3280 3281 3282 3283 3284 3285 3286 3287 3288 3289 | if( iCol<0 ){ /* This case is when the phase presets zCurrentRow */ if( pCur->zCurrentRow==0 ) continue; }else{ if( sqlite3_step(pCur->pStmt)==SQLITE_ROW ){ /* Extract the next row of content */ pCur->zCurrentRow = (const char*)sqlite3_column_text(pCur->pStmt, iCol); }else{ /* When all rows are finished, advance to the next phase */ sqlite3_finalize(pCur->pStmt); pCur->pStmt = 0; pCur->ePhase = eNextPhase; continue; } } if( pCur->nPrefix==0 ) break; | > > | > | | 3219 3220 3221 3222 3223 3224 3225 3226 3227 3228 3229 3230 3231 3232 3233 3234 3235 3236 3237 3238 3239 3240 3241 3242 3243 3244 3245 3246 3247 3248 3249 3250 3251 3252 3253 3254 3255 3256 3257 3258 3259 3260 3261 3262 3263 3264 3265 | if( iCol<0 ){ /* This case is when the phase presets zCurrentRow */ if( pCur->zCurrentRow==0 ) continue; }else{ if( sqlite3_step(pCur->pStmt)==SQLITE_ROW ){ /* Extract the next row of content */ pCur->zCurrentRow = (const char*)sqlite3_column_text(pCur->pStmt, iCol); pCur->szRow = sqlite3_column_bytes(pCur->pStmt, iCol); }else{ /* When all rows are finished, advance to the next phase */ sqlite3_finalize(pCur->pStmt); pCur->pStmt = 0; pCur->ePhase = eNextPhase; continue; } } if( pCur->nPrefix==0 ) break; if( pCur->nPrefix<=pCur->szRow && sqlite3_strnicmp(pCur->zPrefix, pCur->zCurrentRow, pCur->nPrefix)==0 ){ break; } } return SQLITE_OK; } /* ** Return values of columns for the row at which the completion_cursor ** is currently pointing. */ static int completionColumn( sqlite3_vtab_cursor *cur, /* The cursor */ sqlite3_context *ctx, /* First argument to sqlite3_result_...() */ int i /* Which column to return */ ){ completion_cursor *pCur = (completion_cursor*)cur; switch( i ){ case COMPLETION_COLUMN_CANDIDATE: { sqlite3_result_text(ctx, pCur->zCurrentRow, pCur->szRow,SQLITE_TRANSIENT); break; } case COMPLETION_COLUMN_PREFIX: { sqlite3_result_text(ctx, pCur->zPrefix, -1, SQLITE_TRANSIENT); break; } case COMPLETION_COLUMN_WHOLELINE: { |
︙ | ︙ | |||
7664 7665 7666 7667 7668 7669 7670 | IdxHashEntry *pEntry; sqlite3_stmt *pExplain = 0; idxHashClear(&hIdx); rc = idxPrintfPrepareStmt(dbm, &pExplain, pzErr, "EXPLAIN QUERY PLAN %s", pStmt->zSql ); while( rc==SQLITE_OK && sqlite3_step(pExplain)==SQLITE_ROW ){ | | | | | 7612 7613 7614 7615 7616 7617 7618 7619 7620 7621 7622 7623 7624 7625 7626 7627 7628 | IdxHashEntry *pEntry; sqlite3_stmt *pExplain = 0; idxHashClear(&hIdx); rc = idxPrintfPrepareStmt(dbm, &pExplain, pzErr, "EXPLAIN QUERY PLAN %s", pStmt->zSql ); while( rc==SQLITE_OK && sqlite3_step(pExplain)==SQLITE_ROW ){ /* int iId = sqlite3_column_int(pExplain, 0); */ /* int iParent = sqlite3_column_int(pExplain, 1); */ /* int iNotUsed = sqlite3_column_int(pExplain, 2); */ const char *zDetail = (const char*)sqlite3_column_text(pExplain, 3); int nDetail = STRLEN(zDetail); int i; for(i=0; i<nDetail; i++){ const char *zIdx = 0; if( memcmp(&zDetail[i], " USING INDEX ", 13)==0 ){ |
︙ | ︙ | |||
7693 7694 7695 7696 7697 7698 7699 | idxHashAdd(&rc, &hIdx, zSql, 0); if( rc ) goto find_indexes_out; } break; } } | > | < < > | 7641 7642 7643 7644 7645 7646 7647 7648 7649 7650 7651 7652 7653 7654 7655 7656 7657 | idxHashAdd(&rc, &hIdx, zSql, 0); if( rc ) goto find_indexes_out; } break; } } if( zDetail[0]!='-' ){ pStmt->zEQP = idxAppendText(&rc, pStmt->zEQP, "%s\n", zDetail); } } for(pEntry=hIdx.pFirst; pEntry; pEntry=pEntry->pNext){ pStmt->zIdx = idxAppendText(&rc, pStmt->zIdx, "%s;\n", pEntry->zKey); } idxFinalize(&rc, pExplain); |
︙ | ︙ | |||
8525 8526 8527 8528 8529 8530 8531 8532 8533 8534 8535 8536 8537 8538 8539 8540 8541 8542 8543 8544 8545 8546 8547 8548 8549 8550 8551 | typedef struct ExpertInfo ExpertInfo; struct ExpertInfo { sqlite3expert *pExpert; int bVerbose; }; /* ** State information about the database connection is contained in an ** instance of the following structure. */ typedef struct ShellState ShellState; struct ShellState { sqlite3 *db; /* The database */ u8 autoExplain; /* Automatically turn on .explain mode */ u8 autoEQP; /* Run EXPLAIN QUERY PLAN prior to seach SQL stmt */ u8 statsOn; /* True to display memory stats before each finalize */ u8 scanstatsOn; /* True to display scan stats before each finalize */ u8 openMode; /* SHELL_OPEN_NORMAL, _APPENDVFS, or _ZIPFILE */ u8 doXdgOpen; /* Invoke start/open/xdg-open in output_reset() */ int outCount; /* Revert to stdout when reaching zero */ int cnt; /* Number of records displayed so far */ FILE *out; /* Write results here */ FILE *traceOut; /* Output for sqlite3_trace() */ int nErr; /* Number of errors seen */ int mode; /* An output mode setting */ int modePrior; /* Saved mode */ | > > > > > > > > > > > > > > > > > > > > | 8473 8474 8475 8476 8477 8478 8479 8480 8481 8482 8483 8484 8485 8486 8487 8488 8489 8490 8491 8492 8493 8494 8495 8496 8497 8498 8499 8500 8501 8502 8503 8504 8505 8506 8507 8508 8509 8510 8511 8512 8513 8514 8515 8516 8517 8518 8519 | typedef struct ExpertInfo ExpertInfo; struct ExpertInfo { sqlite3expert *pExpert; int bVerbose; }; /* A single line in the EQP output */ typedef struct EQPGraphRow EQPGraphRow; struct EQPGraphRow { int iEqpId; /* ID for this row */ int iParentId; /* ID of the parent row */ EQPGraphRow *pNext; /* Next row in sequence */ char zText[1]; /* Text to display for this row */ }; /* All EQP output is collected into an instance of the following */ typedef struct EQPGraph EQPGraph; struct EQPGraph { EQPGraphRow *pRow; /* Linked list of all rows of the EQP output */ EQPGraphRow *pLast; /* Last element of the pRow list */ char zPrefix[100]; /* Graph prefix */ }; /* ** State information about the database connection is contained in an ** instance of the following structure. */ typedef struct ShellState ShellState; struct ShellState { sqlite3 *db; /* The database */ u8 autoExplain; /* Automatically turn on .explain mode */ u8 autoEQP; /* Run EXPLAIN QUERY PLAN prior to seach SQL stmt */ u8 autoEQPtest; /* autoEQP is in test mode */ u8 statsOn; /* True to display memory stats before each finalize */ u8 scanstatsOn; /* True to display scan stats before each finalize */ u8 openMode; /* SHELL_OPEN_NORMAL, _APPENDVFS, or _ZIPFILE */ u8 doXdgOpen; /* Invoke start/open/xdg-open in output_reset() */ u8 nEqpLevel; /* Depth of the EQP output graph */ unsigned mEqpLines; /* Mask of veritical lines in the EQP output graph */ int outCount; /* Revert to stdout when reaching zero */ int cnt; /* Number of records displayed so far */ FILE *out; /* Write results here */ FILE *traceOut; /* Output for sqlite3_trace() */ int nErr; /* Number of errors seen */ int mode; /* An output mode setting */ int modePrior; /* Saved mode */ |
︙ | ︙ | |||
8571 8572 8573 8574 8575 8576 8577 8578 8579 8580 8581 8582 8583 8584 8585 8586 8587 | char *zFreeOnClose; /* Filename to free when closing */ const char *zVfs; /* Name of VFS to use */ sqlite3_stmt *pStmt; /* Current statement if any. */ FILE *pLog; /* Write log output here */ int *aiIndent; /* Array of indents used in MODE_Explain */ int nIndent; /* Size of array aiIndent[] */ int iIndent; /* Index of current op in aiIndent[] */ #if defined(SQLITE_ENABLE_SESSION) int nSession; /* Number of active sessions */ OpenSession aSession[4]; /* Array of sessions. [0] is in focus. */ #endif ExpertInfo expert; /* Valid if previous command was ".expert OPT..." */ }; /* Allowed values for ShellState.autoEQP */ | > | | | | | 8539 8540 8541 8542 8543 8544 8545 8546 8547 8548 8549 8550 8551 8552 8553 8554 8555 8556 8557 8558 8559 8560 8561 8562 8563 8564 8565 8566 8567 | char *zFreeOnClose; /* Filename to free when closing */ const char *zVfs; /* Name of VFS to use */ sqlite3_stmt *pStmt; /* Current statement if any. */ FILE *pLog; /* Write log output here */ int *aiIndent; /* Array of indents used in MODE_Explain */ int nIndent; /* Size of array aiIndent[] */ int iIndent; /* Index of current op in aiIndent[] */ EQPGraph sGraph; /* Information for the graphical EXPLAIN QUERY PLAN */ #if defined(SQLITE_ENABLE_SESSION) int nSession; /* Number of active sessions */ OpenSession aSession[4]; /* Array of sessions. [0] is in focus. */ #endif ExpertInfo expert; /* Valid if previous command was ".expert OPT..." */ }; /* Allowed values for ShellState.autoEQP */ #define AUTOEQP_off 0 /* Automatic EXPLAIN QUERY PLAN is off */ #define AUTOEQP_on 1 /* Automatic EQP is on */ #define AUTOEQP_trigger 2 /* On and also show plans for triggers */ #define AUTOEQP_full 3 /* Show full EXPLAIN */ /* Allowed values for ShellState.openMode */ #define SHELL_OPEN_UNSPEC 0 /* No open-mode specified */ #define SHELL_OPEN_NORMAL 1 /* Normal database file */ #define SHELL_OPEN_APPENDVFS 2 /* Use appendvfs */ #define SHELL_OPEN_ZIPFILE 3 /* Use the zipfile virtual table */ |
︙ | ︙ | |||
8627 8628 8629 8630 8631 8632 8633 8634 8635 8636 8637 8638 8639 8640 8641 8642 8643 8644 8645 8646 8647 8648 8649 8650 8651 8652 8653 8654 | #define MODE_Insert 5 /* Generate SQL "insert" statements */ #define MODE_Quote 6 /* Quote values as for SQL */ #define MODE_Tcl 7 /* Generate ANSI-C or TCL quoted elements */ #define MODE_Csv 8 /* Quote strings, numbers are plain */ #define MODE_Explain 9 /* Like MODE_Column, but do not truncate data */ #define MODE_Ascii 10 /* Use ASCII unit and record separators (0x1F/0x1E) */ #define MODE_Pretty 11 /* Pretty-print schemas */ static const char *modeDescr[] = { "line", "column", "list", "semi", "html", "insert", "quote", "tcl", "csv", "explain", "ascii", "prettyprint", }; /* ** These are the column/row/line separators used by the various ** import/export modes. */ #define SEP_Column "|" | > > | 8596 8597 8598 8599 8600 8601 8602 8603 8604 8605 8606 8607 8608 8609 8610 8611 8612 8613 8614 8615 8616 8617 8618 8619 8620 8621 8622 8623 8624 8625 | #define MODE_Insert 5 /* Generate SQL "insert" statements */ #define MODE_Quote 6 /* Quote values as for SQL */ #define MODE_Tcl 7 /* Generate ANSI-C or TCL quoted elements */ #define MODE_Csv 8 /* Quote strings, numbers are plain */ #define MODE_Explain 9 /* Like MODE_Column, but do not truncate data */ #define MODE_Ascii 10 /* Use ASCII unit and record separators (0x1F/0x1E) */ #define MODE_Pretty 11 /* Pretty-print schemas */ #define MODE_EQP 12 /* Converts EXPLAIN QUERY PLAN output into a graph */ static const char *modeDescr[] = { "line", "column", "list", "semi", "html", "insert", "quote", "tcl", "csv", "explain", "ascii", "prettyprint", "eqp" }; /* ** These are the column/row/line separators used by the various ** import/export modes. */ #define SEP_Column "|" |
︙ | ︙ | |||
8711 8712 8713 8714 8715 8716 8717 8718 8719 8720 8721 8722 8723 8724 | ){ const char *zEditor; char *zTempFile = 0; sqlite3 *db; char *zCmd = 0; int bBin; int rc; FILE *f = 0; sqlite3_int64 sz; sqlite3_int64 x; unsigned char *p = 0; if( argc==2 ){ zEditor = (const char*)sqlite3_value_text(argv[1]); | > | 8682 8683 8684 8685 8686 8687 8688 8689 8690 8691 8692 8693 8694 8695 8696 | ){ const char *zEditor; char *zTempFile = 0; sqlite3 *db; char *zCmd = 0; int bBin; int rc; int hasCRNL = 0; FILE *f = 0; sqlite3_int64 sz; sqlite3_int64 x; unsigned char *p = 0; if( argc==2 ){ zEditor = (const char*)sqlite3_value_text(argv[1]); |
︙ | ︙ | |||
8742 8743 8744 8745 8746 8747 8748 8749 8750 8751 8752 8753 8754 8755 8756 8757 8758 8759 8760 8761 8762 8763 8764 8765 8766 8767 8768 8769 8770 8771 8772 8773 8774 8775 8776 | zTempFile = sqlite3_mprintf("temp%llx", r); if( zTempFile==0 ){ sqlite3_result_error_nomem(context); return; } } bBin = sqlite3_value_type(argv[0])==SQLITE_BLOB; f = fopen(zTempFile, bBin ? "wb" : "w"); if( f==0 ){ sqlite3_result_error(context, "edit() cannot open temp file", -1); goto edit_func_end; } sz = sqlite3_value_bytes(argv[0]); if( bBin ){ x = fwrite(sqlite3_value_blob(argv[0]), 1, sz, f); }else{ x = fwrite(sqlite3_value_text(argv[0]), 1, sz, f); } fclose(f); f = 0; if( x!=sz ){ sqlite3_result_error(context, "edit() could not write the whole file", -1); goto edit_func_end; } zCmd = sqlite3_mprintf("%s \"%s\"", zEditor, zTempFile); if( zCmd==0 ){ sqlite3_result_error_nomem(context); goto edit_func_end; } rc = system(zCmd); sqlite3_free(zCmd); if( rc ){ sqlite3_result_error(context, "EDITOR returned non-zero", -1); goto edit_func_end; } | > > > > > | < | < < < < > > > > > > > > > > > > > > | 8714 8715 8716 8717 8718 8719 8720 8721 8722 8723 8724 8725 8726 8727 8728 8729 8730 8731 8732 8733 8734 8735 8736 8737 8738 8739 8740 8741 8742 8743 8744 8745 8746 8747 8748 8749 8750 8751 8752 8753 8754 8755 8756 8757 8758 8759 8760 8761 8762 8763 8764 8765 8766 8767 8768 8769 8770 8771 8772 8773 8774 8775 8776 8777 8778 8779 8780 8781 8782 8783 8784 8785 8786 8787 8788 8789 8790 8791 8792 8793 8794 8795 8796 8797 8798 | zTempFile = sqlite3_mprintf("temp%llx", r); if( zTempFile==0 ){ sqlite3_result_error_nomem(context); return; } } bBin = sqlite3_value_type(argv[0])==SQLITE_BLOB; /* When writing the file to be edited, do \n to \r\n conversions on systems ** that want \r\n line endings */ f = fopen(zTempFile, bBin ? "wb" : "w"); if( f==0 ){ sqlite3_result_error(context, "edit() cannot open temp file", -1); goto edit_func_end; } sz = sqlite3_value_bytes(argv[0]); if( bBin ){ x = fwrite(sqlite3_value_blob(argv[0]), 1, sz, f); }else{ const char *z = (const char*)sqlite3_value_text(argv[0]); /* Remember whether or not the value originally contained \r\n */ if( z && strstr(z,"\r\n")!=0 ) hasCRNL = 1; x = fwrite(sqlite3_value_text(argv[0]), 1, sz, f); } fclose(f); f = 0; if( x!=sz ){ sqlite3_result_error(context, "edit() could not write the whole file", -1); goto edit_func_end; } zCmd = sqlite3_mprintf("%s \"%s\"", zEditor, zTempFile); if( zCmd==0 ){ sqlite3_result_error_nomem(context); goto edit_func_end; } rc = system(zCmd); sqlite3_free(zCmd); if( rc ){ sqlite3_result_error(context, "EDITOR returned non-zero", -1); goto edit_func_end; } f = fopen(zTempFile, "rb"); if( f==0 ){ sqlite3_result_error(context, "edit() cannot reopen temp file after edit", -1); goto edit_func_end; } fseek(f, 0, SEEK_END); sz = ftell(f); rewind(f); p = sqlite3_malloc64( sz+(bBin==0) ); if( p==0 ){ sqlite3_result_error_nomem(context); goto edit_func_end; } x = fread(p, 1, sz, f); fclose(f); f = 0; if( x!=sz ){ sqlite3_result_error(context, "could not read back the whole file", -1); goto edit_func_end; } if( bBin ){ sqlite3_result_blob64(context, p, sz, sqlite3_free); }else{ int i, j; if( hasCRNL ){ /* If the original contains \r\n then do no conversions back to \n */ j = sz; }else{ /* If the file did not originally contain \r\n then convert any new ** \r\n back into \n */ for(i=j=0; i<sz; i++){ if( p[i]=='\r' && p[i+1]=='\n' ) i++; p[j++] = p[i]; } sz = j; p[sz] = 0; } sqlite3_result_text64(context, (const char*)p, sz, sqlite3_free, SQLITE_UTF8); } p = 0; edit_func_end: if( f ) fclose(f); |
︙ | ︙ | |||
9189 9190 9191 9192 9193 9194 9195 | if( z[i]=='\n' ) return 1; if( IsSpace(z[i]) ) continue; if( z[i]=='-' && z[i+1]=='-' ) return 1; return 0; } return 1; } | | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 9175 9176 9177 9178 9179 9180 9181 9182 9183 9184 9185 9186 9187 9188 9189 9190 9191 9192 9193 9194 9195 9196 9197 9198 9199 9200 9201 9202 9203 9204 9205 9206 9207 9208 9209 9210 9211 9212 9213 9214 9215 9216 9217 9218 9219 9220 9221 9222 9223 9224 9225 9226 9227 9228 9229 9230 9231 9232 9233 9234 9235 9236 9237 9238 9239 9240 9241 9242 9243 9244 9245 9246 9247 9248 9249 9250 9251 9252 9253 9254 9255 9256 9257 9258 9259 9260 9261 9262 9263 9264 9265 9266 9267 9268 9269 9270 9271 9272 9273 9274 9275 | if( z[i]=='\n' ) return 1; if( IsSpace(z[i]) ) continue; if( z[i]=='-' && z[i+1]=='-' ) return 1; return 0; } return 1; } /* ** Add a new entry to the EXPLAIN QUERY PLAN data */ static void eqp_append(ShellState *p, int iEqpId, int p2, const char *zText){ EQPGraphRow *pNew; int nText = strlen30(zText); if( p->autoEQPtest ){ utf8_printf(p->out, "%d,%d,%s\n", iEqpId, p2, zText); } pNew = sqlite3_malloc64( sizeof(*pNew) + nText ); if( pNew==0 ) shell_out_of_memory(); pNew->iEqpId = iEqpId; pNew->iParentId = p2; memcpy(pNew->zText, zText, nText+1); pNew->pNext = 0; if( p->sGraph.pLast ){ p->sGraph.pLast->pNext = pNew; }else{ p->sGraph.pRow = pNew; } p->sGraph.pLast = pNew; } /* ** Free and reset the EXPLAIN QUERY PLAN data that has been collected ** in p->sGraph. */ static void eqp_reset(ShellState *p){ EQPGraphRow *pRow, *pNext; for(pRow = p->sGraph.pRow; pRow; pRow = pNext){ pNext = pRow->pNext; sqlite3_free(pRow); } memset(&p->sGraph, 0, sizeof(p->sGraph)); } /* Return the next EXPLAIN QUERY PLAN line with iEqpId that occurs after ** pOld, or return the first such line if pOld is NULL */ static EQPGraphRow *eqp_next_row(ShellState *p, int iEqpId, EQPGraphRow *pOld){ EQPGraphRow *pRow = pOld ? pOld->pNext : p->sGraph.pRow; while( pRow && pRow->iParentId!=iEqpId ) pRow = pRow->pNext; return pRow; } /* Render a single level of the graph that has iEqpId as its parent. Called ** recursively to render sublevels. */ static void eqp_render_level(ShellState *p, int iEqpId){ EQPGraphRow *pRow, *pNext; int n = strlen30(p->sGraph.zPrefix); char *z; for(pRow = eqp_next_row(p, iEqpId, 0); pRow; pRow = pNext){ pNext = eqp_next_row(p, iEqpId, pRow); z = pRow->zText; utf8_printf(p->out, "%s%s%s\n", p->sGraph.zPrefix, pNext ? "|--" : "`--", z); if( n<(int)sizeof(p->sGraph.zPrefix)-7 ){ memcpy(&p->sGraph.zPrefix[n], pNext ? "| " : " ", 4); eqp_render_level(p, pRow->iEqpId); p->sGraph.zPrefix[n] = 0; } } } /* ** Display and reset the EXPLAIN QUERY PLAN data */ static void eqp_render(ShellState *p){ EQPGraphRow *pRow = p->sGraph.pRow; if( pRow ){ if( pRow->zText[0]=='-' ){ if( pRow->pNext==0 ){ eqp_reset(p); return; } utf8_printf(p->out, "%s\n", pRow->zText+3); p->sGraph.pRow = pRow->pNext; sqlite3_free(pRow); }else{ utf8_printf(p->out, "QUERY PLAN\n"); } p->sGraph.zPrefix[0] = 0; eqp_render_level(p, 0); eqp_reset(p); } } /* ** This is the callback routine that the shell ** invokes for each row of a query result. */ static int shell_callback( void *pArg, |
︙ | ︙ | |||
9471 9472 9473 9474 9475 9476 9477 | output_quoted_escaped_string(p->out, azArg[i]); } }else if( aiType && aiType[i]==SQLITE_INTEGER ){ utf8_printf(p->out,"%s", azArg[i]); }else if( aiType && aiType[i]==SQLITE_FLOAT ){ char z[50]; double r = sqlite3_column_double(p->pStmt, i); | > > > > > > > | | > | 9543 9544 9545 9546 9547 9548 9549 9550 9551 9552 9553 9554 9555 9556 9557 9558 9559 9560 9561 9562 9563 9564 9565 9566 | output_quoted_escaped_string(p->out, azArg[i]); } }else if( aiType && aiType[i]==SQLITE_INTEGER ){ utf8_printf(p->out,"%s", azArg[i]); }else if( aiType && aiType[i]==SQLITE_FLOAT ){ char z[50]; double r = sqlite3_column_double(p->pStmt, i); sqlite3_uint64 ur; memcpy(&ur,&r,sizeof(r)); if( ur==0x7ff0000000000000LL ){ raw_printf(p->out, "1e999"); }else if( ur==0xfff0000000000000LL ){ raw_printf(p->out, "-1e999"); }else{ sqlite3_snprintf(50,z,"%!.20g", r); raw_printf(p->out, "%s", z); } }else if( aiType && aiType[i]==SQLITE_BLOB && p->pStmt ){ const void *pBlob = sqlite3_column_blob(p->pStmt, i); int nBlob = sqlite3_column_bytes(p->pStmt, i); output_hex_blob(p->out, pBlob, nBlob); }else if( isNumber(azArg[i], 0) ){ utf8_printf(p->out,"%s", azArg[i]); }else if( ShellHasFlag(p, SHFLG_Newlines) ){ |
︙ | ︙ | |||
9539 9540 9541 9542 9543 9544 9545 9546 9547 9548 9549 9550 9551 9552 | if( azArg==0 ) break; for(i=0; i<nArg; i++){ if( i>0 ) utf8_printf(p->out, "%s", p->colSeparator); utf8_printf(p->out,"%s",azArg[i] ? azArg[i] : p->nullValue); } utf8_printf(p->out, "%s", p->rowSeparator); break; } } return 0; } /* ** This is the callback routine that the SQLite library | > > > > | 9619 9620 9621 9622 9623 9624 9625 9626 9627 9628 9629 9630 9631 9632 9633 9634 9635 9636 | if( azArg==0 ) break; for(i=0; i<nArg; i++){ if( i>0 ) utf8_printf(p->out, "%s", p->colSeparator); utf8_printf(p->out,"%s",azArg[i] ? azArg[i] : p->nullValue); } utf8_printf(p->out, "%s", p->rowSeparator); break; } case MODE_EQP: { eqp_append(p, atoi(azArg[0]), atoi(azArg[1]), azArg[3]); break; } } return 0; } /* ** This is the callback routine that the SQLite library |
︙ | ︙ | |||
9638 9639 9640 9641 9642 9643 9644 | p->zDestTable = 0; } if( zName==0 ) return; cQuote = quoteChar(zName); n = strlen30(zName); if( cQuote ) n += n+2; z = p->zDestTable = malloc( n+1 ); | | < < < | 9722 9723 9724 9725 9726 9727 9728 9729 9730 9731 9732 9733 9734 9735 9736 | p->zDestTable = 0; } if( zName==0 ) return; cQuote = quoteChar(zName); n = strlen30(zName); if( cQuote ) n += n+2; z = p->zDestTable = malloc( n+1 ); if( z==0 ) shell_out_of_memory(); n = 0; if( cQuote ) z[n++] = cQuote; for(i=0; zName[i]; i++){ z[n++] = zName[i]; if( zName[i]==cQuote ) z[n++] = cQuote; } if( cQuote ) z[n++] = cQuote; |
︙ | ︙ | |||
10004 10005 10006 10007 10008 10009 10010 | static void explain_data_prepare(ShellState *p, sqlite3_stmt *pSql){ const char *zSql; /* The text of the SQL statement */ const char *z; /* Used to check if this is an EXPLAIN */ int *abYield = 0; /* True if op is an OP_Yield */ int nAlloc = 0; /* Allocated size of p->aiIndent[], abYield */ int iOp; /* Index of operation in p->aiIndent[] */ | | < | 10085 10086 10087 10088 10089 10090 10091 10092 10093 10094 10095 10096 10097 10098 10099 | static void explain_data_prepare(ShellState *p, sqlite3_stmt *pSql){ const char *zSql; /* The text of the SQL statement */ const char *z; /* Used to check if this is an EXPLAIN */ int *abYield = 0; /* True if op is an OP_Yield */ int nAlloc = 0; /* Allocated size of p->aiIndent[], abYield */ int iOp; /* Index of operation in p->aiIndent[] */ const char *azNext[] = { "Next", "Prev", "VPrev", "VNext", "SorterNext", 0 }; const char *azYield[] = { "Yield", "SeekLT", "SeekGT", "RowSetRead", "Rewind", 0 }; const char *azGoto[] = { "Goto", 0 }; /* Try to figure out if this is really an EXPLAIN statement. If this ** cannot be verified, return early. */ if( sqlite3_column_count(pSql)!=8 ){ |
︙ | ︙ | |||
10055 10056 10057 10058 10059 10060 10061 10062 10063 10064 10065 10066 10067 10068 10069 | sqlite3_reset(pSql); return; } } } nAlloc += 100; p->aiIndent = (int*)sqlite3_realloc64(p->aiIndent, nAlloc*sizeof(int)); abYield = (int*)sqlite3_realloc64(abYield, nAlloc*sizeof(int)); } abYield[iOp] = str_in_array(zOp, azYield); p->aiIndent[iOp] = 0; p->nIndent = iOp+1; if( str_in_array(zOp, azNext) ){ for(i=p2op; i<iOp; i++) p->aiIndent[i] += 2; | > > | 10135 10136 10137 10138 10139 10140 10141 10142 10143 10144 10145 10146 10147 10148 10149 10150 10151 | sqlite3_reset(pSql); return; } } } nAlloc += 100; p->aiIndent = (int*)sqlite3_realloc64(p->aiIndent, nAlloc*sizeof(int)); if( p->aiIndent==0 ) shell_out_of_memory(); abYield = (int*)sqlite3_realloc64(abYield, nAlloc*sizeof(int)); if( abYield==0 ) shell_out_of_memory(); } abYield[iOp] = str_in_array(zOp, azYield); p->aiIndent[iOp] = 0; p->nIndent = iOp+1; if( str_in_array(zOp, azNext) ){ for(i=p2op; i<iOp; i++) p->aiIndent[i] += 2; |
︙ | ︙ | |||
10381 10382 10383 10384 10385 10386 10387 | if( pArg->autoEQP>=AUTOEQP_trigger ){ sqlite3_db_config(db, SQLITE_DBCONFIG_TRIGGER_EQP, 1, 0); } zEQP = sqlite3_mprintf("EXPLAIN QUERY PLAN %s", zStmtSql); rc = sqlite3_prepare_v2(db, zEQP, -1, &pExplain, 0); if( rc==SQLITE_OK ){ while( sqlite3_step(pExplain)==SQLITE_ROW ){ | | | | | > > | 10463 10464 10465 10466 10467 10468 10469 10470 10471 10472 10473 10474 10475 10476 10477 10478 10479 10480 10481 10482 10483 | if( pArg->autoEQP>=AUTOEQP_trigger ){ sqlite3_db_config(db, SQLITE_DBCONFIG_TRIGGER_EQP, 1, 0); } zEQP = sqlite3_mprintf("EXPLAIN QUERY PLAN %s", zStmtSql); rc = sqlite3_prepare_v2(db, zEQP, -1, &pExplain, 0); if( rc==SQLITE_OK ){ while( sqlite3_step(pExplain)==SQLITE_ROW ){ const char *zEQPLine = (const char*)sqlite3_column_text(pExplain,3); int iEqpId = sqlite3_column_int(pExplain, 0); int iParentId = sqlite3_column_int(pExplain, 1); if( zEQPLine[0]=='-' ) eqp_render(pArg); eqp_append(pArg, iEqpId, iParentId, zEQPLine); } eqp_render(pArg); } sqlite3_finalize(pExplain); sqlite3_free(zEQP); if( pArg->autoEQP>=AUTOEQP_full ){ /* Also do an EXPLAIN for ".eqp full" mode */ zEQP = sqlite3_mprintf("EXPLAIN %s", zStmtSql); rc = sqlite3_prepare_v2(db, zEQP, -1, &pExplain, 0); |
︙ | ︙ | |||
10407 10408 10409 10410 10411 10412 10413 10414 10415 10416 10417 10418 10419 | sqlite3_free(zEQP); } if( pArg->autoEQP>=AUTOEQP_trigger && triggerEQP==0 ){ sqlite3_db_config(db, SQLITE_DBCONFIG_TRIGGER_EQP, 0, 0); /* Reprepare pStmt before reactiving trace modes */ sqlite3_finalize(pStmt); sqlite3_prepare_v2(db, zSql, -1, &pStmt, 0); } restore_debug_trace_modes(); } if( pArg ){ pArg->cMode = pArg->mode; | > | | | | | > > > > > > | 10491 10492 10493 10494 10495 10496 10497 10498 10499 10500 10501 10502 10503 10504 10505 10506 10507 10508 10509 10510 10511 10512 10513 10514 10515 10516 10517 10518 10519 10520 10521 10522 10523 10524 10525 10526 10527 10528 10529 10530 10531 10532 10533 | sqlite3_free(zEQP); } if( pArg->autoEQP>=AUTOEQP_trigger && triggerEQP==0 ){ sqlite3_db_config(db, SQLITE_DBCONFIG_TRIGGER_EQP, 0, 0); /* Reprepare pStmt before reactiving trace modes */ sqlite3_finalize(pStmt); sqlite3_prepare_v2(db, zSql, -1, &pStmt, 0); if( pArg ) pArg->pStmt = pStmt; } restore_debug_trace_modes(); } if( pArg ){ pArg->cMode = pArg->mode; if( pArg->autoExplain ){ if( sqlite3_column_count(pStmt)==8 && sqlite3_strlike("EXPLAIN%", zStmtSql,0)==0 ){ pArg->cMode = MODE_Explain; } if( sqlite3_column_count(pStmt)==4 && sqlite3_strlike("EXPLAIN QUERY PLAN%", zStmtSql,0)==0 ){ pArg->cMode = MODE_EQP; } } /* If the shell is currently in ".explain" mode, gather the extra ** data required to add indents to the output.*/ if( pArg->cMode==MODE_Explain ){ explain_data_prepare(pArg, pStmt); } } exec_prepared_stmt(pArg, pStmt); explain_data_delete(pArg); eqp_render(pArg); /* print usage stats if stats on */ if( pArg && pArg->statsOn ){ display_stats(db, pArg, 0); } /* print loop-counters if required */ |
︙ | ︙ | |||
10506 10507 10508 10509 10510 10511 10512 | rc = sqlite3_prepare_v2(p->db, zSql, -1, &pStmt, 0); sqlite3_free(zSql); if( rc ) return 0; while( sqlite3_step(pStmt)==SQLITE_ROW ){ if( nCol>=nAlloc-2 ){ nAlloc = nAlloc*2 + nCol + 10; azCol = sqlite3_realloc(azCol, nAlloc*sizeof(azCol[0])); | | < < < | 10597 10598 10599 10600 10601 10602 10603 10604 10605 10606 10607 10608 10609 10610 10611 | rc = sqlite3_prepare_v2(p->db, zSql, -1, &pStmt, 0); sqlite3_free(zSql); if( rc ) return 0; while( sqlite3_step(pStmt)==SQLITE_ROW ){ if( nCol>=nAlloc-2 ){ nAlloc = nAlloc*2 + nCol + 10; azCol = sqlite3_realloc(azCol, nAlloc*sizeof(azCol[0])); if( azCol==0 ) shell_out_of_memory(); } azCol[++nCol] = sqlite3_mprintf("%s", sqlite3_column_text(pStmt, 1)); if( sqlite3_column_int(pStmt, 5) ){ nPK++; if( nPK==1 && sqlite3_stricmp((const char*)sqlite3_column_text(pStmt,2), "INTEGER")==0 |
︙ | ︙ | |||
10755 10756 10757 10758 10759 10760 10761 10762 10763 10764 10765 10766 10767 10768 10769 10770 10771 10772 10773 10774 10775 | #if defined(SQLITE_HAVE_ZLIB) && !defined(SQLITE_OMIT_VIRTUALTABLE) ".archive ... Manage SQL archives: \".archive --help\" for details\n" #endif #ifndef SQLITE_OMIT_AUTHORIZATION ".auth ON|OFF Show authorizer callbacks\n" #endif ".backup ?DB? FILE Backup DB (default \"main\") to FILE\n" ".bail on|off Stop after hitting an error. Default OFF\n" ".binary on|off Turn binary output on or off. Default OFF\n" ".cd DIRECTORY Change the working directory to DIRECTORY\n" ".changes on|off Show number of rows changed by SQL\n" ".check GLOB Fail if output since .testcase does not match\n" ".clone NEWDB Clone data into NEWDB from the existing database\n" ".databases List names and files of attached databases\n" ".dbinfo ?DB? Show status information about the database\n" ".dump ?TABLE? ... Dump the database in an SQL text format\n" " If TABLE specified, only dump tables matching\n" " LIKE pattern TABLE.\n" ".echo on|off Turn command echo on or off\n" ".eqp on|off|full Enable or disable automatic EXPLAIN QUERY PLAN\n" ".excel Display the output of next command in a spreadsheet\n" | > > | 10843 10844 10845 10846 10847 10848 10849 10850 10851 10852 10853 10854 10855 10856 10857 10858 10859 10860 10861 10862 10863 10864 10865 | #if defined(SQLITE_HAVE_ZLIB) && !defined(SQLITE_OMIT_VIRTUALTABLE) ".archive ... Manage SQL archives: \".archive --help\" for details\n" #endif #ifndef SQLITE_OMIT_AUTHORIZATION ".auth ON|OFF Show authorizer callbacks\n" #endif ".backup ?DB? FILE Backup DB (default \"main\") to FILE\n" " Add \"--append\" to open using appendvfs.\n" ".bail on|off Stop after hitting an error. Default OFF\n" ".binary on|off Turn binary output on or off. Default OFF\n" ".cd DIRECTORY Change the working directory to DIRECTORY\n" ".changes on|off Show number of rows changed by SQL\n" ".check GLOB Fail if output since .testcase does not match\n" ".clone NEWDB Clone data into NEWDB from the existing database\n" ".databases List names and files of attached databases\n" ".dbconfig ?op? ?val? List or change sqlite3_db_config() options\n" ".dbinfo ?DB? Show status information about the database\n" ".dump ?TABLE? ... Dump the database in an SQL text format\n" " If TABLE specified, only dump tables matching\n" " LIKE pattern TABLE.\n" ".echo on|off Turn command echo on or off\n" ".eqp on|off|full Enable or disable automatic EXPLAIN QUERY PLAN\n" ".excel Display the output of next command in a spreadsheet\n" |
︙ | ︙ | |||
10971 10972 10973 10974 10975 10976 10977 | ** one of the SHELL_OPEN_* constants. ** ** If the file does not exist or is empty but its name looks like a ZIP ** archive and the dfltZip flag is true, then assume it is a ZIP archive. ** Otherwise, assume an ordinary database regardless of the filename if ** the type cannot be determined from content. */ | | | > > | > | > > > > > > > > > > > > > | < | > > > | > > | 11061 11062 11063 11064 11065 11066 11067 11068 11069 11070 11071 11072 11073 11074 11075 11076 11077 11078 11079 11080 11081 11082 11083 11084 11085 11086 11087 11088 11089 11090 11091 11092 11093 11094 11095 11096 11097 11098 11099 11100 11101 11102 11103 11104 11105 11106 11107 11108 11109 11110 11111 11112 11113 11114 11115 11116 11117 11118 11119 11120 11121 11122 11123 11124 11125 11126 11127 11128 11129 11130 | ** one of the SHELL_OPEN_* constants. ** ** If the file does not exist or is empty but its name looks like a ZIP ** archive and the dfltZip flag is true, then assume it is a ZIP archive. ** Otherwise, assume an ordinary database regardless of the filename if ** the type cannot be determined from content. */ int deduceDatabaseType(const char *zName, int dfltZip){ FILE *f = fopen(zName, "rb"); size_t n; int rc = SHELL_OPEN_UNSPEC; char zBuf[100]; if( f==0 ){ if( dfltZip && sqlite3_strlike("%.zip",zName,0)==0 ){ return SHELL_OPEN_ZIPFILE; }else{ return SHELL_OPEN_NORMAL; } } fseek(f, -25, SEEK_END); n = fread(zBuf, 25, 1, f); if( n==1 && memcmp(zBuf, "Start-Of-SQLite3-", 17)==0 ){ rc = SHELL_OPEN_APPENDVFS; }else{ fseek(f, -22, SEEK_END); n = fread(zBuf, 22, 1, f); if( n==1 && zBuf[0]==0x50 && zBuf[1]==0x4b && zBuf[2]==0x05 && zBuf[3]==0x06 ){ rc = SHELL_OPEN_ZIPFILE; }else if( n==0 && dfltZip && sqlite3_strlike("%.zip",zName,0)==0 ){ rc = SHELL_OPEN_ZIPFILE; } } fclose(f); return rc; } /* Flags for open_db(). ** ** The default behavior of open_db() is to exit(1) if the database fails to ** open. The OPEN_DB_KEEPALIVE flag changes that so that it prints an error ** but still returns without calling exit. ** ** The OPEN_DB_ZIPFILE flag causes open_db() to prefer to open files as a ** ZIP archive if the file does not exist or is empty and its name matches ** the *.zip pattern. */ #define OPEN_DB_KEEPALIVE 0x001 /* Return after error if true */ #define OPEN_DB_ZIPFILE 0x002 /* Open as ZIP if name matches *.zip */ /* ** Make sure the database is open. If it is not, then open it. If ** the database fails to open, print an error message and exit. */ static void open_db(ShellState *p, int openFlags){ if( p->db==0 ){ if( p->openMode==SHELL_OPEN_UNSPEC ){ if( p->zDbFilename==0 || p->zDbFilename[0]==0 ){ p->openMode = SHELL_OPEN_NORMAL; }else{ p->openMode = (u8)deduceDatabaseType(p->zDbFilename, (openFlags & OPEN_DB_ZIPFILE)!=0); } } switch( p->openMode ){ case SHELL_OPEN_APPENDVFS: { sqlite3_open_v2(p->zDbFilename, &p->db, SQLITE_OPEN_READWRITE|SQLITE_OPEN_CREATE, "apndvfs"); break; } |
︙ | ︙ | |||
11032 11033 11034 11035 11036 11037 11038 | break; } } globalDb = p->db; if( p->db==0 || SQLITE_OK!=sqlite3_errcode(p->db) ){ utf8_printf(stderr,"Error: unable to open database \"%s\": %s\n", p->zDbFilename, sqlite3_errmsg(p->db)); | | | 11142 11143 11144 11145 11146 11147 11148 11149 11150 11151 11152 11153 11154 11155 11156 | break; } } globalDb = p->db; if( p->db==0 || SQLITE_OK!=sqlite3_errcode(p->db) ){ utf8_printf(stderr,"Error: unable to open database \"%s\": %s\n", p->zDbFilename, sqlite3_errmsg(p->db)); if( openFlags & OPEN_DB_KEEPALIVE ) return; exit(1); } #ifndef SQLITE_OMIT_LOAD_EXTENSION sqlite3_enable_load_extension(p->db, 1); #endif sqlite3_fileio_init(p->db, 0, 0); sqlite3_shathree_init(p->db, 0, 0); |
︙ | ︙ | |||
11065 11066 11067 11068 11069 11070 11071 11072 11073 11074 11075 11076 11077 11078 | char *zSql = sqlite3_mprintf( "CREATE VIRTUAL TABLE zip USING zipfile(%Q);", p->zDbFilename); sqlite3_exec(p->db, zSql, 0, 0, 0); sqlite3_free(zSql); } } } #if HAVE_READLINE || HAVE_EDITLINE /* ** Readline completion callbacks */ static char *readline_completion_generator(const char *text, int state){ static sqlite3_stmt *pStmt = 0; | > > > > > > > > > > > | 11175 11176 11177 11178 11179 11180 11181 11182 11183 11184 11185 11186 11187 11188 11189 11190 11191 11192 11193 11194 11195 11196 11197 11198 11199 | char *zSql = sqlite3_mprintf( "CREATE VIRTUAL TABLE zip USING zipfile(%Q);", p->zDbFilename); sqlite3_exec(p->db, zSql, 0, 0, 0); sqlite3_free(zSql); } } } /* ** Attempt to close the databaes connection. Report errors. */ void close_db(sqlite3 *db){ int rc = sqlite3_close(db); if( rc ){ utf8_printf(stderr, "Error: sqlite3_close() returns %d: %s\n", rc, sqlite3_errmsg(db)); } } #if HAVE_READLINE || HAVE_EDITLINE /* ** Readline completion callbacks */ static char *readline_completion_generator(const char *text, int state){ static sqlite3_stmt *pStmt = 0; |
︙ | ︙ | |||
11107 11108 11109 11110 11111 11112 11113 | int nLine = strlen30(zLine); int i, iStart; sqlite3_stmt *pStmt = 0; char *zSql; char zBuf[1000]; if( nLine>sizeof(zBuf)-30 ) return; | | | 11228 11229 11230 11231 11232 11233 11234 11235 11236 11237 11238 11239 11240 11241 11242 | int nLine = strlen30(zLine); int i, iStart; sqlite3_stmt *pStmt = 0; char *zSql; char zBuf[1000]; if( nLine>sizeof(zBuf)-30 ) return; if( zLine[0]=='.' || zLine[0]=='#') return; for(i=nLine-1; i>=0 && (isalnum(zLine[i]) || zLine[i]=='_'); i--){} if( i==nLine-1 ) return; iStart = i+1; memcpy(zBuf, zLine, iStart); zSql = sqlite3_mprintf("SELECT DISTINCT candidate COLLATE nocase" " FROM completion(%Q,%Q) ORDER BY 1", &zLine[iStart], zLine); |
︙ | ︙ | |||
11307 11308 11309 11310 11311 11312 11313 | }; /* Append a single byte to z[] */ static void import_append_char(ImportCtx *p, int c){ if( p->n+1>=p->nAlloc ){ p->nAlloc += p->nAlloc + 100; p->z = sqlite3_realloc64(p->z, p->nAlloc); | | < < < | 11428 11429 11430 11431 11432 11433 11434 11435 11436 11437 11438 11439 11440 11441 11442 | }; /* Append a single byte to z[] */ static void import_append_char(ImportCtx *p, int c){ if( p->n+1>=p->nAlloc ){ p->nAlloc += p->nAlloc + 100; p->z = sqlite3_realloc64(p->z, p->nAlloc); if( p->z==0 ) shell_out_of_memory(); } p->z[p->n++] = (char)c; } /* Read a single field of CSV text. Compatible with rfc4180 and extended ** with the option of having a separator other than ",". ** |
︙ | ︙ | |||
11471 11472 11473 11474 11475 11476 11477 | utf8_printf(stderr, "Error %d: %s on [%s]\n", sqlite3_extended_errcode(p->db), sqlite3_errmsg(p->db), zQuery); goto end_data_xfer; } n = sqlite3_column_count(pQuery); zInsert = sqlite3_malloc64(200 + nTable + n*3); | | < < < | 11589 11590 11591 11592 11593 11594 11595 11596 11597 11598 11599 11600 11601 11602 11603 | utf8_printf(stderr, "Error %d: %s on [%s]\n", sqlite3_extended_errcode(p->db), sqlite3_errmsg(p->db), zQuery); goto end_data_xfer; } n = sqlite3_column_count(pQuery); zInsert = sqlite3_malloc64(200 + nTable + n*3); if( zInsert==0 ) shell_out_of_memory(); sqlite3_snprintf(200+nTable,zInsert, "INSERT OR IGNORE INTO \"%s\" VALUES(?", zTable); i = strlen30(zInsert); for(j=1; j<n; j++){ memcpy(zInsert+i, ",?", 2); i += 2; } |
︙ | ︙ | |||
11652 11653 11654 11655 11656 11657 11658 | sqlite3_exec(p->db, "PRAGMA writable_schema=ON;", 0, 0, 0); sqlite3_exec(newDb, "BEGIN EXCLUSIVE;", 0, 0, 0); tryToCloneSchema(p, newDb, "type='table'", tryToCloneData); tryToCloneSchema(p, newDb, "type!='table'", 0); sqlite3_exec(newDb, "COMMIT;", 0, 0, 0); sqlite3_exec(p->db, "PRAGMA writable_schema=OFF;", 0, 0, 0); } | | | 11767 11768 11769 11770 11771 11772 11773 11774 11775 11776 11777 11778 11779 11780 11781 | sqlite3_exec(p->db, "PRAGMA writable_schema=ON;", 0, 0, 0); sqlite3_exec(newDb, "BEGIN EXCLUSIVE;", 0, 0, 0); tryToCloneSchema(p, newDb, "type='table'", tryToCloneData); tryToCloneSchema(p, newDb, "type!='table'", 0); sqlite3_exec(newDb, "COMMIT;", 0, 0, 0); sqlite3_exec(p->db, "PRAGMA writable_schema=OFF;", 0, 0, 0); } close_db(newDb); } /* ** Change the output file back to stdout. ** ** If the p->doXdgOpen flag is set, that means the output was being ** redirected to a temporary file named by p->zTempFile. In that case, |
︙ | ︙ | |||
11751 11752 11753 11754 11755 11756 11757 11758 11759 11760 11761 11762 11763 11764 | "SELECT count(*) FROM %s WHERE type='trigger'" }, { "number of views:", "SELECT count(*) FROM %s WHERE type='view'" }, { "schema size:", "SELECT total(length(sql)) FROM %s" }, }; int i; char *zSchemaTab; char *zDb = nArg>=2 ? azArg[1] : "main"; sqlite3_stmt *pStmt = 0; unsigned char aHdr[100]; open_db(p, 0); if( p->db==0 ) return 1; sqlite3_prepare_v2(p->db,"SELECT data FROM sqlite_dbpage(?1) WHERE pgno=1", | > | 11866 11867 11868 11869 11870 11871 11872 11873 11874 11875 11876 11877 11878 11879 11880 | "SELECT count(*) FROM %s WHERE type='trigger'" }, { "number of views:", "SELECT count(*) FROM %s WHERE type='view'" }, { "schema size:", "SELECT total(length(sql)) FROM %s" }, }; int i; unsigned iDataVersion; char *zSchemaTab; char *zDb = nArg>=2 ? azArg[1] : "main"; sqlite3_stmt *pStmt = 0; unsigned char aHdr[100]; open_db(p, 0); if( p->db==0 ) return 1; sqlite3_prepare_v2(p->db,"SELECT data FROM sqlite_dbpage(?1) WHERE pgno=1", |
︙ | ︙ | |||
11803 11804 11805 11806 11807 11808 11809 11810 11811 11812 11813 11814 11815 11816 11817 11818 11819 11820 11821 | for(i=0; i<ArraySize(aQuery); i++){ char *zSql = sqlite3_mprintf(aQuery[i].zSql, zSchemaTab); int val = db_int(p, zSql); sqlite3_free(zSql); utf8_printf(p->out, "%-20s %d\n", aQuery[i].zName, val); } sqlite3_free(zSchemaTab); return 0; } /* ** Print the current sqlite3_errmsg() value to stderr and return 1. */ static int shellDatabaseError(sqlite3 *db){ const char *zErr = sqlite3_errmsg(db); utf8_printf(stderr, "Error: %s\n", zErr); return 1; } | > > < < < < < < < < | 11919 11920 11921 11922 11923 11924 11925 11926 11927 11928 11929 11930 11931 11932 11933 11934 11935 11936 11937 11938 11939 11940 11941 11942 11943 11944 11945 11946 | for(i=0; i<ArraySize(aQuery); i++){ char *zSql = sqlite3_mprintf(aQuery[i].zSql, zSchemaTab); int val = db_int(p, zSql); sqlite3_free(zSql); utf8_printf(p->out, "%-20s %d\n", aQuery[i].zName, val); } sqlite3_free(zSchemaTab); sqlite3_file_control(p->db, zDb, SQLITE_FCNTL_DATA_VERSION, &iDataVersion); utf8_printf(p->out, "%-20s %u\n", "data version", iDataVersion); return 0; } /* ** Print the current sqlite3_errmsg() value to stderr and return 1. */ static int shellDatabaseError(sqlite3 *db){ const char *zErr = sqlite3_errmsg(db); utf8_printf(stderr, "Error: %s\n", zErr); return 1; } /* ** Compare the pattern in zGlob[] against the text in z[]. Return TRUE ** if they match and FALSE (0) if they do not match. ** ** Globbing rules: ** ** '*' Matches any sequence of zero or more characters. |
︙ | ︙ | |||
12315 12316 12317 12318 12319 12320 12321 12322 12323 12324 12325 12326 12327 12328 | typedef struct ArCommand ArCommand; struct ArCommand { u8 eCmd; /* An AR_CMD_* value */ u8 bVerbose; /* True if --verbose */ u8 bZip; /* True if the archive is a ZIP */ u8 bDryRun; /* True if --dry-run */ u8 bAppend; /* True if --append */ int nArg; /* Number of command arguments */ char *zSrcTable; /* "sqlar", "zipfile($file)" or "zip" */ const char *zFile; /* --file argument, or NULL */ const char *zDir; /* --directory argument, or NULL */ char **azArg; /* Array of command arguments */ ShellState *p; /* Shell state */ sqlite3 *db; /* Database containing the archive */ | > | 12425 12426 12427 12428 12429 12430 12431 12432 12433 12434 12435 12436 12437 12438 12439 | typedef struct ArCommand ArCommand; struct ArCommand { u8 eCmd; /* An AR_CMD_* value */ u8 bVerbose; /* True if --verbose */ u8 bZip; /* True if the archive is a ZIP */ u8 bDryRun; /* True if --dry-run */ u8 bAppend; /* True if --append */ u8 fromCmdLine; /* Run from -A instead of .archive */ int nArg; /* Number of command arguments */ char *zSrcTable; /* "sqlar", "zipfile($file)" or "zip" */ const char *zFile; /* --file argument, or NULL */ const char *zDir; /* --directory argument, or NULL */ char **azArg; /* Array of command arguments */ ShellState *p; /* Shell state */ sqlite3 *db; /* Database containing the archive */ |
︙ | ︙ | |||
12361 12362 12363 12364 12365 12366 12367 | return SQLITE_ERROR; } /* ** Print an error message for the .ar command to stderr and return ** SQLITE_ERROR. */ | | | > > > > > | 12472 12473 12474 12475 12476 12477 12478 12479 12480 12481 12482 12483 12484 12485 12486 12487 12488 12489 12490 12491 12492 12493 12494 12495 12496 12497 | return SQLITE_ERROR; } /* ** Print an error message for the .ar command to stderr and return ** SQLITE_ERROR. */ static int arErrorMsg(ArCommand *pAr, const char *zFmt, ...){ va_list ap; char *z; va_start(ap, zFmt); z = sqlite3_vmprintf(zFmt, ap); va_end(ap); utf8_printf(stderr, "Error: %s\n", z); if( pAr->fromCmdLine ){ utf8_printf(stderr, "Use \"-A\" for more help\n"); }else{ utf8_printf(stderr, "Use \".archive --help\" for more help\n"); } sqlite3_free(z); return SQLITE_ERROR; } /* ** Values for ArCommand.eCmd. */ |
︙ | ︙ | |||
12398 12399 12400 12401 12402 12403 12404 | switch( eSwitch ){ case AR_CMD_CREATE: case AR_CMD_EXTRACT: case AR_CMD_LIST: case AR_CMD_UPDATE: case AR_CMD_HELP: if( pAr->eCmd ){ | | | 12514 12515 12516 12517 12518 12519 12520 12521 12522 12523 12524 12525 12526 12527 12528 | switch( eSwitch ){ case AR_CMD_CREATE: case AR_CMD_EXTRACT: case AR_CMD_LIST: case AR_CMD_UPDATE: case AR_CMD_HELP: if( pAr->eCmd ){ return arErrorMsg(pAr, "multiple command options"); } pAr->eCmd = eSwitch; break; case AR_SWITCH_DRYRUN: pAr->bDryRun = 1; break; |
︙ | ︙ | |||
12458 12459 12460 12461 12462 12463 12464 | int nSwitch = sizeof(aSwitch) / sizeof(struct ArSwitch); struct ArSwitch *pEnd = &aSwitch[nSwitch]; if( nArg<=1 ){ return arUsage(stderr); }else{ char *z = azArg[1]; | < < | | | 12574 12575 12576 12577 12578 12579 12580 12581 12582 12583 12584 12585 12586 12587 12588 12589 12590 12591 12592 12593 12594 12595 12596 12597 12598 12599 12600 12601 12602 12603 | int nSwitch = sizeof(aSwitch) / sizeof(struct ArSwitch); struct ArSwitch *pEnd = &aSwitch[nSwitch]; if( nArg<=1 ){ return arUsage(stderr); }else{ char *z = azArg[1]; if( z[0]!='-' ){ /* Traditional style [tar] invocation */ int i; int iArg = 2; for(i=0; z[i]; i++){ const char *zArg = 0; struct ArSwitch *pOpt; for(pOpt=&aSwitch[0]; pOpt<pEnd; pOpt++){ if( z[i]==pOpt->cShort ) break; } if( pOpt==pEnd ){ return arErrorMsg(pAr, "unrecognized option: %c", z[i]); } if( pOpt->bArg ){ if( iArg>=nArg ){ return arErrorMsg(pAr, "option requires an argument: %c",z[i]); } zArg = azArg[iArg++]; } if( arProcessSwitch(pAr, pOpt->eSwitch, zArg) ) return SQLITE_ERROR; } pAr->nArg = nArg-iArg; if( pAr->nArg>0 ){ |
︙ | ︙ | |||
12509 12510 12511 12512 12513 12514 12515 | for(i=1; i<n; i++){ const char *zArg = 0; struct ArSwitch *pOpt; for(pOpt=&aSwitch[0]; pOpt<pEnd; pOpt++){ if( z[i]==pOpt->cShort ) break; } if( pOpt==pEnd ){ | | | | 12623 12624 12625 12626 12627 12628 12629 12630 12631 12632 12633 12634 12635 12636 12637 12638 12639 12640 12641 12642 12643 12644 12645 | for(i=1; i<n; i++){ const char *zArg = 0; struct ArSwitch *pOpt; for(pOpt=&aSwitch[0]; pOpt<pEnd; pOpt++){ if( z[i]==pOpt->cShort ) break; } if( pOpt==pEnd ){ return arErrorMsg(pAr, "unrecognized option: %c", z[i]); } if( pOpt->bArg ){ if( i<(n-1) ){ zArg = &z[i+1]; i = n; }else{ if( iArg>=(nArg-1) ){ return arErrorMsg(pAr, "option requires an argument: %c",z[i]); } zArg = azArg[++iArg]; } } if( arProcessSwitch(pAr, pOpt->eSwitch, zArg) ) return SQLITE_ERROR; } }else if( z[2]=='\0' ){ |
︙ | ︙ | |||
12539 12540 12541 12542 12543 12544 12545 | const char *zArg = 0; /* Argument for option, if any */ struct ArSwitch *pMatch = 0; /* Matching option */ struct ArSwitch *pOpt; /* Iterator */ for(pOpt=&aSwitch[0]; pOpt<pEnd; pOpt++){ const char *zLong = pOpt->zLong; if( (n-2)<=strlen30(zLong) && 0==memcmp(&z[2], zLong, n-2) ){ if( pMatch ){ | | | | | 12653 12654 12655 12656 12657 12658 12659 12660 12661 12662 12663 12664 12665 12666 12667 12668 12669 12670 12671 12672 12673 12674 12675 12676 12677 12678 12679 | const char *zArg = 0; /* Argument for option, if any */ struct ArSwitch *pMatch = 0; /* Matching option */ struct ArSwitch *pOpt; /* Iterator */ for(pOpt=&aSwitch[0]; pOpt<pEnd; pOpt++){ const char *zLong = pOpt->zLong; if( (n-2)<=strlen30(zLong) && 0==memcmp(&z[2], zLong, n-2) ){ if( pMatch ){ return arErrorMsg(pAr, "ambiguous option: %s",z); }else{ pMatch = pOpt; } } } if( pMatch==0 ){ return arErrorMsg(pAr, "unrecognized option: %s", z); } if( pMatch->bArg ){ if( iArg>=(nArg-1) ){ return arErrorMsg(pAr, "option requires an argument: %s", z); } zArg = azArg[++iArg]; } if( arProcessSwitch(pAr, pMatch->eSwitch, zArg) ) return SQLITE_ERROR; } } } |
︙ | ︙ | |||
12680 12681 12682 12683 12684 12685 12686 12687 12688 12689 12690 12691 12692 12693 12694 12695 12696 12697 12698 | ); }else{ utf8_printf(pAr->p->out, "%s\n", sqlite3_column_text(pSql, 0)); } } } shellFinalize(&rc, pSql); return rc; } /* ** Implementation of .ar "eXtract" command. */ static int arExtractCommand(ArCommand *pAr){ const char *zSql1 = "SELECT " " ($dir || name)," " writefile(($dir || name), %s, mode, mtime) " | > | > | 12794 12795 12796 12797 12798 12799 12800 12801 12802 12803 12804 12805 12806 12807 12808 12809 12810 12811 12812 12813 12814 12815 12816 12817 12818 12819 12820 12821 12822 | ); }else{ utf8_printf(pAr->p->out, "%s\n", sqlite3_column_text(pSql, 0)); } } } shellFinalize(&rc, pSql); sqlite3_free(zWhere); return rc; } /* ** Implementation of .ar "eXtract" command. */ static int arExtractCommand(ArCommand *pAr){ const char *zSql1 = "SELECT " " ($dir || name)," " writefile(($dir || name), %s, mode, mtime) " "FROM %s WHERE (%s) AND (data IS NULL OR $dirOnly = 0)" " AND name NOT GLOB '*..[/\\]*'"; const char *azExtraArg[] = { "sqlar_uncompress(data, sz)", "data" }; sqlite3_stmt *pSql = 0; |
︙ | ︙ | |||
12882 12883 12884 12885 12886 12887 12888 12889 12890 12891 12892 12893 12894 12895 12896 12897 12898 12899 12900 12901 | } /* ** Implementation of ".ar" dot command. */ static int arDotCommand( ShellState *pState, /* Current shell tool state */ char **azArg, /* Array of arguments passed to dot command */ int nArg /* Number of entries in azArg[] */ ){ ArCommand cmd; int rc; memset(&cmd, 0, sizeof(cmd)); rc = arParseCommand(azArg, nArg, &cmd); if( rc==SQLITE_OK ){ int eDbType = SHELL_OPEN_UNSPEC; cmd.p = pState; cmd.db = pState->db; if( cmd.zFile ){ eDbType = deduceDatabaseType(cmd.zFile, 1); | > > | 12998 12999 13000 13001 13002 13003 13004 13005 13006 13007 13008 13009 13010 13011 13012 13013 13014 13015 13016 13017 13018 13019 | } /* ** Implementation of ".ar" dot command. */ static int arDotCommand( ShellState *pState, /* Current shell tool state */ int fromCmdLine, /* True if -A command-line option, not .ar cmd */ char **azArg, /* Array of arguments passed to dot command */ int nArg /* Number of entries in azArg[] */ ){ ArCommand cmd; int rc; memset(&cmd, 0, sizeof(cmd)); cmd.fromCmdLine = fromCmdLine; rc = arParseCommand(azArg, nArg, &cmd); if( rc==SQLITE_OK ){ int eDbType = SHELL_OPEN_UNSPEC; cmd.p = pState; cmd.db = pState->db; if( cmd.zFile ){ eDbType = deduceDatabaseType(cmd.zFile, 1); |
︙ | ︙ | |||
12934 12935 12936 12937 12938 12939 12940 | } sqlite3_fileio_init(cmd.db, 0, 0); sqlite3_sqlar_init(cmd.db, 0, 0); sqlite3_create_function(cmd.db, "shell_putsnl", 1, SQLITE_UTF8, cmd.p, shellPutsFunc, 0, 0); } | | | 13052 13053 13054 13055 13056 13057 13058 13059 13060 13061 13062 13063 13064 13065 13066 | } sqlite3_fileio_init(cmd.db, 0, 0); sqlite3_sqlar_init(cmd.db, 0, 0); sqlite3_create_function(cmd.db, "shell_putsnl", 1, SQLITE_UTF8, cmd.p, shellPutsFunc, 0, 0); } if( cmd.zSrcTable==0 && cmd.bZip==0 && cmd.eCmd!=AR_CMD_HELP ){ if( cmd.eCmd!=AR_CMD_CREATE && sqlite3_table_column_metadata(cmd.db,0,"sqlar","name",0,0,0,0,0) ){ utf8_printf(stderr, "database does not contain an 'sqlar' table\n"); rc = SQLITE_ERROR; goto end_ar_command; } |
︙ | ︙ | |||
12970 12971 12972 12973 12974 12975 12976 | assert( cmd.eCmd==AR_CMD_UPDATE ); rc = arCreateOrUpdateCommand(&cmd, 1); break; } } end_ar_command: if( cmd.db!=pState->db ){ | | | 13088 13089 13090 13091 13092 13093 13094 13095 13096 13097 13098 13099 13100 13101 13102 | assert( cmd.eCmd==AR_CMD_UPDATE ); rc = arCreateOrUpdateCommand(&cmd, 1); break; } } end_ar_command: if( cmd.db!=pState->db ){ close_db(cmd.db); } sqlite3_free(cmd.zSrcTable); return rc; } /* End of the ".archive" or ".ar" command logic **********************************************************************************/ |
︙ | ︙ | |||
13050 13051 13052 13053 13054 13055 13056 | } }else #endif #if !defined(SQLITE_OMIT_VIRTUALTABLE) && defined(SQLITE_HAVE_ZLIB) if( c=='a' && strncmp(azArg[0], "archive", n)==0 ){ open_db(p, 0); | | > | | > > | | > | | | | 13168 13169 13170 13171 13172 13173 13174 13175 13176 13177 13178 13179 13180 13181 13182 13183 13184 13185 13186 13187 13188 13189 13190 13191 13192 13193 13194 13195 13196 13197 13198 13199 13200 13201 13202 13203 13204 13205 13206 13207 13208 13209 13210 13211 13212 13213 13214 13215 13216 13217 13218 13219 13220 13221 13222 13223 13224 13225 13226 13227 13228 13229 13230 13231 13232 13233 13234 13235 13236 13237 13238 13239 13240 13241 13242 13243 | } }else #endif #if !defined(SQLITE_OMIT_VIRTUALTABLE) && defined(SQLITE_HAVE_ZLIB) if( c=='a' && strncmp(azArg[0], "archive", n)==0 ){ open_db(p, 0); rc = arDotCommand(p, 0, azArg, nArg); }else #endif if( (c=='b' && n>=3 && strncmp(azArg[0], "backup", n)==0) || (c=='s' && n>=3 && strncmp(azArg[0], "save", n)==0) ){ const char *zDestFile = 0; const char *zDb = 0; sqlite3 *pDest; sqlite3_backup *pBackup; int j; const char *zVfs = 0; for(j=1; j<nArg; j++){ const char *z = azArg[j]; if( z[0]=='-' ){ if( z[1]=='-' ) z++; if( strcmp(z, "-append")==0 ){ zVfs = "apndvfs"; }else { utf8_printf(stderr, "unknown option: %s\n", azArg[j]); return 1; } }else if( zDestFile==0 ){ zDestFile = azArg[j]; }else if( zDb==0 ){ zDb = zDestFile; zDestFile = azArg[j]; }else{ raw_printf(stderr, "Usage: .backup ?DB? ?--append? FILENAME\n"); return 1; } } if( zDestFile==0 ){ raw_printf(stderr, "missing FILENAME argument on .backup\n"); return 1; } if( zDb==0 ) zDb = "main"; rc = sqlite3_open_v2(zDestFile, &pDest, SQLITE_OPEN_READWRITE|SQLITE_OPEN_CREATE, zVfs); if( rc!=SQLITE_OK ){ utf8_printf(stderr, "Error: cannot open \"%s\"\n", zDestFile); close_db(pDest); return 1; } open_db(p, 0); pBackup = sqlite3_backup_init(pDest, "main", p->db, zDb); if( pBackup==0 ){ utf8_printf(stderr, "Error: %s\n", sqlite3_errmsg(pDest)); close_db(pDest); return 1; } while( (rc = sqlite3_backup_step(pBackup,100))==SQLITE_OK ){} sqlite3_backup_finish(pBackup); if( rc==SQLITE_DONE ){ rc = 0; }else{ utf8_printf(stderr, "Error: %s\n", sqlite3_errmsg(pDest)); rc = 1; } close_db(pDest); }else if( c=='b' && n>=3 && strncmp(azArg[0], "bail", n)==0 ){ if( nArg==2 ){ bail_on_error = booleanValue(azArg[1]); }else{ raw_printf(stderr, "Usage: .bail on|off\n"); |
︙ | ︙ | |||
13219 13220 13221 13222 13223 13224 13225 | if( zErrMsg ){ utf8_printf(stderr,"Error: %s\n", zErrMsg); sqlite3_free(zErrMsg); rc = 1; } }else | > > > > > > > > > > > > > > > > > > > > > > > > > > > > | > | | 13341 13342 13343 13344 13345 13346 13347 13348 13349 13350 13351 13352 13353 13354 13355 13356 13357 13358 13359 13360 13361 13362 13363 13364 13365 13366 13367 13368 13369 13370 13371 13372 13373 13374 13375 13376 13377 13378 13379 13380 13381 13382 13383 13384 13385 13386 13387 13388 13389 13390 13391 13392 | if( zErrMsg ){ utf8_printf(stderr,"Error: %s\n", zErrMsg); sqlite3_free(zErrMsg); rc = 1; } }else if( c=='d' && n>=3 && strncmp(azArg[0], "dbconfig", n)==0 ){ static const struct DbConfigChoices {const char *zName; int op;} aDbConfig[] = { { "enable_fkey", SQLITE_DBCONFIG_ENABLE_FKEY }, { "enable_trigger", SQLITE_DBCONFIG_ENABLE_TRIGGER }, { "fts3_tokenizer", SQLITE_DBCONFIG_ENABLE_FTS3_TOKENIZER }, { "load_extension", SQLITE_DBCONFIG_ENABLE_LOAD_EXTENSION }, { "no_ckpt_on_close", SQLITE_DBCONFIG_NO_CKPT_ON_CLOSE }, { "enable_qpsg", SQLITE_DBCONFIG_ENABLE_QPSG }, { "trigger_eqp", SQLITE_DBCONFIG_TRIGGER_EQP }, { "reset_database", SQLITE_DBCONFIG_RESET_DATABASE }, }; int ii, v; open_db(p, 0); for(ii=0; ii<ArraySize(aDbConfig); ii++){ if( nArg>1 && strcmp(azArg[1], aDbConfig[ii].zName)!=0 ) continue; if( nArg>=3 ){ sqlite3_db_config(p->db, aDbConfig[ii].op, booleanValue(azArg[2]), 0); } sqlite3_db_config(p->db, aDbConfig[ii].op, -1, &v); utf8_printf(p->out, "%18s %s\n", aDbConfig[ii].zName, v ? "on" : "off"); if( nArg>1 ) break; } if( nArg>1 && ii==ArraySize(aDbConfig) ){ utf8_printf(stderr, "Error: unknown dbconfig \"%s\"\n", azArg[1]); utf8_printf(stderr, "Enter \".dbconfig\" with no arguments for a list\n"); } }else if( c=='d' && n>=3 && strncmp(azArg[0], "dbinfo", n)==0 ){ rc = shell_dbinfo_command(p, nArg, azArg); }else if( c=='d' && strncmp(azArg[0], "dump", n)==0 ){ const char *zLike = 0; int i; int savedShowHeader = p->showHeader; int savedShellFlags = p->shellFlgs; ShellClearFlag(p, SHFLG_PreserveRowid|SHFLG_Newlines|SHFLG_Echo); for(i=1; i<nArg; i++){ if( azArg[i][0]=='-' ){ const char *z = azArg[i]+1; if( z[0]=='-' ) z++; if( strcmp(z,"preserve-rowids")==0 ){ #ifdef SQLITE_OMIT_VIRTUALTABLE raw_printf(stderr, "The --preserve-rowids option is not compatible" |
︙ | ︙ | |||
13309 13310 13311 13312 13313 13314 13315 13316 13317 13318 13319 13320 13321 13322 13323 13324 13325 13326 13327 13328 13329 13330 13331 13332 13333 13334 13335 13336 13337 13338 13339 | raw_printf(p->out, "PRAGMA writable_schema=OFF;\n"); p->writableSchema = 0; } sqlite3_exec(p->db, "PRAGMA writable_schema=OFF;", 0, 0, 0); sqlite3_exec(p->db, "RELEASE dump;", 0, 0, 0); raw_printf(p->out, p->nErr ? "ROLLBACK; -- due to errors\n" : "COMMIT;\n"); p->showHeader = savedShowHeader; }else if( c=='e' && strncmp(azArg[0], "echo", n)==0 ){ if( nArg==2 ){ setOrClearFlag(p, SHFLG_Echo, azArg[1]); }else{ raw_printf(stderr, "Usage: .echo on|off\n"); rc = 1; } }else if( c=='e' && strncmp(azArg[0], "eqp", n)==0 ){ if( nArg==2 ){ if( strcmp(azArg[1],"full")==0 ){ p->autoEQP = AUTOEQP_full; }else if( strcmp(azArg[1],"trigger")==0 ){ p->autoEQP = AUTOEQP_trigger; }else{ p->autoEQP = (u8)booleanValue(azArg[1]); } }else{ raw_printf(stderr, "Usage: .eqp off|on|trigger|full\n"); rc = 1; } | > > > > > | 13460 13461 13462 13463 13464 13465 13466 13467 13468 13469 13470 13471 13472 13473 13474 13475 13476 13477 13478 13479 13480 13481 13482 13483 13484 13485 13486 13487 13488 13489 13490 13491 13492 13493 13494 13495 | raw_printf(p->out, "PRAGMA writable_schema=OFF;\n"); p->writableSchema = 0; } sqlite3_exec(p->db, "PRAGMA writable_schema=OFF;", 0, 0, 0); sqlite3_exec(p->db, "RELEASE dump;", 0, 0, 0); raw_printf(p->out, p->nErr ? "ROLLBACK; -- due to errors\n" : "COMMIT;\n"); p->showHeader = savedShowHeader; p->shellFlgs = savedShellFlags; }else if( c=='e' && strncmp(azArg[0], "echo", n)==0 ){ if( nArg==2 ){ setOrClearFlag(p, SHFLG_Echo, azArg[1]); }else{ raw_printf(stderr, "Usage: .echo on|off\n"); rc = 1; } }else if( c=='e' && strncmp(azArg[0], "eqp", n)==0 ){ if( nArg==2 ){ p->autoEQPtest = 0; if( strcmp(azArg[1],"full")==0 ){ p->autoEQP = AUTOEQP_full; }else if( strcmp(azArg[1],"trigger")==0 ){ p->autoEQP = AUTOEQP_trigger; }else if( strcmp(azArg[1],"test")==0 ){ p->autoEQP = AUTOEQP_on; p->autoEQPtest = 1; }else{ p->autoEQP = (u8)booleanValue(azArg[1]); } }else{ raw_printf(stderr, "Usage: .eqp off|on|trigger|full\n"); rc = 1; } |
︙ | ︙ | |||
13414 13415 13416 13417 13418 13419 13420 | raw_printf(p->out, "/* No STAT tables available */\n"); }else{ raw_printf(p->out, "ANALYZE sqlite_master;\n"); sqlite3_exec(p->db, "SELECT 'ANALYZE sqlite_master'", callback, &data, &zErrMsg); data.cMode = data.mode = MODE_Insert; data.zDestTable = "sqlite_stat1"; | | | | | 13570 13571 13572 13573 13574 13575 13576 13577 13578 13579 13580 13581 13582 13583 13584 13585 13586 13587 13588 | raw_printf(p->out, "/* No STAT tables available */\n"); }else{ raw_printf(p->out, "ANALYZE sqlite_master;\n"); sqlite3_exec(p->db, "SELECT 'ANALYZE sqlite_master'", callback, &data, &zErrMsg); data.cMode = data.mode = MODE_Insert; data.zDestTable = "sqlite_stat1"; shell_exec(&data, "SELECT * FROM sqlite_stat1", &zErrMsg); data.zDestTable = "sqlite_stat3"; shell_exec(&data, "SELECT * FROM sqlite_stat3", &zErrMsg); data.zDestTable = "sqlite_stat4"; shell_exec(&data, "SELECT * FROM sqlite_stat4", &zErrMsg); raw_printf(p->out, "ANALYZE sqlite_master;\n"); } }else if( c=='h' && strncmp(azArg[0], "headers", n)==0 ){ if( nArg==2 ){ p->showHeader = booleanValue(azArg[1]); |
︙ | ︙ | |||
13516 13517 13518 13519 13520 13521 13522 | utf8_printf(stderr, "Error: cannot open \"%s\"\n", zFile); return 1; } sCtx.cColSep = p->colSeparator[0]; sCtx.cRowSep = p->rowSeparator[0]; zSql = sqlite3_mprintf("SELECT * FROM %s", zTable); if( zSql==0 ){ | < < > | 13672 13673 13674 13675 13676 13677 13678 13679 13680 13681 13682 13683 13684 13685 13686 13687 | utf8_printf(stderr, "Error: cannot open \"%s\"\n", zFile); return 1; } sCtx.cColSep = p->colSeparator[0]; sCtx.cRowSep = p->rowSeparator[0]; zSql = sqlite3_mprintf("SELECT * FROM %s", zTable); if( zSql==0 ){ xCloser(sCtx.in); shell_out_of_memory(); } nByte = strlen30(zSql); rc = sqlite3_prepare_v2(p->db, zSql, -1, &pStmt, 0); import_append_char(&sCtx, 0); /* To ensure sCtx.z is allocated */ if( rc && sqlite3_strglob("no such table: *", sqlite3_errmsg(p->db))==0 ){ char *zCreate = sqlite3_mprintf("CREATE TABLE %s", zTable); char cSep = '('; |
︙ | ︙ | |||
13563 13564 13565 13566 13567 13568 13569 | } nCol = sqlite3_column_count(pStmt); sqlite3_finalize(pStmt); pStmt = 0; if( nCol==0 ) return 0; /* no columns, no error */ zSql = sqlite3_malloc64( nByte*2 + 20 + nCol*2 ); if( zSql==0 ){ | < < > | 13718 13719 13720 13721 13722 13723 13724 13725 13726 13727 13728 13729 13730 13731 13732 13733 | } nCol = sqlite3_column_count(pStmt); sqlite3_finalize(pStmt); pStmt = 0; if( nCol==0 ) return 0; /* no columns, no error */ zSql = sqlite3_malloc64( nByte*2 + 20 + nCol*2 ); if( zSql==0 ){ xCloser(sCtx.in); shell_out_of_memory(); } sqlite3_snprintf(nByte+20, zSql, "INSERT INTO \"%w\" VALUES(?", zTable); j = strlen30(zSql); for(i=1; i<nCol; i++){ zSql[j++] = ','; zSql[j++] = '?'; } |
︙ | ︙ | |||
13641 13642 13643 13644 13645 13646 13647 | #ifndef SQLITE_UNTESTABLE if( c=='i' && strncmp(azArg[0], "imposter", n)==0 ){ char *zSql; char *zCollist = 0; sqlite3_stmt *pStmt; int tnum = 0; int i; | | | > > > > > | 13795 13796 13797 13798 13799 13800 13801 13802 13803 13804 13805 13806 13807 13808 13809 13810 13811 13812 13813 13814 13815 13816 13817 13818 13819 | #ifndef SQLITE_UNTESTABLE if( c=='i' && strncmp(azArg[0], "imposter", n)==0 ){ char *zSql; char *zCollist = 0; sqlite3_stmt *pStmt; int tnum = 0; int i; if( !(nArg==3 || (nArg==2 && sqlite3_stricmp(azArg[1],"off")==0)) ){ utf8_printf(stderr, "Usage: .imposter INDEX IMPOSTER\n" " .imposter off\n"); rc = 1; goto meta_command_exit; } open_db(p, 0); if( nArg==2 ){ sqlite3_test_control(SQLITE_TESTCTRL_IMPOSTER, p->db, "main", 0, 1); goto meta_command_exit; } zSql = sqlite3_mprintf("SELECT rootpage FROM sqlite_master" " WHERE name='%q' AND type='index'", azArg[1]); sqlite3_prepare_v2(p->db, zSql, -1, &pStmt, 0); sqlite3_free(zSql); if( sqlite3_step(pStmt)==SQLITE_ROW ){ tnum = sqlite3_column_int(pStmt, 0); } |
︙ | ︙ | |||
13888 13889 13890 13891 13892 13893 13894 | if( c=='o' && strncmp(azArg[0], "open", n)==0 && n>=2 ){ char *zNewFilename; /* Name of the database file to open */ int iName = 1; /* Index in azArg[] of the filename */ int newFlag = 0; /* True to delete file before opening */ /* Close the existing database */ session_close_all(p); | | | 14047 14048 14049 14050 14051 14052 14053 14054 14055 14056 14057 14058 14059 14060 14061 | if( c=='o' && strncmp(azArg[0], "open", n)==0 && n>=2 ){ char *zNewFilename; /* Name of the database file to open */ int iName = 1; /* Index in azArg[] of the filename */ int newFlag = 0; /* True to delete file before opening */ /* Close the existing database */ session_close_all(p); close_db(p->db); p->db = 0; p->zDbFilename = 0; sqlite3_free(p->zFreeOnClose); p->zFreeOnClose = 0; p->openMode = SHELL_OPEN_UNSPEC; /* Check for command-line arguments */ for(iName=1; iName<nArg && azArg[iName][0]=='-'; iName++){ |
︙ | ︙ | |||
13918 13919 13920 13921 13922 13923 13924 | } } /* If a filename is specified, try to open it first */ zNewFilename = nArg>iName ? sqlite3_mprintf("%s", azArg[iName]) : 0; if( zNewFilename ){ if( newFlag ) shellDeleteFile(zNewFilename); p->zDbFilename = zNewFilename; | | | 14077 14078 14079 14080 14081 14082 14083 14084 14085 14086 14087 14088 14089 14090 14091 | } } /* If a filename is specified, try to open it first */ zNewFilename = nArg>iName ? sqlite3_mprintf("%s", azArg[iName]) : 0; if( zNewFilename ){ if( newFlag ) shellDeleteFile(zNewFilename); p->zDbFilename = zNewFilename; open_db(p, OPEN_DB_KEEPALIVE); if( p->db==0 ){ utf8_printf(stderr, "Error: cannot open '%s'\n", zNewFilename); sqlite3_free(zNewFilename); }else{ p->zFreeOnClose = zNewFilename; } } |
︙ | ︙ | |||
14068 14069 14070 14071 14072 14073 14074 | raw_printf(stderr, "Usage: .restore ?DB? FILE\n"); rc = 1; goto meta_command_exit; } rc = sqlite3_open(zSrcFile, &pSrc); if( rc!=SQLITE_OK ){ utf8_printf(stderr, "Error: cannot open \"%s\"\n", zSrcFile); | | | | | 14227 14228 14229 14230 14231 14232 14233 14234 14235 14236 14237 14238 14239 14240 14241 14242 14243 14244 14245 14246 14247 14248 14249 14250 14251 14252 14253 14254 14255 14256 14257 14258 14259 14260 14261 14262 14263 14264 14265 14266 14267 14268 | raw_printf(stderr, "Usage: .restore ?DB? FILE\n"); rc = 1; goto meta_command_exit; } rc = sqlite3_open(zSrcFile, &pSrc); if( rc!=SQLITE_OK ){ utf8_printf(stderr, "Error: cannot open \"%s\"\n", zSrcFile); close_db(pSrc); return 1; } open_db(p, 0); pBackup = sqlite3_backup_init(p->db, zDb, pSrc, "main"); if( pBackup==0 ){ utf8_printf(stderr, "Error: %s\n", sqlite3_errmsg(p->db)); close_db(pSrc); return 1; } while( (rc = sqlite3_backup_step(pBackup,100))==SQLITE_OK || rc==SQLITE_BUSY ){ if( rc==SQLITE_BUSY ){ if( nTimeout++ >= 3 ) break; sqlite3_sleep(100); } } sqlite3_backup_finish(pBackup); if( rc==SQLITE_DONE ){ rc = 0; }else if( rc==SQLITE_BUSY || rc==SQLITE_LOCKED ){ raw_printf(stderr, "Error: source database is busy\n"); rc = 1; }else{ utf8_printf(stderr, "Error: %s\n", sqlite3_errmsg(p->db)); rc = 1; } close_db(pSrc); }else if( c=='s' && strncmp(azArg[0], "scanstats", n)==0 ){ if( nArg==2 ){ p->scanstatsOn = (u8)booleanValue(azArg[1]); #ifndef SQLITE_ENABLE_STMT_SCANSTATUS raw_printf(stderr, "Warning: .scanstats not available in this build.\n"); |
︙ | ︙ | |||
14615 14616 14617 14618 14619 14620 14621 | if( strcmp(z,"debug")==0 ){ bDebug = 1; }else { utf8_printf(stderr, "Unknown option \"%s\" on \"%s\"\n", azArg[i], azArg[0]); raw_printf(stderr, "Should be one of: --schema" | | | 14774 14775 14776 14777 14778 14779 14780 14781 14782 14783 14784 14785 14786 14787 14788 | if( strcmp(z,"debug")==0 ){ bDebug = 1; }else { utf8_printf(stderr, "Unknown option \"%s\" on \"%s\"\n", azArg[i], azArg[0]); raw_printf(stderr, "Should be one of: --schema" " --sha3-224 --sha3-256 --sha3-384 --sha3-512\n"); rc = 1; goto meta_command_exit; } }else if( zLike ){ raw_printf(stderr, "Usage: .sha3sum ?OPTIONS? ?LIKE-PATTERN?\n"); rc = 1; goto meta_command_exit; |
︙ | ︙ | |||
14779 14780 14781 14782 14783 14784 14785 | char **azResult; int nRow, nAlloc; int ii; ShellText s; initText(&s); open_db(p, 0); rc = sqlite3_prepare_v2(p->db, "PRAGMA database_list", -1, &pStmt, 0); | > > | > > | 14938 14939 14940 14941 14942 14943 14944 14945 14946 14947 14948 14949 14950 14951 14952 14953 14954 14955 14956 14957 14958 14959 14960 14961 14962 14963 | char **azResult; int nRow, nAlloc; int ii; ShellText s; initText(&s); open_db(p, 0); rc = sqlite3_prepare_v2(p->db, "PRAGMA database_list", -1, &pStmt, 0); if( rc ){ sqlite3_finalize(pStmt); return shellDatabaseError(p->db); } if( nArg>2 && c=='i' ){ /* It is an historical accident that the .indexes command shows an error ** when called with the wrong number of arguments whereas the .tables ** command does not. */ raw_printf(stderr, "Usage: .indexes ?LIKE-PATTERN?\n"); rc = 1; sqlite3_finalize(pStmt); goto meta_command_exit; } for(ii=0; sqlite3_step(pStmt)==SQLITE_ROW; ii++){ const char *zDbName = (const char*)sqlite3_column_text(pStmt, 1); if( zDbName==0 ) continue; if( s.z && s.z[0] ) appendText(&s, " UNION ALL ", 0); if( sqlite3_stricmp(zDbName, "main")==0 ){ |
︙ | ︙ | |||
14831 14832 14833 14834 14835 14836 14837 | sqlite3_bind_text(pStmt, 1, "%", -1, SQLITE_STATIC); } while( sqlite3_step(pStmt)==SQLITE_ROW ){ if( nRow>=nAlloc ){ char **azNew; int n2 = nAlloc*2 + 10; azNew = sqlite3_realloc64(azResult, sizeof(azResult[0])*n2); | | < < < | < < < | 14994 14995 14996 14997 14998 14999 15000 15001 15002 15003 15004 15005 15006 15007 15008 15009 15010 15011 15012 15013 | sqlite3_bind_text(pStmt, 1, "%", -1, SQLITE_STATIC); } while( sqlite3_step(pStmt)==SQLITE_ROW ){ if( nRow>=nAlloc ){ char **azNew; int n2 = nAlloc*2 + 10; azNew = sqlite3_realloc64(azResult, sizeof(azResult[0])*n2); if( azNew==0 ) shell_out_of_memory(); nAlloc = n2; azResult = azNew; } azResult[nRow] = sqlite3_mprintf("%s", sqlite3_column_text(pStmt, 0)); if( 0==azResult[nRow] ) shell_out_of_memory(); nRow++; } if( sqlite3_finalize(pStmt)!=SQLITE_OK ){ rc = shellDatabaseError(p->db); } /* Pretty-print the contents of array azResult[] to the output */ |
︙ | ︙ | |||
14903 14904 14905 14906 14907 14908 14909 | { "always", SQLITE_TESTCTRL_ALWAYS, "BOOLEAN" }, { "assert", SQLITE_TESTCTRL_ASSERT, "BOOLEAN" }, /*{ "benign_malloc_hooks",SQLITE_TESTCTRL_BENIGN_MALLOC_HOOKS, "" },*/ /*{ "bitvec_test", SQLITE_TESTCTRL_BITVEC_TEST, "" },*/ { "byteorder", SQLITE_TESTCTRL_BYTEORDER, "" }, /*{ "fault_install", SQLITE_TESTCTRL_FAULT_INSTALL, "" }, */ { "imposter", SQLITE_TESTCTRL_IMPOSTER, "SCHEMA ON/OFF ROOTPAGE"}, | < < < | 15060 15061 15062 15063 15064 15065 15066 15067 15068 15069 15070 15071 15072 15073 | { "always", SQLITE_TESTCTRL_ALWAYS, "BOOLEAN" }, { "assert", SQLITE_TESTCTRL_ASSERT, "BOOLEAN" }, /*{ "benign_malloc_hooks",SQLITE_TESTCTRL_BENIGN_MALLOC_HOOKS, "" },*/ /*{ "bitvec_test", SQLITE_TESTCTRL_BITVEC_TEST, "" },*/ { "byteorder", SQLITE_TESTCTRL_BYTEORDER, "" }, /*{ "fault_install", SQLITE_TESTCTRL_FAULT_INSTALL, "" }, */ { "imposter", SQLITE_TESTCTRL_IMPOSTER, "SCHEMA ON/OFF ROOTPAGE"}, { "localtime_fault", SQLITE_TESTCTRL_LOCALTIME_FAULT,"BOOLEAN" }, { "never_corrupt", SQLITE_TESTCTRL_NEVER_CORRUPT, "BOOLEAN" }, { "optimizations", SQLITE_TESTCTRL_OPTIMIZATIONS, "DISABLE-MASK" }, #ifdef YYCOVERAGE { "parser_coverage", SQLITE_TESTCTRL_PARSER_COVERAGE, "" }, #endif { "pending_byte", SQLITE_TESTCTRL_PENDING_BYTE, "OFFSET " }, |
︙ | ︙ | |||
15017 15018 15019 15020 15021 15022 15023 | if( nArg==3 ){ int opt = booleanValue(azArg[2]); rc2 = sqlite3_test_control(testctrl, opt); isOk = 3; } break; | < < < < < < < < < < < | 15171 15172 15173 15174 15175 15176 15177 15178 15179 15180 15181 15182 15183 15184 | if( nArg==3 ){ int opt = booleanValue(azArg[2]); rc2 = sqlite3_test_control(testctrl, opt); isOk = 3; } break; case SQLITE_TESTCTRL_IMPOSTER: if( nArg==5 ){ rc2 = sqlite3_test_control(testctrl, p->db, azArg[2], integerValue(azArg[3]), integerValue(azArg[4])); isOk = 3; |
︙ | ︙ | |||
15323 15324 15325 15326 15327 15328 15329 | zSql[nSql+1] = 0; rc = sqlite3_complete(zSql); zSql[nSql] = 0; return rc; } /* | | | 15466 15467 15468 15469 15470 15471 15472 15473 15474 15475 15476 15477 15478 15479 15480 | zSql[nSql+1] = 0; rc = sqlite3_complete(zSql); zSql[nSql] = 0; return rc; } /* ** Run a single line of SQL. Return the number of errors. */ static int runOneSqlLine(ShellState *p, char *zSql, FILE *in, int startline){ int rc; char *zErrMsg = 0; open_db(p, 0); if( ShellHasFlag(p,SHFLG_Backslash) ) resolve_backslashes(zSql); |
︙ | ︙ | |||
15396 15397 15398 15399 15400 15401 15402 | seenInterrupt = 0; } lineno++; if( nSql==0 && _all_whitespace(zLine) ){ if( ShellHasFlag(p, SHFLG_Echo) ) printf("%s\n", zLine); continue; } | | > | | | | | > | < < < | 15539 15540 15541 15542 15543 15544 15545 15546 15547 15548 15549 15550 15551 15552 15553 15554 15555 15556 15557 15558 15559 15560 15561 15562 15563 15564 15565 15566 15567 15568 15569 15570 15571 15572 | seenInterrupt = 0; } lineno++; if( nSql==0 && _all_whitespace(zLine) ){ if( ShellHasFlag(p, SHFLG_Echo) ) printf("%s\n", zLine); continue; } if( zLine && (zLine[0]=='.' || zLine[0]=='#') && nSql==0 ){ if( ShellHasFlag(p, SHFLG_Echo) ) printf("%s\n", zLine); if( zLine[0]=='.' ){ rc = do_meta_command(zLine, p); if( rc==2 ){ /* exit requested */ break; }else if( rc ){ errCnt++; } } continue; } if( line_is_command_terminator(zLine) && line_is_complete(zSql, nSql) ){ memcpy(zLine,";",2); } nLine = strlen30(zLine); if( nSql+nLine+2>=nAlloc ){ nAlloc = nSql+nLine+100; zSql = realloc(zSql, nAlloc); if( zSql==0 ) shell_out_of_memory(); } nSqlPrior = nSql; if( nSql==0 ){ int i; for(i=0; zLine[i] && IsSpace(zLine[i]); i++){} assert( nAlloc>0 && zSql!=0 ); memcpy(zSql, zLine+i, nLine+1-i); |
︙ | ︙ | |||
15447 15448 15449 15450 15451 15452 15453 | } }else if( nSql && _all_whitespace(zSql) ){ if( ShellHasFlag(p, SHFLG_Echo) ) printf("%s\n", zSql); nSql = 0; } } if( nSql && !_all_whitespace(zSql) ){ | | | 15589 15590 15591 15592 15593 15594 15595 15596 15597 15598 15599 15600 15601 15602 15603 | } }else if( nSql && _all_whitespace(zSql) ){ if( ShellHasFlag(p, SHFLG_Echo) ) printf("%s\n", zSql); nSql = 0; } } if( nSql && !_all_whitespace(zSql) ){ errCnt += runOneSqlLine(p, zSql, in, startline); } free(zSql); free(zLine); return errCnt>0; } /* |
︙ | ︙ | |||
15545 15546 15547 15548 15549 15550 15551 | if (sqliterc == NULL) { home_dir = find_home_dir(0); if( home_dir==0 ){ raw_printf(stderr, "-- warning: cannot find home directory;" " cannot read ~/.sqliterc\n"); return; } | < | 15687 15688 15689 15690 15691 15692 15693 15694 15695 15696 15697 15698 15699 15700 | if (sqliterc == NULL) { home_dir = find_home_dir(0); if( home_dir==0 ){ raw_printf(stderr, "-- warning: cannot find home directory;" " cannot read ~/.sqliterc\n"); return; } zBuf = sqlite3_mprintf("%s/.sqliterc",home_dir); sqliterc = zBuf; } in = fopen(sqliterc,"rb"); if( in ){ if( stdin_is_interactive ){ utf8_printf(stderr,"-- Loading resources from %s\n",sqliterc); |
︙ | ︙ | |||
15596 15597 15598 15599 15600 15601 15602 15603 15604 15605 15606 15607 15608 15609 | #endif " -newline SEP set output row separator. Default: '\\n'\n" " -nullvalue TEXT set text string for NULL values. Default ''\n" " -pagecache SIZE N use N slots of SZ bytes each for page cache memory\n" " -quote set output mode to 'quote'\n" " -readonly open the database read-only\n" " -separator SEP set output column separator. Default: '|'\n" " -stats print memory stats before each finalize\n" " -version show SQLite version\n" " -vfs NAME use NAME as the default VFS\n" #ifdef SQLITE_ENABLE_VFSTRACE " -vfstrace enable tracing of all VFS calls\n" #endif #ifdef SQLITE_HAVE_ZLIB | > > > | 15737 15738 15739 15740 15741 15742 15743 15744 15745 15746 15747 15748 15749 15750 15751 15752 15753 | #endif " -newline SEP set output row separator. Default: '\\n'\n" " -nullvalue TEXT set text string for NULL values. Default ''\n" " -pagecache SIZE N use N slots of SZ bytes each for page cache memory\n" " -quote set output mode to 'quote'\n" " -readonly open the database read-only\n" " -separator SEP set output column separator. Default: '|'\n" #ifdef SQLITE_ENABLE_SORTER_REFERENCES " -sorterref SIZE sorter references threshold size\n" #endif " -stats print memory stats before each finalize\n" " -version show SQLite version\n" " -vfs NAME use NAME as the default VFS\n" #ifdef SQLITE_ENABLE_VFSTRACE " -vfstrace enable tracing of all VFS calls\n" #endif #ifdef SQLITE_HAVE_ZLIB |
︙ | ︙ | |||
15618 15619 15620 15621 15622 15623 15624 15625 15626 15627 15628 15629 15630 15631 15632 15633 15634 15635 15636 15637 15638 15639 15640 15641 15642 15643 | if( showDetail ){ utf8_printf(stderr, "OPTIONS include:\n%s", zOptions); }else{ raw_printf(stderr, "Use the -help option for additional information\n"); } exit(1); } /* ** Initialize the state information in data */ static void main_init(ShellState *data) { memset(data, 0, sizeof(*data)); data->normalMode = data->cMode = data->mode = MODE_List; data->autoExplain = 1; memcpy(data->colSeparator,SEP_Column, 2); memcpy(data->rowSeparator,SEP_Row, 2); data->showHeader = 0; data->shellFlgs = SHFLG_Lookaside; sqlite3_config(SQLITE_CONFIG_URI, 1); sqlite3_config(SQLITE_CONFIG_LOG, shellLog, data); sqlite3_config(SQLITE_CONFIG_MULTITHREAD); sqlite3_snprintf(sizeof(mainPrompt), mainPrompt,"sqlite> "); sqlite3_snprintf(sizeof(continuePrompt), continuePrompt," ...> "); } | > > > > > > > > > > > > | 15762 15763 15764 15765 15766 15767 15768 15769 15770 15771 15772 15773 15774 15775 15776 15777 15778 15779 15780 15781 15782 15783 15784 15785 15786 15787 15788 15789 15790 15791 15792 15793 15794 15795 15796 15797 15798 15799 | if( showDetail ){ utf8_printf(stderr, "OPTIONS include:\n%s", zOptions); }else{ raw_printf(stderr, "Use the -help option for additional information\n"); } exit(1); } /* ** Internal check: Verify that the SQLite is uninitialized. Print a ** error message if it is initialized. */ static void verify_uninitialized(void){ if( sqlite3_config(-1)==SQLITE_MISUSE ){ utf8_printf(stdout, "WARNING: attempt to configure SQLite after" " initialization.\n"); } } /* ** Initialize the state information in data */ static void main_init(ShellState *data) { memset(data, 0, sizeof(*data)); data->normalMode = data->cMode = data->mode = MODE_List; data->autoExplain = 1; memcpy(data->colSeparator,SEP_Column, 2); memcpy(data->rowSeparator,SEP_Row, 2); data->showHeader = 0; data->shellFlgs = SHFLG_Lookaside; verify_uninitialized(); sqlite3_config(SQLITE_CONFIG_URI, 1); sqlite3_config(SQLITE_CONFIG_LOG, shellLog, data); sqlite3_config(SQLITE_CONFIG_MULTITHREAD); sqlite3_snprintf(sizeof(mainPrompt), mainPrompt,"sqlite> "); sqlite3_snprintf(sizeof(continuePrompt), continuePrompt," ...> "); } |
︙ | ︙ | |||
15693 15694 15695 15696 15697 15698 15699 15700 15701 15702 15703 15704 15705 15706 15707 15708 15709 15710 15711 15712 15713 15714 15715 15716 15717 15718 15719 15720 15721 15722 | const char *zInitFile = 0; int i; int rc = 0; int warnInmemoryDb = 0; int readStdin = 1; int nCmd = 0; char **azCmd = 0; setBinaryMode(stdin, 0); setvbuf(stderr, 0, _IONBF, 0); /* Make sure stderr is unbuffered */ stdin_is_interactive = isatty(0); stdout_is_console = isatty(1); #if USE_SYSTEM_SQLITE+0!=1 if( strncmp(sqlite3_sourceid(),SQLITE_SOURCE_ID,60)!=0 ){ utf8_printf(stderr, "SQLite header and source version mismatch\n%s\n%s\n", sqlite3_sourceid(), SQLITE_SOURCE_ID); exit(1); } #endif main_init(&data); /* On Windows, we must translate command-line arguments into UTF-8. ** The SQLite memory allocator subsystem has to be enabled in order to ** do this. But we want to run an sqlite3_shutdown() afterwards so that ** subsequent sqlite3_config() calls will work. So copy all results into ** memory that does not come from the SQLite memory allocator. */ #if !SQLITE_SHELL_IS_UTF8 sqlite3_initialize(); | > > > > > > > > > > > > > > > > > > > > > > | > > | < < < | < < < | < < < > | 15849 15850 15851 15852 15853 15854 15855 15856 15857 15858 15859 15860 15861 15862 15863 15864 15865 15866 15867 15868 15869 15870 15871 15872 15873 15874 15875 15876 15877 15878 15879 15880 15881 15882 15883 15884 15885 15886 15887 15888 15889 15890 15891 15892 15893 15894 15895 15896 15897 15898 15899 15900 15901 15902 15903 15904 15905 15906 15907 15908 15909 15910 15911 15912 15913 15914 15915 15916 15917 15918 15919 15920 | const char *zInitFile = 0; int i; int rc = 0; int warnInmemoryDb = 0; int readStdin = 1; int nCmd = 0; char **azCmd = 0; const char *zVfs = 0; /* Value of -vfs command-line option */ #if !SQLITE_SHELL_IS_UTF8 char **argvToFree = 0; int argcToFree = 0; #endif setBinaryMode(stdin, 0); setvbuf(stderr, 0, _IONBF, 0); /* Make sure stderr is unbuffered */ stdin_is_interactive = isatty(0); stdout_is_console = isatty(1); #if !defined(_WIN32_WCE) if( getenv("SQLITE_DEBUG_BREAK") ){ if( isatty(0) && isatty(2) ){ fprintf(stderr, "attach debugger to process %d and press any key to continue.\n", GETPID()); fgetc(stdin); }else{ #if defined(_WIN32) || defined(WIN32) DebugBreak(); #elif defined(SIGTRAP) raise(SIGTRAP); #endif } } #endif #if USE_SYSTEM_SQLITE+0!=1 if( strncmp(sqlite3_sourceid(),SQLITE_SOURCE_ID,60)!=0 ){ utf8_printf(stderr, "SQLite header and source version mismatch\n%s\n%s\n", sqlite3_sourceid(), SQLITE_SOURCE_ID); exit(1); } #endif main_init(&data); /* On Windows, we must translate command-line arguments into UTF-8. ** The SQLite memory allocator subsystem has to be enabled in order to ** do this. But we want to run an sqlite3_shutdown() afterwards so that ** subsequent sqlite3_config() calls will work. So copy all results into ** memory that does not come from the SQLite memory allocator. */ #if !SQLITE_SHELL_IS_UTF8 sqlite3_initialize(); argvToFree = malloc(sizeof(argv[0])*argc*2); argcToFree = argc; argv = argvToFree + argc; if( argv==0 ) shell_out_of_memory(); for(i=0; i<argc; i++){ char *z = sqlite3_win32_unicode_to_utf8(wargv[i]); int n; if( z==0 ) shell_out_of_memory(); n = (int)strlen(z); argv[i] = malloc( n+1 ); if( argv[i]==0 ) shell_out_of_memory(); memcpy(argv[i], z, n+1); argvToFree[i] = argv[i]; sqlite3_free(z); } sqlite3_shutdown(); #endif assert( argc>=1 && argv && argv[0] ); Argv0 = argv[0]; |
︙ | ︙ | |||
15769 15770 15771 15772 15773 15774 15775 15776 15777 15778 15779 15780 15781 15782 15783 15784 15785 15786 15787 | #endif /* Do an initial pass through the command-line argument to locate ** the name of the database file, the name of the initialization file, ** the size of the alternative malloc heap, ** and the first command to execute. */ for(i=1; i<argc; i++){ char *z; z = argv[i]; if( z[0]!='-' ){ if( data.zDbFilename==0 ){ data.zDbFilename = z; }else{ /* Excesss arguments are interpreted as SQL (or dot-commands) and ** mean that nothing is read from stdin */ readStdin = 0; nCmd++; azCmd = realloc(azCmd, sizeof(azCmd[0])*nCmd); | > | < < < | 15941 15942 15943 15944 15945 15946 15947 15948 15949 15950 15951 15952 15953 15954 15955 15956 15957 15958 15959 15960 15961 15962 15963 15964 15965 15966 15967 15968 | #endif /* Do an initial pass through the command-line argument to locate ** the name of the database file, the name of the initialization file, ** the size of the alternative malloc heap, ** and the first command to execute. */ verify_uninitialized(); for(i=1; i<argc; i++){ char *z; z = argv[i]; if( z[0]!='-' ){ if( data.zDbFilename==0 ){ data.zDbFilename = z; }else{ /* Excesss arguments are interpreted as SQL (or dot-commands) and ** mean that nothing is read from stdin */ readStdin = 0; nCmd++; azCmd = realloc(azCmd, sizeof(azCmd[0])*nCmd); if( azCmd==0 ) shell_out_of_memory(); azCmd[nCmd-1] = z; } } if( z[1]=='-' ) z++; if( strcmp(z,"-separator")==0 || strcmp(z,"-nullvalue")==0 || strcmp(z,"-newline")==0 |
︙ | ︙ | |||
15851 15852 15853 15854 15855 15856 15857 | }else if( strcmp(z,"-multiplex")==0 ){ extern int sqlite3_multiple_initialize(const char*,int); sqlite3_multiplex_initialize(0, 1); #endif }else if( strcmp(z,"-mmap")==0 ){ sqlite3_int64 sz = integerValue(cmdline_option_value(argc,argv,++i)); sqlite3_config(SQLITE_CONFIG_MMAP_SIZE, sz, sz); | > | | > | < | | < < > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 16021 16022 16023 16024 16025 16026 16027 16028 16029 16030 16031 16032 16033 16034 16035 16036 16037 16038 16039 16040 16041 16042 16043 16044 16045 16046 16047 16048 16049 16050 16051 16052 16053 16054 16055 16056 16057 16058 16059 16060 16061 16062 16063 16064 16065 16066 16067 16068 16069 16070 16071 16072 16073 16074 16075 16076 16077 16078 16079 16080 16081 16082 16083 16084 16085 | }else if( strcmp(z,"-multiplex")==0 ){ extern int sqlite3_multiple_initialize(const char*,int); sqlite3_multiplex_initialize(0, 1); #endif }else if( strcmp(z,"-mmap")==0 ){ sqlite3_int64 sz = integerValue(cmdline_option_value(argc,argv,++i)); sqlite3_config(SQLITE_CONFIG_MMAP_SIZE, sz, sz); #ifdef SQLITE_ENABLE_SORTER_REFERENCES }else if( strcmp(z,"-sorterref")==0 ){ sqlite3_int64 sz = integerValue(cmdline_option_value(argc,argv,++i)); sqlite3_config(SQLITE_CONFIG_SORTERREF_SIZE, (int)sz); #endif }else if( strcmp(z,"-vfs")==0 ){ zVfs = cmdline_option_value(argc, argv, ++i); #ifdef SQLITE_HAVE_ZLIB }else if( strcmp(z,"-zip")==0 ){ data.openMode = SHELL_OPEN_ZIPFILE; #endif }else if( strcmp(z,"-append")==0 ){ data.openMode = SHELL_OPEN_APPENDVFS; }else if( strcmp(z,"-readonly")==0 ){ data.openMode = SHELL_OPEN_READONLY; #if !defined(SQLITE_OMIT_VIRTUALTABLE) && defined(SQLITE_HAVE_ZLIB) }else if( strncmp(z, "-A",2)==0 ){ /* All remaining command-line arguments are passed to the ".archive" ** command, so ignore them */ break; #endif } } verify_uninitialized(); #ifdef SQLITE_SHELL_INIT_PROC { /* If the SQLITE_SHELL_INIT_PROC macro is defined, then it is the name ** of a C-function that will perform initialization actions on SQLite that ** occur just before or after sqlite3_initialize(). Use this compile-time ** option to embed this shell program in larger applications. */ extern void SQLITE_SHELL_INIT_PROC(void); SQLITE_SHELL_INIT_PROC(); } #else /* All the sqlite3_config() calls have now been made. So it is safe ** to call sqlite3_initialize() and process any command line -vfs option. */ sqlite3_initialize(); #endif if( zVfs ){ sqlite3_vfs *pVfs = sqlite3_vfs_find(zVfs); if( pVfs ){ sqlite3_vfs_register(pVfs, 1); }else{ utf8_printf(stderr, "no such VFS: \"%s\"\n", argv[i]); exit(1); } } if( data.zDbFilename==0 ){ #ifndef SQLITE_OMIT_MEMORYDB data.zDbFilename = ":memory:"; warnInmemoryDb = argc==1; #else utf8_printf(stderr,"%s: Error: no database filename specified\n", Argv0); return 1; |
︙ | ︙ | |||
15987 15988 15989 15990 15991 15992 15993 15994 15995 15996 15997 15998 15999 16000 | i++; }else if( strcmp(z,"-pagecache")==0 ){ i+=2; }else if( strcmp(z,"-lookaside")==0 ){ i+=2; }else if( strcmp(z,"-mmap")==0 ){ i++; }else if( strcmp(z,"-vfs")==0 ){ i++; #ifdef SQLITE_ENABLE_VFSTRACE }else if( strcmp(z,"-vfstrace")==0 ){ i++; #endif #ifdef SQLITE_ENABLE_MULTIPLEX | > > > > | 16184 16185 16186 16187 16188 16189 16190 16191 16192 16193 16194 16195 16196 16197 16198 16199 16200 16201 | i++; }else if( strcmp(z,"-pagecache")==0 ){ i+=2; }else if( strcmp(z,"-lookaside")==0 ){ i+=2; }else if( strcmp(z,"-mmap")==0 ){ i++; #ifdef SQLITE_ENABLE_SORTER_REFERENCES }else if( strcmp(z,"-sorterref")==0 ){ i++; #endif }else if( strcmp(z,"-vfs")==0 ){ i++; #ifdef SQLITE_ENABLE_VFSTRACE }else if( strcmp(z,"-vfstrace")==0 ){ i++; #endif #ifdef SQLITE_ENABLE_MULTIPLEX |
︙ | ︙ | |||
16027 16028 16029 16030 16031 16032 16033 | #if !defined(SQLITE_OMIT_VIRTUALTABLE) && defined(SQLITE_HAVE_ZLIB) }else if( strncmp(z, "-A", 2)==0 ){ if( nCmd>0 ){ utf8_printf(stderr, "Error: cannot mix regular SQL or dot-commands" " with \"%s\"\n", z); return 1; } | | | | | 16228 16229 16230 16231 16232 16233 16234 16235 16236 16237 16238 16239 16240 16241 16242 16243 16244 16245 16246 16247 | #if !defined(SQLITE_OMIT_VIRTUALTABLE) && defined(SQLITE_HAVE_ZLIB) }else if( strncmp(z, "-A", 2)==0 ){ if( nCmd>0 ){ utf8_printf(stderr, "Error: cannot mix regular SQL or dot-commands" " with \"%s\"\n", z); return 1; } open_db(&data, OPEN_DB_ZIPFILE); if( z[2] ){ argv[i] = &z[2]; arDotCommand(&data, 1, argv+(i-1), argc-(i-1)); }else{ arDotCommand(&data, 1, argv+i, argc-i); } readStdin = 0; break; #endif }else{ utf8_printf(stderr,"%s: Error: unknown option: %s\n", Argv0, z); raw_printf(stderr,"Use -help for a list of options.\n"); |
︙ | ︙ | |||
16111 16112 16113 16114 16115 16116 16117 | }else{ rc = process_input(&data, stdin); } } set_table_name(&data, 0); if( data.db ){ session_close_all(&data); | | | | > > > | 16312 16313 16314 16315 16316 16317 16318 16319 16320 16321 16322 16323 16324 16325 16326 16327 16328 16329 16330 16331 16332 16333 16334 | }else{ rc = process_input(&data, stdin); } } set_table_name(&data, 0); if( data.db ){ session_close_all(&data); close_db(data.db); } sqlite3_free(data.zFreeOnClose); find_home_dir(1); output_reset(&data); data.doXdgOpen = 0; clearTempFile(&data); #if !SQLITE_SHELL_IS_UTF8 for(i=0; i<argcToFree; i++) free(argvToFree[i]); free(argvToFree); #endif /* Clear the global data structure so that valgrind will detect memory ** leaks */ memset(&data, 0, sizeof(data)); return rc; } |
Changes to src/sitemap.c.
︙ | ︙ | |||
88 89 90 91 92 93 94 95 96 97 98 99 100 101 | @ <li>%z(href("%R/wiki_rules"))Wiki Formatting Rules</a></li> @ <li>%z(href("%R/md_rules"))Markdown Formatting Rules</a></li> @ <li>%z(href("%R/wiki?name=Sandbox"))Sandbox</a></li> @ <li>%z(href("%R/attachlist"))List of Attachments</a></li> @ </ul> @ </li> } if( g.perm.RdTkt ){ @ <li>%z(href("%R/reportlist"))Tickets</a> @ <ul> if( srchFlags & SRCH_TKT ){ @ <li>%z(href("%R/tktsrch"))Ticket Search</a></li> } @ <li>%z(href("%R/timeline?y=t"))Recent activity</a></li> | > > > | 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 | @ <li>%z(href("%R/wiki_rules"))Wiki Formatting Rules</a></li> @ <li>%z(href("%R/md_rules"))Markdown Formatting Rules</a></li> @ <li>%z(href("%R/wiki?name=Sandbox"))Sandbox</a></li> @ <li>%z(href("%R/attachlist"))List of Attachments</a></li> @ </ul> @ </li> } if( g.perm.RdForum ){ @ <li>%z(href("%R/forum"))Forum</a></li> } if( g.perm.RdTkt ){ @ <li>%z(href("%R/reportlist"))Tickets</a> @ <ul> if( srchFlags & SRCH_TKT ){ @ <li>%z(href("%R/tktsrch"))Ticket Search</a></li> } @ <li>%z(href("%R/timeline?y=t"))Recent activity</a></li> |
︙ | ︙ |
Changes to src/skins.c.
︙ | ︙ | |||
718 719 720 721 722 723 724 | /* Figure out which skin we are editing */ iSkin = atoi(PD("sk","1")); if( iSkin<1 || iSkin>9 ) iSkin = 1; /* Check that the user is authorized to edit this skin. */ if( !g.perm.Setup ){ | | > > > > | > > > | | < < | 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 | /* Figure out which skin we are editing */ iSkin = atoi(PD("sk","1")); if( iSkin<1 || iSkin>9 ) iSkin = 1; /* Check that the user is authorized to edit this skin. */ if( !g.perm.Setup ){ char *zAllowedEditors = ""; Glob *pAllowedEditors; int isMatch = 0; if( login_is_individual() ){ zAllowedEditors = db_get_mprintf("", "draft%d-users", iSkin); } if( zAllowedEditors[0] ){ pAllowedEditors = glob_create(zAllowedEditors); isMatch = glob_match(pAllowedEditors, g.zLogin); glob_free(pAllowedEditors); } if( isMatch==0 ){ login_needed(0); return; } } /* figure out which file is to be edited */ ii = atoi(PD("w","0")); if( ii<0 || ii>count(aSkinAttr) ) ii = 0; zFile = aSkinAttr[ii].zFile; |
︙ | ︙ | |||
872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 | iSkin = atoi(PD("sk","1")); if( iSkin<1 || iSkin>9 ) iSkin = 1; /* Figure out if the current user is allowed to make administrative ** changes and/or edits */ login_check_credentials(); zAllowedEditors = db_get_mprintf("", "draft%d-users", iSkin); if( g.perm.Setup ){ isSetup = isEditor = 1; }else{ Glob *pAllowedEditors; isSetup = isEditor = 0; if( zAllowedEditors[0] ){ pAllowedEditors = glob_create(zAllowedEditors); | > > > > | | 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 | iSkin = atoi(PD("sk","1")); if( iSkin<1 || iSkin>9 ) iSkin = 1; /* Figure out if the current user is allowed to make administrative ** changes and/or edits */ login_check_credentials(); if( !login_is_individual() ){ login_needed(0); return; } zAllowedEditors = db_get_mprintf("", "draft%d-users", iSkin); if( g.perm.Setup ){ isSetup = isEditor = 1; }else{ Glob *pAllowedEditors; isSetup = isEditor = 0; if( zAllowedEditors[0] ){ pAllowedEditors = glob_create(zAllowedEditors); isEditor = glob_match(pAllowedEditors, g.zLogin); glob_free(pAllowedEditors); } } /* Initialize the skin, if requested and authorized. */ if( P("init3")!=0 && isEditor ){ skin_initialize_draft(iSkin, P("initskin")); |
︙ | ︙ | |||
932 933 934 935 936 937 938 | @ </p> @ @ <a name='step2'></a> @ <h1>Step 2: Authenticate</h1> @ if( isSetup ){ @ <p>As an administrator, you can make any edits you like to this or | | | | 941 942 943 944 945 946 947 948 949 950 951 952 953 954 955 956 | @ </p> @ @ <a name='step2'></a> @ <h1>Step 2: Authenticate</h1> @ if( isSetup ){ @ <p>As an administrator, you can make any edits you like to this or @ any other skin. You can also authorize other users to edit this @ skin. Any user whose login name matches the comma-separated list @ of GLOB expressions below is given special permission to edit @ the draft%d(iSkin) skin: @ @ <form method='POST' action='%R/setup_skin#step2' id='f02'> @ <p class='skinInput'> @ <input type='hidden' name='sk' value='%d(iSkin)'> @ Authorized editors for skin draft%d(iSkin): |
︙ | ︙ | |||
1016 1017 1018 1019 1020 1021 1022 | fossil_free(zBase); @ </ul> @ @ <p>You will probably need to press Reload on your browser before any @ CSS changes will take effect.</p> @ @ <a hame='step6'></a> | | | 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 | fossil_free(zBase); @ </ul> @ @ <p>You will probably need to press Reload on your browser before any @ CSS changes will take effect.</p> @ @ <a hame='step6'></a> @ <h1>Step 6: Iterate</h1> @ @ <p>Repeat <a href='#step4'>step 4</a> and @ <a href='#step5'>step 5</a> as many times as necessary to create @ a production-ready skin. @ @ <a name='step7'></a> @ <h1>Step 7: Publish</h1> |
︙ | ︙ | |||
1051 1052 1053 1054 1055 1056 1057 | @ publishing the new skin.</p> } @ @ <a name='step8'></a> @ <h1>Step 8: Cleanup and Undo Actions</h1> @ if( !g.perm.Setup ){ | | | | 1060 1061 1062 1063 1064 1065 1066 1067 1068 1069 1070 1071 1072 1073 1074 1075 | @ publishing the new skin.</p> } @ @ <a name='step8'></a> @ <h1>Step 8: Cleanup and Undo Actions</h1> @ if( !g.perm.Setup ){ @ <p>Administrators can optionally save or restore legacy skins, and/or @ undo a prior publish. }else{ @ <p>Visit the <a href='%R/setup_skin_admin'>Skin Admin</a> page @ for cleanup and recovery actions. } style_load_one_js_file("skin.js"); style_footer(); } |
Added src/smtp.c.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952 953 954 955 956 957 958 959 960 961 962 963 964 965 966 967 968 969 970 971 972 973 974 975 976 977 978 979 980 981 982 983 984 985 986 987 988 989 990 991 992 993 994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 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 1064 1065 1066 1067 1068 1069 1070 1071 1072 1073 1074 1075 1076 1077 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 1105 1106 1107 1108 1109 1110 1111 1112 1113 1114 1115 1116 1117 1118 1119 1120 1121 1122 1123 1124 1125 1126 1127 1128 1129 1130 1131 1132 1133 1134 1135 1136 1137 1138 1139 1140 1141 1142 1143 1144 1145 1146 1147 1148 1149 1150 1151 1152 1153 1154 1155 1156 1157 1158 1159 1160 1161 1162 1163 1164 1165 1166 1167 1168 1169 1170 1171 1172 1173 1174 1175 1176 1177 1178 1179 1180 1181 1182 1183 1184 1185 1186 1187 1188 1189 1190 1191 1192 1193 1194 1195 1196 1197 1198 1199 1200 1201 1202 1203 1204 1205 1206 1207 1208 1209 1210 1211 1212 1213 1214 1215 1216 1217 1218 1219 1220 1221 1222 1223 1224 1225 1226 1227 1228 1229 1230 1231 1232 1233 1234 1235 1236 1237 1238 1239 1240 1241 1242 1243 1244 1245 1246 1247 1248 1249 1250 1251 1252 1253 1254 1255 1256 1257 1258 1259 1260 1261 1262 1263 1264 1265 1266 1267 1268 1269 1270 1271 1272 1273 1274 1275 1276 1277 1278 1279 1280 1281 1282 1283 1284 1285 1286 1287 1288 1289 1290 1291 1292 1293 1294 1295 1296 1297 1298 1299 1300 1301 1302 1303 1304 1305 1306 1307 1308 1309 1310 1311 1312 1313 1314 1315 1316 1317 1318 1319 1320 1321 1322 1323 1324 1325 1326 1327 1328 1329 1330 1331 1332 1333 1334 1335 1336 1337 1338 1339 1340 1341 1342 1343 1344 1345 1346 1347 1348 1349 1350 1351 1352 1353 1354 1355 1356 1357 1358 1359 1360 1361 1362 1363 1364 1365 1366 1367 1368 1369 1370 1371 1372 1373 1374 1375 1376 1377 1378 1379 1380 1381 1382 1383 1384 1385 1386 1387 1388 1389 1390 1391 1392 1393 1394 1395 1396 1397 1398 1399 1400 1401 1402 1403 1404 1405 1406 1407 1408 1409 1410 1411 1412 1413 1414 1415 1416 1417 1418 1419 1420 1421 1422 1423 1424 1425 1426 1427 1428 1429 1430 1431 1432 1433 1434 1435 1436 1437 1438 1439 1440 1441 1442 1443 1444 1445 1446 1447 1448 1449 1450 1451 1452 1453 1454 1455 1456 1457 1458 1459 1460 1461 1462 1463 1464 1465 1466 1467 1468 1469 1470 1471 1472 1473 1474 1475 1476 1477 1478 1479 1480 1481 1482 1483 1484 1485 1486 1487 1488 1489 1490 1491 1492 1493 1494 1495 1496 1497 1498 1499 1500 1501 1502 1503 1504 1505 1506 1507 1508 1509 1510 1511 1512 1513 1514 1515 1516 1517 1518 1519 1520 1521 1522 1523 1524 1525 1526 1527 1528 1529 1530 1531 1532 1533 1534 1535 1536 1537 1538 1539 1540 1541 1542 1543 1544 1545 1546 1547 1548 1549 1550 1551 1552 1553 1554 1555 1556 1557 1558 1559 1560 1561 1562 1563 1564 1565 1566 1567 1568 1569 1570 1571 1572 1573 1574 1575 1576 1577 1578 1579 1580 1581 1582 1583 1584 1585 1586 1587 1588 1589 1590 1591 1592 1593 1594 1595 1596 1597 1598 1599 1600 1601 1602 1603 1604 1605 1606 1607 1608 1609 1610 1611 1612 1613 1614 1615 1616 1617 1618 1619 1620 1621 1622 1623 1624 1625 1626 1627 1628 1629 1630 1631 1632 1633 1634 1635 1636 1637 1638 1639 1640 1641 1642 1643 1644 1645 1646 1647 1648 1649 1650 1651 1652 1653 1654 1655 1656 1657 1658 1659 1660 1661 1662 1663 1664 | /* ** Copyright (c) 2018 D. Richard Hipp ** ** This program is free software; you can redistribute it and/or ** modify it under the terms of the Simplified BSD License (also ** known as the "2-Clause License" or "FreeBSD License".) ** ** This program is distributed in the hope that it will be useful, ** but without any warranty; without even the implied warranty of ** merchantability or fitness for a particular purpose. ** ** Author contact information: ** drh@hwaci.com ** http://www.hwaci.com/drh/ ** ******************************************************************************* ** ** Implementation of SMTP (Simple Mail Transport Protocol) according ** to RFC 5321. */ #include "config.h" #include "smtp.h" #include <assert.h> #if defined(__linux__) && !defined(FOSSIL_OMIT_DNS) # include <sys/types.h> # include <netinet/in.h> # include <arpa/nameser.h> # include <resolv.h> # define FOSSIL_UNIX_STYLE_DNS 1 #endif #if defined(_WIN32) && !defined(__MINGW32__) && !defined(__MINGW64__) # include <windows.h> # include <windns.h> # define FOSSIL_WINDOWS_STYLE_DNS 1 #endif /* ** Find the hostname for receiving email for the domain given ** in zDomain. Return NULL if not found or not implemented. ** If multiple email receivers are advertized, pick the one with ** the lowest preference number. ** ** The returned string is obtained from fossil_malloc() ** and should be released using fossil_free(). */ char *smtp_mx_host(const char *zDomain){ #if defined(FOSSIL_UNIX_STYLE_DNS) int nDns; /* Length of the DNS reply */ int rc; /* Return code from various APIs */ int i; /* Loop counter */ int iBestPriority = 9999999; /* Best priority */ int nRec; /* Number of answers */ ns_msg h; /* DNS reply parser */ const unsigned char *pBest = 0; /* RDATA for the best answer */ unsigned char aDns[5000]; /* Raw DNS reply content */ char zHostname[5000]; /* Hostname for the MX */ nDns = res_query(zDomain, C_IN, T_MX, aDns, sizeof(aDns)); if( nDns<=0 ) return 0; res_init(); rc = ns_initparse(aDns,nDns,&h); if( rc ) return 0; nRec = ns_msg_count(h, ns_s_an); for(i=0; i<nRec; i++){ ns_rr x; int priority, sz; const unsigned char *p; rc = ns_parserr(&h, ns_s_an, i, &x); if( rc ) continue; p = ns_rr_rdata(x); sz = ns_rr_rdlen(x); if( sz>2 ){ priority = p[0]*256 + p[1]; if( priority<iBestPriority ){ pBest = p; iBestPriority = priority; } } } if( pBest ){ ns_name_uncompress(aDns, aDns+nDns, pBest+2, zHostname, sizeof(zHostname)); return fossil_strdup(zHostname); } return 0; #elif defined(FOSSIL_WINDOWS_STYLE_DNS) DNS_STATUS status; /* Return status */ PDNS_RECORDA pDnsRecord, p; /* Pointer to DNS_RECORD structure */ int iBestPriority = 9999999; /* Best priority */ char *pBest = 0; /* RDATA for the best answer */ status = DnsQuery_UTF8(zDomain, /* Domain name */ DNS_TYPE_MX, /* DNS record type */ DNS_QUERY_STANDARD, /* Query options */ NULL, /* List of DNS servers */ &pDnsRecord, /* Query results */ NULL); /* Reserved */ if( status ) return NULL; p = pDnsRecord; while( p ){ if( p->Data.MX.wPreference<iBestPriority ){ iBestPriority = p->Data.MX.wPreference; pBest = p->Data.MX.pNameExchange; } p = p->pNext; } if( pBest ){ pBest = fossil_strdup(pBest); } DnsRecordListFree(pDnsRecord, DnsFreeRecordListDeep); return pBest; #else return 0; #endif /* defined(FOSSIL_WINDOWS_STYLE_DNS) */ } /* ** COMMAND: test-find-mx ** ** Usage: %fossil test-find-mx DOMAIN ... ** ** Do a DNS MX lookup to find the hostname for sending email for ** DOMAIN. */ void test_find_mx(void){ int i; if( g.argc<=2 ){ usage("DOMAIN ..."); } for(i=2; i<g.argc; i++){ char *z = smtp_mx_host(g.argv[i]); fossil_print("%s: %s\n", g.argv[i], z); fossil_free(z); } } #if INTERFACE /* ** Information about a single SMTP connection. */ struct SmtpSession { const char *zFrom; /* Domain from which we are sending */ const char *zDest; /* Domain that will receive the email */ char *zHostname; /* Hostname of SMTP server for zDest */ u32 smtpFlags; /* Flags changing the operation */ FILE *logFile; /* Write session transcript to this log file */ Blob *pTranscript; /* Record session transcript here */ int atEof; /* True after connection closes */ char *zErr; /* Error message */ Blob inbuf; /* Input buffer */ }; /* Allowed values for SmtpSession.smtpFlags */ #define SMTP_TRACE_STDOUT 0x00001 /* Debugging info to console */ #define SMTP_TRACE_FILE 0x00002 /* Debugging info to logFile */ #define SMTP_TRACE_BLOB 0x00004 /* Record transcript */ #define SMTP_DIRECT 0x00008 /* Skip the MX lookup */ #define SMTP_PORT 0x00010 /* Use an alternate port number */ #endif /* ** Shutdown an SmtpSession */ void smtp_session_free(SmtpSession *pSession){ socket_close(); blob_reset(&pSession->inbuf); fossil_free(pSession->zHostname); fossil_free(pSession->zErr); fossil_free(pSession); } /* ** Allocate a new SmtpSession object. ** ** Both zFrom and zDest must be specified. ** ** The ... arguments are in this order: ** ** SMTP_PORT: int ** SMTP_TRACE_FILE: FILE* ** SMTP_TRACE_BLOB: Blob* */ SmtpSession *smtp_session_new( const char *zFrom, /* Domain for the client */ const char *zDest, /* Domain of the server */ u32 smtpFlags, /* Flags */ ... /* Arguments depending on the flags */ ){ SmtpSession *p; va_list ap; UrlData url; p = fossil_malloc( sizeof(*p) ); memset(p, 0, sizeof(*p)); p->zFrom = zFrom; p->zDest = zDest; p->smtpFlags = smtpFlags; memset(&url, 0, sizeof(url)); url.port = 25; blob_init(&p->inbuf, 0, 0); va_start(ap, smtpFlags); if( smtpFlags & SMTP_PORT ){ url.port = va_arg(ap, int); } if( smtpFlags & SMTP_TRACE_FILE ){ p->logFile = va_arg(ap, FILE*); } if( smtpFlags & SMTP_TRACE_BLOB ){ p->pTranscript = va_arg(ap, Blob*); } va_end(ap); if( (smtpFlags & SMTP_DIRECT)!=0 ){ int i; p->zHostname = fossil_strdup(zDest); for(i=0; p->zHostname[i] && p->zHostname[i]!=':'; i++){} if( p->zHostname[i]==':' ){ p->zHostname[i] = 0; url.port = atoi(&p->zHostname[i+1]); } }else{ p->zHostname = smtp_mx_host(zDest); } if( p->zHostname==0 ){ p->atEof = 1; p->zErr = mprintf("cannot locate SMTP server for \"%s\"", zDest); return p; } url.name = p->zHostname; socket_global_init(); if( socket_open(&url) ){ p->atEof = 1; p->zErr = socket_errmsg(); socket_close(); } return p; } /* ** Send a single line of output the SMTP client to the server. */ static void smtp_send_line(SmtpSession *p, const char *zFormat, ...){ Blob b = empty_blob; va_list ap; char *z; int n; if( p->atEof ) return; va_start(ap, zFormat); blob_vappendf(&b, zFormat, ap); va_end(ap); z = blob_buffer(&b); n = blob_size(&b); assert( n>=2 ); assert( z[n-1]=='\n' ); assert( z[n-2]=='\r' ); if( p->smtpFlags & SMTP_TRACE_STDOUT ){ fossil_print("C: %.*s\n", n-2, z); } if( p->smtpFlags & SMTP_TRACE_FILE ){ fprintf(p->logFile, "C: %.*s\n", n-2, z); } if( p->smtpFlags & SMTP_TRACE_BLOB ){ blob_appendf(p->pTranscript, "C: %.*s\n", n-2, z); } socket_send(0, z, n); blob_reset(&b); } /* ** Read a line of input received from the SMTP server. Make in point ** to the next input line. ** ** Content is actually read into the p->in buffer. Then blob_line() ** is used to extract individual lines, passing each to "in". */ static void smtp_recv_line(SmtpSession *p, Blob *in){ int n = blob_size(&p->inbuf); char *z = blob_buffer(&p->inbuf); int i = blob_tell(&p->inbuf); int nDelay = 0; if( i<n && z[n-1]=='\n' ){ blob_line(&p->inbuf, in); }else if( p->atEof ){ blob_init(in, 0, 0); }else{ if( n>0 && i>=n ){ blob_truncate(&p->inbuf, 0); blob_rewind(&p->inbuf); n = 0; } do{ size_t got; blob_resize(&p->inbuf, n+1000); z = blob_buffer(&p->inbuf); got = socket_receive(0, z+n, 1000, 1); if( got>0 ){ in->nUsed += got; n += got; z[n] = 0; if( n>0 && z[n-1]=='\n' ) break; if( got==1000 ) continue; } nDelay++; if( nDelay>100 ){ blob_init(in, 0, 0); p->zErr = mprintf("timeout"); socket_close(); p->atEof = 1; return; }else{ sqlite3_sleep(100); } }while( n<1 || z[n-1]!='\n' ); blob_truncate(&p->inbuf, n); blob_line(&p->inbuf, in); } z = blob_buffer(in); n = blob_size(in); if( n && z[n-1]=='\n' ) n--; if( n && z[n-1]=='\r' ) n--; if( p->smtpFlags & SMTP_TRACE_STDOUT ){ fossil_print("S: %.*s\n", n, z); } if( p->smtpFlags & SMTP_TRACE_FILE ){ fprintf(p->logFile, "S: %.*s\n", n, z); } if( p->smtpFlags & SMTP_TRACE_BLOB ){ blob_appendf(p->pTranscript, "S: %.*s\n", n-2, z); } } /* ** Capture a single-line server reply. */ static void smtp_get_reply_from_server( SmtpSession *p, /* The SMTP connection */ Blob *in, /* Buffer used to hold the reply */ int *piCode, /* The return code */ int *pbMore, /* True if the reply is not complete */ char **pzArg /* Argument */ ){ int n; char *z; blob_truncate(in, 0); smtp_recv_line(p, in); z = blob_str(in); n = blob_size(in); if( z[0]=='#' ){ *piCode = 0; *pbMore = 1; *pzArg = z; }else{ *piCode = atoi(z); *pbMore = n>=4 && z[3]=='-'; *pzArg = n>=4 ? z+4 : ""; } } /* ** Have the client send a QUIT message. */ int smtp_client_quit(SmtpSession *p){ Blob in = BLOB_INITIALIZER; int iCode = 0; int bMore = 0; char *zArg = 0; smtp_send_line(p, "QUIT\r\n"); do{ smtp_get_reply_from_server(p, &in, &iCode, &bMore, &zArg); }while( bMore ); p->atEof = 1; socket_close(); return 0; } /* ** Begin a client SMTP session. Wait for the initial 220 then send ** the EHLO and wait for a 250. ** ** Return 0 on success and non-zero for a failure. */ int smtp_client_startup(SmtpSession *p){ Blob in = BLOB_INITIALIZER; int iCode = 0; int bMore = 0; char *zArg = 0; do{ smtp_get_reply_from_server(p, &in, &iCode, &bMore, &zArg); }while( bMore ); if( iCode!=220 ){ smtp_client_quit(p); return 1; } smtp_send_line(p, "EHLO %s\r\n", p->zFrom); do{ smtp_get_reply_from_server(p, &in, &iCode, &bMore, &zArg); }while( bMore ); if( iCode!=250 ){ smtp_client_quit(p); return 1; } return 0; } /* ** COMMAND: test-smtp-probe ** ** Usage: %fossil test-smtp-probe DOMAIN [ME] ** ** Interact with the SMTP server for DOMAIN by setting up a connection ** and then immediately shutting it back down. Log all interaction ** on the console. Use ME as the domain name of the sender. ** ** Options: ** ** --direct Use DOMAIN directly without going through MX ** --port N Talk on TCP port N */ void test_smtp_probe(void){ SmtpSession *p; const char *zDomain; const char *zSelf; const char *zPort; int iPort = 25; u32 smtpFlags = SMTP_TRACE_STDOUT|SMTP_PORT; if( find_option("direct",0,0)!=0 ) smtpFlags |= SMTP_DIRECT; zPort = find_option("port",0,1); if( zPort ) iPort = atoi(zPort); verify_all_options(); if( g.argc!=3 && g.argc!=4 ) usage("DOMAIN [ME]"); zDomain = g.argv[2]; zSelf = g.argc==4 ? g.argv[3] : "fossil-scm.org"; p = smtp_session_new(zSelf, zDomain, smtpFlags, iPort); if( p->zErr ){ fossil_fatal("%s", p->zErr); } fossil_print("Connection to \"%s\"\n", p->zHostname); smtp_client_startup(p); smtp_client_quit(p); if( p->zErr ){ fossil_fatal("ERROR: %s\n", p->zErr); } smtp_session_free(p); } /* ** Send the content of an email message followed by a single ** "." line. All lines must be \r\n terminated. Any isolated ** \n line terminators in the input must be converted. Also, ** a line beginning with "." must have the dot doubled per ** https://tools.ietf.org/html/rfc5321#section-4.5.2 */ static void smtp_send_email_body( const char *zMsg, /* Message to send */ size_t (*xSend)(void*,const void*,size_t), /* Sender callback function */ void *pArg /* First arg to sender */ ){ Blob in; Blob out = BLOB_INITIALIZER; Blob line; blob_init(&in, zMsg, -1); while( blob_line(&in, &line) ){ char *z = blob_buffer(&line); int n = blob_size(&line); if( n==0 ) break; n--; if( n && z[n-1]=='\r' ) n--; if( z[0]=='.' ){ blob_append(&out, "..", 2); /* RFC 5321 § 4.5.2 */ blob_append(&out, z+1, n-1); }else{ blob_append(&out, z, n); } blob_append(&out, "\r\n", 2); } blob_append(&out, ".\r\n", 3); xSend(pArg, blob_buffer(&out), blob_size(&out)); blob_reset(&out); blob_reset(&line); } /* A sender function appropriate for use by smtp_send_email_body() to ** send all content to the console, for testing. */ static size_t smtp_test_sender(void *NotUsed, const void *pContent, size_t N){ return fwrite(pContent, 1, N, stdout); } /* ** COMMAND: test-smtp-senddata ** ** Usage: %fossil test-smtp-senddata FILE ** ** Read content from FILE, then send it to stdout encoded as if sent ** to the DATA portion of an SMTP session. This command is used to ** test the encoding logic. */ void test_smtp_senddata(void){ Blob f; if( g.argc!=3 ) usage("FILE"); blob_read_from_file(&f, g.argv[2], ExtFILE); smtp_send_email_body(blob_str(&f), smtp_test_sender, 0); blob_reset(&f); } /* ** Send a single email message to the SMTP server. ** ** All email addresses (zFrom and azTo) must be plain "local@domain" ** format without the surrounding "<..>". This routine will add the ** necessary "<..>". ** ** The body of the email should be well-structured. This routine will ** convert any \n line endings into \r\n and will escape lines containing ** just ".", but will not make any other alterations or corrections to ** the message content. ** ** Return 0 on success. Otherwise an error code. */ int smtp_send_msg( SmtpSession *p, /* The SMTP server to which the message is sent */ const char *zFrom, /* Who the message is from */ int nTo, /* Number of receipients */ const char **azTo, /* Email address of each recipient */ const char *zMsg /* Body of the message */ ){ int i; int iCode = 0; int bMore = 0; char *zArg = 0; Blob in; blob_init(&in, 0, 0); smtp_send_line(p, "MAIL FROM:<%s>\r\n", zFrom); do{ smtp_get_reply_from_server(p, &in, &iCode, &bMore, &zArg); }while( bMore ); if( iCode!=250 ) return 1; for(i=0; i<nTo; i++){ smtp_send_line(p, "RCPT TO:<%s>\r\n", azTo[i]); do{ smtp_get_reply_from_server(p, &in, &iCode, &bMore, &zArg); }while( bMore ); if( iCode!=250 ) return 1; } smtp_send_line(p, "DATA\r\n"); do{ smtp_get_reply_from_server(p, &in, &iCode, &bMore, &zArg); }while( bMore ); if( iCode!=354 ) return 1; smtp_send_email_body(zMsg, socket_send, 0); if( p->smtpFlags & SMTP_TRACE_STDOUT ){ fossil_print("C: # message content\nC: .\n"); } if( p->smtpFlags & SMTP_TRACE_FILE ){ fprintf(p->logFile, "C: # message content\nC: .\n"); } if( p->smtpFlags & SMTP_TRACE_BLOB ){ blob_appendf(p->pTranscript, "C: # message content\nC: .\n"); } do{ smtp_get_reply_from_server(p, &in, &iCode, &bMore, &zArg); }while( bMore ); if( iCode!=250 ) return 1; return 0; } /* ** The input is a base email address of the form "local@domain". ** Return a pointer to just the "domain" part. */ static const char *domainOfAddr(const char *z){ while( z[0] && z[0]!='@' ) z++; if( z[0]==0 ) return 0; return z+1; } /* ** COMMAND: test-smtp-send ** ** Usage: %fossil test-smtp-send EMAIL FROM TO ... ** ** Use SMTP to send the email message contained in the file named EMAIL ** to the list of users TO. FROM is the sender of the email. ** ** Options: ** ** --direct Go directly to the TO domain. Bypass MX lookup ** --port N Use TCP port N instead of 25 ** --trace Show the SMTP conversation on the console */ void test_smtp_send(void){ SmtpSession *p; const char *zFrom; int nTo; const char *zToDomain; const char *zFromDomain; const char **azTo; int smtpPort = 25; const char *zPort; Blob body; u32 smtpFlags = SMTP_PORT; if( find_option("trace",0,0)!=0 ) smtpFlags |= SMTP_TRACE_STDOUT; if( find_option("direct",0,0)!=0 ) smtpFlags |= SMTP_DIRECT; zPort = find_option("port",0,1); if( zPort ) smtpPort = atoi(zPort); verify_all_options(); if( g.argc<5 ) usage("EMAIL FROM TO ..."); blob_read_from_file(&body, g.argv[2], ExtFILE); zFrom = g.argv[3]; nTo = g.argc-4; azTo = (const char**)g.argv+4; zFromDomain = domainOfAddr(zFrom); zToDomain = domainOfAddr(azTo[0]); p = smtp_session_new(zFromDomain, zToDomain, smtpFlags, smtpPort); if( p->zErr ){ fossil_fatal("%s", p->zErr); } fossil_print("Connection to \"%s\"\n", p->zHostname); smtp_client_startup(p); smtp_send_msg(p, zFrom, nTo, azTo, blob_str(&body)); smtp_client_quit(p); if( p->zErr ){ fossil_fatal("ERROR: %s\n", p->zErr); } smtp_session_free(p); blob_reset(&body); } /***************************************************************************** ** Server implementation *****************************************************************************/ /* ** Schema used by the email processing system. */ static const char zEmailSchema[] = @ -- bulk storage is in this table. This table can store either @ -- the body of email messages or transcripts of an smtp session. @ CREATE TABLE IF NOT EXISTS repository.emailblob( @ emailid INTEGER PRIMARY KEY AUTOINCREMENT, -- numeric idea for the entry @ enref INT, -- Number of references to this blob @ ets INT, -- Corresponding transcript, or NULL @ etime INT, -- insertion time, secs since 1970 @ esz INT, -- uncompressed content size @ etxt TEXT -- content of this entry @ ); @ @ -- One row for each mailbox entry. All users emails are stored in @ -- this same table. @ CREATE TABLE IF NOT EXISTS repository.emailbox( @ ebid INTEGER PRIMARY KEY, -- Unique id for each mailbox entry @ euser TEXT, -- User who received this email @ edate INT, -- Date received. Seconds since 1970 @ efrom TEXT, -- Who is the email from @ emsgid INT, -- Raw email text @ estate INT, -- 0: Unread, 1: read, 2: trash 3: sent @ esubject TEXT, -- Subject line for display @ etags TEXT -- zero or more tags @ ); @ @ -- Information on how to deliver incoming email. @ CREATE TABLE IF NOT EXISTS repository.emailroute( @ eaddr TEXT PRIMARY KEY, -- Email address @ epolicy TEXT -- How to handle email sent to this address @ ) WITHOUT ROWID; @ @ -- Outgoing email queue @ CREATE TABLE IF NOT EXISTS repository.emailoutq( @ edomain TEXT, -- Destination domain. (ex: "fossil-scm.org") @ efrom TEXT, -- Sender email address (envelope "from") @ eto TEXT, -- Recipient email address (envelope "to") @ emsgid INT, -- Message body in the emailblob table @ ectime INT, -- Time enqueued. Seconds since 1970 @ emtime INT, -- Time of last send attempt. Sec since 1970 @ ensend INT, -- Number of send attempts @ ets INT -- Transcript of last failed attempt @ ); @ @ -- Triggers to automatically keep the emailblob.enref field up to date @ -- as entries in the emailblob, emailbox, and emailoutq tables are @ -- deleted. @ CREATE TRIGGER IF NOT EXISTS repository.emailblob_d1 @ AFTER DELETE ON emailblob BEGIN @ UPDATE emailblob SET enref=enref-1 WHERE emailid=old.ets; @ END; @ CREATE TRIGGER IF NOT EXISTS repository.emailbox_d1 @ AFTER DELETE ON emailbox BEGIN @ UPDATE emailblob SET enref=enref-1 WHERE emailid=old.emsgid; @ END; @ CREATE TRIGGER IF NOT EXISTS repository.emailoutq_d1 @ AFTER DELETE ON emailoutq BEGIN @ UPDATE emailblob SET enref=enref-1 WHERE emailid IN (old.ets,old.emsgid); @ END; @ @ -- An index on the emailblob entries which are unreferenced. @ CREATE INDEX IF NOT EXISTS repository.emailblob_nref ON emailblob(enref) @ WHERE enref<=0; ; /* ** Code used to delete the email tables. */ static const char zEmailDrop[] = @ DROP TABLE IF EXISTS emailblob; @ DROP TABLE IF EXISTS emailbox; @ DROP TABLE IF EXISTS emailroute; @ DROP TABLE IF EXISTS emailqueue; ; #if INTERFACE /* ** Mailbox message states */ #define MSG_UNREAD 0 #define MSG_READ 1 #define MSG_TRASH 2 #endif /* INTERFACE */ /* ** Populate the schema of a database. ** ** eForce==0 Fast ** eForce==1 Run CREATE TABLE statements every time ** eForce==2 DROP then rerun CREATE TABLE */ void smtp_server_schema(int eForce){ if( eForce==2 ){ db_multi_exec(zEmailDrop/*works-like:""*/); } if( eForce==1 || !db_table_exists("repository","emailblob") ){ db_multi_exec(zEmailSchema/*works-like:""*/); } } /* ** WEBPAGE: setup_smtp ** ** Administrative page for configuring and controlling inbound email and ** output email queuing. This page is available to administrators ** only via the /Admin/EmailServer menu. */ void setup_smtp(void){ Stmt q; login_check_credentials(); if( !g.perm.Setup ){ login_needed(0); return; } db_begin_transaction(); style_header("Email Server Setup"); if( db_table_exists("repository","emailroute") ){ style_submenu_element("emailblob table", "%R/emailblob"); style_submenu_element("emailoutq table", "%R/emailoutq"); db_prepare(&q, "SELECT eaddr, epolicy FROM emailroute ORDER BY 1"); }else{ db_prepare(&q, "SELECT null, null WHERE false"); } @ <h1>Email Routing Table</h1> @ <table class="emailroutetab" cellpadding="5" border="1" cellspacing="0"> @ <thead> @ <tr> @ <th>Email Address @ <th>Routing @ <th> @ </tr> @ </thead><tbody> while( db_step(&q)==SQLITE_ROW ){ const char *zEAddr = db_column_text(&q, 0); const char *zEPolicy = db_column_text(&q, 1); @ <tr> @ <td valign="top">%h(zEAddr)</td> @ <td valign="top"><span style="white-space:pre;">%h(zEPolicy)</span></td> @ <td valign="top"><form method="POST" action="%R/setup_smtp_route"> @ <input type="hidden" name="oaddr" value="%h(zEAddr)"> @ <input type="submit" value="Edit"> @ </form> } db_finalize(&q); @ <tr> @ <td colspan="3"> @ <form method="POST" action="%R/setup_smtp_route"> @ <input type="submit" value="New"> @ ← Add a new email address @ </form> @ </table> style_footer(); db_end_transaction(0); } /* ** WEBPAGE: setup_smtp_route ** ** Edit a single entry in the emailroute table. ** Query parameters: ** ** eaddr=ADDR ADDR is the email address as edited. ** ** oaddr=ADDR The original email address prior to editing. ** Omit to add a new address. ** ** epolicy=TXT The routing policy. */ void setup_smtp_route(void){ char *zEAddr = PT("eaddr"); /* new email address */ char *zEPolicy = PT("epolicy"); /* new routing policy */ char *zOAddr = PT("oaddr"); /* original email address */ char *zErr = 0; int iErr = 0; login_check_credentials(); if( !g.perm.Setup ){ login_needed(0); return; } style_header("Email Route Editor"); if( P("edit") && cgi_csrf_safe(1) && zEAddr!=0 && zEPolicy!=0 ){ smtp_server_schema(0); if( (zOAddr==0 || fossil_strcmp(zEAddr,zOAddr)!=0) ){ /* New or changed email address */ if( db_exists("SELECT 1 FROM emailroute WHERE eaddr=%Q",zEAddr) ){ iErr = 1; zErr = mprintf("email address \"%h(zEAddr)\" already exists",zEAddr); goto smtp_route_edit; } if( zEPolicy[0]==0 ){ iErr = 2; zErr = mprintf("empty route"); goto smtp_route_edit; } } /* If the email address has changed, or if the new policy is blank, ** delete the old address and route information */ db_begin_transaction(); if( (zOAddr && fossil_strcmp(zEAddr,zOAddr)!=0) || zEPolicy[0]==0 ){ db_multi_exec("DELETE FROM emailroute WHERE eaddr=%Q", zOAddr); } if( zEPolicy[0] ){ /* Insert the new address and route */ db_multi_exec( "REPLACE INTO emailroute(eaddr,epolicy) VALUES(%Q,%Q)", zEAddr, zEPolicy ); } db_end_transaction(0); cgi_redirectf("%R/setup_smtp"); } if( P("cancel")!=0 ){ cgi_redirectf("%R/setup_smtp"); } smtp_route_edit: if( zEAddr==0 ) zEAddr = zOAddr; if( zEPolicy==0 && db_table_exists("repository","emailroute") ){ zEPolicy = db_text(0, "SELECT epolicy FROM emailroute WHERE eaddr=%Q", zEAddr); } if( zEPolicy==0 ) zEPolicy = ""; @ <form method="POST" action="%R/setup_smtp_route"> if( zOAddr ){ @ <input type="hidden" name="oaddr" value="%h(zOAddr)"> } @ <table class="label-value"> @ <tr> @ <th>Email Address:</th> @ <td><input type="text" size=30 name="eaddr" value="%h(zEAddr)"> if( iErr==1 ){ @ <td><span class="generalError">← %z(zErr)</span> } @ </tr> if( zOAddr && fossil_strcmp(zOAddr,zEAddr)!=0 ){ @ <tr> @ <th>Original Address:</th> @ <td>%h(zOAddr) @ </tr> } @ <tr> @ <th>Routing:</th> @ <td><textarea name="epolicy" rows="3" cols="40">%h(zEPolicy)</textarea> if( iErr==2 ){ @ <td valign="top"><span class="generalError">← %z(zErr)</span> } @ </tr> @ <tr> @ <td> @ <td><input type="submit" name="edit" value="Apply"> @ <input type="submit" name="cancel" value="Cancel"> @ </tr> @ </table> @ <hr> @ <h1>Instructions</h1> @ @ <p>The "Routing" field consists of zero or more lines where each @ line is an "action" followed by an "argument". Available actions: @ <ul> @ <li><p><b>forward</b> <i>email-address</i> @ <p>Forward the message to <i>email-address</i>. @ <li><p><b>mbox</b> <i>login-name</i> @ <p>Store the message in the local mailbox for the user @ with USER.LOGIN=<i>login-name</i>. @ </ul> @ @ <p>To delete a route → erase all text from the "Routing" field then @ press the "Apply" button. style_footer(); } #if LOCAL_INTERFACE /* ** State information for the server */ struct SmtpServer { sqlite3_int64 idTranscript; /* Transcript ID number */ sqlite3_int64 idMsg; /* Message ID number */ const char *zIpAddr; /* Remote IP address */ char *zEhlo; /* Client domain on the EHLO line */ char *zFrom; /* MAIL FROM: argument */ int nTo; /* Number of RCPT TO: lines seen */ struct SmtpTo { char *z; /* Address in each RCPT TO line */ int okRemote; /* zTo can be in another domain */ } *aTo; u32 srvrFlags; /* Control flags */ int nEts; /* Number of references to the transcript */ int nRef; /* Number of references to idMsg */ Blob msg; /* Content following DATA */ Blob transcript; /* Session transcript */ }; #define SMTPSRV_CLEAR_MSG 1 /* smtp_server_clear() last message only */ #define SMTPSRV_CLEAR_ALL 2 /* smtp_server_clear() everything */ #define SMTPSRV_LOG 0x001 /* Record a transcript of the interaction */ #define SMTPSRV_STDERR 0x002 /* Transcription written to stderr */ #define SMTPSRV_DRYRUN 0x004 /* Do not record anything in database */ #endif /* LOCAL_INTERFACE */ /* ** Clear the SmtpServer object. Deallocate resources. ** How much to clear depends on eHowMuch */ static void smtp_server_clear(SmtpServer *p, int eHowMuch){ int i; if( eHowMuch>=SMTPSRV_CLEAR_MSG ){ fossil_free(p->zFrom); p->zFrom = 0; for(i=0; i<p->nTo; i++) fossil_free(p->aTo[i].z); fossil_free(p->aTo); p->aTo = 0; p->nTo = 0; blob_reset(&p->msg); p->idMsg = 0; } if( eHowMuch>=SMTPSRV_CLEAR_ALL ){ blob_reset(&p->transcript); p->idTranscript = 0; fossil_free(p->zEhlo); p->zEhlo = 0; } } /* ** Turn raw memory into an SmtpServer object. */ static void smtp_server_init(SmtpServer *p){ memset(p, 0, sizeof(*p)); blob_init(&p->msg, 0, 0); blob_init(&p->transcript, 0, 0); } /* ** Append a new TO entry to the SmtpServer object. Do not do the ** append if the same entry is already on the list. ** ** The zAddr argument is obtained from fossil_malloc(). This ** routine assumes ownership of the allocation. */ static void smtp_append_to(SmtpServer *p, char *zAddr, int okRemote){ int i; for(i=0; zAddr[i]; i++){ zAddr[i] = fossil_tolower(zAddr[i]); } for(i=0; i<p->nTo; i++){ if( strcmp(zAddr, p->aTo[i].z)==0 ){ fossil_free(zAddr); if( p->aTo[i].okRemote==0 ) p->aTo[i].okRemote = okRemote; return; } } p->aTo = fossil_realloc(p->aTo, (p->nTo+1)*sizeof(p->aTo[0])); p->aTo[p->nTo].z = zAddr; p->aTo[p->nTo].okRemote = okRemote; p->nTo++; } /* ** Send a single line of output from the server to the client. */ static void smtp_server_send(SmtpServer *p, const char *zFormat, ...){ Blob b = empty_blob; va_list ap; char *z; int n; va_start(ap, zFormat); blob_vappendf(&b, zFormat, ap); va_end(ap); z = blob_buffer(&b); n = blob_size(&b); assert( n>=2 ); assert( z[n-1]=='\n' ); assert( z[n-2]=='\r' ); if( p->srvrFlags & SMTPSRV_LOG ){ blob_appendf(&p->transcript, "S: %.*s\n", n-2, z); } if( p->srvrFlags & SMTPSRV_STDERR ){ fprintf(stderr, "S: %.*s\n", n-2, z); } fwrite(z, n, 1, stdout); fflush(stdout); blob_reset(&b); } /* ** Read a single line from the client. */ static int smtp_server_gets(SmtpServer *p, char *aBuf, int nBuf){ int rc = fgets(aBuf, nBuf, stdin)!=0; if( rc ){ if( (p->srvrFlags & SMTPSRV_LOG)!=0 ){ blob_appendf(&p->transcript, "C: %s", aBuf); } if( (p->srvrFlags & SMTPSRV_STDERR)!=0 ){ fprintf(stderr, "C: %s", aBuf); } } return rc; } /* ** RFC-5321 requires certain content be prepended to an email header ** as that email is received. */ static void smtp_server_prepend_header_lines(SmtpServer *p){ blob_appendf(&p->msg, "Received: from %s by Fossil-smtp\r\n", p->zIpAddr); } /* ** Capture the incoming email data into the p->msg blob. Dequote ** lines of "..\r\n" into just ".\r\n". */ static void smtp_server_capture_data(SmtpServer *p, char *z, int n){ int nLine = 0; while( fgets(z, n, stdin) ){ if( strncmp(z, ".\r\n", 3)==0 || strncmp(z, ".\n",2)==0 ) break; nLine++; if( strncmp(z, "..\r\n", 4)==0 || strncmp(z, "..\n",3)==0 ){ memmove(z, z+1, 4); } blob_append(&p->msg, z, -1); } if( p->srvrFlags & SMTPSRV_LOG ){ blob_appendf(&p->transcript, "C: # %d lines, %d bytes of content\n", nLine, blob_size(&p->msg)); } if( p->srvrFlags & SMTPSRV_STDERR ){ fprintf(stderr, "C: # %d lines, %d bytes of content\n", nLine, blob_size(&p->msg)); } } /* ** Send an email to a single email addess that is registered with ** this system, according to the instructions in emailroute. If ** zAddr is not in the emailroute table, then this routine is a ** no-op. Or if zAddr has already been processed, then this ** routine is a no-op. */ static void smtp_server_send_one_user( SmtpServer *p, /* The current inbound email */ const char *zAddr, /* Who to forward this to */ int okRemote /* True if ok to foward to another domain */ ){ char *zPolicy; Blob policy, line, token, tail; zPolicy = db_text(0, "SELECT epolicy FROM emailroute WHERE eaddr=%Q", zAddr); if( zPolicy==0 ){ if( okRemote ){ int i; for(i=0; zAddr[i] && zAddr[i]!='@'; i++){} if( zAddr[i]=='@' && zAddr[i+1]!=0 ){ db_multi_exec( "INSERT INTO emailoutq(edomain,efrom,eto,emsgid,ectime," "emtime,ensend)" "VALUES(%Q,%Q,%Q,%lld,now(),0,0)", zAddr+i+1, p->zFrom, zAddr, p->idMsg ); p->nRef++; } } return; } blob_init(&policy, zPolicy, -1); while( blob_line(&policy, &line) ){ blob_trim(&line); blob_token(&line, &token); blob_tail(&line, &tail); if( blob_size(&tail)==0 ) continue; if( blob_eq_str(&token, "mbox", 4) ){ Blob subj; email_header_value(&p->msg, "subject", &subj); db_multi_exec( "INSERT INTO emailbox(euser,edate,efrom,emsgid,estate,esubject)" " VALUES(%Q,now(),%Q,%lld,0,%Q)", blob_str(&tail), p->zFrom, p->idMsg, blob_str(&subj) ); blob_reset(&subj); p->nRef++; } if( blob_eq_str(&token, "forward", 7) ){ smtp_append_to(p, fossil_strdup(blob_str(&tail)), 1); } blob_reset(&tail); } } /* ** The SmtpServer object contains a complete incoming email. ** Add this email to the database. */ static void smtp_server_route_incoming(SmtpServer *p, int bFinish){ Stmt s; int i; int nEtsStart = p->nEts; if( p->zFrom && p->nTo && blob_size(&p->msg) && (p->srvrFlags & SMTPSRV_DRYRUN)==0 ){ db_begin_write(); if( p->idTranscript==0 ) smtp_server_schema(0); p->nRef = 0; db_prepare(&s, "INSERT INTO emailblob(ets,etime,etxt,enref,esz)" " VALUES(:ets,now(),compress(:etxt),0,:esz)" ); p->nEts++; if( !bFinish && p->idTranscript==0 ){ db_bind_null(&s, ":ets"); db_bind_null(&s, ":etxt"); db_bind_null(&s, ":esz"); db_step(&s); db_reset(&s); p->idTranscript = db_last_insert_rowid(); }else if( bFinish ){ if( p->idTranscript ){ db_multi_exec( "UPDATE emailblob SET etxt=compress(%Q), enref=%d, esz=%d" " WHERE emailid=%lld", blob_str(&p->transcript), p->nEts, blob_size(&p->transcript), p->idTranscript); }else{ db_bind_null(&s, ":ets"); db_bind_str(&s, ":etxt", &p->transcript); db_bind_int(&s, ":esz", blob_size(&p->transcript)); db_step(&s); db_reset(&s); p->idTranscript = db_last_insert_rowid(); db_multi_exec( "UPDATE emailblob SET enref=%d WHERE emailid=%lld", p->nEts, p->idTranscript); } /* smtp_server_send(p, "221-Transcript id %lld nref %d\r\n", ** p->idTranscript, p->nEts); */ } db_bind_int64(&s, ":ets", p->idTranscript); db_bind_str(&s, ":etxt", &p->msg); db_bind_int(&s, ":esz", blob_size(&p->msg)); db_step(&s); db_finalize(&s); p->idMsg = db_last_insert_rowid(); /* make entries in emailbox and emailoutq */ for(i=0; i<p->nTo; i++){ int okRemote = p->aTo[i].okRemote; p->aTo[i].okRemote = 1; smtp_server_send_one_user(p, p->aTo[i].z, okRemote); } /* Fix up the emailblob.enref field of the email message body */ if( p->nRef ){ db_multi_exec( "UPDATE emailblob SET enref=%d WHERE emailid=%lld", p->nRef, p->idMsg ); }else{ db_multi_exec( "DELETE FROM emailblob WHERE emailid=%lld", p->idMsg ); p->nEts = nEtsStart; } /* Clean out legacy entries */ if( bFinish ){ db_multi_exec("DELETE FROM emailblob WHERE enref<=0"); } /* Finish the transaction after all changes are implemented */ db_commit_transaction(); } smtp_server_clear(p, SMTPSRV_CLEAR_MSG); } /* ** Remove stale content from the emailblob table. */ void smtp_cleanup(void){ if( db_table_exists("repository","emailblob") ){ db_begin_transaction(); db_multi_exec( "UPDATE emailblob SET ets=NULL WHERE enref<=0;" "DELETE FROM emailblob WHERE enref<=0;" ); db_end_transaction(0); } } /* ** COMMAND: test-emailblob-refcheck ** ** Usage: %fossil test-emailblob-refcheck [--repair] [--full] [--clean] ** ** Verify that the emailblob.enref field is correct. Report any errors. ** Use the --repair command to fix up the enref field. The --full option ** gives a full report showing the enref value on all entries in the ** emailblob table. If the --clean flags is used together with --repair, ** then emailblob table entires with enref==0 are removed. */ void test_refcheck_emailblob(void){ int doRepair; int fullReport; int doClean; Blob sql; Stmt q; int nErr = 0; db_find_and_open_repository(0, 0); fullReport = find_option("full",0,0)!=0; doRepair = find_option("repair",0,0)!=0; doClean = find_option("clean",0,0)!=0; verify_all_options(); if( !db_table_exists("repository","emailblob") ){ fossil_print("emailblob table is not configured - nothing to check\n"); return; } db_multi_exec( "CREATE TEMP TABLE refcnt(id INTEGER PRIMARY KEY, n);" "INSERT INTO refcnt SELECT ets, count(*) FROM (" " SELECT ets FROM emailblob" " UNION ALL" " SELECT emsgid FROM emailbox" " UNION ALL" " SELECT emsgid FROM emailoutq" ") WHERE ets IS NOT NULL GROUP BY 1;" "INSERT OR IGNORE INTO refcnt(id,n) SELECT emailid, 0 FROM emailblob;" ); if( doRepair ){ db_multi_exec( "UPDATE emailblob SET enref=(SELECT n FROM refcnt WHERE id=emailid)" ); if( doClean ){ smtp_cleanup(); } } blob_init(&sql, 0, 0); blob_append_sql(&sql, "SELECT a.emailid, a.enref, b.n" " FROM emailblob AS a JOIN refcnt AS b ON a.emailid=b.id" ); if( !fullReport ){ blob_append_sql(&sql, " WHERE a.enref!=b.n"); } db_prepare_blob(&q, &sql); blob_reset(&sql); while( db_step(&q)==SQLITE_ROW ){ sqlite3_int64 id = db_column_int64(&q,0); int n1 = db_column_int(&q, 1); int n2 = db_column_int(&q, 2); if( n1!=n2 ) nErr++; fossil_print("%12lld %4d %4d%s\n", id, n1, n2, n1!=n2 ? " ERROR" : ""); } db_finalize(&q); if( nErr ){ fossil_print("Number of incorrect emailblob.enref values: %d\n",nErr); } } /* ** COMMAND: smtpd ** ** Usage: %fossil smtpd [OPTIONS] REPOSITORY ** ** Begin a SMTP conversation with a client using stdin/stdout. The ** received email is stored in REPOSITORY. ** ** Options: ** ** --dryrun Do not record any emails in the database ** ** --trace Print a transcript of the conversation on stderr ** for debugging and analysis ** ** --ipaddr ADDR The SMTP connection originates at ADDR. Or if ADDR ** is the name of an environment variable, the address ** is taken from that environment variable. */ void smtp_server(void){ char *zDbName; const char *zDomain; SmtpServer x; char z[5000]; smtp_server_init(&x); zDomain = find_option("domain",0,1); if( zDomain==0 ) zDomain = ""; x.srvrFlags = SMTPSRV_LOG; if( find_option("trace",0,0)!=0 ) x.srvrFlags |= SMTPSRV_STDERR; if( find_option("dryrun",0,0)!=0 ) x.srvrFlags |= SMTPSRV_DRYRUN; x.zIpAddr = find_option("ipaddr",0,1); if( x.zIpAddr ){ const char *zNew = fossil_getenv(x.zIpAddr); if( zNew && zNew[0] ) x.zIpAddr = zNew; } if( x.zIpAddr==0 ){ x.zIpAddr = cgi_remote_ip(0); if( x.zIpAddr==0 ) x.zIpAddr = "?.?.?.?"; } verify_all_options(); if( g.argc!=3 ) usage("DBNAME"); zDbName = g.argv[2]; zDbName = enter_chroot_jail(zDbName, 0); db_open_repository(zDbName); add_content_sql_commands(g.db); smtp_server_send(&x, "220 %s ESMTP https://fossil-scm.org/ %s\r\n", zDomain, MANIFEST_VERSION); while( smtp_server_gets(&x, z, sizeof(z)) ){ if( strncmp(z, "EHLO", 4)==0 && fossil_isspace(z[4]) ){ smtp_server_send(&x, "250 ok\r\n"); }else if( strncmp(z, "HELO", 4)==0 && fossil_isspace(z[4]) ){ smtp_server_send(&x, "250 ok\r\n"); }else if( strncmp(z, "MAIL FROM:<", 11)==0 ){ smtp_server_route_incoming(&x, 0); smtp_server_clear(&x, SMTPSRV_CLEAR_MSG); x.zFrom = email_copy_addr(z+11,'>'); if( x.zFrom==0 ){ smtp_server_send(&x, "500 unacceptable email address\r\n"); }else{ smtp_server_send(&x, "250 ok\r\n"); } }else if( strncmp(z, "RCPT TO:<", 9)==0 ){ char *zAddr; if( x.zFrom==0 ){ smtp_server_send(&x, "500 missing MAIL FROM\r\n"); continue; } zAddr = email_copy_addr(z+9, '>'); if( zAddr==0 ){ smtp_server_send(&x, "505 no such user\r\n"); continue; } smtp_append_to(&x, zAddr, 0); if( x.nTo>=100 ){ smtp_server_send(&x, "452 too many recipients\r\n"); continue; } smtp_server_send(&x, "250 ok\r\n"); }else if( strncmp(z, "DATA", 4)==0 && fossil_isspace(z[4]) ){ if( x.zFrom==0 || x.nTo==0 ){ smtp_server_send(&x, "500 missing RCPT TO\r\n"); continue; } smtp_server_send(&x, "354 ready\r\n"); smtp_server_prepend_header_lines(&x); smtp_server_capture_data(&x, z, sizeof(z)); smtp_server_send(&x, "250 ok\r\n"); }else if( strncmp(z, "QUIT", 4)==0 && fossil_isspace(z[4]) ){ smtp_server_route_incoming(&x, 1); smtp_server_send(&x, "221 closing connection\r\n"); break; }else { smtp_server_send(&x, "500 unknown command\r\n"); } } smtp_server_clear(&x, SMTPSRV_CLEAR_ALL); } /* ** Zero-terminate the argument. Return a pointer the start of the ** next argument, or to NULL if there are no more arguments. */ static char *pop3d_arg(char *z){ if( z[0]==0 || fossil_isspace(z[0]) ){ return 0; } z++; while( z[0] && !fossil_isspace(z[0]) ){ z++; } if( z[0]==0 ) return 0; z[0] = 0; z++; if( z[0]==0 || fossil_isspace(z[0]) ) return 0; return z; } /* ** Write formatted output back to the pop3 client, and also to the ** log file, if there is a log file. */ static void pop3_print(FILE *pLog, const char *zFormat, ...){ va_list ap; char zLine[500]; va_start(ap, zFormat); sqlite3_vsnprintf(sizeof(zLine),zLine,zFormat,ap); va_end(ap); printf("%s\r\n", zLine); fflush(stdout); if( pLog ) fprintf(pLog, "S: %s\n", zLine); } /* ** Try to log in for zUser and zPass. ** ** zUser can either point to a Fossil user name or to an email address ** found in the user table's info field, in angle brackets. */ static int pop3_login(const char *zUser, char *zPass){ return login_search_uid(&zUser, zPass) != 0; } /* ** COMMAND: pop3d ** ** Usage: %fossil pop3d [OPTIONS] REPOSITORY ** ** Begin a POP3 conversation with a client using stdin/stdout using ** the mailboxes stored in REPOSITORY. ** ** If launched as root, the process first enters a chroot jail using ** the directory of REPOSITORY as root, then drops all privileges and ** assumes the user and group of REPOSITORY before reading any content ** off of the wire. ** ** --logdir DIR Each pop3d session creates a new logfile ** in the directory DIR and records a transcript ** of the session there. The logfile is opened ** before entering the chroot jail. */ void pop3d_command(void){ char *zDbName; char *zA1, *zA2, *zCmd, *z; int inAuth = 1; int i; FILE *pLog = 0; const char *zDir; Stmt q; char zIn[1000]; char zUser[100]; zDir = find_option("logdir",0,1); if( zDir ){ char *zFile = file_time_tempname(zDir, ".txt"); pLog = fossil_fopen(zFile, "w"); fossil_free(zFile); } verify_all_options(); if( g.argc!=3 ) usage("DBNAME"); zDbName = g.argv[2]; zDbName = enter_chroot_jail(zDbName, 0); db_open_repository(zDbName); add_content_sql_commands(g.db); pop3_print(pLog, "+OK POP3 server ready"); while( fgets(zIn, sizeof(zIn), stdin) ){ if( pLog ) fprintf(pLog, "C: %s", zIn); zCmd = zIn; zA1 = pop3d_arg(zCmd); zA2 = zA1 ? pop3d_arg(zA1) : 0; for(i=0; zCmd[i]; i++){ zCmd[i] = fossil_tolower(zCmd[i]); } if( strcmp(zCmd,"quit")==0 ){ if( !inAuth ){ db_multi_exec( "UPDATE emailbox SET estate=2" " WHERE estate<2 AND ebid IN (SELECT ebid FROM pop3 WHERE isDel);" ); } pop3_print(pLog, "+OK"); break; } if( strcmp(zCmd,"capa")==0 ){ static const char *azCap[] = { "TOP", "USER", "UIDL", }; int i; pop3_print(pLog, "+OK"); for(i=0; i<sizeof(azCap)/sizeof(azCap[0]); i++){ pop3_print(pLog, azCap[i]); } pop3_print(pLog, "."); continue; } if( inAuth ){ if( strcmp(zCmd,"user")==0 ){ if( zA1==0 || zA2!=0 ) goto cmd_error; sqlite3_snprintf(sizeof(zUser),zUser,"%s",zA1); goto cmd_ok; } if( strcmp(zCmd,"pass")==0 ){ if( zA1==0 || zA2!=0 ) goto cmd_error; if( pop3_login(zUser,zA1)==0 ){ goto cmd_error; }else{ inAuth = 0; db_multi_exec( "CREATE TEMP TABLE pop3(" " id INTEGER PRIMARY KEY," " emailid INT," " ebid INT," " isDel INT," " esz INT" ");" "INSERT INTO pop3(id,emailid,ebid,isDel,esz)" " SELECT NULL, emailid, ebid, 0, esz FROM emailblob, emailbox" " WHERE emailid=emsgid AND euser=%Q AND estate<=1" " ORDER BY edate;", zUser ); goto cmd_ok; } } /* Fossil cannot process APOP since the users clear-text password is ** unknown. */ goto cmd_error; }else{ if( strcmp(zCmd,"stat")==0 ){ db_prepare(&q, "SELECT count(*), sum(esz) FROM pop3 WHERE NOT isDel"); if( db_step(&q)==SQLITE_ROW ){ pop3_print(pLog, "+OK %d %d", db_column_int(&q,0), db_column_int(&q,1)); }else{ pop3_print(pLog,"-ERR"); } db_finalize(&q); continue; } if( strcmp(zCmd,"list")==0 ){ if( zA1 ){ db_prepare(&q, "SELECT id, esz FROM pop3" " WHERE id=%d AND NOT isDel", atoi(zA1)); if( db_step(&q)==SQLITE_ROW ){ pop3_print(pLog, "+OK %d %d", db_column_int(&q,0), db_column_int(&q,1)); }else{ pop3_print(pLog, "-ERR"); } }else{ pop3_print(pLog, "+OK"); db_prepare(&q, "SELECT id, esz FROM pop3 WHERE NOT isDel"); while( db_step(&q)==SQLITE_ROW ){ pop3_print(pLog, "%d %d", db_column_int(&q,0), db_column_int(&q,1)); } pop3_print(pLog, "."); } db_finalize(&q); continue; } if( strcmp(zCmd,"retr")==0 || strcmp(zCmd,"top")==0 ){ Blob all, line; int nLine = 0; int iLimit; int hdrPending = 1; if( zA1==0 ) goto cmd_error; iLimit = zA2 ? atoi(zA2) : 2147483647; if( iLimit<0 ) goto cmd_error; z = db_text(0, "SELECT decompress(emailblob.etxt) " " FROM emailblob, pop3" " WHERE emailblob.emailid=pop3.emailid" " AND pop3.id=%d AND NOT pop3.isDel", atoi(zA1)); if( z==0 ) goto cmd_error; pop3_print(pLog, "+OK"); blob_init(&all, z, -1); while( (hdrPending || iLimit>0) && blob_line(&all, &line) ){ char c = blob_buffer(&line)[0]; if( c=='.' ){ fputc('.', stdout); }else if( c=='\r' || c=='\n' ){ hdrPending = 0; } fwrite(blob_buffer(&line), 1, blob_size(&line), stdout); nLine++; if( !hdrPending ) iLimit--; } if( pLog ) fprintf(pLog, "S: # %d lines of content\n", nLine); pop3_print(pLog, "."); fossil_free(z); blob_reset(&all); blob_reset(&line); fflush(stdout); continue; } if( strcmp(zCmd,"dele")==0 ){ if( zA1==0 ) goto cmd_error; db_multi_exec("UPDATE pop3 SET isDel=1 WHERE id=%d",atoi(zA1)); goto cmd_ok; } if( strcmp(zCmd,"rset")==0 ){ db_multi_exec("UPDATE pop3 SET isDel=0"); goto cmd_ok; } if( strcmp(zCmd,"uidl")==0 ){ if( zA1 ){ db_prepare(&q, "SELECT id, emailid FROM pop3" " WHERE id=%d AND NOT isDel", atoi(zA1)); if( db_step(&q)==SQLITE_ROW ){ pop3_print(pLog, "+OK %d %d", db_column_int(&q,0), db_column_int(&q,1)); }else{ pop3_print(pLog,"-ERR"); } }else{ pop3_print(pLog, "+OK"); db_prepare(&q, "SELECT id, emailid FROM pop3 WHERE NOT isDel"); while( db_step(&q)==SQLITE_ROW ){ pop3_print(pLog, "%d %d", db_column_int(&q,0), db_column_int(&q,1)); } pop3_print(pLog, "."); } db_finalize(&q); continue; } if( strcmp(zCmd,"noop")==0 ){ goto cmd_ok; } /* Else, fall through into cmd_error */ } cmd_error: pop3_print(pLog, "-ERR"); continue; cmd_ok: pop3_print(pLog, "+OK"); continue; } if( pLog ) fclose(pLog); } |
Changes to src/sqlcmd.c.
︙ | ︙ | |||
173 174 175 176 177 178 179 | ** atexit() handler that cleans up global state modified by this module. */ static void sqlcmd_atexit(void) { g.zConfigDbName = 0; /* prevent panic */ } /* | | | | < > > > > > > > > > | 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 | ** atexit() handler that cleans up global state modified by this module. */ static void sqlcmd_atexit(void) { g.zConfigDbName = 0; /* prevent panic */ } /* ** This routine is called by the sqlite3 command-line shell to ** to load the name the Fossil repository database. */ void sqlcmd_get_dbname(const char **pzRepoName){ *pzRepoName = g.zRepositoryName; } /* ** This routine is called by the sqlite3 command-line shell to do ** extra initialization prior to starting up the shell. */ void sqlcmd_init_proc(void){ sqlite3_initialize(); sqlite3_auto_extension((void(*)(void))sqlcmd_autoinit); } #if USE_SEE /* ** This routine is called by the patched sqlite3 command-line shell in order ** to load the encryption key for the open Fossil database. The memory that ** is pointed to by the value placed in pzKey must be obtained from SQLite. */ |
︙ | ︙ | |||
205 206 207 208 209 210 211 | *pzKey = zKey; if( fossil_getenv("FOSSIL_USE_SEE_TEXTKEY")==0 ){ *pnKey = (int)strlen(zKey); }else{ *pnKey = -1; } }else{ | | | 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 | *pzKey = zKey; if( fossil_getenv("FOSSIL_USE_SEE_TEXTKEY")==0 ){ *pnKey = (int)strlen(zKey); }else{ *pnKey = -1; } }else{ fossil_panic("failed to allocate %u bytes for key", nByte); } } #endif /* ** This routine closes the Fossil databases and/or invalidates the global ** state variables that keep track of them. |
︙ | ︙ | |||
288 289 290 291 292 293 294 | fossil_close(1, noRepository); sqlite3_shutdown(); #ifndef _WIN32 linenoiseSetMultiLine(1); #endif atexit(sqlcmd_atexit); g.zConfigDbName = zConfigDb; | > | | 296 297 298 299 300 301 302 303 304 305 306 307 | fossil_close(1, noRepository); sqlite3_shutdown(); #ifndef _WIN32 linenoiseSetMultiLine(1); #endif atexit(sqlcmd_atexit); g.zConfigDbName = zConfigDb; g.argv[1] = "-quote"; sqlite3_shell(g.argc, g.argv); sqlite3_cancel_auto_extension((void(*)(void))sqlcmd_autoinit); fossil_close(0, noRepository); } |
Changes to src/sqlite3.c.
more than 10,000 changes
Changes to src/sqlite3.h.
︙ | ︙ | |||
119 120 121 122 123 124 125 | ** been edited in any way since it was last checked in, then the last ** four hexadecimal digits of the hash may be modified. ** ** See also: [sqlite3_libversion()], ** [sqlite3_libversion_number()], [sqlite3_sourceid()], ** [sqlite_version()] and [sqlite_source_id()]. */ | | | | | 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 | ** been edited in any way since it was last checked in, then the last ** four hexadecimal digits of the hash may be modified. ** ** 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-08-16 16:24:24 456842924bb33c0af8af29402f06e5f25b6791f698a0d12a080258b20b0cfb61" /* ** 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 |
︙ | ︙ | |||
500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 | #define SQLITE_IOERR_CONVPATH (SQLITE_IOERR | (26<<8)) #define SQLITE_IOERR_VNODE (SQLITE_IOERR | (27<<8)) #define SQLITE_IOERR_AUTH (SQLITE_IOERR | (28<<8)) #define SQLITE_IOERR_BEGIN_ATOMIC (SQLITE_IOERR | (29<<8)) #define SQLITE_IOERR_COMMIT_ATOMIC (SQLITE_IOERR | (30<<8)) #define SQLITE_IOERR_ROLLBACK_ATOMIC (SQLITE_IOERR | (31<<8)) #define SQLITE_LOCKED_SHAREDCACHE (SQLITE_LOCKED | (1<<8)) #define SQLITE_BUSY_RECOVERY (SQLITE_BUSY | (1<<8)) #define SQLITE_BUSY_SNAPSHOT (SQLITE_BUSY | (2<<8)) #define SQLITE_CANTOPEN_NOTEMPDIR (SQLITE_CANTOPEN | (1<<8)) #define SQLITE_CANTOPEN_ISDIR (SQLITE_CANTOPEN | (2<<8)) #define SQLITE_CANTOPEN_FULLPATH (SQLITE_CANTOPEN | (3<<8)) #define SQLITE_CANTOPEN_CONVPATH (SQLITE_CANTOPEN | (4<<8)) #define SQLITE_CORRUPT_VTAB (SQLITE_CORRUPT | (1<<8)) #define SQLITE_READONLY_RECOVERY (SQLITE_READONLY | (1<<8)) #define SQLITE_READONLY_CANTLOCK (SQLITE_READONLY | (2<<8)) #define SQLITE_READONLY_ROLLBACK (SQLITE_READONLY | (3<<8)) #define SQLITE_READONLY_DBMOVED (SQLITE_READONLY | (4<<8)) #define SQLITE_READONLY_CANTINIT (SQLITE_READONLY | (5<<8)) #define SQLITE_READONLY_DIRECTORY (SQLITE_READONLY | (6<<8)) #define SQLITE_ABORT_ROLLBACK (SQLITE_ABORT | (2<<8)) | > > > | 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 | #define SQLITE_IOERR_CONVPATH (SQLITE_IOERR | (26<<8)) #define SQLITE_IOERR_VNODE (SQLITE_IOERR | (27<<8)) #define SQLITE_IOERR_AUTH (SQLITE_IOERR | (28<<8)) #define SQLITE_IOERR_BEGIN_ATOMIC (SQLITE_IOERR | (29<<8)) #define SQLITE_IOERR_COMMIT_ATOMIC (SQLITE_IOERR | (30<<8)) #define SQLITE_IOERR_ROLLBACK_ATOMIC (SQLITE_IOERR | (31<<8)) #define SQLITE_LOCKED_SHAREDCACHE (SQLITE_LOCKED | (1<<8)) #define SQLITE_LOCKED_VTAB (SQLITE_LOCKED | (2<<8)) #define SQLITE_BUSY_RECOVERY (SQLITE_BUSY | (1<<8)) #define SQLITE_BUSY_SNAPSHOT (SQLITE_BUSY | (2<<8)) #define SQLITE_CANTOPEN_NOTEMPDIR (SQLITE_CANTOPEN | (1<<8)) #define SQLITE_CANTOPEN_ISDIR (SQLITE_CANTOPEN | (2<<8)) #define SQLITE_CANTOPEN_FULLPATH (SQLITE_CANTOPEN | (3<<8)) #define SQLITE_CANTOPEN_CONVPATH (SQLITE_CANTOPEN | (4<<8)) #define SQLITE_CANTOPEN_DIRTYWAL (SQLITE_CANTOPEN | (5<<8)) /* Not Used */ #define SQLITE_CORRUPT_VTAB (SQLITE_CORRUPT | (1<<8)) #define SQLITE_CORRUPT_SEQUENCE (SQLITE_CORRUPT | (2<<8)) #define SQLITE_READONLY_RECOVERY (SQLITE_READONLY | (1<<8)) #define SQLITE_READONLY_CANTLOCK (SQLITE_READONLY | (2<<8)) #define SQLITE_READONLY_ROLLBACK (SQLITE_READONLY | (3<<8)) #define SQLITE_READONLY_DBMOVED (SQLITE_READONLY | (4<<8)) #define SQLITE_READONLY_CANTINIT (SQLITE_READONLY | (5<<8)) #define SQLITE_READONLY_DIRECTORY (SQLITE_READONLY | (6<<8)) #define SQLITE_ABORT_ROLLBACK (SQLITE_ABORT | (2<<8)) |
︙ | ︙ | |||
880 881 882 883 884 885 886 | ** is not changed but instead the prior value of that setting is written ** into the array entry, allowing the current retry settings to be ** interrogated. The zDbName parameter is ignored. ** ** <li>[[SQLITE_FCNTL_PERSIST_WAL]] ** ^The [SQLITE_FCNTL_PERSIST_WAL] opcode is used to set or query the ** persistent [WAL | Write Ahead Log] setting. By default, the auxiliary | > | | 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 | ** is not changed but instead the prior value of that setting is written ** into the array entry, allowing the current retry settings to be ** interrogated. The zDbName parameter is ignored. ** ** <li>[[SQLITE_FCNTL_PERSIST_WAL]] ** ^The [SQLITE_FCNTL_PERSIST_WAL] opcode is used to set or query the ** persistent [WAL | Write Ahead Log] setting. By default, the auxiliary ** write ahead log ([WAL file]) and shared memory ** files used for transaction control ** are automatically deleted when the latest connection to the database ** closes. Setting persistent WAL mode causes those files to persist after ** close. Persisting the files is useful when other processes that do not ** have write permission on the directory containing the database file want ** to read the database file, as the WAL and shared memory files must exist ** in order for the database to be readable. The fourth parameter to ** [sqlite3_file_control()] for this opcode should be a pointer to an integer. |
︙ | ︙ | |||
1066 1067 1068 1069 1070 1071 1072 1073 1074 1075 1076 1077 1078 1079 | ** a prior successful call to [SQLITE_FCNTL_BEGIN_ATOMIC_WRITE]. ** ** <li>[[SQLITE_FCNTL_LOCK_TIMEOUT]] ** The [SQLITE_FCNTL_LOCK_TIMEOUT] opcode causes attempts to obtain ** a file lock using the xLock or xShmLock methods of the VFS to wait ** for up to M milliseconds before failing, where M is the single ** unsigned integer parameter. ** </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 | > > > > > > > > > > > > > > > > > > > | 1070 1071 1072 1073 1074 1075 1076 1077 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 | ** a prior successful call to [SQLITE_FCNTL_BEGIN_ATOMIC_WRITE]. ** ** <li>[[SQLITE_FCNTL_LOCK_TIMEOUT]] ** The [SQLITE_FCNTL_LOCK_TIMEOUT] opcode causes attempts to obtain ** a file lock using the xLock or xShmLock methods of the VFS to wait ** for up to M milliseconds before failing, where M is the single ** unsigned integer parameter. ** ** <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 |
︙ | ︙ | |||
1101 1102 1103 1104 1105 1106 1107 1108 1109 1110 1111 1112 1113 1114 | #define SQLITE_FCNTL_JOURNAL_POINTER 28 #define SQLITE_FCNTL_WIN32_GET_HANDLE 29 #define SQLITE_FCNTL_PDB 30 #define SQLITE_FCNTL_BEGIN_ATOMIC_WRITE 31 #define SQLITE_FCNTL_COMMIT_ATOMIC_WRITE 32 #define SQLITE_FCNTL_ROLLBACK_ATOMIC_WRITE 33 #define SQLITE_FCNTL_LOCK_TIMEOUT 34 /* deprecated names */ #define SQLITE_GET_LOCKPROXYFILE SQLITE_FCNTL_GET_LOCKPROXYFILE #define SQLITE_SET_LOCKPROXYFILE SQLITE_FCNTL_SET_LOCKPROXYFILE #define SQLITE_LAST_ERRNO SQLITE_FCNTL_LAST_ERRNO | > | 1124 1125 1126 1127 1128 1129 1130 1131 1132 1133 1134 1135 1136 1137 1138 | #define SQLITE_FCNTL_JOURNAL_POINTER 28 #define SQLITE_FCNTL_WIN32_GET_HANDLE 29 #define SQLITE_FCNTL_PDB 30 #define SQLITE_FCNTL_BEGIN_ATOMIC_WRITE 31 #define SQLITE_FCNTL_COMMIT_ATOMIC_WRITE 32 #define SQLITE_FCNTL_ROLLBACK_ATOMIC_WRITE 33 #define SQLITE_FCNTL_LOCK_TIMEOUT 34 #define SQLITE_FCNTL_DATA_VERSION 35 /* deprecated names */ #define SQLITE_GET_LOCKPROXYFILE SQLITE_FCNTL_GET_LOCKPROXYFILE #define SQLITE_SET_LOCKPROXYFILE SQLITE_FCNTL_SET_LOCKPROXYFILE #define SQLITE_LAST_ERRNO SQLITE_FCNTL_LAST_ERRNO |
︙ | ︙ | |||
1926 1927 1928 1929 1930 1931 1932 1933 1934 1935 1936 1937 1938 1939 | ** Or if the threshold is -1, statement journals are always held ** exclusively in memory. ** Since many statement journals never become large, setting the spill ** threshold to a value such as 64KiB can greatly reduce the amount of ** I/O required to support statement rollback. ** The default value for this setting is controlled by the ** [SQLITE_STMTJRNL_SPILL] compile-time option. ** </dl> */ #define SQLITE_CONFIG_SINGLETHREAD 1 /* nil */ #define SQLITE_CONFIG_MULTITHREAD 2 /* nil */ #define SQLITE_CONFIG_SERIALIZED 3 /* nil */ #define SQLITE_CONFIG_MALLOC 4 /* sqlite3_mem_methods* */ #define SQLITE_CONFIG_GETMALLOC 5 /* sqlite3_mem_methods* */ | > > > > > > > > > > > > > > > > | 1950 1951 1952 1953 1954 1955 1956 1957 1958 1959 1960 1961 1962 1963 1964 1965 1966 1967 1968 1969 1970 1971 1972 1973 1974 1975 1976 1977 1978 1979 | ** Or if the threshold is -1, statement journals are always held ** exclusively in memory. ** Since many statement journals never become large, setting the spill ** threshold to a value such as 64KiB can greatly reduce the amount of ** I/O required to support statement rollback. ** The default value for this setting is controlled by the ** [SQLITE_STMTJRNL_SPILL] compile-time option. ** ** [[SQLITE_CONFIG_SORTERREF_SIZE]] ** <dt>SQLITE_CONFIG_SORTERREF_SIZE ** <dd>The SQLITE_CONFIG_SORTERREF_SIZE option accepts a single parameter ** of type (int) - the new value of the sorter-reference size threshold. ** Usually, when SQLite uses an external sort to order records according ** to an ORDER BY clause, all fields required by the caller are present in the ** sorted records. However, if SQLite determines based on the declared type ** of a table column that its values are likely to be very large - larger ** than the configured sorter-reference size threshold - then a reference ** is stored in each sorted record and the required column values loaded ** from the database as records are returned in sorted order. The default ** value for this option is to never use this optimization. Specifying a ** negative value for this option restores the default behaviour. ** This option is only available if SQLite is compiled with the ** [SQLITE_ENABLE_SORTER_REFERENCES] compile-time option. ** </dl> */ #define SQLITE_CONFIG_SINGLETHREAD 1 /* nil */ #define SQLITE_CONFIG_MULTITHREAD 2 /* nil */ #define SQLITE_CONFIG_SERIALIZED 3 /* nil */ #define SQLITE_CONFIG_MALLOC 4 /* sqlite3_mem_methods* */ #define SQLITE_CONFIG_GETMALLOC 5 /* sqlite3_mem_methods* */ |
︙ | ︙ | |||
1955 1956 1957 1958 1959 1960 1961 1962 1963 1964 1965 1966 1967 1968 | #define SQLITE_CONFIG_SQLLOG 21 /* xSqllog, void* */ #define SQLITE_CONFIG_MMAP_SIZE 22 /* sqlite3_int64, sqlite3_int64 */ #define SQLITE_CONFIG_WIN32_HEAPSIZE 23 /* int nByte */ #define SQLITE_CONFIG_PCACHE_HDRSZ 24 /* int *psz */ #define SQLITE_CONFIG_PMASZ 25 /* unsigned int szPma */ #define SQLITE_CONFIG_STMTJRNL_SPILL 26 /* int nByte */ #define SQLITE_CONFIG_SMALL_MALLOC 27 /* boolean */ /* ** CAPI3REF: Database Connection Configuration Options ** ** These constants are the available integer configuration options that ** can be passed as the second argument to the [sqlite3_db_config()] interface. ** | > | 1995 1996 1997 1998 1999 2000 2001 2002 2003 2004 2005 2006 2007 2008 2009 | #define SQLITE_CONFIG_SQLLOG 21 /* xSqllog, void* */ #define SQLITE_CONFIG_MMAP_SIZE 22 /* sqlite3_int64, sqlite3_int64 */ #define SQLITE_CONFIG_WIN32_HEAPSIZE 23 /* int nByte */ #define SQLITE_CONFIG_PCACHE_HDRSZ 24 /* int *psz */ #define SQLITE_CONFIG_PMASZ 25 /* unsigned int szPma */ #define SQLITE_CONFIG_STMTJRNL_SPILL 26 /* int nByte */ #define SQLITE_CONFIG_SMALL_MALLOC 27 /* boolean */ #define SQLITE_CONFIG_SORTERREF_SIZE 28 /* int nByte */ /* ** CAPI3REF: Database Connection Configuration Options ** ** These constants are the available integer configuration options that ** can be passed as the second argument to the [sqlite3_db_config()] interface. ** |
︙ | ︙ | |||
2091 2092 2093 2094 2095 2096 2097 2098 2099 2100 2101 2102 2103 2104 2105 2106 2107 2108 | ** behavior. The first parameter passed to this operation is an integer - ** positive to enable output for trigger programs, or zero to disable it, ** or negative to leave the setting unchanged. ** The second parameter is a pointer to an integer into which is written ** 0 or 1 to indicate whether output-for-triggers has been disabled - 0 if ** it is not disabled, 1 if it is. ** </dd> ** </dl> */ #define SQLITE_DBCONFIG_MAINDBNAME 1000 /* const char* */ #define SQLITE_DBCONFIG_LOOKASIDE 1001 /* void* int int */ #define SQLITE_DBCONFIG_ENABLE_FKEY 1002 /* int int* */ #define SQLITE_DBCONFIG_ENABLE_TRIGGER 1003 /* int int* */ #define SQLITE_DBCONFIG_ENABLE_FTS3_TOKENIZER 1004 /* int int* */ #define SQLITE_DBCONFIG_ENABLE_LOAD_EXTENSION 1005 /* int int* */ #define SQLITE_DBCONFIG_NO_CKPT_ON_CLOSE 1006 /* int int* */ #define SQLITE_DBCONFIG_ENABLE_QPSG 1007 /* int int* */ #define SQLITE_DBCONFIG_TRIGGER_EQP 1008 /* int int* */ | > > > > > > > > > > > > > > > > > > > > > > | | 2132 2133 2134 2135 2136 2137 2138 2139 2140 2141 2142 2143 2144 2145 2146 2147 2148 2149 2150 2151 2152 2153 2154 2155 2156 2157 2158 2159 2160 2161 2162 2163 2164 2165 2166 2167 2168 2169 2170 2171 2172 2173 2174 2175 2176 2177 2178 2179 | ** behavior. The first parameter passed to this operation is an integer - ** positive to enable output for trigger programs, or zero to disable it, ** or negative to leave the setting unchanged. ** The second parameter is a pointer to an integer into which is written ** 0 or 1 to indicate whether output-for-triggers has been disabled - 0 if ** it is not disabled, 1 if it is. ** </dd> ** ** <dt>SQLITE_DBCONFIG_RESET_DATABASE</dt> ** <dd> Set the SQLITE_DBCONFIG_RESET_DATABASE flag and then run ** [VACUUM] in order to reset a database back to an empty database ** with no schema and no content. The following process works even for ** a badly corrupted database file: ** <ol> ** <li> If the database connection is newly opened, make sure it has read the ** database schema by preparing then discarding some query against the ** database, or calling sqlite3_table_column_metadata(), ignoring any ** errors. This step is only necessary if the application desires to keep ** the database in WAL mode after the reset if it was in WAL mode before ** the reset. ** <li> sqlite3_db_config(db, SQLITE_DBCONFIG_RESET_DATABASE, 1, 0); ** <li> [sqlite3_exec](db, "[VACUUM]", 0, 0, 0); ** <li> sqlite3_db_config(db, SQLITE_DBCONFIG_RESET_DATABASE, 0, 0); ** </ol> ** Because resetting a database is destructive and irreversible, the ** process requires the use of this obscure API and multiple steps to help ** ensure that it does not happen by accident. ** </dd> ** </dl> */ #define SQLITE_DBCONFIG_MAINDBNAME 1000 /* const char* */ #define SQLITE_DBCONFIG_LOOKASIDE 1001 /* void* int int */ #define SQLITE_DBCONFIG_ENABLE_FKEY 1002 /* int int* */ #define SQLITE_DBCONFIG_ENABLE_TRIGGER 1003 /* int int* */ #define SQLITE_DBCONFIG_ENABLE_FTS3_TOKENIZER 1004 /* int int* */ #define SQLITE_DBCONFIG_ENABLE_LOAD_EXTENSION 1005 /* int int* */ #define SQLITE_DBCONFIG_NO_CKPT_ON_CLOSE 1006 /* int int* */ #define SQLITE_DBCONFIG_ENABLE_QPSG 1007 /* int int* */ #define SQLITE_DBCONFIG_TRIGGER_EQP 1008 /* int int* */ #define SQLITE_DBCONFIG_RESET_DATABASE 1009 /* int int* */ #define SQLITE_DBCONFIG_MAX 1009 /* Largest DBCONFIG */ /* ** CAPI3REF: Enable Or Disable Extended Result Codes ** METHOD: sqlite3 ** ** ^The sqlite3_extended_result_codes() routine enables or disables the ** [extended result codes] feature of SQLite. ^The extended result |
︙ | ︙ | |||
2230 2231 2232 2233 2234 2235 2236 | ** ^This means that if the changes() SQL function (or similar) is used ** by the first INSERT, UPDATE or DELETE statement within a trigger, it ** returns the value as set when the calling statement began executing. ** ^If it is used by the second or subsequent such statement within a trigger ** program, the value returned reflects the number of rows modified by the ** previous INSERT, UPDATE or DELETE statement within the same trigger. ** | < < < > > > > > > > > | | > > | > > | > > > > > > > > > | 2293 2294 2295 2296 2297 2298 2299 2300 2301 2302 2303 2304 2305 2306 2307 2308 2309 2310 2311 2312 2313 2314 2315 2316 2317 2318 2319 2320 2321 2322 2323 2324 2325 2326 2327 2328 2329 2330 2331 2332 2333 2334 2335 2336 2337 2338 2339 2340 2341 2342 2343 2344 2345 2346 2347 2348 2349 2350 2351 2352 2353 2354 | ** ^This means that if the changes() SQL function (or similar) is used ** by the first INSERT, UPDATE or DELETE statement within a trigger, it ** returns the value as set when the calling statement began executing. ** ^If it is used by the second or subsequent such statement within a trigger ** program, the value returned reflects the number of rows modified by the ** previous INSERT, UPDATE or DELETE statement within the same trigger. ** ** If a separate thread makes changes on the same database connection ** while [sqlite3_changes()] is running then the value returned ** is unpredictable and not meaningful. ** ** See also: ** <ul> ** <li> the [sqlite3_total_changes()] interface ** <li> the [count_changes pragma] ** <li> the [changes() SQL function] ** <li> the [data_version pragma] ** </ul> */ SQLITE_API int sqlite3_changes(sqlite3*); /* ** CAPI3REF: Total Number Of Rows Modified ** METHOD: sqlite3 ** ** ^This function returns the total number of rows inserted, modified or ** deleted by all [INSERT], [UPDATE] or [DELETE] statements completed ** since the database connection was opened, including those executed as ** part of trigger programs. ^Executing any other type of SQL statement ** does not affect the value returned by sqlite3_total_changes(). ** ** ^Changes made as part of [foreign key actions] are included in the ** count, but those made as part of REPLACE constraint resolution are ** not. ^Changes to a view that are intercepted by INSTEAD OF triggers ** are not counted. ** ** This the [sqlite3_total_changes(D)] interface only reports the number ** of rows that changed due to SQL statement run against database ** connection D. Any changes by other database connections are ignored. ** To detect changes against a database file from other database ** connections use the [PRAGMA data_version] command or the ** [SQLITE_FCNTL_DATA_VERSION] [file control]. ** ** If a separate thread makes changes on the same database connection ** while [sqlite3_total_changes()] is running then the value ** returned is unpredictable and not meaningful. ** ** See also: ** <ul> ** <li> the [sqlite3_changes()] interface ** <li> the [count_changes pragma] ** <li> the [changes() SQL function] ** <li> the [data_version pragma] ** <li> the [SQLITE_FCNTL_DATA_VERSION] [file control] ** </ul> */ SQLITE_API int sqlite3_total_changes(sqlite3*); /* ** CAPI3REF: Interrupt A Long-Running Query ** METHOD: sqlite3 ** |
︙ | ︙ | |||
3315 3316 3317 3318 3319 3320 3321 | ** CAPI3REF: Error Codes And Messages ** METHOD: sqlite3 ** ** ^If the most recent sqlite3_* API call associated with ** [database connection] D failed, then the sqlite3_errcode(D) interface ** returns the numeric [result code] or [extended result code] for that ** API call. | < < > > > > > > > > > > > > > | 3396 3397 3398 3399 3400 3401 3402 3403 3404 3405 3406 3407 3408 3409 3410 3411 3412 3413 3414 3415 3416 3417 3418 3419 3420 3421 3422 3423 3424 3425 3426 | ** CAPI3REF: Error Codes And Messages ** METHOD: sqlite3 ** ** ^If the most recent sqlite3_* API call associated with ** [database connection] D failed, then the sqlite3_errcode(D) interface ** returns the numeric [result code] or [extended result code] for that ** API call. ** ^The sqlite3_extended_errcode() ** interface is the same except that it always returns the ** [extended result code] even when extended result codes are ** disabled. ** ** The values returned by sqlite3_errcode() and/or ** sqlite3_extended_errcode() might change with each API call. ** Except, there are some interfaces that are guaranteed to never ** change the value of the error code. The error-code preserving ** interfaces are: ** ** <ul> ** <li> sqlite3_errcode() ** <li> sqlite3_extended_errcode() ** <li> sqlite3_errmsg() ** <li> sqlite3_errmsg16() ** </ul> ** ** ^The sqlite3_errmsg() and sqlite3_errmsg16() return English-language ** text that describes the error, as either UTF-8 or UTF-16 respectively. ** ^(Memory to hold the error message string is managed internally. ** The application does not need to worry about freeing the result. ** However, the error string might be overwritten or deallocated by ** subsequent calls to other SQLite interface functions.)^ |
︙ | ︙ | |||
4475 4476 4477 4478 4479 4480 4481 | ** ^The pointers returned are valid until a type conversion occurs as ** described above, or until [sqlite3_step()] or [sqlite3_reset()] or ** [sqlite3_finalize()] is called. ^The memory space used to hold strings ** and BLOBs is freed automatically. Do not pass the pointers returned ** from [sqlite3_column_blob()], [sqlite3_column_text()], etc. into ** [sqlite3_free()]. ** | > | > > > > > > > > > > > > | < > | | > | 4567 4568 4569 4570 4571 4572 4573 4574 4575 4576 4577 4578 4579 4580 4581 4582 4583 4584 4585 4586 4587 4588 4589 4590 4591 4592 4593 4594 4595 4596 4597 4598 4599 | ** ^The pointers returned are valid until a type conversion occurs as ** described above, or until [sqlite3_step()] or [sqlite3_reset()] or ** [sqlite3_finalize()] is called. ^The memory space used to hold strings ** and BLOBs is freed automatically. Do not pass the pointers returned ** from [sqlite3_column_blob()], [sqlite3_column_text()], etc. into ** [sqlite3_free()]. ** ** As long as the input parameters are correct, these routines will only ** fail if an out-of-memory error occurs during a format conversion. ** Only the following subset of interfaces are subject to out-of-memory ** errors: ** ** <ul> ** <li> sqlite3_column_blob() ** <li> sqlite3_column_text() ** <li> sqlite3_column_text16() ** <li> sqlite3_column_bytes() ** <li> sqlite3_column_bytes16() ** </ul> ** ** If an out-of-memory error occurs, then the return value from these ** routines is the same as if the column had contained an SQL NULL value. ** Valid SQL NULL returns can be distinguished from out-of-memory errors ** by invoking the [sqlite3_errcode()] immediately after the suspect ** return value is obtained and before any ** other SQLite interface is called on the same [database connection]. */ SQLITE_API const void *sqlite3_column_blob(sqlite3_stmt*, int iCol); SQLITE_API double sqlite3_column_double(sqlite3_stmt*, int iCol); SQLITE_API int sqlite3_column_int(sqlite3_stmt*, int iCol); SQLITE_API sqlite3_int64 sqlite3_column_int64(sqlite3_stmt*, int iCol); SQLITE_API const unsigned char *sqlite3_column_text(sqlite3_stmt*, int iCol); SQLITE_API const void *sqlite3_column_text16(sqlite3_stmt*, int iCol); |
︙ | ︙ | |||
4556 4557 4558 4559 4560 4561 4562 | ** KEYWORDS: {function creation routines} ** KEYWORDS: {application-defined SQL function} ** KEYWORDS: {application-defined SQL functions} ** METHOD: sqlite3 ** ** ^These functions (collectively known as "function creation routines") ** are used to add SQL functions or aggregates or to redefine the behavior | | | | | | > > | 4662 4663 4664 4665 4666 4667 4668 4669 4670 4671 4672 4673 4674 4675 4676 4677 4678 4679 4680 4681 4682 | ** KEYWORDS: {function creation routines} ** KEYWORDS: {application-defined SQL function} ** KEYWORDS: {application-defined SQL functions} ** METHOD: sqlite3 ** ** ^These functions (collectively known as "function creation routines") ** are used to add SQL functions or aggregates or to redefine the behavior ** of existing SQL functions or aggregates. The only differences between ** the three "sqlite3_create_function*" routines are the text encoding ** expected for the second parameter (the name of the function being ** created) and the presence or absence of a destructor callback for ** the application data pointer. Function sqlite3_create_window_function() ** is similar, but allows the user to supply the extra callback functions ** needed by [aggregate window functions]. ** ** ^The first parameter is the [database connection] to which the SQL ** function is to be added. ^If an application uses more than one database ** connection then application-defined SQL functions must be added ** to each database connection separately. ** ** ^The second parameter is the name of the SQL function to be created or |
︙ | ︙ | |||
4606 4607 4608 4609 4610 4611 4612 | ** function that is not deterministic. The SQLite query planner is able to ** perform additional optimizations on deterministic functions, so use ** of the [SQLITE_DETERMINISTIC] flag is recommended where possible. ** ** ^(The fifth parameter is an arbitrary pointer. The implementation of the ** function can gain access to this pointer using [sqlite3_user_data()].)^ ** | | > > > > > > > > > > > | | | | | | < | | | 4714 4715 4716 4717 4718 4719 4720 4721 4722 4723 4724 4725 4726 4727 4728 4729 4730 4731 4732 4733 4734 4735 4736 4737 4738 4739 4740 4741 4742 4743 4744 4745 4746 4747 4748 4749 4750 4751 4752 4753 4754 4755 | ** function that is not deterministic. The SQLite query planner is able to ** perform additional optimizations on deterministic functions, so use ** of the [SQLITE_DETERMINISTIC] flag is recommended where possible. ** ** ^(The fifth parameter is an arbitrary pointer. The implementation of the ** function can gain access to this pointer using [sqlite3_user_data()].)^ ** ** ^The sixth, seventh and eighth parameters passed to the three ** "sqlite3_create_function*" functions, xFunc, xStep and xFinal, are ** pointers to C-language functions that implement the SQL function or ** aggregate. ^A scalar SQL function requires an implementation of the xFunc ** callback only; NULL pointers must be passed as the xStep and xFinal ** 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]. ** ** ^(If the final parameter to sqlite3_create_function_v2() or ** sqlite3_create_window_function() is not NULL, then it is destructor for ** the application data pointer. The destructor is invoked when the function ** is deleted, either by being overloaded or when the database connection ** closes.)^ ^The destructor is also invoked if the call to ** sqlite3_create_function_v2() fails. ^When the destructor callback is ** invoked, it is passed a single argument which is a copy of the application ** data pointer which was the fifth parameter to sqlite3_create_function_v2(). ** ** ^It is permitted to register multiple implementations of the same ** functions with the same name but with either differing numbers of ** arguments or differing preferred text encodings. ^SQLite will use ** the implementation that most closely matches the way in which the ** SQL function is used. ^A function implementation with a non-negative ** nArg parameter is a better match than a function implementation with |
︙ | ︙ | |||
4675 4676 4677 4678 4679 4680 4681 4682 4683 4684 4685 4686 4687 4688 | int nArg, int eTextRep, void *pApp, void (*xFunc)(sqlite3_context*,int,sqlite3_value**), void (*xStep)(sqlite3_context*,int,sqlite3_value**), void (*xFinal)(sqlite3_context*), void(*xDestroy)(void*) ); /* ** CAPI3REF: Text Encodings ** ** These constant define integer codes that represent the various ** text encodings supported by SQLite. | > > > > > > > > > > > > | 4793 4794 4795 4796 4797 4798 4799 4800 4801 4802 4803 4804 4805 4806 4807 4808 4809 4810 4811 4812 4813 4814 4815 4816 4817 4818 | int nArg, int eTextRep, void *pApp, void (*xFunc)(sqlite3_context*,int,sqlite3_value**), void (*xStep)(sqlite3_context*,int,sqlite3_value**), void (*xFinal)(sqlite3_context*), void(*xDestroy)(void*) ); SQLITE_API int sqlite3_create_window_function( sqlite3 *db, const char *zFunctionName, int nArg, int eTextRep, void *pApp, void (*xStep)(sqlite3_context*,int,sqlite3_value**), void (*xFinal)(sqlite3_context*), void (*xValue)(sqlite3_context*), void (*xInverse)(sqlite3_context*,int,sqlite3_value**), void(*xDestroy)(void*) ); /* ** CAPI3REF: Text Encodings ** ** These constant define integer codes that represent the various ** text encodings supported by SQLite. |
︙ | ︙ | |||
4818 4819 4820 4821 4822 4823 4824 4825 4826 4827 4828 4829 4830 4831 | ** from [sqlite3_value_blob()], [sqlite3_value_text()], or ** [sqlite3_value_text16()] can be invalidated by a subsequent call to ** [sqlite3_value_bytes()], [sqlite3_value_bytes16()], [sqlite3_value_text()], ** or [sqlite3_value_text16()]. ** ** These routines must be called from the same thread as ** the SQL function that supplied the [sqlite3_value*] parameters. */ SQLITE_API const void *sqlite3_value_blob(sqlite3_value*); SQLITE_API double sqlite3_value_double(sqlite3_value*); SQLITE_API int sqlite3_value_int(sqlite3_value*); SQLITE_API sqlite3_int64 sqlite3_value_int64(sqlite3_value*); SQLITE_API void *sqlite3_value_pointer(sqlite3_value*, const char*); SQLITE_API const unsigned char *sqlite3_value_text(sqlite3_value*); | > > > > > > > > > > > > > > > > > > > > > > | 4948 4949 4950 4951 4952 4953 4954 4955 4956 4957 4958 4959 4960 4961 4962 4963 4964 4965 4966 4967 4968 4969 4970 4971 4972 4973 4974 4975 4976 4977 4978 4979 4980 4981 4982 4983 | ** from [sqlite3_value_blob()], [sqlite3_value_text()], or ** [sqlite3_value_text16()] can be invalidated by a subsequent call to ** [sqlite3_value_bytes()], [sqlite3_value_bytes16()], [sqlite3_value_text()], ** or [sqlite3_value_text16()]. ** ** These routines must be called from the same thread as ** the SQL function that supplied the [sqlite3_value*] parameters. ** ** As long as the input parameter is correct, these routines can only ** fail if an out-of-memory error occurs during a format conversion. ** Only the following subset of interfaces are subject to out-of-memory ** errors: ** ** <ul> ** <li> sqlite3_value_blob() ** <li> sqlite3_value_text() ** <li> sqlite3_value_text16() ** <li> sqlite3_value_text16le() ** <li> sqlite3_value_text16be() ** <li> sqlite3_value_bytes() ** <li> sqlite3_value_bytes16() ** </ul> ** ** If an out-of-memory error occurs, then the return value from these ** routines is the same as if the column had contained an SQL NULL value. ** Valid SQL NULL returns can be distinguished from out-of-memory errors ** by invoking the [sqlite3_errcode()] immediately after the suspect ** return value is obtained and before any ** other SQLite interface is called on the same [database connection]. */ SQLITE_API const void *sqlite3_value_blob(sqlite3_value*); SQLITE_API double sqlite3_value_double(sqlite3_value*); SQLITE_API int sqlite3_value_int(sqlite3_value*); SQLITE_API sqlite3_int64 sqlite3_value_int64(sqlite3_value*); SQLITE_API void *sqlite3_value_pointer(sqlite3_value*, const char*); SQLITE_API const unsigned char *sqlite3_value_text(sqlite3_value*); |
︙ | ︙ | |||
5488 5489 5490 5491 5492 5493 5494 5495 5496 5497 5498 5499 5500 5501 | ** using [sqlite3_free]. ** Hence, if this variable is modified directly, either it should be ** made NULL or made to point to memory obtained from [sqlite3_malloc] ** or else the use of the [data_store_directory pragma] should be avoided. */ SQLITE_API SQLITE_EXTERN char *sqlite3_data_directory; /* ** CAPI3REF: Test For Auto-Commit Mode ** KEYWORDS: {autocommit mode} ** METHOD: sqlite3 ** ** ^The sqlite3_get_autocommit() interface returns non-zero or ** zero if the given database connection is or is not in autocommit mode, | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 5640 5641 5642 5643 5644 5645 5646 5647 5648 5649 5650 5651 5652 5653 5654 5655 5656 5657 5658 5659 5660 5661 5662 5663 5664 5665 5666 5667 5668 5669 5670 5671 5672 5673 5674 5675 5676 5677 5678 5679 5680 5681 5682 5683 5684 5685 5686 5687 5688 | ** using [sqlite3_free]. ** Hence, if this variable is modified directly, either it should be ** made NULL or made to point to memory obtained from [sqlite3_malloc] ** or else the use of the [data_store_directory pragma] should be avoided. */ SQLITE_API SQLITE_EXTERN char *sqlite3_data_directory; /* ** CAPI3REF: Win32 Specific Interface ** ** These interfaces are available only on Windows. The ** [sqlite3_win32_set_directory] interface is used to set the value associated ** with the [sqlite3_temp_directory] or [sqlite3_data_directory] variable, to ** zValue, depending on the value of the type parameter. The zValue parameter ** should be NULL to cause the previous value to be freed via [sqlite3_free]; ** a non-NULL value will be copied into memory obtained from [sqlite3_malloc] ** prior to being used. The [sqlite3_win32_set_directory] interface returns ** [SQLITE_OK] to indicate success, [SQLITE_ERROR] if the type is unsupported, ** or [SQLITE_NOMEM] if memory could not be allocated. The value of the ** [sqlite3_data_directory] variable is intended to act as a replacement for ** the current directory on the sub-platforms of Win32 where that concept is ** not present, e.g. WinRT and UWP. The [sqlite3_win32_set_directory8] and ** [sqlite3_win32_set_directory16] interfaces behave exactly the same as the ** sqlite3_win32_set_directory interface except the string parameter must be ** UTF-8 or UTF-16, respectively. */ SQLITE_API int sqlite3_win32_set_directory( unsigned long type, /* Identifier for directory being set or reset */ void *zValue /* New value for directory being set or reset */ ); SQLITE_API int sqlite3_win32_set_directory8(unsigned long type, const char *zValue); SQLITE_API int sqlite3_win32_set_directory16(unsigned long type, const void *zValue); /* ** CAPI3REF: Win32 Directory Types ** ** These macros are only available on Windows. They define the allowed values ** for the type argument to the [sqlite3_win32_set_directory] interface. */ #define SQLITE_WIN32_DATA_DIRECTORY_TYPE 1 #define SQLITE_WIN32_TEMP_DIRECTORY_TYPE 2 /* ** CAPI3REF: Test For Auto-Commit Mode ** KEYWORDS: {autocommit mode} ** METHOD: sqlite3 ** ** ^The sqlite3_get_autocommit() interface returns non-zero or ** zero if the given database connection is or is not in autocommit mode, |
︙ | ︙ | |||
6220 6221 6222 6223 6224 6225 6226 6227 6228 6229 6230 6231 6232 6233 | int idxFlags; /* Mask of SQLITE_INDEX_SCAN_* flags */ /* Fields below are only available in SQLite 3.10.0 and later */ sqlite3_uint64 colUsed; /* Input: Mask of columns used by statement */ }; /* ** CAPI3REF: Virtual Table Scan Flags */ #define SQLITE_INDEX_SCAN_UNIQUE 1 /* Scan visits at most 1 row */ /* ** CAPI3REF: Virtual Table Constraint Operator Codes ** ** These macros defined the allowed values for the | > > > > | 6407 6408 6409 6410 6411 6412 6413 6414 6415 6416 6417 6418 6419 6420 6421 6422 6423 6424 | int idxFlags; /* Mask of SQLITE_INDEX_SCAN_* flags */ /* Fields below are only available in SQLite 3.10.0 and later */ sqlite3_uint64 colUsed; /* Input: Mask of columns used by statement */ }; /* ** CAPI3REF: Virtual Table Scan Flags ** ** Virtual table implementations are allowed to set the ** [sqlite3_index_info].idxFlags field to some combination of ** these bits. */ #define SQLITE_INDEX_SCAN_UNIQUE 1 /* Scan visits at most 1 row */ /* ** CAPI3REF: Virtual Table Constraint Operator Codes ** ** These macros defined the allowed values for the |
︙ | ︙ | |||
6921 6922 6923 6924 6925 6926 6927 6928 6929 6930 6931 6932 6933 6934 6935 6936 6937 6938 6939 6940 6941 6942 6943 | ** routine returns a NULL pointer. */ SQLITE_API sqlite3_mutex *sqlite3_db_mutex(sqlite3*); /* ** CAPI3REF: Low-Level Control Of Database Files ** METHOD: sqlite3 ** ** ^The [sqlite3_file_control()] interface makes a direct call to the ** xFileControl method for the [sqlite3_io_methods] object associated ** with a particular database identified by the second argument. ^The ** name of the database is "main" for the main database or "temp" for the ** TEMP database, or the name that appears after the AS keyword for ** databases that are added using the [ATTACH] SQL command. ** ^A NULL pointer can be used in place of "main" to refer to the ** main database file. ** ^The third and fourth parameters to this routine ** are passed directly through to the second and third parameters of ** the xFileControl method. ^The return value of the xFileControl ** method becomes the return value of this routine. ** ** ^The [SQLITE_FCNTL_FILE_POINTER] value for the op parameter causes ** a pointer to the underlying [sqlite3_file] object to be written into | > > > > | < > > > | > > | 7112 7113 7114 7115 7116 7117 7118 7119 7120 7121 7122 7123 7124 7125 7126 7127 7128 7129 7130 7131 7132 7133 7134 7135 7136 7137 7138 7139 7140 7141 7142 7143 7144 7145 7146 7147 7148 7149 7150 7151 7152 | ** routine returns a NULL pointer. */ SQLITE_API sqlite3_mutex *sqlite3_db_mutex(sqlite3*); /* ** CAPI3REF: Low-Level Control Of Database Files ** METHOD: sqlite3 ** KEYWORDS: {file control} ** ** ^The [sqlite3_file_control()] interface makes a direct call to the ** xFileControl method for the [sqlite3_io_methods] object associated ** with a particular database identified by the second argument. ^The ** name of the database is "main" for the main database or "temp" for the ** TEMP database, or the name that appears after the AS keyword for ** databases that are added using the [ATTACH] SQL command. ** ^A NULL pointer can be used in place of "main" to refer to the ** main database file. ** ^The third and fourth parameters to this routine ** are passed directly through to the second and third parameters of ** the xFileControl method. ^The return value of the xFileControl ** method becomes the return value of this routine. ** ** A few opcodes for [sqlite3_file_control()] are handled directly ** by the SQLite core and never invoke the ** sqlite3_io_methods.xFileControl method. ** ^The [SQLITE_FCNTL_FILE_POINTER] value for the op parameter causes ** a pointer to the underlying [sqlite3_file] object to be written into ** the space pointed to by the 4th parameter. The ** [SQLITE_FCNTL_JOURNAL_POINTER] works similarly except that it returns ** the [sqlite3_file] object associated with the journal file instead of ** the main database. The [SQLITE_FCNTL_VFS_POINTER] opcode returns ** a pointer to the underlying [sqlite3_vfs] object for the file. ** The [SQLITE_FCNTL_DATA_VERSION] returns the data version counter ** from the pager. ** ** ^If the second parameter (zDbName) does not match the name of any ** open database file, then SQLITE_ERROR is returned. ^This error ** code is not remembered and will not be recalled by [sqlite3_errcode()] ** or [sqlite3_errmsg()]. The underlying xFileControl method might ** also return SQLITE_ERROR. There is no way to distinguish between ** an incorrect zDbName and an SQLITE_ERROR return from the underlying |
︙ | ︙ | |||
6995 6996 6997 6998 6999 7000 7001 | #define SQLITE_TESTCTRL_FAULT_INSTALL 9 #define SQLITE_TESTCTRL_BENIGN_MALLOC_HOOKS 10 #define SQLITE_TESTCTRL_PENDING_BYTE 11 #define SQLITE_TESTCTRL_ASSERT 12 #define SQLITE_TESTCTRL_ALWAYS 13 #define SQLITE_TESTCTRL_RESERVE 14 #define SQLITE_TESTCTRL_OPTIMIZATIONS 15 | | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 7194 7195 7196 7197 7198 7199 7200 7201 7202 7203 7204 7205 7206 7207 7208 7209 7210 7211 7212 7213 7214 7215 7216 7217 7218 7219 7220 7221 7222 7223 7224 7225 7226 7227 7228 7229 7230 7231 7232 7233 7234 7235 7236 7237 7238 7239 7240 7241 7242 7243 7244 7245 7246 7247 7248 7249 7250 7251 7252 7253 7254 7255 7256 7257 7258 7259 7260 7261 7262 7263 7264 7265 7266 7267 7268 7269 7270 7271 7272 7273 7274 7275 7276 7277 7278 7279 7280 7281 7282 7283 7284 7285 7286 7287 7288 7289 7290 7291 7292 7293 7294 7295 7296 7297 7298 7299 7300 7301 7302 7303 7304 7305 7306 7307 7308 7309 7310 7311 7312 7313 7314 7315 7316 7317 7318 7319 7320 7321 7322 7323 7324 7325 7326 7327 7328 7329 7330 7331 7332 7333 7334 7335 7336 7337 7338 7339 7340 7341 7342 7343 7344 7345 7346 7347 7348 7349 7350 7351 7352 7353 7354 7355 7356 7357 7358 7359 7360 7361 7362 7363 7364 7365 7366 7367 7368 7369 7370 7371 7372 7373 7374 7375 7376 7377 7378 7379 7380 7381 7382 7383 7384 7385 7386 7387 7388 7389 7390 7391 7392 7393 7394 7395 7396 7397 7398 7399 7400 7401 7402 7403 7404 | #define SQLITE_TESTCTRL_FAULT_INSTALL 9 #define SQLITE_TESTCTRL_BENIGN_MALLOC_HOOKS 10 #define SQLITE_TESTCTRL_PENDING_BYTE 11 #define SQLITE_TESTCTRL_ASSERT 12 #define SQLITE_TESTCTRL_ALWAYS 13 #define SQLITE_TESTCTRL_RESERVE 14 #define SQLITE_TESTCTRL_OPTIMIZATIONS 15 #define SQLITE_TESTCTRL_ISKEYWORD 16 /* NOT USED */ #define SQLITE_TESTCTRL_SCRATCHMALLOC 17 /* NOT USED */ #define SQLITE_TESTCTRL_LOCALTIME_FAULT 18 #define SQLITE_TESTCTRL_EXPLAIN_STMT 19 /* NOT USED */ #define SQLITE_TESTCTRL_ONCE_RESET_THRESHOLD 19 #define SQLITE_TESTCTRL_NEVER_CORRUPT 20 #define SQLITE_TESTCTRL_VDBE_COVERAGE 21 #define SQLITE_TESTCTRL_BYTEORDER 22 #define SQLITE_TESTCTRL_ISINIT 23 #define SQLITE_TESTCTRL_SORTER_MMAP 24 #define SQLITE_TESTCTRL_IMPOSTER 25 #define SQLITE_TESTCTRL_PARSER_COVERAGE 26 #define SQLITE_TESTCTRL_LAST 26 /* Largest TESTCTRL */ /* ** CAPI3REF: SQL Keyword Checking ** ** These routines provide access to the set of SQL language keywords ** recognized by SQLite. Applications can uses these routines to determine ** whether or not a specific identifier needs to be escaped (for example, ** by enclosing in double-quotes) so as not to confuse the parser. ** ** The sqlite3_keyword_count() interface returns the number of distinct ** keywords understood by SQLite. ** ** The sqlite3_keyword_name(N,Z,L) interface finds the N-th keyword and ** makes *Z point to that keyword expressed as UTF8 and writes the number ** of bytes in the keyword into *L. The string that *Z points to is not ** zero-terminated. The sqlite3_keyword_name(N,Z,L) routine returns ** SQLITE_OK if N is within bounds and SQLITE_ERROR if not. If either Z ** or L are NULL or invalid pointers then calls to ** sqlite3_keyword_name(N,Z,L) result in undefined behavior. ** ** The sqlite3_keyword_check(Z,L) interface checks to see whether or not ** the L-byte UTF8 identifier that Z points to is a keyword, returning non-zero ** if it is and zero if not. ** ** The parser used by SQLite is forgiving. It is often possible to use ** a keyword as an identifier as long as such use does not result in a ** parsing ambiguity. For example, the statement ** "CREATE TABLE BEGIN(REPLACE,PRAGMA,END);" is accepted by SQLite, and ** creates a new table named "BEGIN" with three columns named ** "REPLACE", "PRAGMA", and "END". Nevertheless, best practice is to avoid ** using keywords as identifiers. Common techniques used to avoid keyword ** name collisions include: ** <ul> ** <li> Put all identifier names inside double-quotes. This is the official ** SQL way to escape identifier names. ** <li> Put identifier names inside [...]. This is not standard SQL, ** but it is what SQL Server does and so lots of programmers use this ** technique. ** <li> Begin every identifier with the letter "Z" as no SQL keywords start ** with "Z". ** <li> Include a digit somewhere in every identifier name. ** </ul> ** ** Note that the number of keywords understood by SQLite can depend on ** compile-time options. For example, "VACUUM" is not a keyword if ** SQLite is compiled with the [-DSQLITE_OMIT_VACUUM] option. Also, ** new keywords may be added to future releases of SQLite. */ SQLITE_API int sqlite3_keyword_count(void); SQLITE_API int sqlite3_keyword_name(int,const char**,int*); SQLITE_API int sqlite3_keyword_check(const char*,int); /* ** CAPI3REF: Dynamic String Object ** KEYWORDS: {dynamic string} ** ** An instance of the sqlite3_str object contains a dynamically-sized ** string under construction. ** ** The lifecycle of an sqlite3_str object is as follows: ** <ol> ** <li> ^The sqlite3_str object is created using [sqlite3_str_new()]. ** <li> ^Text is appended to the sqlite3_str object using various ** methods, such as [sqlite3_str_appendf()]. ** <li> ^The sqlite3_str object is destroyed and the string it created ** is returned using the [sqlite3_str_finish()] interface. ** </ol> */ typedef struct sqlite3_str sqlite3_str; /* ** CAPI3REF: Create A New Dynamic String Object ** CONSTRUCTOR: sqlite3_str ** ** ^The [sqlite3_str_new(D)] interface allocates and initializes ** a new [sqlite3_str] object. To avoid memory leaks, the object returned by ** [sqlite3_str_new()] must be freed by a subsequent call to ** [sqlite3_str_finish(X)]. ** ** ^The [sqlite3_str_new(D)] interface always returns a pointer to a ** valid [sqlite3_str] object, though in the event of an out-of-memory ** error the returned object might be a special singleton that will ** silently reject new text, always return SQLITE_NOMEM from ** [sqlite3_str_errcode()], always return 0 for ** [sqlite3_str_length()], and always return NULL from ** [sqlite3_str_finish(X)]. It is always safe to use the value ** returned by [sqlite3_str_new(D)] as the sqlite3_str parameter ** to any of the other [sqlite3_str] methods. ** ** The D parameter to [sqlite3_str_new(D)] may be NULL. If the ** D parameter in [sqlite3_str_new(D)] is not NULL, then the maximum ** length of the string contained in the [sqlite3_str] object will be ** the value set for [sqlite3_limit](D,[SQLITE_LIMIT_LENGTH]) instead ** of [SQLITE_MAX_LENGTH]. */ SQLITE_API sqlite3_str *sqlite3_str_new(sqlite3*); /* ** CAPI3REF: Finalize A Dynamic String ** DESTRUCTOR: sqlite3_str ** ** ^The [sqlite3_str_finish(X)] interface destroys the sqlite3_str object X ** and returns a pointer to a memory buffer obtained from [sqlite3_malloc64()] ** that contains the constructed string. The calling application should ** pass the returned value to [sqlite3_free()] to avoid a memory leak. ** ^The [sqlite3_str_finish(X)] interface may return a NULL pointer if any ** errors were encountered during construction of the string. ^The ** [sqlite3_str_finish(X)] interface will also return a NULL pointer if the ** string in [sqlite3_str] object X is zero bytes long. */ SQLITE_API char *sqlite3_str_finish(sqlite3_str*); /* ** CAPI3REF: Add Content To A Dynamic String ** METHOD: sqlite3_str ** ** These interfaces add content to an sqlite3_str object previously obtained ** from [sqlite3_str_new()]. ** ** ^The [sqlite3_str_appendf(X,F,...)] and ** [sqlite3_str_vappendf(X,F,V)] interfaces uses the [built-in printf] ** functionality of SQLite to append formatted text onto the end of ** [sqlite3_str] object X. ** ** ^The [sqlite3_str_append(X,S,N)] method appends exactly N bytes from string S ** onto the end of the [sqlite3_str] object X. N must be non-negative. ** S must contain at least N non-zero bytes of content. To append a ** zero-terminated string in its entirety, use the [sqlite3_str_appendall()] ** method instead. ** ** ^The [sqlite3_str_appendall(X,S)] method appends the complete content of ** zero-terminated string S onto the end of [sqlite3_str] object X. ** ** ^The [sqlite3_str_appendchar(X,N,C)] method appends N copies of the ** single-byte character C onto the end of [sqlite3_str] object X. ** ^This method can be used, for example, to add whitespace indentation. ** ** ^The [sqlite3_str_reset(X)] method resets the string under construction ** inside [sqlite3_str] object X back to zero bytes in length. ** ** These methods do not return a result code. ^If an error occurs, that fact ** is recorded in the [sqlite3_str] object and can be recovered by a ** subsequent call to [sqlite3_str_errcode(X)]. */ SQLITE_API void sqlite3_str_appendf(sqlite3_str*, const char *zFormat, ...); SQLITE_API void sqlite3_str_vappendf(sqlite3_str*, const char *zFormat, va_list); SQLITE_API void sqlite3_str_append(sqlite3_str*, const char *zIn, int N); SQLITE_API void sqlite3_str_appendall(sqlite3_str*, const char *zIn); SQLITE_API void sqlite3_str_appendchar(sqlite3_str*, int N, char C); SQLITE_API void sqlite3_str_reset(sqlite3_str*); /* ** CAPI3REF: Status Of A Dynamic String ** METHOD: sqlite3_str ** ** These interfaces return the current status of an [sqlite3_str] object. ** ** ^If any prior errors have occurred while constructing the dynamic string ** in sqlite3_str X, then the [sqlite3_str_errcode(X)] method will return ** an appropriate error code. ^The [sqlite3_str_errcode(X)] method returns ** [SQLITE_NOMEM] following any out-of-memory error, or ** [SQLITE_TOOBIG] if the size of the dynamic string exceeds ** [SQLITE_MAX_LENGTH], or [SQLITE_OK] if there have been no errors. ** ** ^The [sqlite3_str_length(X)] method returns the current length, in bytes, ** of the dynamic string under construction in [sqlite3_str] object X. ** ^The length returned by [sqlite3_str_length(X)] does not include the ** zero-termination byte. ** ** ^The [sqlite3_str_value(X)] method returns a pointer to the current ** content of the dynamic string under construction in X. The value ** returned by [sqlite3_str_value(X)] is managed by the sqlite3_str object X ** and might be freed or altered by any subsequent method on the same ** [sqlite3_str] object. Applications must not used the pointer returned ** [sqlite3_str_value(X)] after any subsequent method call on the same ** object. ^Applications may change the content of the string returned ** by [sqlite3_str_value(X)] as long as they do not write into any bytes ** outside the range of 0 to [sqlite3_str_length(X)] and do not read or ** write any byte after any subsequent sqlite3_str method call. */ SQLITE_API int sqlite3_str_errcode(sqlite3_str*); SQLITE_API int sqlite3_str_length(sqlite3_str*); SQLITE_API char *sqlite3_str_value(sqlite3_str*); /* ** CAPI3REF: SQLite Runtime Status ** ** ^These interfaces are used to retrieve runtime status information ** about the performance of SQLite, and optionally to reset various ** highwater marks. ^The first argument is an integer code for ** the specific parameter to measure. ^(Recognized integer codes |
︙ | ︙ | |||
8278 8279 8280 8281 8282 8283 8284 | /* ** CAPI3REF: Determine If Virtual Table Column Access Is For UPDATE ** ** If the sqlite3_vtab_nochange(X) routine is called within the [xColumn] ** method of a [virtual table], then it returns true if and only if the ** column is being fetched as part of an UPDATE operation during which the ** column value will not change. Applications might use this to substitute | | | | | 8660 8661 8662 8663 8664 8665 8666 8667 8668 8669 8670 8671 8672 8673 8674 8675 8676 8677 8678 | /* ** CAPI3REF: Determine If Virtual Table Column Access Is For UPDATE ** ** If the sqlite3_vtab_nochange(X) routine is called within the [xColumn] ** method of a [virtual table], then it returns true if and only if the ** column is being fetched as part of an UPDATE operation during which the ** column value will not change. Applications might use this to substitute ** a return value that is less expensive to compute and that the corresponding ** [xUpdate] method understands as a "no-change" value. ** ** If the [xColumn] method calls sqlite3_vtab_nochange() and finds that ** the column is not changed by the UPDATE statement, then the xColumn ** method can optionally return without setting a result, without calling ** any of the [sqlite3_result_int|sqlite3_result_xxxxx() interfaces]. ** In that case, [sqlite3_value_nochange(X)] will return true for the ** same column in the [xUpdate] method. */ SQLITE_API int sqlite3_vtab_nochange(sqlite3_context*); |
︙ | ︙ | |||
8575 8576 8577 8578 8579 8580 8581 | ** as ENOSPC, EAUTH, EISDIR, and so forth. */ SQLITE_API int sqlite3_system_errno(sqlite3*); /* ** CAPI3REF: Database Snapshot ** KEYWORDS: {snapshot} {sqlite3_snapshot} | < < < < < < | | | 8957 8958 8959 8960 8961 8962 8963 8964 8965 8966 8967 8968 8969 8970 8971 8972 8973 8974 8975 8976 8977 8978 8979 8980 8981 8982 8983 8984 8985 8986 8987 8988 8989 8990 8991 8992 8993 8994 8995 8996 8997 8998 8999 9000 9001 9002 9003 9004 9005 9006 9007 9008 9009 9010 | ** as ENOSPC, EAUTH, EISDIR, and so forth. */ SQLITE_API int sqlite3_system_errno(sqlite3*); /* ** CAPI3REF: Database Snapshot ** KEYWORDS: {snapshot} {sqlite3_snapshot} ** ** An instance of the snapshot object records the state of a [WAL mode] ** database for some specific point in history. ** ** In [WAL mode], multiple [database connections] that are open on the ** same database file can each be reading a different historical version ** of the database file. When a [database connection] begins a read ** transaction, that connection sees an unchanging copy of the database ** as it existed for the point in time when the transaction first started. ** Subsequent changes to the database from other connections are not seen ** by the reader until a new read transaction is started. ** ** The sqlite3_snapshot object records state information about an historical ** version of the database file so that it is possible to later open a new read ** transaction that sees that historical version of the database rather than ** the most recent version. */ typedef struct sqlite3_snapshot { unsigned char hidden[48]; } sqlite3_snapshot; /* ** CAPI3REF: Record A Database Snapshot ** CONSTRUCTOR: sqlite3_snapshot ** ** ^The [sqlite3_snapshot_get(D,S,P)] interface attempts to make a ** new [sqlite3_snapshot] object that records the current state of ** schema S in database connection D. ^On success, the ** [sqlite3_snapshot_get(D,S,P)] interface writes a pointer to the newly ** created [sqlite3_snapshot] object into *P and returns SQLITE_OK. ** If there is not already a read-transaction open on schema S when ** this function is called, one is opened automatically. ** ** The following must be true for this function to succeed. If any of ** the following statements are false when sqlite3_snapshot_get() is ** called, SQLITE_ERROR is returned. The final value of *P is undefined ** in this case. ** ** <ul> ** <li> The database handle must not be in [autocommit mode]. ** ** <li> Schema S of [database connection] D must be a [WAL mode] database. ** ** <li> There must not be a write transaction open on schema S of database ** connection D. ** ** <li> One or more transactions must have been written to the current wal |
︙ | ︙ | |||
8643 8644 8645 8646 8647 8648 8649 | ** whether or not a read transaction is opened on schema S is undefined. ** ** The [sqlite3_snapshot] object returned from a successful call to ** [sqlite3_snapshot_get()] must be freed using [sqlite3_snapshot_free()] ** to avoid a memory leak. ** ** The [sqlite3_snapshot_get()] interface is only available when the | | | | | | | < | | | < | > > > > | > | < | > | > > > > > > > > | | | | > > > | | | | > | < > | < | | > > > | 9019 9020 9021 9022 9023 9024 9025 9026 9027 9028 9029 9030 9031 9032 9033 9034 9035 9036 9037 9038 9039 9040 9041 9042 9043 9044 9045 9046 9047 9048 9049 9050 9051 9052 9053 9054 9055 9056 9057 9058 9059 9060 9061 9062 9063 9064 9065 9066 9067 9068 9069 9070 9071 9072 9073 9074 9075 9076 9077 9078 9079 9080 9081 9082 9083 9084 9085 9086 9087 9088 9089 9090 9091 9092 9093 9094 9095 9096 9097 9098 9099 9100 9101 9102 9103 9104 9105 9106 9107 9108 9109 9110 9111 9112 9113 9114 9115 9116 9117 9118 9119 9120 9121 9122 9123 9124 9125 9126 9127 9128 9129 9130 9131 9132 9133 9134 9135 9136 9137 9138 9139 9140 9141 9142 9143 9144 9145 9146 9147 9148 9149 9150 9151 9152 9153 9154 | ** whether or not a read transaction is opened on schema S is undefined. ** ** The [sqlite3_snapshot] object returned from a successful call to ** [sqlite3_snapshot_get()] must be freed using [sqlite3_snapshot_free()] ** to avoid a memory leak. ** ** The [sqlite3_snapshot_get()] interface is only available when the ** [SQLITE_ENABLE_SNAPSHOT] compile-time option is used. */ SQLITE_API SQLITE_EXPERIMENTAL int sqlite3_snapshot_get( sqlite3 *db, const char *zSchema, sqlite3_snapshot **ppSnapshot ); /* ** CAPI3REF: Start a read transaction on an historical snapshot ** METHOD: sqlite3_snapshot ** ** ^The [sqlite3_snapshot_open(D,S,P)] interface either starts a new read ** transaction or upgrades an existing one for schema S of ** [database connection] D such that the read transaction refers to ** historical [snapshot] P, rather than the most recent change to the ** database. ^The [sqlite3_snapshot_open()] interface returns SQLITE_OK ** on success or an appropriate [error code] if it fails. ** ** ^In order to succeed, the database connection must not be in ** [autocommit mode] when [sqlite3_snapshot_open(D,S,P)] is called. If there ** is already a read transaction open on schema S, then the database handle ** must have no active statements (SELECT statements that have been passed ** to sqlite3_step() but not sqlite3_reset() or sqlite3_finalize()). ** SQLITE_ERROR is returned if either of these conditions is violated, or ** if schema S does not exist, or if the snapshot object is invalid. ** ** ^A call to sqlite3_snapshot_open() will fail to open if the specified ** snapshot has been overwritten by a [checkpoint]. In this case ** SQLITE_BUSY_SNAPSHOT is returned. ** ** If there is already a read transaction open when this function is ** invoked, then the same read transaction remains open (on the same ** database snapshot) if SQLITE_ERROR, SQLITE_BUSY or SQLITE_BUSY_SNAPSHOT ** is returned. If another error code - for example SQLITE_PROTOCOL or an ** SQLITE_IOERR error code - is returned, then the final state of the ** read transaction is undefined. If SQLITE_OK is returned, then the ** read transaction is now open on database snapshot P. ** ** ^(A call to [sqlite3_snapshot_open(D,S,P)] will fail if the ** database connection D does not know that the database file for ** schema S is in [WAL mode]. A database connection might not know ** that the database file is in [WAL mode] if there has been no prior ** I/O on that database connection, or if the database entered [WAL mode] ** after the most recent I/O on the database connection.)^ ** (Hint: Run "[PRAGMA application_id]" against a newly opened ** database connection in order to make it ready to use snapshots.) ** ** The [sqlite3_snapshot_open()] interface is only available when the ** [SQLITE_ENABLE_SNAPSHOT] compile-time option is used. */ SQLITE_API SQLITE_EXPERIMENTAL int sqlite3_snapshot_open( sqlite3 *db, const char *zSchema, sqlite3_snapshot *pSnapshot ); /* ** CAPI3REF: Destroy a snapshot ** DESTRUCTOR: sqlite3_snapshot ** ** ^The [sqlite3_snapshot_free(P)] interface destroys [sqlite3_snapshot] P. ** The application must eventually free every [sqlite3_snapshot] object ** using this routine to avoid a memory leak. ** ** The [sqlite3_snapshot_free()] interface is only available when the ** [SQLITE_ENABLE_SNAPSHOT] compile-time option is used. */ SQLITE_API SQLITE_EXPERIMENTAL void sqlite3_snapshot_free(sqlite3_snapshot*); /* ** CAPI3REF: Compare the ages of two snapshot handles. ** METHOD: sqlite3_snapshot ** ** The sqlite3_snapshot_cmp(P1, P2) interface is used to compare the ages ** of two valid snapshot handles. ** ** If the two snapshot handles are not associated with the same database ** file, the result of the comparison is undefined. ** ** Additionally, the result of the comparison is only valid if both of the ** snapshot handles were obtained by calling sqlite3_snapshot_get() since the ** last time the wal file was deleted. The wal file is deleted when the ** database is changed back to rollback mode or when the number of database ** clients drops to zero. If either snapshot handle was obtained before the ** wal file was last deleted, the value returned by this function ** is undefined. ** ** Otherwise, this API returns a negative value if P1 refers to an older ** snapshot than P2, zero if the two handles refer to the same database ** snapshot, and a positive value if P1 is a newer snapshot than P2. ** ** This interface is only available if SQLite is compiled with the ** [SQLITE_ENABLE_SNAPSHOT] option. */ SQLITE_API SQLITE_EXPERIMENTAL int sqlite3_snapshot_cmp( sqlite3_snapshot *p1, sqlite3_snapshot *p2 ); /* ** CAPI3REF: Recover snapshots from a wal file ** METHOD: sqlite3_snapshot ** ** If a [WAL file] remains on disk after all database connections close ** (either through the use of the [SQLITE_FCNTL_PERSIST_WAL] [file control] ** or because the last process to have the database opened exited without ** calling [sqlite3_close()]) and a new connection is subsequently opened ** on that database and [WAL file], the [sqlite3_snapshot_open()] interface ** will only be able to open the last transaction added to the WAL file ** even though the WAL file contains other valid transactions. ** ** This function attempts to scan the WAL file associated with database zDb ** of database handle db and make all valid snapshots available to ** sqlite3_snapshot_open(). It is an error if there is already a read ** transaction open on the database, or if the database is not a WAL mode ** database. ** ** SQLITE_OK is returned if successful, or an SQLite error code otherwise. ** ** This interface is only available if SQLite is compiled with the ** [SQLITE_ENABLE_SNAPSHOT] option. */ SQLITE_API SQLITE_EXPERIMENTAL int sqlite3_snapshot_recover(sqlite3 *db, const char *zDb); /* ** CAPI3REF: Serialize a database ** ** The sqlite3_serialize(D,S,P,F) interface returns a pointer to memory |
︙ | ︙ | |||
8777 8778 8779 8780 8781 8782 8783 | ** to the contiguous memory representation of the database that SQLite ** is currently using for that database, or NULL if the no such contiguous ** memory representation of the database exists. A contiguous memory ** representation of the database will usually only exist if there has ** been a prior call to [sqlite3_deserialize(D,S,...)] with the same ** values of D and S. ** The size of the database is written into *P even if the | | | 9170 9171 9172 9173 9174 9175 9176 9177 9178 9179 9180 9181 9182 9183 9184 | ** to the contiguous memory representation of the database that SQLite ** is currently using for that database, or NULL if the no such contiguous ** memory representation of the database exists. A contiguous memory ** representation of the database will usually only exist if there has ** been a prior call to [sqlite3_deserialize(D,S,...)] with the same ** values of D and S. ** The size of the database is written into *P even if the ** SQLITE_SERIALIZE_NOCOPY bit is set but no contiguous copy ** of the database exists. ** ** A call to sqlite3_serialize(D,S,P,F) might return NULL even if the ** SQLITE_SERIALIZE_NOCOPY bit is omitted from argument F if a memory ** allocation error occurs. ** ** This interface is only available if SQLite is compiled with the |
︙ | ︙ |
Changes to src/stat.c.
︙ | ︙ | |||
49 50 51 52 53 54 55 56 57 58 59 60 61 62 | sqlite3_snprintf(nOut, zOut, "%.1fKB", (double)v/1000.0); }else if( v<1000000000 ){ sqlite3_snprintf(nOut, zOut, "%.1fMB", (double)v/1000000.0); }else{ sqlite3_snprintf(nOut, zOut, "%.1fGB", (double)v/1000000000.0); } } /* ** WEBPAGE: stat ** ** Show statistics and global information about the repository. */ void stat_page(void){ | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 | sqlite3_snprintf(nOut, zOut, "%.1fKB", (double)v/1000.0); }else if( v<1000000000 ){ sqlite3_snprintf(nOut, zOut, "%.1fMB", (double)v/1000000.0); }else{ sqlite3_snprintf(nOut, zOut, "%.1fGB", (double)v/1000000000.0); } } /* ** Generate stats for the email notification subsystem. */ void stats_for_email(void){ const char *zDest = db_get("email-send-method",0); int nSub, nASub, nPend, nDPend; const char *zDir, *zDb, *zCmd, *zRelay; @ <tr><th>Outgoing Email:</th><td> if( fossil_strcmp(zDest,"pipe")==0 && (zCmd = db_get("email-send-command",0))!=0 ){ @ Piped to command "%h(zCmd)" }else if( fossil_strcmp(zDest,"db")==0 && (zDb = db_get("email-send-db",0))!=0 ){ sqlite3 *db; sqlite3_stmt *pStmt; int rc; @ Queued to database "%h(zDb)" rc = sqlite3_open(zDb, &db); if( rc==SQLITE_OK ){ rc = sqlite3_prepare_v2(db, "SELECT count(*) FROM email",-1,&pStmt,0); if( rc==SQLITE_OK && sqlite3_step(pStmt)==SQLITE_ROW ){ @ (%,d(sqlite3_column_int(pStmt,0)) messages, @ %,d(file_size(zDb,ExtFILE)) bytes) } sqlite3_finalize(pStmt); } sqlite3_close(db); }else if( fossil_strcmp(zDest,"dir")==0 && (zDir = db_get("email-send-dir",0))!=0 ){ @ Written to files in "%h(zDir)" @ (%,d(file_directory_size(zDir,0,1)) messages) }else if( fossil_strcmp(zDest,"relay")==0 && (zRelay = db_get("email-send-relayhost",0))!=0 ){ @ Relay to %h(zRelay) using SMTP } else{ @ Off } @ </td></tr> nPend = db_int(0,"SELECT count(*) FROM pending_alert WHERE NOT sentSep"); nDPend = db_int(0,"SELECT count(*) FROM pending_alert" " WHERE NOT sentDigest"); @ <tr><th>Pending Alerts:</th><td> @ %,d(nPend) normal, %,d(nDPend) digest @ </td></tr> @ <tr><th>Subscribers:</th><td> nSub = db_int(0, "SELECT count(*) FROM subscriber"); nASub = db_int(0, "SELECT count(*) FROM subscriber WHERE sverified" " AND NOT sdonotcall AND length(ssub)>1"); @ %,d(nASub) active, %,d(nSub) total @ </td></tr> } /* ** WEBPAGE: stat ** ** Show statistics and global information about the repository. */ void stat_page(void){ |
︙ | ︙ | |||
196 197 198 199 200 201 202 203 204 205 206 207 208 209 | @ <tr><th>Database Stats:</th><td> @ %,d(db_int(0, "PRAGMA repository.page_count")) pages, @ %d(db_int(0, "PRAGMA repository.page_size")) bytes/page, @ %,d(db_int(0, "PRAGMA repository.freelist_count")) free pages, @ %s(db_text(0, "PRAGMA repository.encoding")), @ %s(db_text(0, "PRAGMA repository.journal_mode")) mode @ </td></tr> @ </table> style_footer(); } /* ** COMMAND: dbstat* | > > > > > > > > > > > > > > > | 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 | @ <tr><th>Database Stats:</th><td> @ %,d(db_int(0, "PRAGMA repository.page_count")) pages, @ %d(db_int(0, "PRAGMA repository.page_size")) bytes/page, @ %,d(db_int(0, "PRAGMA repository.freelist_count")) free pages, @ %s(db_text(0, "PRAGMA repository.encoding")), @ %s(db_text(0, "PRAGMA repository.journal_mode")) mode @ </td></tr> if( g.perm.Admin && g.zErrlog && g.zErrlog[0] ){ i64 szFile = file_size(g.zErrlog, ExtFILE); if( szFile>=0 ){ @ <tr><th>Error Log:</th> @ <td><a href='%R/errorlog'>%h(g.zErrlog)</a> (%,lld(szFile) bytes) } @ </td></tr> } if( g.perm.Admin ){ @ <tr><th>Backoffice:</th> @ <td>Last run: %z(backoffice_last_run())</td></tr> } if( g.perm.Admin && email_enabled() ){ stats_for_email(); } @ </table> style_footer(); } /* ** COMMAND: dbstat* |
︙ | ︙ |
Changes to src/style.c.
︙ | ︙ | |||
18 19 20 21 22 23 24 | ** This file contains code to implement the basic web page look and feel. ** */ #include "VERSION.h" #include "config.h" #include "style.h" | < | 18 19 20 21 22 23 24 25 26 27 28 29 30 31 | ** This file contains code to implement the basic web page look and feel. ** */ #include "VERSION.h" #include "config.h" #include "style.h" /* ** Elements of the submenu are collected into the following ** structure and displayed below the main menu. ** ** Populate these structure with calls to ** ** style_submenu_element() |
︙ | ︙ | |||
371 372 373 374 375 376 377 378 379 380 381 382 383 384 | */ static char zDfltHeader[] = @ <html> @ <head> @ <base href="$baseurl/$current_page" /> @ <meta http-equiv="Content-Security-Policy" \ @ content="default-src 'self' data: 'unsafe-inline'" /> @ <title>$<project_name>: $<title></title> @ <link rel="alternate" type="application/rss+xml" title="RSS Feed" \ @ href="$home/timeline.rss" /> @ <link rel="stylesheet" href="$stylesheet_url" type="text/css" \ @ media="screen" /> @ </head> @ <body> | > | 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 | */ static char zDfltHeader[] = @ <html> @ <head> @ <base href="$baseurl/$current_page" /> @ <meta http-equiv="Content-Security-Policy" \ @ content="default-src 'self' data: 'unsafe-inline'" /> @ <meta name="viewport" content="width=device-width, initial-scale=1.0"> @ <title>$<project_name>: $<title></title> @ <link rel="alternate" type="application/rss+xml" title="RSS Feed" \ @ href="$home/timeline.rss" /> @ <link rel="stylesheet" href="$stylesheet_url" type="text/css" \ @ media="screen" /> @ </head> @ <body> |
︙ | ︙ | |||
420 421 422 423 424 425 426 | Th_Store("compiler_name", COMPILER_NAME); url_var("stylesheet", "css", "style.css"); image_url_var("logo"); image_url_var("background"); if( !login_is_nobody() ){ Th_Store("login", g.zLogin); } | | > > > > > | 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 | Th_Store("compiler_name", COMPILER_NAME); url_var("stylesheet", "css", "style.css"); image_url_var("logo"); image_url_var("background"); if( !login_is_nobody() ){ Th_Store("login", g.zLogin); } if( sqlite3_strlike("%<body%", zHeader, 0)!=0 ){ Th_Render(zDfltHeader); } if( g.thTrace ) Th_Trace("BEGIN_HEADER_SCRIPT<br />\n", -1); Th_Render(zHeader); if( g.thTrace ) Th_Trace("END_HEADER<br />\n", -1); Th_Unstore("title"); /* Avoid collisions with ticket field names */ cgi_destination(CGI_BODY); g.cgiOutput = 1; headerHasBeenGenerated = 1; sideboxUsed = 0; if( g.perm.Debug && P("showqp") ){ @ <div class="debug"> cgi_print_all(0, 0); @ </div> } } #if INTERFACE /* Allowed parameters for style_adunit() */ #define ADUNIT_OFF 0x0001 /* Do not allow ads on this page */ #define ADUNIT_RIGHT_OK 0x0002 /* Right-side vertical ads ok here */ #endif |
︙ | ︙ | |||
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 | /* ** Generate code to load a single javascript file */ void style_load_one_js_file(const char *zFile){ @ <script src='%R/builtin/%s(zFile)?id=%S(MANIFEST_UUID)'></script> } /* ** Generate code to load all required javascript files. */ static void style_load_all_js_files(void){ if( needHrefJs ){ int nDelay = db_get_int("auto-hyperlink-delay",0); int bMouseover; /* Load up the page data */ bMouseover = (!g.isHuman || db_get_boolean("auto-hyperlink-ishuman",0)) && db_get_boolean("auto-hyperlink-mouseover",0); @ <script id='href-data' type='application/json'>\ @ {"delay":%d(nDelay),"mouseover":%d(bMouseover)}</script> style_load_one_js_file("href.js"); } if( needSortJs ){ style_load_one_js_file("sorttable.js"); } if( needGraphJs ){ style_load_one_js_file("graph.js"); } } /* ** Draw the footer at the bottom of the page. */ void style_footer(void){ const char *zFooter; | > > > > > > > > > > > > > > > > > > > > > > > > | 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 | /* ** Generate code to load a single javascript file */ void style_load_one_js_file(const char *zFile){ @ <script src='%R/builtin/%s(zFile)?id=%S(MANIFEST_UUID)'></script> } /* ** All extra JS files to load. */ static const char *azJsToLoad[4]; static int nJsToLoad = 0; /* ** Register a new JS file to load at the end of the document. */ void style_load_js(const char *zName){ int i; for(i=0; i<nJsToLoad; i++){ if( fossil_strcmp(zName, azJsToLoad[i])==0 ) return; } if( nJsToLoad>=sizeof(azJsToLoad)/sizeof(azJsToLoad[0]) ){ fossil_panic("too man JS files"); } azJsToLoad[nJsToLoad++] = zName; } /* ** Generate code to load all required javascript files. */ static void style_load_all_js_files(void){ int i; if( needHrefJs ){ int nDelay = db_get_int("auto-hyperlink-delay",0); int bMouseover; /* Load up the page data */ bMouseover = (!g.isHuman || db_get_boolean("auto-hyperlink-ishuman",0)) && db_get_boolean("auto-hyperlink-mouseover",0); @ <script id='href-data' type='application/json'>\ @ {"delay":%d(nDelay),"mouseover":%d(bMouseover)}</script> style_load_one_js_file("href.js"); } if( needSortJs ){ style_load_one_js_file("sorttable.js"); } if( needGraphJs ){ style_load_one_js_file("graph.js"); } for(i=0; i<nJsToLoad; i++){ style_load_one_js_file(azJsToLoad[i]); } } /* ** Draw the footer at the bottom of the page. */ void style_footer(void){ const char *zFooter; |
︙ | ︙ | |||
862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 | }else{ etag_check(0,0); } blob_init(&out, zTxt, -1); cgi_set_content(&out); } /* ** WEBPAGE: test_env ** ** Display CGI-variables and other aspects of the run-time ** environment, for debugging and trouble-shooting purposes. */ void page_test_env(void){ | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | > > > > > > > > > > > > > > > > > > > > > > > | | < | > > > > > > > > > > > > > | | | | | > > | | | | | | | < < < < | | | | | | | | | | < < < < | < | | | | | | | | | | | | | | < < < > > > > > | > > > > > < | | > | | > > > > | 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952 953 954 955 956 957 958 959 960 961 962 963 964 965 966 967 968 969 970 971 972 973 974 975 976 977 978 979 980 981 982 983 984 985 986 987 988 989 990 991 992 993 994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 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 1064 1065 1066 1067 1068 1069 | }else{ etag_check(0,0); } blob_init(&out, zTxt, -1); cgi_set_content(&out); } /* ** All possible capabilities */ static const char allCap[] = "abcdefghijklmnopqrstuvwxyz0123456789ABCDEFGHIJKL"; /* ** Compute the current login capabilities */ static char *find_capabilities(char *zCap){ int i, j; char c; for(i=j=0; (c = allCap[j])!=0; j++){ if( login_has_capability(&c, 1, 0) ) zCap[i++] = c; } zCap[i] = 0; return zCap; } /* ** Compute the current login capabilities that were ** contributed by Anonymous */ static char *find_anon_capabilities(char *zCap){ int i, j; char c; for(i=j=0; (c = allCap[j])!=0; j++){ if( login_has_capability(&c, 1, LOGIN_ANON) && !login_has_capability(&c, 1, 0) ) zCap[i++] = c; } zCap[i] = 0; return zCap; } /* ** WEBPAGE: test_env ** ** Display CGI-variables and other aspects of the run-time ** environment, for debugging and trouble-shooting purposes. */ void page_test_env(void){ webpage_error(""); } /* ** WEBPAGE: honeypot ** This page is a honeypot for spiders and bots. */ void honeypot_page(void){ cgi_set_status(403, "Forbidden"); @ <p>Please enable javascript or log in to see this content</p> } /* ** Webpages that encounter an error due to missing or incorrect ** query parameters can jump to this routine to render an error ** message screen. ** ** For administators, or if the test_env_enable setting is true, then ** details of the request environment are displayed. Otherwise, just ** the error message is shown. ** ** If zFormat is an empty string, then this is the /test_env page. */ void webpage_error(const char *zFormat, ...){ int i; int showAll; char *zErr = 0; int isAuth = 0; char zCap[100]; static const char *const azCgiVars[] = { "COMSPEC", "DOCUMENT_ROOT", "GATEWAY_INTERFACE", "HTTP_ACCEPT", "HTTP_ACCEPT_CHARSET", "HTTP_ACCEPT_ENCODING", "HTTP_ACCEPT_LANGUAGE", "HTTP_AUTHENICATION", "HTTP_CONNECTION", "HTTP_HOST", "HTTP_IF_NONE_MATCH", "HTTP_IF_MODIFIED_SINCE", "HTTP_USER_AGENT", "HTTP_REFERER", "PATH_INFO", "PATH_TRANSLATED", "QUERY_STRING", "REMOTE_ADDR", "REMOTE_PORT", "REMOTE_USER", "REQUEST_METHOD", "REQUEST_URI", "SCRIPT_FILENAME", "SCRIPT_NAME", "SERVER_PROTOCOL", "HOME", "FOSSIL_HOME", "USERNAME", "USER", "FOSSIL_USER", "SQLITE_TMPDIR", "TMPDIR", "TEMP", "TMP", "FOSSIL_VFS", "FOSSIL_FORCE_TICKET_MODERATION", "FOSSIL_FORCE_WIKI_MODERATION", "FOSSIL_TCL_PATH", "TH1_DELETE_INTERP", "TH1_ENABLE_DOCS", "TH1_ENABLE_HOOKS", "TH1_ENABLE_TCL", "REMOTE_HOST" }; login_check_credentials(); if( g.perm.Admin || g.perm.Setup || db_get_boolean("test_env_enable",0) ){ isAuth = 1; } for(i=0; i<count(azCgiVars); i++) (void)P(azCgiVars[i]); if( zFormat[0] ){ va_list ap; va_start(ap, zFormat); zErr = vmprintf(zFormat, ap); va_end(ap); style_header("Bad Request"); @ <h1>/%h(g.zPath): %h(zErr)</h1> showAll = 0; cgi_set_status(500, "Bad Request"); }else if( !isAuth ){ login_needed(0); return; }else{ style_header("Environment Test"); showAll = PB("showall"); style_submenu_checkbox("showall", "Cookies", 0, 0); style_submenu_element("Stats", "%R/stat"); } if( isAuth ){ #if !defined(_WIN32) @ uid=%d(getuid()), gid=%d(getgid())<br /> #endif @ g.zBaseURL = %h(g.zBaseURL)<br /> @ g.zHttpsURL = %h(g.zHttpsURL)<br /> @ g.zTop = %h(g.zTop)<br /> @ g.zPath = %h(g.zPath)<br /> @ g.userUid = %d(g.userUid)<br /> @ g.zLogin = %h(g.zLogin)<br /> @ g.isHuman = %d(g.isHuman)<br /> if( g.nRequest ){ @ g.nRequest = %d(g.nRequest)<br /> } if( g.nPendingRequest>1 ){ @ g.nPendingRequest = %d(g.nPendingRequest)<br /> } @ capabilities = %s(find_capabilities(zCap))<br /> if( zCap[0] ){ @ anonymous-adds = %s(find_anon_capabilities(zCap))<br /> } @ g.zRepositoryName = %h(g.zRepositoryName)<br /> @ load_average() = %f(load_average())<br /> @ cgi_csrf_safe(0) = %d(cgi_csrf_safe(0))<br /> @ <hr /> P("HTTP_USER_AGENT"); cgi_print_all(showAll, 0); if( showAll && blob_size(&g.httpHeader)>0 ){ @ <hr /> @ <pre> @ %h(blob_str(&g.httpHeader)) @ </pre> } } style_footer(); if( zErr ){ cgi_reply(); fossil_exit(1); } } /* ** Generate a Not Yet Implemented error page. */ void webpage_not_yet_implemented(void){ webpage_error("Not yet implemented"); } /* ** Generate a webpage for a webpage_assert(). */ void webpage_assert_page(const char *zFile, int iLine, const char *zExpr){ fossil_warning("assertion fault at %s:%d - %s", zFile, iLine, zExpr); cgi_reset_content(); webpage_error("assertion fault at %s:%d - %s", zFile, iLine, zExpr); } #if INTERFACE # define webpage_assert(T) if(!(T)){webpage_assert_page(__FILE__,__LINE__,#T);} #endif |
Changes to src/tar.c.
︙ | ︙ | |||
250 251 252 253 254 255 256 | if(blen > next10){ blen++; } /* build the string */ blob_appendf(&tball.pax, "%d %s=%*.*s\n", blen, zField, nValue, nValue, zValue); /* this _must_ be right */ if(blob_size(&tball.pax) != blen){ | | | 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 | if(blen > next10){ blen++; } /* build the string */ blob_appendf(&tball.pax, "%d %s=%*.*s\n", blen, zField, nValue, nValue, zValue); /* this _must_ be right */ if(blob_size(&tball.pax) != blen){ fossil_panic("internal error: PAX tar header has bad length"); } } /* ** set the header type, calculate the checksum and output ** the header |
︙ | ︙ |
Changes to src/timeline.c.
︙ | ︙ | |||
354 355 356 357 358 359 360 | ** (2) YYYY-MM-DD HH:MM ** (3) YYMMDD HH:MM ** (4) (off) */ if( dateFormat<2 ){ if( fossil_strnicmp(zDate, zPrevDate, 10) ){ sqlite3_snprintf(sizeof(zPrevDate), zPrevDate, "%.10s", zDate); | | | 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 | ** (2) YYYY-MM-DD HH:MM ** (3) YYMMDD HH:MM ** (4) (off) */ if( dateFormat<2 ){ if( fossil_strnicmp(zDate, zPrevDate, 10) ){ sqlite3_snprintf(sizeof(zPrevDate), zPrevDate, "%.10s", zDate); @ <tr class="timelineDateRow"><td> @ <div class="divider timelineDate">%s(zPrevDate)</div> @ </td><td></td><td></td></tr> } memcpy(zTime, &zDate[11], 5+dateFormat*3); zTime[5+dateFormat*3] = 0; }else if( 2==dateFormat ){ /* YYYY-MM-DD HH:MM */ |
︙ | ︙ | |||
458 459 460 461 462 463 464 | @ • } if( modPending ){ @ <span class="modpending">(Awaiting Moderator Approval)</span> } if( (tmFlags & TIMELINE_BISECT)!=0 && zType[0]=='c' ){ static Stmt bisectQuery; | > | | 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 | @ • } if( modPending ){ @ <span class="modpending">(Awaiting Moderator Approval)</span> } if( (tmFlags & TIMELINE_BISECT)!=0 && zType[0]=='c' ){ static Stmt bisectQuery; db_static_prepare(&bisectQuery, "SELECT seq, stat FROM bilog WHERE rid=:rid"); db_bind_int(&bisectQuery, ":rid", rid); if( db_step(&bisectQuery)==SQLITE_ROW ){ @ <b>%s(db_column_text(&bisectQuery,1))</b> @ (%d(db_column_int(&bisectQuery,0))) } db_reset(&bisectQuery); } |
︙ | ︙ | |||
572 573 574 575 576 577 578 | cgi_printf("check-in: %z%S</a> ",href("%R/info/%!S",zUuid),zUuid); }else if( zType[0]=='e' && tagid ){ cgi_printf("technote: "); hyperlink_to_event_tagid(tagid<0?-tagid:tagid); }else{ cgi_printf("artifact: %z%S</a> ",href("%R/info/%!S",zUuid),zUuid); } | | | 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 | cgi_printf("check-in: %z%S</a> ",href("%R/info/%!S",zUuid),zUuid); }else if( zType[0]=='e' && tagid ){ cgi_printf("technote: "); hyperlink_to_event_tagid(tagid<0?-tagid:tagid); }else{ cgi_printf("artifact: %z%S</a> ",href("%R/info/%!S",zUuid),zUuid); } }else if( zType[0]=='g' || zType[0]=='w' || zType[0]=='t' || zType[0]=='f'){ cgi_printf("artifact: %z%S</a> ",href("%R/info/%!S",zUuid),zUuid); } if( g.perm.Hyperlink && fossil_strcmp(zDispUser, zThisUser)!=0 ){ char *zLink = mprintf("%R/timeline?u=%h&c=%t&nd&n=200", zDispUser, zDate); cgi_printf("user: %z%h</a>", href("%z",zLink), zDispUser); }else{ |
︙ | ︙ | |||
770 771 772 773 774 775 776 | if( whiteFg ){ /* Make the color lighter */ static const unsigned int t = 215; if( mx<t ) for(i=0; i<3; i++) x[i] += t - mx; }else{ /* Make the color darker */ static const unsigned int t = 128; | > | > > > | 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 | if( whiteFg ){ /* Make the color lighter */ static const unsigned int t = 215; if( mx<t ) for(i=0; i<3; i++) x[i] += t - mx; }else{ /* Make the color darker */ static const unsigned int t = 128; if( mx>t ){ for(i=0; i<3; i++){ x[i] = x[i]>=mx-t ? x[i] - (mx-t) : 0; } } } sqlite3_snprintf(sizeof(zRes),zRes,"#%02x%02x%02x",x[0],x[1],x[2]); return zRes; } /* ** Generate all of the necessary javascript to generate a timeline |
︙ | ︙ | |||
1025 1026 1027 1028 1029 1030 1031 | /* ** Add the select/option box to the timeline submenu that is used to ** set the y= parameter that determines which elements to display ** on the timeline. */ static void timeline_y_submenu(int isDisabled){ static int i = 0; | | | 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 | /* ** Add the select/option box to the timeline submenu that is used to ** set the y= parameter that determines which elements to display ** on the timeline. */ static void timeline_y_submenu(int isDisabled){ static int i = 0; static const char *az[14]; if( i==0 ){ az[0] = "all"; az[1] = "Any Type"; i = 2; if( g.perm.Read ){ az[i++] = "ci"; az[i++] = "Check-ins"; |
︙ | ︙ | |||
1047 1048 1049 1050 1051 1052 1053 1054 1055 1056 1057 1058 1059 1060 | if( g.perm.RdTkt ){ az[i++] = "t"; az[i++] = "Tickets"; } if( g.perm.RdWiki ){ az[i++] = "w"; az[i++] = "Wiki"; } assert( i<=count(az) ); } if( i>2 ){ style_submenu_multichoice("y", i/2, az, isDisabled); } } | > > > > | 1052 1053 1054 1055 1056 1057 1058 1059 1060 1061 1062 1063 1064 1065 1066 1067 1068 1069 | if( g.perm.RdTkt ){ az[i++] = "t"; az[i++] = "Tickets"; } if( g.perm.RdWiki ){ az[i++] = "w"; az[i++] = "Wiki"; } if( g.perm.RdForum ){ az[i++] = "f"; az[i++] = "Forum"; } assert( i<=count(az) ); } if( i>2 ){ style_submenu_multichoice("y", i/2, az, isDisabled); } } |
︙ | ︙ | |||
1112 1113 1114 1115 1116 1117 1118 | glob_expr("filename.name", zChng)); } static void addFileGlobDescription( const char *zChng, /* The filename GLOB list */ Blob *pDescription /* Result description */ ){ if( zChng==0 || zChng[0]==0 ) return; | | | 1121 1122 1123 1124 1125 1126 1127 1128 1129 1130 1131 1132 1133 1134 1135 | glob_expr("filename.name", zChng)); } static void addFileGlobDescription( const char *zChng, /* The filename GLOB list */ Blob *pDescription /* Result description */ ){ if( zChng==0 || zChng[0]==0 ) return; blob_appendf(pDescription, " that include changes to files matching '%h'", zChng); } /* ** Tag match expression type code. */ typedef enum { |
︙ | ︙ | |||
1351 1352 1353 1354 1355 1356 1357 | ** dp=CHECKIN The same as d=CHECKIN&p=CHECKIN ** t=TAG Show only check-ins with the given TAG ** r=TAG Show check-ins related to TAG, equivalent to t=TAG&rel ** rel Show related check-ins as well as those matching t=TAG ** mionly Limit rel to show ancestors but not descendants ** ms=MATCHSTYLE Set tag match style to EXACT, GLOB, LIKE, REGEXP ** u=USER Only show items associated with USER | | > > | 1360 1361 1362 1363 1364 1365 1366 1367 1368 1369 1370 1371 1372 1373 1374 1375 1376 1377 1378 1379 1380 1381 1382 1383 1384 1385 1386 1387 1388 1389 1390 1391 1392 1393 1394 1395 | ** dp=CHECKIN The same as d=CHECKIN&p=CHECKIN ** t=TAG Show only check-ins with the given TAG ** r=TAG Show check-ins related to TAG, equivalent to t=TAG&rel ** rel Show related check-ins as well as those matching t=TAG ** mionly Limit rel to show ancestors but not descendants ** ms=MATCHSTYLE Set tag match style to EXACT, GLOB, LIKE, REGEXP ** u=USER Only show items associated with USER ** y=TYPE 'ci', 'w', 't', 'e', 'f', or 'all'. ** ss=VIEWSTYLE c: "Compact" v: "Verbose" m: "Modern" j: "Columnar" ** advm Use the "Advanced" or "Busy" menu design. ** ng No Graph. ** nd Do not highlight the focus check-in ** v Show details of files changed ** f=CHECKIN Show family (immediate parents and children) of CHECKIN ** from=CHECKIN Path from... ** to=CHECKIN ... to this ** shortest ... show only the shortest path ** uf=FILE_HASH Show only check-ins that contain the given file version ** chng=GLOBLIST Show only check-ins that involve changes to a file whose ** name matches one of the comma-separate GLOBLIST ** brbg Background color from branch name ** ubg Background color from user ** namechng Show only check-ins that have filename changes ** forks Show only forks and their children ** ym=YYYY-MM Show only events for the given year/month ** yw=YYYY-WW Show only events for the given week of the given year ** yw=YYYY-MM-DD Show events for the week that includes the given day ** ymd=YYYY-MM-DD Show only events on the given day ** days=N Show events over the previous N days ** datefmt=N Override the date format ** bisect Show the check-ins that are in the current bisect ** showid Show RIDs ** showsql Show the SQL text ** ** p= and d= can appear individually or together. If either p= or d= ** appear, then u=, y=, a=, and b= are ignored. |
︙ | ︙ | |||
1410 1411 1412 1413 1414 1415 1416 1417 1418 1419 1420 1421 1422 1423 1424 | const char *zMatchDesc = 0; /* Tag match expression description text */ const char *zError = 0; /* Tag match error string */ const char *zTagSql = 0; /* Tag/branch match SQL expression */ const char *zSearch = P("s"); /* Search string */ const char *zUses = P("uf"); /* Only show check-ins hold this file */ const char *zYearMonth = P("ym"); /* Show check-ins for the given YYYY-MM */ const char *zYearWeek = P("yw"); /* Check-ins for YYYY-WW (week-of-year) */ const char *zDay = P("ymd"); /* Check-ins for the day YYYY-MM-DD */ const char *zChng = P("chng"); /* List of GLOBs for files that changed */ int useDividers = P("nd")==0; /* Show dividers if "nd" is missing */ int renameOnly = P("namechng")!=0; /* Show only check-ins that rename files */ int forkOnly = PB("forks"); /* Show only forks and their children */ int bisectOnly = PB("bisect"); /* Show the check-ins of the bisect */ int tmFlags = 0; /* Timeline flags */ const char *zThisTag = 0; /* Suppress links to this tag */ | > > > | 1421 1422 1423 1424 1425 1426 1427 1428 1429 1430 1431 1432 1433 1434 1435 1436 1437 1438 | const char *zMatchDesc = 0; /* Tag match expression description text */ const char *zError = 0; /* Tag match error string */ const char *zTagSql = 0; /* Tag/branch match SQL expression */ const char *zSearch = P("s"); /* Search string */ const char *zUses = P("uf"); /* Only show check-ins hold this file */ const char *zYearMonth = P("ym"); /* Show check-ins for the given YYYY-MM */ const char *zYearWeek = P("yw"); /* Check-ins for YYYY-WW (week-of-year) */ char *zYearWeekStart = 0; /* YYYY-MM-DD for start of YYYY-WW */ const char *zDay = P("ymd"); /* Check-ins for the day YYYY-MM-DD */ const char *zNDays = P("days"); /* Show events over the previous N days */ int nDays = 0; /* Numeric value for zNDays */ const char *zChng = P("chng"); /* List of GLOBs for files that changed */ int useDividers = P("nd")==0; /* Show dividers if "nd" is missing */ int renameOnly = P("namechng")!=0; /* Show only check-ins that rename files */ int forkOnly = PB("forks"); /* Show only forks and their children */ int bisectOnly = PB("bisect"); /* Show the check-ins of the bisect */ int tmFlags = 0; /* Timeline flags */ const char *zThisTag = 0; /* Suppress links to this tag */ |
︙ | ︙ | |||
1433 1434 1435 1436 1437 1438 1439 1440 1441 1442 1443 1444 1445 1446 | double rBefore, rAfter, rCirca; /* Boundary times */ const char *z; char *zOlderButton = 0; /* URL for Older button at the bottom */ char *zNewerButton = 0; /* URL for Newer button at the top */ int selectedRid = -9999999; /* Show a highlight on this RID */ int disableY = 0; /* Disable type selector on submenu */ int advancedMenu = 0; /* Use the advanced menu design */ /* Set number of rows to display */ cookie_read_parameter("n","n"); z = P("n"); if( z==0 ) z = db_get("timeline-default-length",0); if( z ){ if( fossil_strcmp(z,"all")==0 ){ | > | 1447 1448 1449 1450 1451 1452 1453 1454 1455 1456 1457 1458 1459 1460 1461 | double rBefore, rAfter, rCirca; /* Boundary times */ const char *z; char *zOlderButton = 0; /* URL for Older button at the bottom */ char *zNewerButton = 0; /* URL for Newer button at the top */ int selectedRid = -9999999; /* Show a highlight on this RID */ int disableY = 0; /* Disable type selector on submenu */ int advancedMenu = 0; /* Use the advanced menu design */ char *zPlural; /* Ending for plural forms */ /* Set number of rows to display */ cookie_read_parameter("n","n"); z = P("n"); if( z==0 ) z = db_get("timeline-default-length",0); if( z ){ if( fossil_strcmp(z,"all")==0 ){ |
︙ | ︙ | |||
1465 1466 1467 1468 1469 1470 1471 | /* To view the timeline, must have permission to read project data. */ pd_rid = name_to_typed_rid(P("dp"),"ci"); if( pd_rid ){ p_rid = d_rid = pd_rid; } login_check_credentials(); | | | 1480 1481 1482 1483 1484 1485 1486 1487 1488 1489 1490 1491 1492 1493 1494 | /* To view the timeline, must have permission to read project data. */ pd_rid = name_to_typed_rid(P("dp"),"ci"); if( pd_rid ){ p_rid = d_rid = pd_rid; } login_check_credentials(); if( (!g.perm.Read && !g.perm.RdTkt && !g.perm.RdWiki && !g.perm.RdForum) || (bisectOnly && !g.perm.Setup) ){ login_needed(g.anon.Read && g.anon.RdTkt && g.anon.RdWiki); return; } cookie_read_parameter("y","y"); zType = P("y"); |
︙ | ︙ | |||
1731 1732 1733 1734 1735 1736 1737 | if( advancedMenu ){ style_submenu_checkbox("unhide", "Unhide", 0, 0); style_submenu_checkbox("v", "Files", (zType[0]!='a' && zType[0]!='c'),0); } }else{ /* Otherwise, a timeline based on a span of time */ int n; | | | 1746 1747 1748 1749 1750 1751 1752 1753 1754 1755 1756 1757 1758 1759 1760 | if( advancedMenu ){ style_submenu_checkbox("unhide", "Unhide", 0, 0); style_submenu_checkbox("v", "Files", (zType[0]!='a' && zType[0]!='c'),0); } }else{ /* Otherwise, a timeline based on a span of time */ int n; const char *zEType = "event"; char *zDate; Blob cond; blob_zero(&cond); if( zChng && *zChng ){ addFileGlobExclusion(zChng, &cond); tmFlags |= TIMELINE_DISJOINT; } |
︙ | ︙ | |||
1756 1757 1758 1759 1760 1761 1762 1763 1764 1765 1766 | blob_append_sql(&cond, " AND event.objid IN (SELECT rid FROM bilog) "); } if( zYearMonth ){ blob_append_sql(&cond, " AND %Q=strftime('%%Y-%%m',event.mtime) ", zYearMonth); } else if( zYearWeek ){ blob_append_sql(&cond, " AND %Q=strftime('%%Y-%%W',event.mtime) ", zYearWeek); } else if( zDay ){ | > > > > > > > > > > > > > > > > > > > > > > > > > | > > > > > > > > | 1771 1772 1773 1774 1775 1776 1777 1778 1779 1780 1781 1782 1783 1784 1785 1786 1787 1788 1789 1790 1791 1792 1793 1794 1795 1796 1797 1798 1799 1800 1801 1802 1803 1804 1805 1806 1807 1808 1809 1810 1811 1812 1813 1814 1815 1816 1817 1818 1819 1820 1821 1822 1823 | blob_append_sql(&cond, " AND event.objid IN (SELECT rid FROM bilog) "); } if( zYearMonth ){ blob_append_sql(&cond, " AND %Q=strftime('%%Y-%%m',event.mtime) ", zYearMonth); } else if( zYearWeek ){ char *z = db_text(0, "SELECT strftime('%%Y-%%W',%Q)", zYearWeek); if( z && z[0] ){ zYearWeekStart = db_text(0, "SELECT date(%Q,'-6 days','weekday 1')", zYearWeek); zYearWeek = z; }else{ if( strlen(zYearWeek)==7 ){ zYearWeekStart = db_text(0, "SELECT date('%.4q-01-01','+%d days','weekday 1')", zYearWeek, atoi(zYearWeek+5)*7); }else{ zYearWeekStart = 0; } if( zYearWeekStart==0 || zYearWeekStart[0]==0 ){ zYearWeekStart = db_text(0, "SELECT date('now','-6 days','weekday 1');"); zYearWeek = db_text(0, "SELECT strftime('%%Y-%%W','now','-6 days','weekday 1')"); } } blob_append_sql(&cond, " AND %Q=strftime('%%Y-%%W',event.mtime) ", zYearWeek); nEntry = -1; } else if( zDay ){ zDay = db_text(0, "SELECT date(%Q)", zDay); if( zDay==0 || zDay[0]==0 ){ zDay = db_text(0, "SELECT date('now')"); } blob_append_sql(&cond, " AND %Q=date(event.mtime) ", zDay); nEntry = -1; } else if( zNDays ){ nDays = atoi(zNDays); if( nDays<1 ) nDays = 1; blob_append_sql(&cond, " AND event.mtime>=julianday('now','-%d days') ", nDays); nEntry = -1; } if( zTagSql ){ blob_append_sql(&cond, " AND (EXISTS(SELECT 1 FROM tagxref NATURAL JOIN tag" " WHERE %s AND tagtype>0 AND rid=blob.rid)\n", zTagSql/*safe-for-%s*/); if( related ){ |
︙ | ︙ | |||
1809 1810 1811 1812 1813 1814 1815 1816 1817 1818 1819 1820 1821 1822 1823 1824 1825 1826 1827 1828 1829 1830 1831 1832 1833 1834 1835 1836 1837 1838 1839 1840 1841 | blob_append_sql(&cond, ")"); } if( (zType[0]=='w' && !g.perm.RdWiki) || (zType[0]=='t' && !g.perm.RdTkt) || (zType[0]=='e' && !g.perm.RdWiki) || (zType[0]=='c' && !g.perm.Read) || (zType[0]=='g' && !g.perm.Read) ){ zType = "all"; } if( zType[0]=='a' ){ if( !g.perm.Read || !g.perm.RdWiki || !g.perm.RdTkt ){ char cSep = '('; blob_append_sql(&cond, " AND event.type IN "); if( g.perm.Read ){ blob_append_sql(&cond, "%c'ci','g'", cSep); cSep = ','; } if( g.perm.RdWiki ){ blob_append_sql(&cond, "%c'w','e'", cSep); cSep = ','; } if( g.perm.RdTkt ){ blob_append_sql(&cond, "%c't'", cSep); cSep = ','; } blob_append_sql(&cond, ")"); } }else{ /* zType!="all" */ blob_append_sql(&cond, " AND event.type=%Q", zType); if( zType[0]=='c' ){ zEType = "check-in"; }else if( zType[0]=='w' ){ | > > > > > | > > | 1857 1858 1859 1860 1861 1862 1863 1864 1865 1866 1867 1868 1869 1870 1871 1872 1873 1874 1875 1876 1877 1878 1879 1880 1881 1882 1883 1884 1885 1886 1887 1888 1889 1890 1891 1892 1893 1894 1895 1896 1897 1898 1899 1900 1901 1902 1903 1904 1905 1906 1907 1908 1909 1910 | blob_append_sql(&cond, ")"); } if( (zType[0]=='w' && !g.perm.RdWiki) || (zType[0]=='t' && !g.perm.RdTkt) || (zType[0]=='e' && !g.perm.RdWiki) || (zType[0]=='c' && !g.perm.Read) || (zType[0]=='g' && !g.perm.Read) || (zType[0]=='f' && !g.perm.RdForum) ){ zType = "all"; } if( zType[0]=='a' ){ if( !g.perm.Read || !g.perm.RdWiki || !g.perm.RdTkt ){ char cSep = '('; blob_append_sql(&cond, " AND event.type IN "); if( g.perm.Read ){ blob_append_sql(&cond, "%c'ci','g'", cSep); cSep = ','; } if( g.perm.RdWiki ){ blob_append_sql(&cond, "%c'w','e'", cSep); cSep = ','; } if( g.perm.RdTkt ){ blob_append_sql(&cond, "%c't'", cSep); cSep = ','; } if( g.perm.RdForum ){ blob_append_sql(&cond, "%c'f'", cSep); cSep = ','; } blob_append_sql(&cond, ")"); } }else{ /* zType!="all" */ blob_append_sql(&cond, " AND event.type=%Q", zType); if( zType[0]=='c' ){ zEType = "check-in"; }else if( zType[0]=='w' ){ zEType = "wiki"; }else if( zType[0]=='t' ){ zEType = "ticket change"; }else if( zType[0]=='e' ){ zEType = "technical note"; }else if( zType[0]=='g' ){ zEType = "tag"; }else if( zType[0]=='f' ){ zEType = "forum post"; } } if( zUser ){ int n = db_int(0,"SELECT count(*) FROM event" " WHERE user=%Q OR euser=%Q", zUser, zUser); if( n<=nEntry ){ zCirca = zBefore = zAfter = 0; |
︙ | ︙ | |||
1909 1910 1911 1912 1913 1914 1915 1916 | }else{ blob_append_sql(&sql, " ORDER BY event.mtime DESC"); } if( nEntry>0 ) blob_append_sql(&sql, " LIMIT %d", nEntry); db_multi_exec("%s", blob_sql_text(&sql)); n = db_int(0, "SELECT count(*) FROM timeline WHERE etype!='div' /*scan*/"); if( zYearMonth ){ | > | | > > > | > | | | 1964 1965 1966 1967 1968 1969 1970 1971 1972 1973 1974 1975 1976 1977 1978 1979 1980 1981 1982 1983 1984 1985 1986 1987 1988 1989 1990 1991 1992 | }else{ blob_append_sql(&sql, " ORDER BY event.mtime DESC"); } if( nEntry>0 ) blob_append_sql(&sql, " LIMIT %d", nEntry); db_multi_exec("%s", blob_sql_text(&sql)); n = db_int(0, "SELECT count(*) FROM timeline WHERE etype!='div' /*scan*/"); zPlural = n==1 ? "" : "s"; if( zYearMonth ){ blob_appendf(&desc, "%d %s%s for %h", n, zEType, zPlural, zYearMonth); }else if( zYearWeek ){ blob_appendf(&desc, "%d %s%s for week %h beginning on %h", n, zEType, zPlural, zYearWeek, zYearWeekStart); }else if( zDay ){ blob_appendf(&desc, "%d %s%s occurring on %h", n, zEType, zPlural, zDay); }else if( zNDays ){ blob_appendf(&desc, "%d %s%s within the past %d day%s", n, zEType, zPlural, nDays, nDays>1 ? "s" : ""); }else if( zBefore==0 && zCirca==0 && n>=nEntry && nEntry>0 ){ blob_appendf(&desc, "%d most recent %s%s", n, zEType, zPlural); }else{ blob_appendf(&desc, "%d %s%s", n, zEType, zPlural); } if( zUses ){ char *zFilenames = names_of_file(zUses); blob_appendf(&desc, " using file %s version %z%S</a>", zFilenames, href("%R/artifact/%!S",zUses), zUses); tmFlags |= TIMELINE_DISJOINT; } |
︙ | ︙ | |||
2042 2043 2044 2045 2046 2047 2048 2049 2050 2051 2052 2053 2054 2055 | if( PB("showid") ) tmFlags |= TIMELINE_SHOWRID; if( useDividers && zMark && zMark[0] ){ double r = symbolic_name_to_mtime(zMark); if( r>0.0 ) selectedRid = timeline_add_divider(r); } blob_zero(&sql); db_prepare(&q, "SELECT * FROM timeline ORDER BY sortby DESC /*scan*/"); @ <h2>%b(&desc)</h2> blob_reset(&desc); /* Report any errors. */ if( zError ){ @ <p class="generalError">%h(zError)</p> } | > > > | 2102 2103 2104 2105 2106 2107 2108 2109 2110 2111 2112 2113 2114 2115 2116 2117 2118 | if( PB("showid") ) tmFlags |= TIMELINE_SHOWRID; if( useDividers && zMark && zMark[0] ){ double r = symbolic_name_to_mtime(zMark); if( r>0.0 ) selectedRid = timeline_add_divider(r); } blob_zero(&sql); db_prepare(&q, "SELECT * FROM timeline ORDER BY sortby DESC /*scan*/"); if( fossil_islower(desc.aData[0]) ){ desc.aData[0] = fossil_toupper(desc.aData[0]); } @ <h2>%b(&desc)</h2> blob_reset(&desc); /* Report any errors. */ if( zError ){ @ <p class="generalError">%h(zError)</p> } |
︙ | ︙ | |||
2283 2284 2285 2286 2287 2288 2289 2290 2291 2292 2293 2294 2295 2296 | ** Options: ** -n|--limit N If N is positive, output the first N entries. If ** N is negative, output the first -N lines. If N is ** zero, no limit. Default is -20 meaning 20 lines. ** -p|--path PATH Output items affecting PATH only. ** PATH can be a file or a sub directory. ** --offset P skip P changes ** -t|--type TYPE Output items from the given types only, such as: ** ci = file commits only ** e = technical notes only ** t = tickets only ** w = wiki commits only ** -v|--verbose Output the list of files changed by each commit ** and the type of each change (edited, deleted, | > | 2346 2347 2348 2349 2350 2351 2352 2353 2354 2355 2356 2357 2358 2359 2360 | ** Options: ** -n|--limit N If N is positive, output the first N entries. If ** N is negative, output the first -N lines. If N is ** zero, no limit. Default is -20 meaning 20 lines. ** -p|--path PATH Output items affecting PATH only. ** PATH can be a file or a sub directory. ** --offset P skip P changes ** --sql Show the SQL used to generate the timeline ** -t|--type TYPE Output items from the given types only, such as: ** ci = file commits only ** e = technical notes only ** t = tickets only ** w = wiki commits only ** -v|--verbose Output the list of files changed by each commit ** and the type of each change (edited, deleted, |
︙ | ︙ | |||
2314 2315 2316 2317 2318 2319 2320 2321 2322 2323 2324 2325 2326 2327 2328 2329 2330 2331 2332 2333 2334 2335 2336 2337 | int objid = 0; Blob uuid; int mode = TIMELINE_MODE_NONE; int verboseFlag = 0 ; int iOffset; const char *zFilePattern = 0; Blob treeName; verboseFlag = find_option("verbose","v", 0)!=0; if( !verboseFlag){ verboseFlag = find_option("showfiles","f", 0)!=0; /* deprecated */ } db_find_and_open_repository(0, 0); zLimit = find_option("limit","n",1); zWidth = find_option("width","W",1); zType = find_option("type","t",1); zFilePattern = find_option("path","p",1); if( !zLimit ){ zLimit = find_option("count",0,1); } if( zLimit ){ n = atoi(zLimit); }else{ | > > | 2378 2379 2380 2381 2382 2383 2384 2385 2386 2387 2388 2389 2390 2391 2392 2393 2394 2395 2396 2397 2398 2399 2400 2401 2402 2403 | int objid = 0; Blob uuid; int mode = TIMELINE_MODE_NONE; int verboseFlag = 0 ; int iOffset; const char *zFilePattern = 0; Blob treeName; int showSql = 0; verboseFlag = find_option("verbose","v", 0)!=0; if( !verboseFlag){ verboseFlag = find_option("showfiles","f", 0)!=0; /* deprecated */ } db_find_and_open_repository(0, 0); zLimit = find_option("limit","n",1); zWidth = find_option("width","W",1); zType = find_option("type","t",1); zFilePattern = find_option("path","p",1); showSql = find_option("sql",0,0)!=0; if( !zLimit ){ zLimit = find_option("count",0,1); } if( zLimit ){ n = atoi(zLimit); }else{ |
︙ | ︙ | |||
2467 2468 2469 2470 2471 2472 2473 | } blob_append_sql(&sql, "\nORDER BY event.mtime DESC"); if( iOffset>0 ){ /* Don't handle LIMIT here, otherwise print_timeline() * will not determine the end-marker correctly! */ blob_append_sql(&sql, "\n LIMIT -1 OFFSET %d", iOffset); } | > > > | | 2533 2534 2535 2536 2537 2538 2539 2540 2541 2542 2543 2544 2545 2546 2547 2548 2549 2550 | } blob_append_sql(&sql, "\nORDER BY event.mtime DESC"); if( iOffset>0 ){ /* Don't handle LIMIT here, otherwise print_timeline() * will not determine the end-marker correctly! */ blob_append_sql(&sql, "\n LIMIT -1 OFFSET %d", iOffset); } if( showSql ){ fossil_print("%s\n", blob_str(&sql)); } db_prepare_blob(&q, &sql); blob_reset(&sql); print_timeline(&q, n, width, verboseFlag); db_finalize(&q); } /* |
︙ | ︙ |
Changes to src/tkt.c.
︙ | ︙ | |||
370 371 372 373 374 375 376 | db_multi_exec( "DROP TABLE IF EXISTS ticket;" "DROP TABLE IF EXISTS ticketchng;" ); zSql = ticket_table_schema(); if( separateConnection ){ | | | 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 | db_multi_exec( "DROP TABLE IF EXISTS ticket;" "DROP TABLE IF EXISTS ticketchng;" ); zSql = ticket_table_schema(); if( separateConnection ){ if( db_transaction_nesting_depth() ) db_end_transaction(0); db_init_database(g.zRepositoryName, zSql, 0); }else{ db_multi_exec("%s", zSql/*safe-for-%s*/); } } /* |
︙ | ︙ | |||
592 593 594 595 596 597 598 | const char *zUuid; int i; int nJ = 0; Blob tktchng, cksum; int needMod; login_verify_csrf_secret(); | | | 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 | const char *zUuid; int i; int nJ = 0; Blob tktchng, cksum; int needMod; login_verify_csrf_secret(); if( !captcha_is_correct(0) ){ @ <p class="generalError">Error: Incorrect security code.</p> return TH_OK; } zUuid = (const char *)pUuid; blob_zero(&tktchng); zDate = date_in_standard_format("now"); blob_appendf(&tktchng, "D %s\n", zDate); |
︙ | ︙ |
Changes to src/translate.c.
︙ | ︙ | |||
201 202 203 204 205 206 207 208 209 210 | inPrint = 1; }else{ fprintf(out,"\n%*s\"%s%s\"",indent+5, "", zOut, zNewline); } } } } int main(int argc, char **argv){ if( argc==2 ){ | > > > > > > > > > > > > > > > > > > > > > > > > < < < < < < | < < < | 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 | inPrint = 1; }else{ fprintf(out,"\n%*s\"%s%s\"",indent+5, "", zOut, zNewline); } } } } static void print_source_ref(const char *zSrcFile, FILE *out){ /* Set source line reference to the original source file. * This makes compiler show the original file name in the compile error * messages, instead of referring to the translated file. * NOTE: This somewhat complicates stepping in debugger, as the resuling * code would not match the referenced sources. */ #ifndef FOSSIL_DEBUG const char *arg; if( !*zSrcFile ){ return; } fprintf(out,"#line 1 \""); for(arg=zSrcFile; *arg; arg++){ if( *arg!='\\' ){ fprintf(out,"%c", *arg); }else{ fprintf(out,"\\\\"); } } fprintf(out,"\"\n"); #endif } int main(int argc, char **argv){ if( argc==2 ){ FILE *in = fopen(argv[1], "r"); if( in==0 ){ fprintf(stderr,"can not open %s\n", argv[1]); exit(1); } zInFile = argv[1]; print_source_ref(zInFile, stdout); trans(in, stdout); fclose(in); }else{ trans(stdin, stdout); } return 0; } |
Changes to src/unicode.c.
︙ | ︙ | |||
45 46 47 48 49 50 51 | 0x0002D001, 0x0002D803, 0x0002EC01, 0x0002FC01, 0x00035C01, 0x0003DC01, 0x000B0804, 0x000B480E, 0x000B9407, 0x000BB401, 0x000BBC81, 0x000DD401, 0x000DF801, 0x000E1002, 0x000E1C01, 0x000FD801, 0x00120808, 0x00156806, 0x00162402, 0x00163403, 0x00164437, 0x0017CC02, 0x0018001D, 0x00187802, 0x00192C15, 0x0019A804, 0x0019C001, 0x001B5001, 0x001B580F, 0x001B9C07, 0x001BF402, 0x001C000E, 0x001C3C01, 0x001C4401, 0x001CC01B, | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | > | | | | | | | | > | | | | | | | > | < | | | | > | | 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 | 0x0002D001, 0x0002D803, 0x0002EC01, 0x0002FC01, 0x00035C01, 0x0003DC01, 0x000B0804, 0x000B480E, 0x000B9407, 0x000BB401, 0x000BBC81, 0x000DD401, 0x000DF801, 0x000E1002, 0x000E1C01, 0x000FD801, 0x00120808, 0x00156806, 0x00162402, 0x00163403, 0x00164437, 0x0017CC02, 0x0018001D, 0x00187802, 0x00192C15, 0x0019A804, 0x0019C001, 0x001B5001, 0x001B580F, 0x001B9C07, 0x001BF402, 0x001C000E, 0x001C3C01, 0x001C4401, 0x001CC01B, 0x001E980B, 0x001FAC09, 0x001FD804, 0x001FF403, 0x00205804, 0x00206C09, 0x00209403, 0x0020A405, 0x0020C00F, 0x00216403, 0x00217801, 0x00234C31, 0x0024E803, 0x0024F812, 0x00254407, 0x00258804, 0x0025C001, 0x00260403, 0x0026F001, 0x0026F807, 0x00271C02, 0x00272C03, 0x00275C01, 0x00278802, 0x0027C802, 0x0027E802, 0x0027F402, 0x00280403, 0x0028F001, 0x0028F805, 0x00291C02, 0x00292C03, 0x00294401, 0x0029C002, 0x0029D402, 0x002A0403, 0x002AF001, 0x002AF808, 0x002B1C03, 0x002B2C03, 0x002B8802, 0x002BC002, 0x002BE806, 0x002C0403, 0x002CF001, 0x002CF807, 0x002D1C02, 0x002D2C03, 0x002D5802, 0x002D8802, 0x002DC001, 0x002E0801, 0x002EF805, 0x002F1803, 0x002F2804, 0x002F5C01, 0x002FCC08, 0x00300005, 0x0030F807, 0x00311803, 0x00312804, 0x00315402, 0x00318802, 0x0031FC01, 0x00320404, 0x0032F001, 0x0032F807, 0x00331803, 0x00332804, 0x00335402, 0x00338802, 0x00340004, 0x0034EC02, 0x0034F807, 0x00351803, 0x00352804, 0x00353C01, 0x00355C01, 0x00358802, 0x0035E401, 0x00360802, 0x00372801, 0x00373C06, 0x00375801, 0x00376008, 0x0037C803, 0x0038C401, 0x0038D007, 0x0038FC01, 0x00391C09, 0x00396802, 0x003AC401, 0x003AD006, 0x003AEC02, 0x003B2006, 0x003C041F, 0x003CD00C, 0x003DC417, 0x003E340B, 0x003E6424, 0x003EF80F, 0x003F380D, 0x0040AC14, 0x00412806, 0x00415804, 0x00417803, 0x00418803, 0x00419C07, 0x0041C404, 0x0042080C, 0x00423C01, 0x00426806, 0x0043EC01, 0x004D740C, 0x004E400A, 0x00500001, 0x0059B402, 0x005A0001, 0x005A6C02, 0x005BAC03, 0x005C4803, 0x005CC805, 0x005D4802, 0x005DC802, 0x005ED023, 0x005F6004, 0x005F7401, 0x0060000F, 0x00621402, 0x0062A401, 0x0064800C, 0x0064C00C, 0x00650001, 0x00651002, 0x00677822, 0x00685C05, 0x00687802, 0x0069540A, 0x0069801D, 0x0069FC01, 0x006A8007, 0x006AA006, 0x006AC00F, 0x006C0005, 0x006CD011, 0x006D6823, 0x006E0003, 0x006E840D, 0x006F980E, 0x006FF004, 0x00709014, 0x0070EC05, 0x0071F802, 0x00730008, 0x00734019, 0x0073B401, 0x0073C803, 0x0073DC03, 0x0077003A, 0x0077EC05, 0x007EF401, 0x007EFC03, 0x007F3403, 0x007F7403, 0x007FB403, 0x007FF402, 0x00800065, 0x0081980A, 0x0081E805, 0x00822805, 0x00828020, 0x00834021, 0x00840002, 0x00840C04, 0x00842002, 0x00845001, 0x00845803, 0x00847806, 0x00849401, 0x00849C01, 0x0084A401, 0x0084B801, 0x0084E802, 0x00850005, 0x00852804, 0x00853C01, 0x00862802, 0x00864297, 0x0091000B, 0x0092704E, 0x00940276, 0x009E53E0, 0x00ADD820, 0x00AE6031, 0x00AF2835, 0x00B39406, 0x00B3BC03, 0x00B3E404, 0x00B3F802, 0x00B5C001, 0x00B5FC01, 0x00B7804F, 0x00B8C01F, 0x00BA001A, 0x00BA6C59, 0x00BC00D6, 0x00BFC00C, 0x00C00005, 0x00C02019, 0x00C0A807, 0x00C0D802, 0x00C0F403, 0x00C26404, 0x00C28001, 0x00C3EC01, 0x00C64002, 0x00C6580A, 0x00C70024, 0x00C8001F, 0x00C8A81E, 0x00C94001, 0x00C98020, 0x00CA2827, 0x00CB003F, 0x00CC0100, 0x01370040, 0x02924037, 0x0293F802, 0x02983403, 0x0299BC10, 0x029A7802, 0x029BC008, 0x029C0017, 0x029C8002, 0x029E2402, 0x02A00801, 0x02A01801, 0x02A02C01, 0x02A08C09, 0x02A0D804, 0x02A1D004, 0x02A20002, 0x02A2D012, 0x02A33802, 0x02A38012, 0x02A3E003, 0x02A3F001, 0x02A3FC01, 0x02A4980A, 0x02A51C0D, 0x02A57C01, 0x02A60004, 0x02A6CC1B, 0x02A77802, 0x02A79401, 0x02A8A40E, 0x02A90C01, 0x02A93002, 0x02A97004, 0x02A9DC03, 0x02A9EC03, 0x02AAC001, 0x02AAC803, 0x02AADC02, 0x02AAF802, 0x02AB0401, 0x02AB7802, 0x02ABAC07, 0x02ABD402, 0x02AD6C01, 0x02AF8C0B, 0x03600001, 0x036DFC02, 0x036FFC02, 0x037FFC01, 0x03EC7801, 0x03ECA401, 0x03EEC810, 0x03F4F802, 0x03F7F002, 0x03F8001A, 0x03F88033, 0x03F95013, 0x03F9A004, 0x03FBFC01, 0x03FC040F, 0x03FC6807, 0x03FCEC06, 0x03FD6C0B, 0x03FF8007, 0x03FFA007, 0x03FFE405, 0x04040003, 0x0404DC09, 0x0405E411, 0x04063003, 0x0406400C, 0x04068001, 0x0407402E, 0x040B8001, 0x040DD805, 0x040E7C01, 0x040F4001, 0x0415BC01, 0x04215C01, 0x0421DC02, 0x04247C01, 0x0424FC01, 0x04280403, 0x04281402, 0x04283004, 0x0428E003, 0x0428FC01, 0x04294009, 0x0429FC01, 0x042B2001, 0x042B9402, 0x042BC007, 0x042CE407, 0x042E6404, 0x04349004, 0x043D180B, 0x043D5405, 0x04400003, 0x0440E016, 0x0441FC04, 0x0442C012, 0x04433401, 0x04440003, 0x04449C0E, 0x04450004, 0x04451402, 0x0445CC03, 0x04460003, 0x0446CC0E, 0x04471409, 0x04476C01, 0x04477403, 0x0448B013, 0x044AA401, 0x044B7C0C, 0x044C0004, 0x044CEC02, 0x044CF807, 0x044D1C02, 0x044D2C03, 0x044D5C01, 0x044D8802, 0x044D9807, 0x044DC005, 0x0450D412, 0x04512C05, 0x04516C01, 0x04517402, 0x0452C014, 0x04531801, 0x0456BC07, 0x0456E020, 0x04577002, 0x0458C014, 0x0459800D, 0x045AAC0D, 0x045C740F, 0x045CF004, 0x0460B010, 0x0468040A, 0x0468CC07, 0x0468EC0D, 0x0469440B, 0x046A2813, 0x046A7805, 0x0470BC08, 0x0470E008, 0x04710405, 0x0471C002, 0x04724816, 0x0472A40E, 0x0474C406, 0x0474E801, 0x0474F002, 0x0474FC07, 0x04751C01, 0x04762805, 0x04764002, 0x04764C05, 0x047BCC06, 0x0491C005, 0x05A9B802, 0x05ABC006, 0x05ACC010, 0x05AD1002, 0x05BA5C04, 0x05BD442E, 0x05BE3C04, 0x06F27008, 0x074000F6, 0x07440027, 0x0744A4C0, 0x07480046, 0x074C0057, 0x075B0401, 0x075B6C01, 0x075BEC01, 0x075C5401, 0x075CD401, 0x075D3C01, 0x075DBC01, 0x075E2401, 0x075EA401, 0x075F0C01, 0x0760028C, 0x076A6C05, 0x076A840F, 0x07800007, 0x07802011, 0x07806C07, 0x07808C02, 0x07809805, 0x07A34007, 0x07A51007, 0x07A57802, 0x07B2B001, 0x07B2C001, 0x07BBC002, 0x07C0002C, 0x07C0C064, 0x07C2800F, 0x07C2C40F, 0x07C3040F, 0x07C34425, 0x07C4405C, 0x07C5C03D, 0x07C7981D, 0x07C8402C, 0x07C90009, 0x07C94002, 0x07C98006, 0x07CC03D5, 0x07DB800D, 0x07DBC00A, 0x07DC0074, 0x07DE0059, 0x07E0000C, 0x07E04038, 0x07E1400A, 0x07E18028, 0x07E2401E, 0x07E4000C, 0x07E4402F, 0x07E50031, 0x07E5CC04, 0x07E5E801, 0x07E5F027, 0x07E6C00A, 0x07E70003, 0x07E74030, 0x07E9800E, 0x38000401, 0x38008060, 0x380400F0, }; static const unsigned int aAscii[4] = { 0xFFFFFFFF, 0xFC00FFFF, 0xF8000001, 0xF8000001, }; if( (unsigned int)c<128 ){ return ( (aAscii[c >> 5] & ((unsigned int)1 << (c & 0x001F)))==0 ); |
︙ | ︙ | |||
265 266 267 268 269 270 271 | static const struct TableEntry { unsigned short iCode; unsigned char flags; unsigned char nRange; } aEntry[] = { {65, 14, 26}, {181, 66, 1}, {192, 14, 23}, {216, 14, 7}, {256, 1, 48}, {306, 1, 6}, | | | | | | | | | | | | | > | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | 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 | static const struct TableEntry { unsigned short iCode; unsigned char flags; unsigned char nRange; } aEntry[] = { {65, 14, 26}, {181, 66, 1}, {192, 14, 23}, {216, 14, 7}, {256, 1, 48}, {306, 1, 6}, {313, 1, 16}, {330, 1, 46}, {376, 152, 1}, {377, 1, 6}, {383, 140, 1}, {385, 52, 1}, {386, 1, 4}, {390, 46, 1}, {391, 0, 1}, {393, 44, 2}, {395, 0, 1}, {398, 34, 1}, {399, 40, 1}, {400, 42, 1}, {401, 0, 1}, {403, 44, 1}, {404, 48, 1}, {406, 54, 1}, {407, 50, 1}, {408, 0, 1}, {412, 54, 1}, {413, 56, 1}, {415, 58, 1}, {416, 1, 6}, {422, 62, 1}, {423, 0, 1}, {425, 62, 1}, {428, 0, 1}, {430, 62, 1}, {431, 0, 1}, {433, 60, 2}, {435, 1, 4}, {439, 64, 1}, {440, 0, 1}, {444, 0, 1}, {452, 2, 1}, {453, 0, 1}, {455, 2, 1}, {456, 0, 1}, {458, 2, 1}, {459, 1, 18}, {478, 1, 18}, {497, 2, 1}, {498, 1, 4}, {502, 158, 1}, {503, 170, 1}, {504, 1, 40}, {544, 146, 1}, {546, 1, 18}, {570, 74, 1}, {571, 0, 1}, {573, 144, 1}, {574, 72, 1}, {577, 0, 1}, {579, 142, 1}, {580, 30, 1}, {581, 32, 1}, {582, 1, 10}, {837, 38, 1}, {880, 1, 4}, {886, 0, 1}, {895, 38, 1}, {902, 20, 1}, {904, 18, 3}, {908, 28, 1}, {910, 26, 2}, {913, 14, 17}, {931, 14, 9}, {962, 0, 1}, {975, 4, 1}, {976, 176, 1}, {977, 178, 1}, {981, 182, 1}, {982, 180, 1}, {984, 1, 24}, {1008, 172, 1}, {1009, 174, 1}, {1012, 166, 1}, {1013, 164, 1}, {1015, 0, 1}, {1017, 188, 1}, {1018, 0, 1}, {1021, 146, 3}, {1024, 36, 16}, {1040, 14, 32}, {1120, 1, 34}, {1162, 1, 54}, {1216, 6, 1}, {1217, 1, 14}, {1232, 1, 96}, {1329, 24, 38}, {4256, 70, 38}, {4295, 70, 1}, {4301, 70, 1}, {5112, 186, 6}, {7296, 122, 1}, {7297, 124, 1}, {7298, 126, 1}, {7299, 130, 2}, {7301, 128, 1}, {7302, 132, 1}, {7303, 134, 1}, {7304, 96, 1}, {7312, 138, 43}, {7357, 138, 3}, {7680, 1, 150}, {7835, 168, 1}, {7838, 116, 1}, {7840, 1, 96}, {7944, 186, 8}, {7960, 186, 6}, {7976, 186, 8}, {7992, 186, 8}, {8008, 186, 6}, {8025, 187, 8}, {8040, 186, 8}, {8072, 186, 8}, {8088, 186, 8}, {8104, 186, 8}, {8120, 186, 2}, {8122, 162, 2}, {8124, 184, 1}, {8126, 120, 1}, {8136, 160, 4}, {8140, 184, 1}, {8152, 186, 2}, {8154, 156, 2}, {8168, 186, 2}, {8170, 154, 2}, {8172, 188, 1}, {8184, 148, 2}, {8186, 150, 2}, {8188, 184, 1}, {8486, 118, 1}, {8490, 112, 1}, {8491, 114, 1}, {8498, 12, 1}, {8544, 8, 16}, {8579, 0, 1}, {9398, 10, 26}, {11264, 24, 47}, {11360, 0, 1}, {11362, 108, 1}, {11363, 136, 1}, {11364, 110, 1}, {11367, 1, 6}, {11373, 104, 1}, {11374, 106, 1}, {11375, 100, 1}, {11376, 102, 1}, {11378, 0, 1}, {11381, 0, 1}, {11390, 98, 2}, {11392, 1, 100}, {11499, 1, 4}, {11506, 0, 1}, {42560, 1, 46}, {42624, 1, 28}, {42786, 1, 14}, {42802, 1, 62}, {42873, 1, 4}, {42877, 94, 1}, {42878, 1, 10}, {42891, 0, 1}, {42893, 86, 1}, {42896, 1, 4}, {42902, 1, 20}, {42922, 80, 1}, {42923, 76, 1}, {42924, 78, 1}, {42925, 82, 1}, {42926, 80, 1}, {42928, 90, 1}, {42929, 84, 1}, {42930, 88, 1}, {42931, 68, 1}, {42932, 1, 6}, {43888, 92, 80}, {65313, 14, 26}, }; static const unsigned short aiOff[] = { 1, 2, 8, 15, 16, 26, 28, 32, 34, 37, 38, 40, 48, 63, 64, 69, 71, 79, 80, 116, 202, 203, 205, 206, 207, 209, 210, 211, 213, 214, 217, 218, 219, 775, 928, 7264, 10792, 10795, 23217, 23221, 23228, 23231, 23254, 23256, 23275, 23278, 26672, 30204, 35267, 54721, 54753, 54754, 54756, 54787, 54793, 54809, 57153, 57274, 57921, 58019, 58363, 59314, 59315, 59324, 59325, 59326, 59332, 59356, 61722, 62528, 65268, 65341, 65373, 65406, 65408, 65410, 65415, 65424, 65436, 65439, 65450, 65462, 65472, 65476, 65478, 65480, 65482, 65488, 65506, 65511, 65514, 65521, 65527, 65528, 65529, }; int ret = c; assert( sizeof(unsigned short)==2 && sizeof(unsigned char)==1 ); if( c<128 ){ |
︙ | ︙ | |||
385 386 387 388 389 390 391 392 393 394 395 396 397 398 | ret = c + 40; } else if( c>=68736 && c<68787 ){ ret = c + 64; } else if( c>=71840 && c<71872 ){ ret = c + 32; } else if( c>=125184 && c<125218 ){ ret = c + 34; } return ret; } | > > > | 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 | ret = c + 40; } else if( c>=68736 && c<68787 ){ ret = c + 64; } else if( c>=71840 && c<71872 ){ ret = c + 32; } else if( c>=93760 && c<93792 ){ ret = c + 32; } else if( c>=125184 && c<125218 ){ ret = c + 34; } return ret; } |
Changes to src/unversioned.c.
︙ | ︙ | |||
216 217 218 219 220 221 222 | ** Unversioned files (UV-files) are artifacts that are synced and are available ** for download but which do not preserve history. Only the most recent version ** of each UV-file is retained. Changes to an UV-file are permanent and cannot ** be undone, so use appropriate caution with this command. ** ** Subcommands: ** | | | | | > > | > > > | | | | | | | | | | | | | | | | | | | | | | | | | | 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 | ** Unversioned files (UV-files) are artifacts that are synced and are available ** for download but which do not preserve history. Only the most recent version ** of each UV-file is retained. Changes to an UV-file are permanent and cannot ** be undone, so use appropriate caution with this command. ** ** Subcommands: ** ** add FILE ... Add or update one or more unversioned files in ** the local repository so that they match FILEs ** on disk. Changes are not pushed to other ** repositories until the next sync. ** ** add FILE --as UVFILE Add or update a single file named FILE on disk ** and UVFILE in the repository unversioned file ** namespace. This variant of the 'add' command allows ** the name to be different in the repository versus ** what appears on disk, but it only allows adding ** a single file at a time. ** ** cat FILE ... Concatenate the content of FILEs to stdout. ** ** edit FILE Bring up FILE in a text editor for modification. ** ** export FILE OUTPUT Write the content of FILE into OUTPUT on disk ** ** list | ls Show all unversioned files held in the local ** repository. ** ** revert ?URL? Restore the state of all unversioned files in the ** local repository to match the remote repository ** URL. ** ** Options: ** -v|--verbose Extra diagnostic output ** -n|--dryrun Show what would have happened ** ** remove|rm|delete FILE ... ** Remove unversioned files from the local repository. ** Changes are not pushed to other repositories until ** the next sync. ** ** sync ?URL? Synchronize the state of all unversioned files with ** the remote repository URL. The most recent version ** of each file is propagated to all repositories and ** all prior versions are permanently forgotten. ** ** Options: ** -v|--verbose Extra diagnostic output ** -n|--dryrun Show what would have happened ** ** touch FILE ... Update the TIMESTAMP on all of the listed files ** ** Options: ** ** --mtime TIMESTAMP Use TIMESTAMP instead of "now" for the "add", ** "edit", "remove", and "touch" subcommands. */ void unversioned_cmd(void){ const char *zCmd; int nCmd; const char *zMtime = find_option("mtime", 0, 1); sqlite3_int64 mtime; db_find_and_open_repository(0, 0); |
︙ | ︙ |
Changes to src/url.c.
︙ | ︙ | |||
530 531 532 533 534 535 536 537 538 539 540 541 542 543 | blob_appendf(&p->url, "%s%s", zSep, p->azName[i]); if( z && z[0] ) blob_appendf(&p->url, "=%T", z); zSep = "&"; } if( zName1 && zValue1 ){ blob_appendf(&p->url, "%s%s", zSep, zName1); if( zValue1[0] ) blob_appendf(&p->url, "=%T", zValue1); } if( zName2 && zValue2 ){ blob_appendf(&p->url, "%s%s", zSep, zName2); if( zValue2[0] ) blob_appendf(&p->url, "=%T", zValue2); } return blob_str(&p->url); } | > | 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 | blob_appendf(&p->url, "%s%s", zSep, p->azName[i]); if( z && z[0] ) blob_appendf(&p->url, "=%T", z); zSep = "&"; } if( zName1 && zValue1 ){ blob_appendf(&p->url, "%s%s", zSep, zName1); if( zValue1[0] ) blob_appendf(&p->url, "=%T", zValue1); zSep = "&"; } if( zName2 && zValue2 ){ blob_appendf(&p->url, "%s%s", zSep, zName2); if( zValue2[0] ) blob_appendf(&p->url, "=%T", zValue2); } return blob_str(&p->url); } |
︙ | ︙ |
Changes to src/util.c.
︙ | ︙ | |||
39 40 41 42 43 44 45 46 47 48 49 50 51 52 | /* ** Exit. Take care to close the database first. */ NORETURN void fossil_exit(int rc){ db_close(1); exit(rc); } /* ** Malloc and free routines that cannot fail */ void *fossil_malloc(size_t n){ | > > > > > > | 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 | /* ** Exit. Take care to close the database first. */ NORETURN void fossil_exit(int rc){ db_close(1); #ifndef _WIN32 if( g.fAnyTrace ){ fprintf(stderr, "/***** Subprocess %d exit(%d) *****/\n", getpid(), rc); fflush(stderr); } #endif exit(rc); } /* ** Malloc and free routines that cannot fail */ void *fossil_malloc(size_t n){ |
︙ | ︙ | |||
90 91 92 93 94 95 96 | fossil_get_page_size(&pageSize); assert( pageSize>0 ); assert( pageSize%2==0 ); #if defined(_WIN32) p = VirtualAlloc(NULL, pageSize, MEM_COMMIT|MEM_RESERVE, PAGE_READWRITE); if( p==NULL ){ | | | | | | | | | | 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 | fossil_get_page_size(&pageSize); assert( pageSize>0 ); assert( pageSize%2==0 ); #if defined(_WIN32) p = VirtualAlloc(NULL, pageSize, MEM_COMMIT|MEM_RESERVE, PAGE_READWRITE); if( p==NULL ){ fossil_panic("VirtualAlloc failed: %lu\n", GetLastError()); } if( !VirtualLock(p, pageSize) ){ fossil_panic("VirtualLock failed: %lu\n", GetLastError()); } #elif defined(USE_MMAN_H) p = mmap(0, pageSize, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0); if( p==MAP_FAILED ){ fossil_panic("mmap failed: %d\n", errno); } if( mlock(p, pageSize) ){ fossil_panic("mlock failed: %d\n", errno); } #else p = fossil_malloc(pageSize); #endif fossil_secure_zero(p, pageSize); if( pN ) *pN = pageSize; return p; } void fossil_secure_free_page(void *p, size_t n){ if( !p ) return; assert( n>0 ); fossil_secure_zero(p, n); #if defined(_WIN32) if( !VirtualUnlock(p, n) ){ fossil_panic("VirtualUnlock failed: %lu\n", GetLastError()); } if( !VirtualFree(p, 0, MEM_RELEASE) ){ fossil_panic("VirtualFree failed: %lu\n", GetLastError()); } #elif defined(USE_MMAN_H) if( munlock(p, n) ){ fossil_panic("munlock failed: %d\n", errno); } if( munmap(p, n) ){ fossil_panic("munmap failed: %d\n", errno); } #else fossil_free(p); #endif } /* |
︙ | ︙ | |||
314 315 316 317 318 319 320 | ** fossil_timer_start() was called and returned the given timer ID (or ** since it was last reset). Returns 0 if timerId is out of range. */ sqlite3_uint64 fossil_timer_fetch(int timerId){ if( timerId>0 && timerId<=FOSSIL_TIMER_COUNT ){ struct FossilTimer * start = &fossilTimerList[timerId-1]; if( !start->id ){ | | | | 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 | ** fossil_timer_start() was called and returned the given timer ID (or ** since it was last reset). Returns 0 if timerId is out of range. */ sqlite3_uint64 fossil_timer_fetch(int timerId){ if( timerId>0 && timerId<=FOSSIL_TIMER_COUNT ){ struct FossilTimer * start = &fossilTimerList[timerId-1]; if( !start->id ){ fossil_panic("Invalid call to fetch a non-allocated " "timer (#%d)", timerId); /*NOTREACHED*/ }else{ sqlite3_uint64 eu = 0, es = 0; fossil_cpu_times( &eu, &es ); return (eu - start->u) + (es - start->s); } } return 0; } /* ** Resets the timer associated with the given ID, as obtained via ** fossil_timer_start(), to the current CPU time values. */ sqlite3_uint64 fossil_timer_reset(int timerId){ if( timerId>0 && timerId<=FOSSIL_TIMER_COUNT ){ struct FossilTimer * start = &fossilTimerList[timerId-1]; if( !start->id ){ fossil_panic("Invalid call to reset a non-allocated " "timer (#%d)", timerId); /*NOTREACHED*/ }else{ sqlite3_uint64 const rc = fossil_timer_fetch(timerId); fossil_cpu_times( &start->u, &start->s ); return rc; } |
︙ | ︙ | |||
512 513 514 515 516 517 518 | ** ** On platforms that have pledge(), use this routine. ** On all other platforms, this routine does not exist, but instead ** a macro defined in config.h is used to provide a no-op. */ void fossil_pledge(const char *promises){ if( pledge(promises, 0) ){ | | | 518 519 520 521 522 523 524 525 526 527 528 529 | ** ** On platforms that have pledge(), use this routine. ** On all other platforms, this routine does not exist, but instead ** a macro defined in config.h is used to provide a no-op. */ void fossil_pledge(const char *promises){ if( pledge(promises, 0) ){ fossil_panic("pledge(\"%s\",NULL) fails with errno=%d", promises, (int)errno); } } #endif /* defined(HAVE_PLEDGE) */ |
Changes to src/verify.c.
︙ | ︙ | |||
43 44 45 46 47 48 49 | blob_zero(&uuid); db_blob(&uuid, "SELECT uuid FROM blob WHERE rid=%d", rid); if( !hname_validate(blob_buffer(&uuid), blob_size(&uuid)) ){ fossil_fatal("not a valid rid: %d", rid); } if( content_get(rid, &content) ){ if( !hname_verify_hash(&content, blob_buffer(&uuid), blob_size(&uuid)) ){ | | | 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 | blob_zero(&uuid); db_blob(&uuid, "SELECT uuid FROM blob WHERE rid=%d", rid); if( !hname_validate(blob_buffer(&uuid), blob_size(&uuid)) ){ fossil_fatal("not a valid rid: %d", rid); } if( content_get(rid, &content) ){ if( !hname_verify_hash(&content, blob_buffer(&uuid), blob_size(&uuid)) ){ fossil_panic("hash of rid %d does not match its uuid (%b)", rid, &uuid); } blob_reset(&content); } blob_reset(&uuid); } |
︙ | ︙ |
Added src/webmail.c.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 | /* ** Copyright (c) 2018 D. Richard Hipp ** ** This program is free software; you can redistribute it and/or ** modify it under the terms of the Simplified BSD License (also ** known as the "2-Clause License" or "FreeBSD License".) ** ** This program is distributed in the hope that it will be useful, ** but without any warranty; without even the implied warranty of ** merchantability or fitness for a particular purpose. ** ** Author contact information: ** drh@hwaci.com ** http://www.hwaci.com/drh/ ** ******************************************************************************* ** ** Implementation of web pages for managing the email storage tables ** (if they exist): ** ** emailbox ** emailblob ** emailroute */ #include "config.h" #include "webmail.h" #include <assert.h> #if INTERFACE /* Recognized content encodings */ #define EMAILENC_NONE 0 /* No encoding */ #define EMAILENC_B64 1 /* Base64 encoded */ #define EMAILENC_QUOTED 2 /* Quoted printable */ /* An instance of the following object records the location of important ** attributes on a single element in a multipart email message body. */ struct EmailBody { char zMimetype[32]; /* Mimetype */ u8 encoding; /* Type of encoding */ char *zFilename; /* From content-disposition: */ char *zContent; /* Content. \0 terminator inserted */ }; /* ** An instance of the following object describes the struture of ** an rfc-2822 email message. */ struct EmailToc { int nHdr; /* Number of header lines */ int nHdrAlloc; /* Number of header lines allocated */ char **azHdr; /* Pointer to header line. \0 terminator inserted */ int nBody; /* Number of body segments */ int nBodyAlloc; /* Number of body segments allocated */ EmailBody *aBody; /* Location of body information */ }; #endif /* ** Free An EmailToc object */ void emailtoc_free(EmailToc *p){ int i; fossil_free(p->azHdr); for(i=0; i<p->nBody; i++){ fossil_free(p->aBody[i].zFilename); } fossil_free(p->aBody); fossil_free(p); } /* ** Allocate a new EmailToc object */ EmailToc *emailtoc_alloc(void){ EmailToc *p = fossil_malloc( sizeof(*p) ); memset(p, 0, sizeof(*p)); return p; } /* ** Add a new body element to an EmailToc. */ EmailBody *emailtoc_new_body(EmailToc *p){ EmailBody *pNew; p->nBody++; if( p->nBody>p->nBodyAlloc ){ p->nBodyAlloc = (p->nBodyAlloc+1)*2; p->aBody = fossil_realloc(p->aBody, sizeof(p->aBody[0])*p->nBodyAlloc); } pNew = &p->aBody[p->nBody-1]; memset(pNew, 0, sizeof(*pNew)); return pNew; } /* ** Add a new header line to the EmailToc. */ void emailtoc_new_header_line(EmailToc *p, char *z){ p->nHdr++; if( p->nHdr>p->nHdrAlloc ){ p->nHdrAlloc = (p->nHdrAlloc+1)*2; p->azHdr = fossil_realloc(p->azHdr, sizeof(p->azHdr[0])*p->nHdrAlloc); } p->azHdr[p->nHdr-1] = z; } /* ** Return the length of a line in an email header. Continuation lines ** are included. Hence, this routine returns the number of bytes up to ** and including the first \n character that is followed by something ** other than whitespace. */ static int email_line_length(const char *z){ int i; for(i=0; z[i] && (z[i]!='\n' || z[i+1]==' ' || z[i+1]=='\t'); i++){} if( z[i]=='\n' ) i++; return i; } /* ** Look for a parameter of the form NAME=VALUE in the given email ** header line. Return a copy of VALUE in space obtained from ** fossil_malloc(). Or return NULL if there is no such parameter. */ static char *email_hdr_value(const char *z, const char *zName){ int nName = (int)strlen(zName); int i; const char *z2 = strstr(z, zName); if( z2==0 ) return 0; z2 += nName; if( z2[0]!='=' ) return 0; z2++; if( z2[0]=='"' ){ z2++; for(i=0; z2[i] && z2[i]!='"'; i++){} if( z2[i]!='"' ) return 0; }else{ for(i=0; z2[i] && !fossil_isspace(z2[i]); i++){} } return mprintf("%.*s", i, z2); } /* ** Return a pointer to the first non-whitespace character in z */ static const char *firstToken(const char *z){ while( fossil_isspace(*z) ){ z++; } return z; } /* ** The n-bytes of content in z is a single multipart mime segment ** with its own header and body. Decode this one segment and add it to p; ** ** Rows of the header of the segment are added to p if bAddHeader is ** true. */ LOCAL void emailtoc_add_multipart_segment( EmailToc *p, /* Append the segments here */ char *z, /* The body component */ int bAddHeader /* True to add header lines to p */ ){ int i, j; int n; int multipartBody = 0; EmailBody *pBody = emailtoc_new_body(p); i = 0; while( z[i] ){ n = email_line_length(&z[i]); if( (n==2 && z[i]=='\r' && z[i+1]=='\n') || z[i]=='\n' || n==0 ){ /* This is the blank line at the end of the header */ i += n; break; } for(j=i+n; j>i && fossil_isspace(z[j-1]); j--){} z[j] = 0; if( sqlite3_strnicmp(z+i, "Content-Type:", 13)==0 ){ const char *z2 = firstToken(z+i+13); if( z2 && strncmp(z2, "multipart/", 10)==0 ){ multipartBody = 1; }else{ int j; for(j=0; z2[j]=='/' || fossil_isalnum(z2[j]); j++){} if( j>=sizeof(pBody->zMimetype) ) j = sizeof(pBody->zMimetype); memcpy(pBody->zMimetype, z2, j); pBody->zMimetype[j] = 0; } } /* 123456789 123456789 123456 */ if( sqlite3_strnicmp(z+i, "Content-Transfer-Encoding:", 26)==0 ){ const char *z2 = firstToken(z+(i+26)); if( z2 && sqlite3_strnicmp(z2, "base64", 6)==0 ){ pBody->encoding = EMAILENC_B64; /* 123456789 123456 */ }else if( sqlite3_strnicmp(z2, "quoted-printable", 16)==0 ){ pBody->encoding = EMAILENC_QUOTED; }else{ pBody->encoding = EMAILENC_NONE; } } if( bAddHeader ){ emailtoc_new_header_line(p, z+i); }else if( sqlite3_strnicmp(z+i, "Content-Disposition:", 20)==0 ){ /* 123456789 123456789 */ fossil_free(pBody->zFilename); pBody->zFilename = email_hdr_value(z+i, "filename"); } i += n; } if( multipartBody ){ p->nBody--; emailtoc_add_multipart(p, z+i); }else{ pBody->zContent = z+i; } } /* ** The n-bytes of content in z are a multipart/ body component for ** an email message. Decode this into its individual segments. ** ** The component should start and end with a boundary line. There ** may be additional boundary lines in the middle. */ LOCAL void emailtoc_add_multipart( EmailToc *p, /* Append the segments here */ char *z /* The body component. zero-terminated */ ){ int nB; /* Size of the boundary string */ int iStart; /* Start of the coding region past boundary mark */ int i; /* Loop index */ char *zBoundary = 0; /* Boundary marker */ /* Skip forward to the beginning of the boundary mark. The boundary ** mark always begins with "--" */ while( z[0]!='-' || z[1]!='-' ){ while( z[0] && z[0]!='\n' ) z++; if( z[0]==0 ) return; z++; } /* Find the length of the boundary mark. */ zBoundary = z; for(nB=0; z[nB] && !fossil_isspace(z[nB]); nB++){} if( nB==0 ) return; z += nB; while( fossil_isspace(z[0]) ) z++; zBoundary[nB] = 0; for(i=iStart=0; z[i]; i++){ if( z[i]=='\n' && strncmp(z+i+1, zBoundary, nB)==0 ){ z[i+1] = 0; emailtoc_add_multipart_segment(p, z+iStart, 0); iStart = i+nB; if( z[iStart]=='-' && z[iStart+1]=='-' ) return; while( fossil_isspace(z[iStart]) ) iStart++; i = iStart; } } } /* ** Compute a table-of-contents (EmailToc) for the email message ** provided on the input. ** ** This routine will cause pEmail to become zero-terminated if it is ** not already. It will also insert zero characters into parts of ** the message, to delimit the various components. */ EmailToc *emailtoc_from_email(Blob *pEmail){ char *z; EmailToc *p = emailtoc_alloc(); blob_terminate(pEmail); z = blob_buffer(pEmail); emailtoc_add_multipart_segment(p, z, 1); return p; } /* ** Inplace-unfolding of an email header line. ** ** Actually - this routine works by converting all contiguous sequences ** of whitespace into a single space character. */ static void email_hdr_unfold(char *z){ int i, j; char c; for(i=j=0; (c = z[i])!=0; i++){ if( fossil_isspace(c) ){ c = ' '; if( j && z[j-1]==' ' ) continue; } z[j++] = c; } z[j] = 0; } /* ** COMMAND: test-decode-email ** ** Usage: %fossil test-decode-email FILE ** ** Read an rfc-2822 formatted email out of FILE, then write a decoding ** to stdout. Use for testing and validating the email decoder. */ void test_email_decode_cmd(void){ Blob email; EmailToc *p; int i; verify_all_options(); if( g.argc!=3 ) usage("FILE"); blob_read_from_file(&email, g.argv[2], ExtFILE); p = emailtoc_from_email(&email); fossil_print("%d header line and %d content segments\n", p->nHdr, p->nBody); for(i=0; i<p->nHdr; i++){ email_hdr_unfold(p->azHdr[i]); fossil_print("%3d: %s\n", i, p->azHdr[i]); } for(i=0; i<p->nBody; i++){ fossil_print("\nBODY %d mime \"%s\" encoding %d", i, p->aBody[i].zMimetype, p->aBody[i].encoding); if( p->aBody[i].zFilename ){ fossil_print(" filename \"%s\"", p->aBody[i].zFilename); } fossil_print("\n"); if( strncmp(p->aBody[i].zMimetype,"text/",5)!=0 ) continue; switch( p->aBody[i].encoding ){ case EMAILENC_B64: { int n = 0; decodeBase64(p->aBody[i].zContent, &n, p->aBody[i].zContent); fossil_print("%s", p->aBody[i].zContent); if( n && p->aBody[i].zContent[n-1]!='\n' ) fossil_print("\n"); break; } case EMAILENC_QUOTED: { int n = 0; decodeQuotedPrintable(p->aBody[i].zContent, &n); fossil_print("%s", p->aBody[i].zContent); if( n && p->aBody[i].zContent[n-1]!='\n' ) fossil_print("\n"); break; } default: { fossil_print("%s\n", p->aBody[i].zContent); break; } } } emailtoc_free(p); blob_reset(&email); } /* ** Add the select/option box to the timeline submenu that shows ** the various email message formats. */ static void webmail_f_submenu(void){ static const char *az[] = { "0", "Normal", "1", "Decoded", "2", "Raw", }; style_submenu_multichoice("f", sizeof(az)/(2*sizeof(az[0])), az, 0); } /* ** If the first N characters of z[] are the name of a header field ** that should be shown in "Normal" mode, then return 1. */ static int webmail_normal_header(const char *z, int N){ static const char *az[] = { "To", "Cc", "Bcc", "Date", "From", "Subject", }; int i; for(i=0; i<sizeof(az)/sizeof(az[0]); i++){ if( sqlite3_strnicmp(z, az[i], N)==0 ) return 1; } return 0; } /* ** Paint a page showing a single email message */ static void webmail_show_one_message( HQuery *pUrl, /* Calling context */ int emailid, /* emailbox.ebid to display */ const char *zUser /* User who owns it, or NULL if does not matter */ ){ Blob sql; Stmt q; int eState = -1; int eTranscript = 0; char zENum[30]; style_submenu_element("Index", "%s", url_render(pUrl,"id",0,0,0)); webmail_f_submenu(); blob_init(&sql, 0, 0); db_begin_transaction(); blob_append_sql(&sql, "SELECT decompress(etxt), estate, emailblob.ets" " FROM emailblob, emailbox" " WHERE emailid=emsgid AND ebid=%d", emailid ); if( zUser ) blob_append_sql(&sql, " AND euser=%Q", zUser); db_prepare_blob(&q, &sql); blob_reset(&sql); style_header("Message %d",emailid); if( db_step(&q)==SQLITE_ROW ){ Blob msg = db_column_text_as_blob(&q, 0); int eFormat = atoi(PD("f","0")); eState = db_column_int(&q, 1); eTranscript = db_column_int(&q, 2); if( eFormat==2 ){ @ <pre>%h(db_column_text(&q, 0))</pre> }else{ EmailToc *p = emailtoc_from_email(&msg); int i, j; @ <p> for(i=0; i<p->nHdr; i++){ char *z = p->azHdr[i]; email_hdr_unfold(z); for(j=0; z[j] && z[j]!=':'; j++){} if( eFormat==0 && !webmail_normal_header(z, j) ) continue; if( z[j]!=':' ){ @ %h(z)<br> }else{ z[j] = 0; @ <b>%h(z):</b> %h(z+j+1)<br> } } for(i=0; i<p->nBody; i++){ @ <hr><b>Messsage Body #%d(i): %h(p->aBody[i].zMimetype) \ if( p->aBody[i].zFilename ){ @ "%h(p->aBody[i].zFilename)" } @ </b> if( eFormat==0 ){ if( strncmp(p->aBody[i].zMimetype, "text/plain", 10)!=0 ) continue; if( p->aBody[i].zFilename ) continue; }else{ if( strncmp(p->aBody[i].zMimetype, "text/", 5)!=0 ) continue; } switch( p->aBody[i].encoding ){ case EMAILENC_B64: { int n = 0; decodeBase64(p->aBody[i].zContent, &n, p->aBody[i].zContent); break; } case EMAILENC_QUOTED: { int n = 0; decodeQuotedPrintable(p->aBody[i].zContent, &n); break; } } @ <pre>%h(p->aBody[i].zContent)</pre> } } } db_finalize(&q); /* Optionally show the SMTP transcript */ if( eTranscript>0 && db_exists("SELECT 1 FROM emailblob WHERE emailid=%d", eTranscript) ){ if( P("ts")==0 ){ sqlite3_snprintf(sizeof(zENum), zENum, "%d", emailid); style_submenu_element("SMTP Transcript","%s", url_render(pUrl, "ts", "1", "id", zENum)); }else{ db_prepare(&q, "SELECT decompress(etxt) FROM emailblob WHERE emailid=%d", eTranscript ); if( db_step(&q)==SQLITE_ROW ){ const char *zTranscript = db_column_text(&q, 0); @ <hr> @ <pre>%h(zTranscript)</pre> } db_finalize(&q); } } if( eState==0 ){ /* If is message is currently Unread, change it to Read */ blob_append_sql(&sql, "UPDATE emailbox SET estate=1 " " WHERE estate=0 AND ebid=%d", emailid ); if( zUser ) blob_append_sql(&sql, " AND euser=%Q", zUser); db_multi_exec("%s", blob_sql_text(&sql)); blob_reset(&sql); eState = 1; } url_add_parameter(pUrl, "id", 0); sqlite3_snprintf(sizeof(zENum), zENum, "e%d", emailid); if( eState==2 ){ style_submenu_element("Undelete","%s", url_render(pUrl,"read","1",zENum,"1")); } if( eState==1 ){ style_submenu_element("Delete", "%s", url_render(pUrl,"trash","1",zENum,"1")); style_submenu_element("Mark As Unread", "%s", url_render(pUrl,"unread","1",zENum,"1")); } if( eState==3 ){ style_submenu_element("Delete", "%s", url_render(pUrl,"trash","1",zENum,"1")); } db_end_transaction(0); style_footer(); return; } /* ** Scan the query parameters looking for parameters with name of the ** form "eN" where N is an integer. For all such integers, change ** the state of every emailbox entry with ebid==N to eStateNew provided ** that either zUser is NULL or matches. ** ** Or if eNewState==99, then delete the entries. */ static void webmail_change_state(int eNewState, const char *zUser){ Blob sql; int sep = '('; int i; const char *zName; int n; if( !cgi_csrf_safe(0) ) return; blob_init(&sql, 0, 0); if( eNewState==99 ){ blob_append_sql(&sql, "DELETE FROM emailbox WHERE estate==2 AND ebid IN "); }else{ blob_append_sql(&sql, "UPDATE emailbox SET estate=%d WHERE ebid IN ", eNewState); } for(i=0; (zName = cgi_parameter_name(i))!=0; i++){ if( zName[0]!='e' ) continue; if( !fossil_isdigit(zName[1]) ) continue; n = atoi(zName+1); blob_append_sql(&sql, "%c%d", sep, n); sep = ','; } if( zUser ){ blob_append_sql(&sql, ") AND euser=%Q", zUser); }else{ blob_append_sql(&sql, ")"); } if( sep==',' ){ db_multi_exec("%s", blob_sql_text(&sql)); } blob_reset(&sql); } /* ** Add the select/option box to the timeline submenu that shows ** which messages to include in the index. */ static void webmail_d_submenu(void){ static const char *az[] = { "0", "InBox", "1", "Unread", "2", "Trash", "3", "Sent", "4", "Everything", }; style_submenu_multichoice("d", sizeof(az)/(2*sizeof(az[0])), az, 0); } /* ** WEBPAGE: webmail ** ** This page can be used to read content from the EMAILBOX table ** that contains email received by the "fossil smtpd" command. ** ** Query parameters: ** ** id=N Show a single email entry emailbox.ebid==N ** f=N Display format. 0: decoded 1: raw ** user=USER Show mailbox for USER (admin only). ** user=* Show mailbox for all users (admin only). ** d=N 0: inbox+unread 1: unread-only 2: trash 3: all ** eN Select email entry emailbox.ebid==N ** trash Move selected entries to trash (estate=2) ** read Mark selected entries as read (estate=1) ** unread Mark selected entries as unread (estate=0) ** */ void webmail_page(void){ int emailid; Stmt q; Blob sql; int showAll = 0; const char *zUser = 0; int d = 0; /* Display mode. 0..3. d= query parameter */ int pg = 0; /* Page number */ int N = 50; /* Results per page */ int got; /* Number of results on this page */ char zPPg[30]; /* Previous page */ char zNPg[30]; /* Next page */ HQuery url; login_check_credentials(); if( !login_is_individual() ){ login_needed(0); return; } if( !db_table_exists("repository","emailbox") ){ style_header("Webmail Not Available"); @ <p>This repository is not configured to provide webmail</p> style_footer(); return; } add_content_sql_commands(g.db); emailid = atoi(PD("id","0")); url_initialize(&url, "webmail"); if( g.perm.Admin ){ zUser = PD("user",g.zLogin); if( zUser ){ url_add_parameter(&url, "user", zUser); if( fossil_strcmp(zUser,"*")==0 ){ showAll = 1; zUser = 0; } } }else{ zUser = g.zLogin; } if( P("d") ) url_add_parameter(&url, "d", P("d")); if( emailid>0 ){ webmail_show_one_message(&url, emailid, zUser); return; } style_header("Webmail"); webmail_d_submenu(); db_begin_transaction(); if( P("trash")!=0 ) webmail_change_state(2,zUser); if( P("unread")!=0 ) webmail_change_state(0,zUser); if( P("read")!=0 ) webmail_change_state(1,zUser); if( P("purge")!=0 ) webmail_change_state(99,zUser); blob_init(&sql, 0, 0); blob_append_sql(&sql, "CREATE TEMP TABLE tmbox AS " "SELECT ebid," /* 0 */ " efrom," /* 1 */ " datetime(edate,'unixepoch')," /* 2 */ " estate," /* 3 */ " esubject," /* 4 */ " euser" /* 5 */ " FROM emailbox" ); d = atoi(PD("d","0")); switch( d ){ case 0: { /* Show unread and read */ blob_append_sql(&sql, " WHERE estate<=1"); break; } case 1: { /* Unread messages only */ blob_append_sql(&sql, " WHERE estate=0"); break; } case 2: { /* Trashcan only */ blob_append_sql(&sql, " WHERE estate=2"); break; } case 3: { /* Outgoing email only */ blob_append_sql(&sql, " WHERE estate=3"); break; } case 4: { /* Everything */ blob_append_sql(&sql, " WHERE 1"); break; } } if( showAll ){ style_submenu_element("My Emails", "%s", url_render(&url,"user",0,0,0)); }else if( zUser!=0 ){ style_submenu_element("All Users", "%s", url_render(&url,"user","*",0,0)); if( fossil_strcmp(zUser, g.zLogin)!=0 ){ style_submenu_element("My Emails", "%s", url_render(&url,"user",0,0,0)); } if( zUser ){ blob_append_sql(&sql, " AND euser=%Q", zUser); }else{ blob_append_sql(&sql, " AND euser=%Q", g.zLogin); } }else{ if( g.perm.Admin ){ style_submenu_element("All Users", "%s", url_render(&url,"user","*",0,0)); } blob_append_sql(&sql, " AND euser=%Q", g.zLogin); } pg = atoi(PD("pg","0")); blob_append_sql(&sql, " ORDER BY edate DESC limit %d offset %d", N+1, pg*N); db_multi_exec("%s", blob_sql_text(&sql)); got = db_int(0, "SELECT count(*) FROM tmbox"); db_prepare(&q, "SELECT * FROM tmbox LIMIT %d", N); blob_reset(&sql); @ <form action="%R/webmail" method="POST"> @ <input type="hidden" name="d" value="%d(d)"> @ <input type="hidden" name="user" value="%h(zUser?zUser:"*")"> @ <table border="0" width="100%%"> @ <tr><td align="left"> if( d==2 ){ @ <input type="submit" name="read" value="Undelete"> @ <input type="submit" name="purge" value="Delete Permanently"> }else{ @ <input type="submit" name="trash" value="Delete"> if( d!=1 ){ @ <input type="submit" name="unread" value="Mark as unread"> } @ <input type="submit" name="read" value="Mark as read"> } @ <button onclick="webmailSelectAll(); return false;">Select All</button> @ <a href="%h(url_render(&url,0,0,0,0))">refresh</a> @ </td><td align="right"> if( pg>0 ){ sqlite3_snprintf(sizeof(zPPg), zPPg, "%d", pg-1); @ <a href="%s(url_render(&url,"pg",zPPg,0,0))">< Newer</a> } if( got>50 ){ sqlite3_snprintf(sizeof(zNPg),zNPg,"%d",pg+1); @ <a href="%s(url_render(&url,"pg",zNPg,0,0))">Older ></a></td> } @ </table> @ <table> while( db_step(&q)==SQLITE_ROW ){ const char *zId = db_column_text(&q,0); const char *zFrom = db_column_text(&q, 1); const char *zDate = db_column_text(&q, 2); const char *zSubject = db_column_text(&q, 4); if( zSubject==0 || zSubject[0]==0 ) zSubject = "(no subject)"; @ <tr> @ <td><input type="checkbox" class="webmailckbox" name="e%s(zId)"></td> @ <td>%h(zFrom)</td> @ <td><a href="%h(url_render(&url,"id",zId,0,0))">%h(zSubject)</a> \ @ %s(zDate)</td> if( showAll ){ const char *zTo = db_column_text(&q,5); @ <td><a href="%h(url_render(&url,"user",zTo,0,0))">%h(zTo)</a></td> } @ </tr> } db_finalize(&q); @ </table> @ </form> @ <script> @ function webmailSelectAll(){ @ var x = document.getElementsByClassName("webmailckbox"); @ for(i=0; i<x.length; i++){ @ x[i].checked = true; @ } @ } @ </script> style_footer(); db_end_transaction(0); } /* ** WEBPAGE: emailblob ** ** This page, accessible only to administrators, allows easy viewing of ** the emailblob table - the table that contains the text of email messages ** both inbound and outbound, and transcripts of SMTP sessions. ** ** id=N Show the text of emailblob with emailid==N ** */ void webmail_emailblob_page(void){ int id = atoi(PD("id","0")); Stmt q; login_check_credentials(); if( !g.perm.Setup ){ login_needed(0); return; } add_content_sql_commands(g.db); style_header("emailblob table"); if( id>0 ){ style_submenu_element("Index", "%R/emailblob"); @ <ul> db_prepare(&q, "SELECT emailid FROM emailblob WHERE ets=%d", id); while( db_step(&q)==SQLITE_ROW ){ int id = db_column_int(&q, 0); @ <li> <a href="%R/emailblob?id=%d(id)">emailblob entry %d(id)</a> } db_finalize(&q); db_prepare(&q, "SELECT euser, estate FROM emailbox WHERE emsgid=%d", id); while( db_step(&q)==SQLITE_ROW ){ const char *zUser = db_column_text(&q, 0); int e = db_column_int(&q, 1); @ <li> emailbox for %h(zUser) state %d(e) } db_finalize(&q); db_prepare(&q, "SELECT efrom, eto FROM emailoutq WHERE emsgid=%d", id); while( db_step(&q)==SQLITE_ROW ){ const char *zFrom = db_column_text(&q, 0); const char *zTo = db_column_text(&q, 1); @ <li> emailoutq message body from %h(zFrom) to %h(zTo) } db_finalize(&q); db_prepare(&q, "SELECT efrom, eto FROM emailoutq WHERE ets=%d", id); while( db_step(&q)==SQLITE_ROW ){ const char *zFrom = db_column_text(&q, 0); const char *zTo = db_column_text(&q, 1); @ <li> emailoutq transcript from %h(zFrom) to %h(zTo) } db_finalize(&q); @ </ul> @ <hr> db_prepare(&q, "SELECT decompress(etxt) FROM emailblob WHERE emailid=%d", id); while( db_step(&q)==SQLITE_ROW ){ const char *zContent = db_column_text(&q, 0); @ <pre>%h(zContent)</pre> } db_finalize(&q); }else{ style_submenu_element("emailoutq table","%R/emailoutq"); db_prepare(&q, "SELECT emailid, enref, ets, datetime(etime,'unixepoch'), esz," " length(etxt)" " FROM emailblob ORDER BY etime DESC, emailid DESC"); @ <table border="1" cellpadding="5" cellspacing="0" class="sortable" \ @ data-column-types='nnntkk'> @ <thead><tr><th> emailid <th> enref <th> ets <th> etime \ @ <th> uncompressed <th> compressed </tr></thead><tbody> while( db_step(&q)==SQLITE_ROW ){ int id = db_column_int(&q, 0); int nref = db_column_int(&q, 1); int ets = db_column_int(&q, 2); const char *zDate = db_column_text(&q, 3); int sz = db_column_int(&q,4); int csz = db_column_int(&q,5); @ <tr> @ <td align="right"><a href="%R/emailblob?id=%d(id)">%d(id)</a> @ <td align="right">%d(nref)</td> if( ets>0 ){ @ <td align="right">%d(ets)</td> }else{ @ <td> </td> } @ <td>%h(zDate)</td> @ <td align="right" data-sortkey='%08x(sz)'>%,d(sz)</td> @ <td align="right" data-sortkey='%08x(csz)'>%,d(csz)</td> @ </tr> } @ </tbody></table> db_finalize(&q); style_table_sorter(); } style_footer(); } /* ** WEBPAGE: emailoutq ** ** This page, accessible only to administrators, allows easy viewing of ** the emailoutq table - the table that contains the email messages ** that are queued for transmission via SMTP. */ void webmail_emailoutq_page(void){ Stmt q; login_check_credentials(); if( !g.perm.Setup ){ login_needed(0); return; } add_content_sql_commands(g.db); style_header("emailoutq table"); style_submenu_element("emailblob table","%R/emailblob"); db_prepare(&q, "SELECT edomain, efrom, eto, emsgid, " " datetime(ectime,'unixepoch')," " datetime(nullif(emtime,0),'unixepoch')," " ensend, ets" " FROM emailoutq" ); @ <table border="1" cellpadding="5" cellspacing="0" class="sortable" \ @ data-column-types='tttnttnn'> @ <thead><tr><th> edomain <th> efrom <th> eto <th> emsgid \ @ <th> ectime <th> emtime <th> ensend <th> ets </tr></thead><tbody> while( db_step(&q)==SQLITE_ROW ){ const char *zDomain = db_column_text(&q, 0); const char *zFrom = db_column_text(&q, 1); const char *zTo = db_column_text(&q, 2); int emsgid = db_column_int(&q, 3); const char *zCTime = db_column_text(&q, 4); const char *zMTime = db_column_text(&q, 5); int ensend = db_column_int(&q, 6); int ets = db_column_int(&q, 7); @ <tr> @ <td>%h(zDomain) @ <td>%h(zFrom) @ <td>%h(zTo) @ <td align="right"><a href="%R/emailblob?id=%d(emsgid)">%d(emsgid)</a> @ <td>%h(zCTime) @ <td>%h(zMTime) @ <td align="right">%d(ensend) if( ets>0 ){ @ <td align="right"><a href="%R/emailblob?id=%d(ets)">%d(ets)</a></td> }else{ @ <td> </td> } } @ </tbody></table> db_finalize(&q); style_table_sorter(); style_footer(); } |
Changes to src/wiki.c.
1 2 3 4 5 6 7 | /* ** Copyright (c) 2007 D. Richard Hipp ** Copyright (c) 2008 Stephan Beal ** ** This program is free software; you can redistribute it and/or ** modify it under the terms of the Simplified BSD License (also ** known as the "2-Clause License" or "FreeBSD License".) | | | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | /* ** Copyright (c) 2007 D. Richard Hipp ** Copyright (c) 2008 Stephan Beal ** ** This program is free software; you can redistribute it and/or ** modify it under the terms of the Simplified BSD License (also ** known as the "2-Clause License" or "FreeBSD License".) ** ** This program is distributed in the hope that it will be useful, ** but without any warranty; without even the implied warranty of ** merchantability or fitness for a particular purpose. ** ** Author contact information: ** drh@hwaci.com ** http://www.hwaci.com/drh/ |
︙ | ︙ | |||
165 166 167 168 169 170 171 | wiki_convert(pWiki, 0, 0); }else if( fossil_strcmp(zMimetype, "text/x-markdown")==0 ){ Blob tail = BLOB_INITIALIZER; markdown_to_html(pWiki, 0, &tail); @ %s(blob_str(&tail)) blob_reset(&tail); }else{ | | | 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 | wiki_convert(pWiki, 0, 0); }else if( fossil_strcmp(zMimetype, "text/x-markdown")==0 ){ Blob tail = BLOB_INITIALIZER; markdown_to_html(pWiki, 0, &tail); @ %s(blob_str(&tail)) blob_reset(&tail); }else{ @ <pre class='textPlain'> @ %h(blob_str(pWiki)) @ </pre> } } /* ** WEBPAGE: md_rules |
︙ | ︙ | |||
424 425 426 427 428 429 430 | manifest_destroy(pWiki); style_footer(); } /* ** Write a wiki artifact into the repository */ | | > | 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 | manifest_destroy(pWiki); style_footer(); } /* ** Write a wiki artifact into the repository */ int wiki_put(Blob *pWiki, int parent, int needMod){ int nrid; if( !needMod ){ nrid = content_put_ex(pWiki, 0, 0, 0, 0); if( parent) content_deltify(parent, &nrid, 1, 0); }else{ nrid = content_put_ex(pWiki, 0, 0, 0, 1); moderation_table_create(); db_multi_exec("INSERT INTO modreq(objid) VALUES(%d)", nrid); } db_multi_exec("INSERT OR IGNORE INTO unsent VALUES(%d)", nrid); db_multi_exec("INSERT OR IGNORE INTO unclustered VALUES(%d);", nrid); manifest_crosslink(nrid, pWiki, MC_NONE); return nrid; } /* ** Output a selection box from which the user can select the ** wiki mimetype. */ void mimetype_option_menu(const char *zMimetype){ |
︙ | ︙ | |||
532 533 534 535 536 537 538 | } if( zBody==0 && (pWiki = manifest_get(rid, CFTYPE_WIKI, 0))!=0 ){ zBody = pWiki->zWiki; zMimetype = pWiki->zMimetype; } } if( P("submit")!=0 && zBody!=0 | | | 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 | } if( zBody==0 && (pWiki = manifest_get(rid, CFTYPE_WIKI, 0))!=0 ){ zBody = pWiki->zWiki; zMimetype = pWiki->zMimetype; } } if( P("submit")!=0 && zBody!=0 && (goodCaptcha = captcha_is_correct(0)) ){ char *zDate; Blob cksum; blob_zero(&wiki); db_begin_transaction(); if( isSandbox ){ db_set("sandbox",zBody,0); |
︙ | ︙ | |||
756 757 758 759 760 761 762 | } } if( !g.perm.ApndWiki ){ login_needed(g.anon.ApndWiki); return; } if( P("submit")!=0 && P("r")!=0 && P("u")!=0 | | | 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 | } } if( !g.perm.ApndWiki ){ login_needed(g.anon.ApndWiki); return; } if( P("submit")!=0 && P("r")!=0 && P("u")!=0 && (goodCaptcha = captcha_is_correct(0)) ){ char *zDate; Blob cksum; Blob body; Blob wiki; Manifest *pWiki = 0; |
︙ | ︙ |
Changes to src/winfile.c.
︙ | ︙ | |||
280 281 282 283 284 285 286 | ** characters are converted to '/'. */ void win32_getcwd(char *zBuf, int nBuf){ int i; char *zUtf8; wchar_t *zWide = fossil_malloc( sizeof(wchar_t)*nBuf ); if( GetCurrentDirectoryW(nBuf, zWide)==0 ){ | | | 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 | ** characters are converted to '/'. */ void win32_getcwd(char *zBuf, int nBuf){ int i; char *zUtf8; wchar_t *zWide = fossil_malloc( sizeof(wchar_t)*nBuf ); if( GetCurrentDirectoryW(nBuf, zWide)==0 ){ fossil_panic("cannot find current working directory."); } zUtf8 = fossil_path_to_utf8(zWide); fossil_free(zWide); for(i=0; zUtf8[i]; i++) if( zUtf8[i]=='\\' ) zUtf8[i] = '/'; strncpy(zBuf, zUtf8, nBuf); fossil_path_free(zUtf8); } |
︙ | ︙ |
Changes to src/winhttp.c.
︙ | ︙ | |||
18 19 20 21 22 23 24 25 26 27 28 29 30 31 | ** This file implements a very simple (and low-performance) HTTP server ** for windows. It also implements a Windows Service which allows the HTTP ** server to be run without any user logged on. */ #include "config.h" #ifdef _WIN32 /* This code is for win32 only */ #include <ws2tcpip.h> #include <windows.h> #include <process.h> #include "winhttp.h" #ifndef IPV6_V6ONLY # define IPV6_V6ONLY 27 /* Because this definition is missing in MinGW */ | > > > > | 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 | ** This file implements a very simple (and low-performance) HTTP server ** for windows. It also implements a Windows Service which allows the HTTP ** server to be run without any user logged on. */ #include "config.h" #ifdef _WIN32 /* This code is for win32 only */ # if !defined(_WIN32_WINNT) # define _WIN32_WINNT 0x0501 # endif #include <winsock2.h> #include <ws2tcpip.h> #include <windows.h> #include <process.h> #include "winhttp.h" #ifndef IPV6_V6ONLY # define IPV6_V6ONLY 27 /* Because this definition is missing in MinGW */ |
︙ | ︙ | |||
282 283 284 285 286 287 288 | ** Issue a fatal error. */ static NORETURN void winhttp_fatal( const char *zOp, const char *zService, const char *zErr ){ | | | 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 | ** Issue a fatal error. */ static NORETURN void winhttp_fatal( const char *zOp, const char *zService, const char *zErr ){ fossil_panic("unable to %s service '%s': %s", zOp, zService, zErr); } /* ** Make sure the server stops as soon as possible after the stopper file ** is found. If there is no stopper file name, do nothing. */ static void win32_server_stopper(void *pAppData){ |
︙ | ︙ | |||
385 386 387 388 389 390 391 | ** The repository name is only needed if there was no open checkout. This ** is designed to allow the open checkout for the interactive user to work ** with the local Fossil server started via the "ui" command. */ zIp = SocketAddr_toString(&p->addr); if( (p->flags & HTTP_SERVER_HAD_CHECKOUT)==0 ){ assert( g.zRepositoryName && g.zRepositoryName[0] ); | | | | > | 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 | ** The repository name is only needed if there was no open checkout. This ** is designed to allow the open checkout for the interactive user to work ** with the local Fossil server started via the "ui" command. */ zIp = SocketAddr_toString(&p->addr); if( (p->flags & HTTP_SERVER_HAD_CHECKOUT)==0 ){ assert( g.zRepositoryName && g.zRepositoryName[0] ); sqlite3_snprintf(sizeof(zCmd), zCmd, "%s--in %s\n--out %s\n--ipaddr %s\n%s", get_utf8_bom(0), zRequestFName, zReplyFName, zIp, g.zRepositoryName ); }else{ sqlite3_snprintf(sizeof(zCmd), zCmd, "%s--in %s\n--out %s\n--ipaddr %s", get_utf8_bom(0), zRequestFName, zReplyFName, zIp ); } fossil_free(zIp); aux = fossil_fopen(zCmdFName, "wb"); if( aux==0 ) goto end_request; fwrite(zCmd, 1, strlen(zCmd), aux); sqlite3_snprintf(sizeof(zCmd), zCmd, "\"%s\" http -args \"%s\" --nossl%s", g.nameOfExe, zCmdFName, p->zOptions ); in = fossil_fopen(zReplyFName, "w+b"); fflush(out); fflush(aux); fossil_system(zCmd); if( in ){ |
︙ | ︙ | |||
468 469 470 471 472 473 474 | if( got<=0 ) break; fwrite(zHdr, 1, got, out); wanted += got; } assert( g.zRepositoryName && g.zRepositoryName[0] ); zIp = SocketAddr_toString(&p->addr); sqlite3_snprintf(sizeof(zCmd), zCmd, | > | | 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 | if( got<=0 ) break; fwrite(zHdr, 1, got, out); wanted += got; } assert( g.zRepositoryName && g.zRepositoryName[0] ); zIp = SocketAddr_toString(&p->addr); sqlite3_snprintf(sizeof(zCmd), zCmd, "\"%s\" http --in \"%s\" --out \"%s\" --ipaddr %s \"%s\"" " --scgi --nossl%s", g.nameOfExe, zRequestFName, zReplyFName, zIp, g.zRepositoryName, p->zOptions ); fossil_free(zIp); in = fossil_fopen(zReplyFName, "w+b"); fflush(out); fossil_system(zCmd); |
︙ | ︙ | |||
558 559 560 561 562 563 564 | savedKeySize = db_get_saved_encryption_key_size(); if( zSavedKey!=0 && savedKeySize>0 ){ blob_appendf(&options, " --usepidkey %lu:%p:%u", GetCurrentProcessId(), zSavedKey, savedKeySize); } #endif if( WSAStartup(MAKEWORD(2,0), &wd) ){ | | | 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 | savedKeySize = db_get_saved_encryption_key_size(); if( zSavedKey!=0 && savedKeySize>0 ){ blob_appendf(&options, " --usepidkey %lu:%p:%u", GetCurrentProcessId(), zSavedKey, savedKeySize); } #endif if( WSAStartup(MAKEWORD(2,0), &wd) ){ fossil_panic("unable to initialize winsock"); } DualSocket_init(&ds); while( iPort<=mxPort ){ if( zIpAddr ){ if( DualSocket_listen(&ds, zIpAddr, iPort)==0 ){ iPort++; continue; |
︙ | ︙ | |||
580 581 582 583 584 585 586 | continue; } } break; } if( iPort>mxPort ){ if( mnPort==mxPort ){ | | | | | 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 | continue; } } break; } if( iPort>mxPort ){ if( mnPort==mxPort ){ fossil_panic("unable to open listening socket on port %d", mnPort); }else{ fossil_panic("unable to open listening socket on any" " port in the range %d..%d", mnPort, mxPort); } } if( !GetTempPathW(MAX_PATH, zTmpPath) ){ fossil_panic("unable to get path to the temporary directory."); } zTempPrefix = mprintf("%sfossil_server_P%d", fossil_unicode_to_utf8(zTmpPath), iPort); fossil_print("Temporary files: %s*\n", zTempPrefix); fossil_print("Listening for %s requests on TCP port %d\n", (flags&HTTP_SERVER_SCGI)!=0?"SCGI":"HTTP", iPort); if( zBrowser ){ |
︙ | ︙ | |||
638 639 640 641 642 643 644 | wsaError = WSAGetLastError(); DualSocket_close(&ds); if( (wsaError==WSAEINTR) || (wsaError==WSAENOTSOCK) ){ WSACleanup(); return; }else{ WSACleanup(); | | | 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 | wsaError = WSAGetLastError(); DualSocket_close(&ds); if( (wsaError==WSAEINTR) || (wsaError==WSAENOTSOCK) ){ WSACleanup(); return; }else{ WSACleanup(); fossil_panic("error from accept()"); } } if( client.s4!=INVALID_SOCKET ){ pRequest = fossil_malloc(sizeof(HttpRequest)); pRequest->id = ++idCnt; pRequest->s = client.s4; memcpy(&pRequest->addr, &client_addr.a4, sizeof(client_addr.a4)); |
︙ | ︙ | |||
738 739 740 741 742 743 744 | 0, NULL ); } if( nMsg ){ zMsg = fossil_unicode_to_utf8(tmp); }else{ | | | 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 | 0, NULL ); } if( nMsg ){ zMsg = fossil_unicode_to_utf8(tmp); }else{ fossil_panic("unable to get system error message."); } if( tmp ){ LocalFree((HLOCAL) tmp); } return zMsg; } |
︙ | ︙ | |||
883 884 885 886 887 888 889 | if( GetStdHandle(STD_INPUT_HANDLE)!=NULL ){ return 1; } /* Try to start the control dispatcher thread for the service. */ if( !StartServiceCtrlDispatcherW(ServiceTable) ){ if( GetLastError()==ERROR_FAILED_SERVICE_CONTROLLER_CONNECT ){ return 1; }else{ | | | 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 | if( GetStdHandle(STD_INPUT_HANDLE)!=NULL ){ return 1; } /* Try to start the control dispatcher thread for the service. */ if( !StartServiceCtrlDispatcherW(ServiceTable) ){ if( GetLastError()==ERROR_FAILED_SERVICE_CONTROLLER_CONNECT ){ return 1; }else{ fossil_panic("error from StartServiceCtrlDispatcher()"); } } return 0; } /* Duplicate #ifdef needed for mkindex */ #ifdef _WIN32 |
︙ | ︙ |
Changes to src/xfer.c.
︙ | ︙ | |||
1304 1305 1306 1307 1308 1309 1310 | if( xfer.nToken==3 && (blob_eq(&xfer.aToken[0], "pull") || blob_eq(&xfer.aToken[0], "push")) && blob_is_hname(&xfer.aToken[2]) ){ const char *zPCode; zPCode = db_get("project-code", 0); if( zPCode==0 ){ | | | 1304 1305 1306 1307 1308 1309 1310 1311 1312 1313 1314 1315 1316 1317 1318 | if( xfer.nToken==3 && (blob_eq(&xfer.aToken[0], "pull") || blob_eq(&xfer.aToken[0], "push")) && blob_is_hname(&xfer.aToken[2]) ){ const char *zPCode; zPCode = db_get("project-code", 0); if( zPCode==0 ){ fossil_fatal("missing project code"); } if( !blob_eq_str(&xfer.aToken[2], zPCode, -1) ){ cgi_reset_content(); @ error wrong\sproject nErr++; break; } |
︙ | ︙ | |||
1418 1419 1420 1421 1422 1423 1424 | && xfer.nToken==2 ){ if( g.perm.Read ){ char *zName = blob_str(&xfer.aToken[1]); if( zName[0]=='/' ){ /* New style configuration transfer */ int groupMask = configure_name_to_mask(&zName[1], 0); | | | 1418 1419 1420 1421 1422 1423 1424 1425 1426 1427 1428 1429 1430 1431 1432 | && xfer.nToken==2 ){ if( g.perm.Read ){ char *zName = blob_str(&xfer.aToken[1]); if( zName[0]=='/' ){ /* New style configuration transfer */ int groupMask = configure_name_to_mask(&zName[1], 0); if( !g.perm.Admin ) groupMask &= ~(CONFIGSET_USER|CONFIGSET_SCRIBER); if( !g.perm.RdAddr ) groupMask &= ~CONFIGSET_ADDR; configure_send_group(xfer.pOut, groupMask, 0); } } }else /* config NAME SIZE \n CONTENT |
︙ | ︙ |
Changes to src/zip.c.
︙ | ︙ | |||
889 890 891 892 893 894 895 | eType = ARCHIVE_SQLAR; zType = "SQL"; }else{ eType = ARCHIVE_ZIP; zType = "ZIP"; } load_control(); | | | 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 | eType = ARCHIVE_SQLAR; zType = "SQL"; }else{ eType = ARCHIVE_ZIP; zType = "ZIP"; } load_control(); zName = fossil_strdup(PD("name","")); z = P("r"); if( z==0 ) z = P("uuid"); if( z==0 ) z = tar_uuid_from_name(&zName); if( z==0 ) z = "trunk"; nName = strlen(zName); g.zOpenRevision = zRid = fossil_strdup(z); nRid = strlen(zRid); |
︙ | ︙ |
Changes to test/tester.tcl.
︙ | ︙ | |||
718 719 720 721 722 723 724 | if {$knownBug && !$::STRICT} { protOut "test $name FAILED (knownBug)!" 1 lappend ignored_test $name } else { protOut "test $name FAILED!" 1 if {$::QUIET} {protOut "RESULT: $RESULT" 1} lappend bad_test $name | | | 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 | if {$knownBug && !$::STRICT} { protOut "test $name FAILED (knownBug)!" 1 lappend ignored_test $name } else { protOut "test $name FAILED!" 1 if {$::QUIET} {protOut "RESULT: $RESULT" 1} lappend bad_test $name if {$::HALT} {exit 1} } } } set bad_test {} set ignored_test {} # Return a random string N characters long. |
︙ | ︙ |
Added tools/decode-email.c.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 | /* ** This program reads a raw email file and attempts to decode it into ** a more human-readable format. The following decodings are done: ** ** (1) Header values are prefixed by "| " at the left margin. ** ** (2) Content-Transfer-Encoding is recognized and the content is ** decoded for display. */ #define _GNU_SOURCE #include <stdio.h> #include <string.h> #include <ctype.h> #define BINARY 0 #define BASE64 1 #define QUOTED 2 static int decode_hex(char c){ if( c>='0' && c<='9' ) return c - '0'; if( c>='A' && c<='F' ) return c - 'A' + 10; if( c>='a' && c<='f' ) return c - 'a' + 10; return -1; } static void convert_file(const char *zFilename, FILE *in){ int inHdr = 1; int n; int nBoundary; int decodeType = 0; int textMimetype = 1; char *zB; char zBoundary[200]; char zLine[5000]; char zOut[5000]; while( fgets(zLine, sizeof(zLine), in) ){ if( !inHdr && zLine[0]=='-' && zLine[1]=='-' && strncmp(zLine+2,zBoundary,nBoundary)==0 ){ printf("|----------------- end of body section ---------|\n"); inHdr = 1; } if( !inHdr ){ if( textMimetype && decodeType==BASE64 ){ int ii, jj, c, x, y; int bits = 0; for(ii=jj=0; (c = zLine[ii])!=0; ii++){ if( c>='A' && c<='Z' ){ x = c - 'A'; }else if( c>='a' && c<='z' ){ x = c - 'a' + 26; }else if( c>='0' && c<='9' ){ x = c - '0' + 52; }else if( c=='+' ){ x = 62; }else if( c=='/' ){ x = 63; }else if( c=='=' ){ x = 0; }else{ continue; } if( bits==0 ){ y = x; bits = 6; }else if( bits==6 ){ zOut[jj++] = ((y<<2) & 0xfc) | ((x>>4) & 0x03); y = x & 0xf; bits = 4; }else if( bits==4 ){ zOut[jj++] = ((y<<4) & 0xf0) | ((x>>2) & 0x0f); y = x & 0x3; bits = 2; }else if( bits==2 ){ zOut[jj++] = ((y<<6) & 0xc0) | (x & 0x3f); bits = 0; } } zOut[jj] = 0; printf("%s", zOut); }else if( textMimetype && decodeType==QUOTED ){ int ii, jj, c; for(ii=jj=0; (c = zLine[ii])!=0; ii++){ if( c=='=' ){ int x1 = decode_hex(zLine[ii+1]); int x2 = decode_hex(zLine[ii+2]); if( x1>=0 && x2>=0 ){ zOut[jj++] = (x1<<4) | x2; ii += 2; }else if( zLine[ii+1]=='\r' && zLine[ii+2]=='\n' ){ ii += 2; } }else{ zOut[jj++] = c; } } zOut[jj] = 0; printf("%s", zOut); }else{ printf("%s", zLine); } continue; } n = (int)strlen(zLine); while( n>0 && isspace(zLine[n-1]) ){ n--; } zLine[n] = 0; if( n==0 ){ inHdr = 0; printf("|----------------- end of header ---------------|\n"); continue; } printf("| %s\n", zLine); if( strncasecmp(zLine,"Content-Type:", 13)==0 ){ textMimetype = strstr(zLine, "text/")!=0; printf("|** %s content type **|\n", textMimetype ? "Text" : "Non-text"); } if( strncasecmp(zLine,"Content-Transfer-Encoding:", 26)==0 ){ if( strcasestr(zLine, "base64") ){ decodeType = BASE64; }else if( strcasestr(zLine, "quoted-printable") ){ decodeType = QUOTED; }else{ decodeType = BINARY; } printf("|** Content encoding %s **|\n", decodeType==BASE64 ? "BASE64" : decodeType==QUOTED ? "QUOTED" : "BINARY"); } zB = strstr(zLine, "boundary=\""); if( zB ){ int kk; zB += 10; for(kk=0; zB[kk] && zB[kk]!='"' && kk<sizeof(zBoundary)-1; kk++){ zBoundary[kk] = zB[kk]; } zBoundary[kk] = 0; nBoundary = kk; printf("|** boundary [%s] **|\n", zBoundary); } } } int main(int argc, char **argv){ if( argc==1 ){ convert_file("<stdin>", stdin); return 0; }else{ int i; for(i=1; i<argc; i++){ FILE *in = fopen(argv[i], "rb"); if( in==0 ){ fprintf(stderr, "cannot open \"%s\"", argv[i]); }else{ convert_file(argv[i], in); fclose(in); } } } return 0; } |
Added tools/email-monitor.tcl.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 | #!/usr/bin/tcl # # Monitor the database file named on the command line for # incoming email messages. Print the "To:" line of each # email on standard output as it is received. # # It should be relatively easy to modify this scribe to actually # deliver the emails to a real email transfer agent such as # Postfix. # # For long-term use, set the polling interval to something # greater than the default 100 milliseconds. Polling once # every 10 seconds is probably sufficient. # set POLLING_INTERVAL 100 ;# milliseconds set dbfile [lindex $argv 0] if {[llength $argv]!=1} { puts stderr "Usage: $argv0 DBFILE" exit 1 } package require sqlite3 puts "SQLite version [sqlite3 -version]" sqlite3 db $dbfile db timeout 2000 catch {db eval {PRAGMA journal_mode=WAL}} db eval { CREATE TABLE IF NOT EXISTS email( emailid INTEGER PRIMARY KEY, msg TXT ); } while {1} { db transaction immediate { set n 0 db eval {SELECT msg FROM email} { set email ??? regexp {To: \S*} $msg to puts "$to ([string length $msg] bytes)" incr n } if {$n>0} { db eval {DELETE FROM email} } # Hold the write lock a little longer in order to exercise # the SQLITE_BUSY handling logic on the writing inside of # Fossil. Probably comment-out this line for production use. after 100 } after $POLLING_INTERVAL } |
Added tools/email-sender.tcl.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 | #!/usr/bin/tcl # # Monitor the database file named by the DBFILE variable # looking for email messages sent by Fossil. Forward each # to /usr/sbin/sendmail. # set POLLING_INTERVAL 10000 ;# milliseconds set DBFILE /home/www/fossil/emailqueue.db set PIPE "/usr/sbin/sendmail -ti" package require sqlite3 # puts "SQLite version [sqlite3 -version]" sqlite3 db $DBFILE db timeout 5000 catch {db eval {PRAGMA journal_mode=WAL}} db eval { CREATE TABLE IF NOT EXISTS email( emailid INTEGER PRIMARY KEY, msg TXT ); } while {1} { db transaction immediate { set n 0 db eval {SELECT msg FROM email} { set out [open |$PIPE w] puts -nonewline $out $msg flush $out close $out incr n } if {$n>0} { db eval {DELETE FROM email} } } after $POLLING_INTERVAL } |
Changes to win/Makefile.PellesCGMake.
︙ | ︙ | |||
56 57 58 59 60 61 62 | B=.. SRCDIR=$(B)/src/ WINDIR=$(B)/win/ ZLIBSRCDIR=../../zlib/ # define linker command and options LINK=$(PellesCDir)/bin/polink.exe | | | 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 | B=.. SRCDIR=$(B)/src/ WINDIR=$(B)/win/ ZLIBSRCDIR=../../zlib/ # define linker command and options LINK=$(PellesCDir)/bin/polink.exe LINKFLAGS=-subsystem:console -machine:$(TARGETMACHINE_LN) /LIBPATH:$(PellesCDir)\lib\win$(TARGETEXTEND) /LIBPATH:$(PellesCDir)\lib kernel32.lib advapi32.lib delayimp$(TARGETEXTEND).lib Wsock32.lib dnsapi.lib Crtmt$(TARGETEXTEND).lib # define standard C-compiler and flags, used to compile # the fossil binary. Some special definitions follow for # special files follow CC=$(PellesCDir)\bin\pocc.exe DEFINES=-D_pgmptr=g.argv[0] CCFLAGS=-T$(TARGETMACHINE_CC)-coff -Ot -W2 -Gd -Go -Ze -MT $(DEFINES) |
︙ | ︙ | |||
87 88 89 90 91 92 93 | SQLITEOBJ=$(foreach sf,$(SQLITESRC),$(sf:.c=.obj)) SQLITEDEFINES=-DNDEBUG=1 -DSQLITE_THREADSAFE=0 -DSQLITE_DEFAULT_MEMSTATUS=0 -DSQLITE_DEFAULT_WAL_SYNCHRONOUS=1 -DSQLITE_LIKE_DOESNT_MATCH_BLOBS -DSQLITE_OMIT_DECLTYPE -DSQLITE_OMIT_DEPRECATED -DSQLITE_OMIT_GET_TABLE -DSQLITE_OMIT_PROGRESS_CALLBACK -DSQLITE_OMIT_SHARED_CACHE -DSQLITE_OMIT_LOAD_EXTENSION -DSQLITE_MAX_EXPR_DEPTH=0 -DSQLITE_USE_ALLOCA -DSQLITE_ENABLE_LOCKING_STYLE=0 -DSQLITE_DEFAULT_FILE_FORMAT=4 -DSQLITE_ENABLE_EXPLAIN_COMMENTS -DSQLITE_ENABLE_FTS4 -DSQLITE_ENABLE_FTS3_PARENTHESIS -DSQLITE_ENABLE_DBSTAT_VTAB -DSQLITE_ENABLE_JSON1 -DSQLITE_ENABLE_FTS5 -DSQLITE_ENABLE_STMTVTAB -DSQLITE_HAVE_ZLIB -DSQLITE_INTROSPECTION_PRAGMAS -DSQLITE_ENABLE_DBPAGE_VTAB -DSQLITE_WIN32_NO_ANSI # define the SQLite shell files, which need special flags on compile SQLITESHELLSRC=shell.c ORIGSQLITESHELLSRC=$(foreach sf,$(SQLITESHELLSRC),$(SRCDIR)$(sf)) SQLITESHELLOBJ=$(foreach sf,$(SQLITESHELLSRC),$(sf:.c=.obj)) | | | 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 | SQLITEOBJ=$(foreach sf,$(SQLITESRC),$(sf:.c=.obj)) SQLITEDEFINES=-DNDEBUG=1 -DSQLITE_THREADSAFE=0 -DSQLITE_DEFAULT_MEMSTATUS=0 -DSQLITE_DEFAULT_WAL_SYNCHRONOUS=1 -DSQLITE_LIKE_DOESNT_MATCH_BLOBS -DSQLITE_OMIT_DECLTYPE -DSQLITE_OMIT_DEPRECATED -DSQLITE_OMIT_GET_TABLE -DSQLITE_OMIT_PROGRESS_CALLBACK -DSQLITE_OMIT_SHARED_CACHE -DSQLITE_OMIT_LOAD_EXTENSION -DSQLITE_MAX_EXPR_DEPTH=0 -DSQLITE_USE_ALLOCA -DSQLITE_ENABLE_LOCKING_STYLE=0 -DSQLITE_DEFAULT_FILE_FORMAT=4 -DSQLITE_ENABLE_EXPLAIN_COMMENTS -DSQLITE_ENABLE_FTS4 -DSQLITE_ENABLE_FTS3_PARENTHESIS -DSQLITE_ENABLE_DBSTAT_VTAB -DSQLITE_ENABLE_JSON1 -DSQLITE_ENABLE_FTS5 -DSQLITE_ENABLE_STMTVTAB -DSQLITE_HAVE_ZLIB -DSQLITE_INTROSPECTION_PRAGMAS -DSQLITE_ENABLE_DBPAGE_VTAB -DSQLITE_WIN32_NO_ANSI # define the SQLite shell files, which need special flags on compile SQLITESHELLSRC=shell.c ORIGSQLITESHELLSRC=$(foreach sf,$(SQLITESHELLSRC),$(SRCDIR)$(sf)) SQLITESHELLOBJ=$(foreach sf,$(SQLITESHELLSRC),$(sf:.c=.obj)) SQLITESHELLDEFINES=-DNDEBUG=1 -DSQLITE_THREADSAFE=0 -DSQLITE_DEFAULT_MEMSTATUS=0 -DSQLITE_DEFAULT_WAL_SYNCHRONOUS=1 -DSQLITE_LIKE_DOESNT_MATCH_BLOBS -DSQLITE_OMIT_DECLTYPE -DSQLITE_OMIT_DEPRECATED -DSQLITE_OMIT_GET_TABLE -DSQLITE_OMIT_PROGRESS_CALLBACK -DSQLITE_OMIT_SHARED_CACHE -DSQLITE_OMIT_LOAD_EXTENSION -DSQLITE_MAX_EXPR_DEPTH=0 -DSQLITE_USE_ALLOCA -DSQLITE_ENABLE_LOCKING_STYLE=0 -DSQLITE_DEFAULT_FILE_FORMAT=4 -DSQLITE_ENABLE_EXPLAIN_COMMENTS -DSQLITE_ENABLE_FTS4 -DSQLITE_ENABLE_FTS3_PARENTHESIS -DSQLITE_ENABLE_DBSTAT_VTAB -DSQLITE_ENABLE_JSON1 -DSQLITE_ENABLE_FTS5 -DSQLITE_ENABLE_STMTVTAB -DSQLITE_HAVE_ZLIB -DSQLITE_INTROSPECTION_PRAGMAS -DSQLITE_ENABLE_DBPAGE_VTAB -Dmain=sqlite3_shell -DSQLITE_SHELL_IS_UTF8=1 -DSQLITE_OMIT_LOAD_EXTENSION=1 -DUSE_SYSTEM_SQLITE=$(USE_SYSTEM_SQLITE) -DSQLITE_SHELL_DBNAME_PROC=sqlcmd_get_dbname -DSQLITE_SHELL_INIT_PROC=sqlcmd_init_proc -Daccess=file_access -Dsystem=fossil_system -Dgetenv=fossil_getenv -Dfopen=fossil_fopen # define the th scripting files, which need special flags on compile THSRC=th.c th_lang.c ORIGTHSRC=$(foreach sf,$(THSRC),$(SRCDIR)$(sf)) THOBJ=$(foreach sf,$(THSRC),$(sf:.c=.obj)) # define the zlib files, needed by this compile |
︙ | ︙ |
Changes to win/Makefile.dmc.
︙ | ︙ | |||
20 21 22 23 24 25 26 | #SSL = -DFOSSIL_ENABLE_SSL=1 SSL = CFLAGS = -o BCC = $(DMDIR)\bin\dmc $(CFLAGS) TCC = $(DMDIR)\bin\dmc $(CFLAGS) $(DMCDEF) $(SSL) $(INCL) | | | | | | | 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 | #SSL = -DFOSSIL_ENABLE_SSL=1 SSL = CFLAGS = -o BCC = $(DMDIR)\bin\dmc $(CFLAGS) TCC = $(DMDIR)\bin\dmc $(CFLAGS) $(DMCDEF) $(SSL) $(INCL) LIBS = $(DMDIR)\extra\lib\ zlib wsock32 advapi32 dnsapi SQLITE_OPTIONS = -DNDEBUG=1 -DSQLITE_THREADSAFE=0 -DSQLITE_DEFAULT_MEMSTATUS=0 -DSQLITE_DEFAULT_WAL_SYNCHRONOUS=1 -DSQLITE_LIKE_DOESNT_MATCH_BLOBS -DSQLITE_OMIT_DECLTYPE -DSQLITE_OMIT_DEPRECATED -DSQLITE_OMIT_GET_TABLE -DSQLITE_OMIT_PROGRESS_CALLBACK -DSQLITE_OMIT_SHARED_CACHE -DSQLITE_OMIT_LOAD_EXTENSION -DSQLITE_MAX_EXPR_DEPTH=0 -DSQLITE_USE_ALLOCA -DSQLITE_ENABLE_LOCKING_STYLE=0 -DSQLITE_DEFAULT_FILE_FORMAT=4 -DSQLITE_ENABLE_EXPLAIN_COMMENTS -DSQLITE_ENABLE_FTS4 -DSQLITE_ENABLE_FTS3_PARENTHESIS -DSQLITE_ENABLE_DBSTAT_VTAB -DSQLITE_ENABLE_JSON1 -DSQLITE_ENABLE_FTS5 -DSQLITE_ENABLE_STMTVTAB -DSQLITE_HAVE_ZLIB -DSQLITE_INTROSPECTION_PRAGMAS -DSQLITE_ENABLE_DBPAGE_VTAB SHELL_OPTIONS = -DNDEBUG=1 -DSQLITE_THREADSAFE=0 -DSQLITE_DEFAULT_MEMSTATUS=0 -DSQLITE_DEFAULT_WAL_SYNCHRONOUS=1 -DSQLITE_LIKE_DOESNT_MATCH_BLOBS -DSQLITE_OMIT_DECLTYPE -DSQLITE_OMIT_DEPRECATED -DSQLITE_OMIT_GET_TABLE -DSQLITE_OMIT_PROGRESS_CALLBACK -DSQLITE_OMIT_SHARED_CACHE -DSQLITE_OMIT_LOAD_EXTENSION -DSQLITE_MAX_EXPR_DEPTH=0 -DSQLITE_USE_ALLOCA -DSQLITE_ENABLE_LOCKING_STYLE=0 -DSQLITE_DEFAULT_FILE_FORMAT=4 -DSQLITE_ENABLE_EXPLAIN_COMMENTS -DSQLITE_ENABLE_FTS4 -DSQLITE_ENABLE_FTS3_PARENTHESIS -DSQLITE_ENABLE_DBSTAT_VTAB -DSQLITE_ENABLE_JSON1 -DSQLITE_ENABLE_FTS5 -DSQLITE_ENABLE_STMTVTAB -DSQLITE_HAVE_ZLIB -DSQLITE_INTROSPECTION_PRAGMAS -DSQLITE_ENABLE_DBPAGE_VTAB -Dmain=sqlite3_shell -DSQLITE_SHELL_IS_UTF8=1 -DSQLITE_OMIT_LOAD_EXTENSION=1 -DUSE_SYSTEM_SQLITE=$(USE_SYSTEM_SQLITE) -DSQLITE_SHELL_DBNAME_PROC=sqlcmd_get_dbname -DSQLITE_SHELL_INIT_PROC=sqlcmd_init_proc -Daccess=file_access -Dsystem=fossil_system -Dgetenv=fossil_getenv -Dfopen=fossil_fopen SRC = add_.c allrepo_.c attach_.c backoffice_.c bag_.c bisect_.c blob_.c branch_.c browse_.c builtin_.c bundle_.c cache_.c capabilities_.c captcha_.c cgi_.c checkin_.c checkout_.c clearsign_.c clone_.c comformat_.c configure_.c content_.c cookies_.c db_.c delta_.c deltacmd_.c descendants_.c diff_.c diffcmd_.c dispatch_.c doc_.c email_.c encode_.c etag_.c event_.c export_.c file_.c finfo_.c foci_.c forum_.c fshell_.c fusefs_.c glob_.c graph_.c gzip_.c hname_.c http_.c http_socket_.c http_ssl_.c http_transport_.c import_.c info_.c json_.c json_artifact_.c json_branch_.c json_config_.c json_diff_.c json_dir_.c json_finfo_.c json_login_.c json_query_.c json_report_.c json_status_.c json_tag_.c json_timeline_.c json_user_.c json_wiki_.c leaf_.c loadctrl_.c login_.c lookslike_.c main_.c manifest_.c markdown_.c markdown_html_.c md5_.c merge_.c merge3_.c moderate_.c name_.c path_.c piechart_.c pivot_.c popen_.c pqueue_.c printf_.c publish_.c purge_.c rebuild_.c regexp_.c report_.c rss_.c schema_.c search_.c security_audit_.c setup_.c setupuser_.c sha1_.c sha1hard_.c sha3_.c shun_.c sitemap_.c skins_.c smtp_.c sqlcmd_.c stash_.c stat_.c statrep_.c style_.c sync_.c tag_.c tar_.c th_main_.c timeline_.c tkt_.c tktsetup_.c undo_.c unicode_.c unversioned_.c update_.c url_.c user_.c utf8_.c util_.c verify_.c vfile_.c webmail_.c wiki_.c wikiformat_.c winfile_.c winhttp_.c wysiwyg_.c xfer_.c xfersetup_.c zip_.c OBJ = $(OBJDIR)\add$O $(OBJDIR)\allrepo$O $(OBJDIR)\attach$O $(OBJDIR)\backoffice$O $(OBJDIR)\bag$O $(OBJDIR)\bisect$O $(OBJDIR)\blob$O $(OBJDIR)\branch$O $(OBJDIR)\browse$O $(OBJDIR)\builtin$O $(OBJDIR)\bundle$O $(OBJDIR)\cache$O $(OBJDIR)\capabilities$O $(OBJDIR)\captcha$O $(OBJDIR)\cgi$O $(OBJDIR)\checkin$O $(OBJDIR)\checkout$O $(OBJDIR)\clearsign$O $(OBJDIR)\clone$O $(OBJDIR)\comformat$O $(OBJDIR)\configure$O $(OBJDIR)\content$O $(OBJDIR)\cookies$O $(OBJDIR)\db$O $(OBJDIR)\delta$O $(OBJDIR)\deltacmd$O $(OBJDIR)\descendants$O $(OBJDIR)\diff$O $(OBJDIR)\diffcmd$O $(OBJDIR)\dispatch$O $(OBJDIR)\doc$O $(OBJDIR)\email$O $(OBJDIR)\encode$O $(OBJDIR)\etag$O $(OBJDIR)\event$O $(OBJDIR)\export$O $(OBJDIR)\file$O $(OBJDIR)\finfo$O $(OBJDIR)\foci$O $(OBJDIR)\forum$O $(OBJDIR)\fshell$O $(OBJDIR)\fusefs$O $(OBJDIR)\glob$O $(OBJDIR)\graph$O $(OBJDIR)\gzip$O $(OBJDIR)\hname$O $(OBJDIR)\http$O $(OBJDIR)\http_socket$O $(OBJDIR)\http_ssl$O $(OBJDIR)\http_transport$O $(OBJDIR)\import$O $(OBJDIR)\info$O $(OBJDIR)\json$O $(OBJDIR)\json_artifact$O $(OBJDIR)\json_branch$O $(OBJDIR)\json_config$O $(OBJDIR)\json_diff$O $(OBJDIR)\json_dir$O $(OBJDIR)\json_finfo$O $(OBJDIR)\json_login$O $(OBJDIR)\json_query$O $(OBJDIR)\json_report$O $(OBJDIR)\json_status$O $(OBJDIR)\json_tag$O $(OBJDIR)\json_timeline$O $(OBJDIR)\json_user$O $(OBJDIR)\json_wiki$O $(OBJDIR)\leaf$O $(OBJDIR)\loadctrl$O $(OBJDIR)\login$O $(OBJDIR)\lookslike$O $(OBJDIR)\main$O $(OBJDIR)\manifest$O $(OBJDIR)\markdown$O $(OBJDIR)\markdown_html$O $(OBJDIR)\md5$O $(OBJDIR)\merge$O $(OBJDIR)\merge3$O $(OBJDIR)\moderate$O $(OBJDIR)\name$O $(OBJDIR)\path$O $(OBJDIR)\piechart$O $(OBJDIR)\pivot$O $(OBJDIR)\popen$O $(OBJDIR)\pqueue$O $(OBJDIR)\printf$O $(OBJDIR)\publish$O $(OBJDIR)\purge$O $(OBJDIR)\rebuild$O $(OBJDIR)\regexp$O $(OBJDIR)\report$O $(OBJDIR)\rss$O $(OBJDIR)\schema$O $(OBJDIR)\search$O $(OBJDIR)\security_audit$O $(OBJDIR)\setup$O $(OBJDIR)\setupuser$O $(OBJDIR)\sha1$O $(OBJDIR)\sha1hard$O $(OBJDIR)\sha3$O $(OBJDIR)\shun$O $(OBJDIR)\sitemap$O $(OBJDIR)\skins$O $(OBJDIR)\smtp$O $(OBJDIR)\sqlcmd$O $(OBJDIR)\stash$O $(OBJDIR)\stat$O $(OBJDIR)\statrep$O $(OBJDIR)\style$O $(OBJDIR)\sync$O $(OBJDIR)\tag$O $(OBJDIR)\tar$O $(OBJDIR)\th_main$O $(OBJDIR)\timeline$O $(OBJDIR)\tkt$O $(OBJDIR)\tktsetup$O $(OBJDIR)\undo$O $(OBJDIR)\unicode$O $(OBJDIR)\unversioned$O $(OBJDIR)\update$O $(OBJDIR)\url$O $(OBJDIR)\user$O $(OBJDIR)\utf8$O $(OBJDIR)\util$O $(OBJDIR)\verify$O $(OBJDIR)\vfile$O $(OBJDIR)\webmail$O $(OBJDIR)\wiki$O $(OBJDIR)\wikiformat$O $(OBJDIR)\winfile$O $(OBJDIR)\winhttp$O $(OBJDIR)\wysiwyg$O $(OBJDIR)\xfer$O $(OBJDIR)\xfersetup$O $(OBJDIR)\zip$O $(OBJDIR)\shell$O $(OBJDIR)\sqlite3$O $(OBJDIR)\th$O $(OBJDIR)\th_lang$O RC=$(DMDIR)\bin\rcc RCFLAGS=-32 -w1 -I$(SRCDIR) /D__DMC__ APPNAME = $(OBJDIR)\fossil$(E) all: $(APPNAME) $(APPNAME) : translate$E mkindex$E codecheck1$E headers $(OBJ) $(OBJDIR)\link cd $(OBJDIR) codecheck1$E $(SRC) $(DMDIR)\bin\link @link $(OBJDIR)\fossil.res: $B\win\fossil.rc $(RC) $(RCFLAGS) -o$@ $** $(OBJDIR)\link: $B\win\Makefile.dmc $(OBJDIR)\fossil.res +echo add allrepo attach backoffice bag bisect blob branch browse builtin bundle cache capabilities captcha cgi checkin checkout clearsign clone comformat configure content cookies db delta deltacmd descendants diff diffcmd dispatch doc email encode etag event export file finfo foci forum fshell fusefs glob graph gzip hname http http_socket http_ssl http_transport import info json json_artifact json_branch json_config json_diff json_dir json_finfo json_login json_query json_report json_status json_tag json_timeline json_user json_wiki leaf loadctrl login lookslike main manifest markdown markdown_html md5 merge merge3 moderate name path piechart pivot popen pqueue printf publish purge rebuild regexp report rss schema search security_audit setup setupuser sha1 sha1hard sha3 shun sitemap skins smtp sqlcmd stash stat statrep style sync tag tar th_main timeline tkt tktsetup undo unicode unversioned update url user utf8 util verify vfile webmail wiki wikiformat winfile winhttp wysiwyg xfer xfersetup zip shell sqlite3 th th_lang > $@ +echo fossil >> $@ +echo fossil >> $@ +echo $(LIBS) >> $@ +echo. >> $@ +echo fossil >> $@ translate$E: $(SRCDIR)\translate.c |
︙ | ︙ | |||
144 145 146 147 148 149 150 151 152 153 154 155 156 157 | +translate$E $** > $@ $(OBJDIR)\attach$O : attach_.c attach.h $(TCC) -o$@ -c attach_.c attach_.c : $(SRCDIR)\attach.c +translate$E $** > $@ $(OBJDIR)\bag$O : bag_.c bag.h $(TCC) -o$@ -c bag_.c bag_.c : $(SRCDIR)\bag.c +translate$E $** > $@ | > > > > > > | 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 | +translate$E $** > $@ $(OBJDIR)\attach$O : attach_.c attach.h $(TCC) -o$@ -c attach_.c attach_.c : $(SRCDIR)\attach.c +translate$E $** > $@ $(OBJDIR)\backoffice$O : backoffice_.c backoffice.h $(TCC) -o$@ -c backoffice_.c backoffice_.c : $(SRCDIR)\backoffice.c +translate$E $** > $@ $(OBJDIR)\bag$O : bag_.c bag.h $(TCC) -o$@ -c bag_.c bag_.c : $(SRCDIR)\bag.c +translate$E $** > $@ |
︙ | ︙ | |||
192 193 194 195 196 197 198 199 200 201 202 203 204 205 | +translate$E $** > $@ $(OBJDIR)\cache$O : cache_.c cache.h $(TCC) -o$@ -c cache_.c cache_.c : $(SRCDIR)\cache.c +translate$E $** > $@ $(OBJDIR)\captcha$O : captcha_.c captcha.h $(TCC) -o$@ -c captcha_.c captcha_.c : $(SRCDIR)\captcha.c +translate$E $** > $@ | > > > > > > | 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 | +translate$E $** > $@ $(OBJDIR)\cache$O : cache_.c cache.h $(TCC) -o$@ -c cache_.c cache_.c : $(SRCDIR)\cache.c +translate$E $** > $@ $(OBJDIR)\capabilities$O : capabilities_.c capabilities.h $(TCC) -o$@ -c capabilities_.c capabilities_.c : $(SRCDIR)\capabilities.c +translate$E $** > $@ $(OBJDIR)\captcha$O : captcha_.c captcha.h $(TCC) -o$@ -c captcha_.c captcha_.c : $(SRCDIR)\captcha.c +translate$E $** > $@ |
︙ | ︙ | |||
300 301 302 303 304 305 306 307 308 309 310 311 312 313 | +translate$E $** > $@ $(OBJDIR)\doc$O : doc_.c doc.h $(TCC) -o$@ -c doc_.c doc_.c : $(SRCDIR)\doc.c +translate$E $** > $@ $(OBJDIR)\encode$O : encode_.c encode.h $(TCC) -o$@ -c encode_.c encode_.c : $(SRCDIR)\encode.c +translate$E $** > $@ | > > > > > > | 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 | +translate$E $** > $@ $(OBJDIR)\doc$O : doc_.c doc.h $(TCC) -o$@ -c doc_.c doc_.c : $(SRCDIR)\doc.c +translate$E $** > $@ $(OBJDIR)\email$O : email_.c email.h $(TCC) -o$@ -c email_.c email_.c : $(SRCDIR)\email.c +translate$E $** > $@ $(OBJDIR)\encode$O : encode_.c encode.h $(TCC) -o$@ -c encode_.c encode_.c : $(SRCDIR)\encode.c +translate$E $** > $@ |
︙ | ︙ | |||
342 343 344 345 346 347 348 349 350 351 352 353 354 355 | +translate$E $** > $@ $(OBJDIR)\foci$O : foci_.c foci.h $(TCC) -o$@ -c foci_.c foci_.c : $(SRCDIR)\foci.c +translate$E $** > $@ $(OBJDIR)\fshell$O : fshell_.c fshell.h $(TCC) -o$@ -c fshell_.c fshell_.c : $(SRCDIR)\fshell.c +translate$E $** > $@ | > > > > > > | 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 | +translate$E $** > $@ $(OBJDIR)\foci$O : foci_.c foci.h $(TCC) -o$@ -c foci_.c foci_.c : $(SRCDIR)\foci.c +translate$E $** > $@ $(OBJDIR)\forum$O : forum_.c forum.h $(TCC) -o$@ -c forum_.c forum_.c : $(SRCDIR)\forum.c +translate$E $** > $@ $(OBJDIR)\fshell$O : fshell_.c fshell.h $(TCC) -o$@ -c fshell_.c fshell_.c : $(SRCDIR)\fshell.c +translate$E $** > $@ |
︙ | ︙ | |||
678 679 680 681 682 683 684 685 686 687 688 689 690 691 | +translate$E $** > $@ $(OBJDIR)\setup$O : setup_.c setup.h $(TCC) -o$@ -c setup_.c setup_.c : $(SRCDIR)\setup.c +translate$E $** > $@ $(OBJDIR)\sha1$O : sha1_.c sha1.h $(TCC) -o$@ -c sha1_.c sha1_.c : $(SRCDIR)\sha1.c +translate$E $** > $@ | > > > > > > | 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 | +translate$E $** > $@ $(OBJDIR)\setup$O : setup_.c setup.h $(TCC) -o$@ -c setup_.c setup_.c : $(SRCDIR)\setup.c +translate$E $** > $@ $(OBJDIR)\setupuser$O : setupuser_.c setupuser.h $(TCC) -o$@ -c setupuser_.c setupuser_.c : $(SRCDIR)\setupuser.c +translate$E $** > $@ $(OBJDIR)\sha1$O : sha1_.c sha1.h $(TCC) -o$@ -c sha1_.c sha1_.c : $(SRCDIR)\sha1.c +translate$E $** > $@ |
︙ | ︙ | |||
714 715 716 717 718 719 720 721 722 723 724 725 726 727 | +translate$E $** > $@ $(OBJDIR)\skins$O : skins_.c skins.h $(TCC) -o$@ -c skins_.c skins_.c : $(SRCDIR)\skins.c +translate$E $** > $@ $(OBJDIR)\sqlcmd$O : sqlcmd_.c sqlcmd.h $(TCC) -o$@ -c sqlcmd_.c sqlcmd_.c : $(SRCDIR)\sqlcmd.c +translate$E $** > $@ | > > > > > > | 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 | +translate$E $** > $@ $(OBJDIR)\skins$O : skins_.c skins.h $(TCC) -o$@ -c skins_.c skins_.c : $(SRCDIR)\skins.c +translate$E $** > $@ $(OBJDIR)\smtp$O : smtp_.c smtp.h $(TCC) -o$@ -c smtp_.c smtp_.c : $(SRCDIR)\smtp.c +translate$E $** > $@ $(OBJDIR)\sqlcmd$O : sqlcmd_.c sqlcmd.h $(TCC) -o$@ -c sqlcmd_.c sqlcmd_.c : $(SRCDIR)\sqlcmd.c +translate$E $** > $@ |
︙ | ︙ | |||
846 847 848 849 850 851 852 853 854 855 856 857 858 859 | +translate$E $** > $@ $(OBJDIR)\vfile$O : vfile_.c vfile.h $(TCC) -o$@ -c vfile_.c vfile_.c : $(SRCDIR)\vfile.c +translate$E $** > $@ $(OBJDIR)\wiki$O : wiki_.c wiki.h $(TCC) -o$@ -c wiki_.c wiki_.c : $(SRCDIR)\wiki.c +translate$E $** > $@ | > > > > > > | 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 | +translate$E $** > $@ $(OBJDIR)\vfile$O : vfile_.c vfile.h $(TCC) -o$@ -c vfile_.c vfile_.c : $(SRCDIR)\vfile.c +translate$E $** > $@ $(OBJDIR)\webmail$O : webmail_.c webmail.h $(TCC) -o$@ -c webmail_.c webmail_.c : $(SRCDIR)\webmail.c +translate$E $** > $@ $(OBJDIR)\wiki$O : wiki_.c wiki.h $(TCC) -o$@ -c wiki_.c wiki_.c : $(SRCDIR)\wiki.c +translate$E $** > $@ |
︙ | ︙ | |||
896 897 898 899 900 901 902 | $(OBJDIR)\zip$O : zip_.c zip.h $(TCC) -o$@ -c zip_.c zip_.c : $(SRCDIR)\zip.c +translate$E $** > $@ headers: makeheaders$E page_index.h builtin_data.h default_css.h VERSION.h | | | 938 939 940 941 942 943 944 945 946 | $(OBJDIR)\zip$O : zip_.c zip.h $(TCC) -o$@ -c zip_.c zip_.c : $(SRCDIR)\zip.c +translate$E $** > $@ headers: makeheaders$E page_index.h builtin_data.h default_css.h VERSION.h +makeheaders$E add_.c:add.h allrepo_.c:allrepo.h attach_.c:attach.h backoffice_.c:backoffice.h bag_.c:bag.h bisect_.c:bisect.h blob_.c:blob.h branch_.c:branch.h browse_.c:browse.h builtin_.c:builtin.h bundle_.c:bundle.h cache_.c:cache.h capabilities_.c:capabilities.h captcha_.c:captcha.h cgi_.c:cgi.h checkin_.c:checkin.h checkout_.c:checkout.h clearsign_.c:clearsign.h clone_.c:clone.h comformat_.c:comformat.h configure_.c:configure.h content_.c:content.h cookies_.c:cookies.h db_.c:db.h delta_.c:delta.h deltacmd_.c:deltacmd.h descendants_.c:descendants.h diff_.c:diff.h diffcmd_.c:diffcmd.h dispatch_.c:dispatch.h doc_.c:doc.h email_.c:email.h encode_.c:encode.h etag_.c:etag.h event_.c:event.h export_.c:export.h file_.c:file.h finfo_.c:finfo.h foci_.c:foci.h forum_.c:forum.h fshell_.c:fshell.h fusefs_.c:fusefs.h glob_.c:glob.h graph_.c:graph.h gzip_.c:gzip.h hname_.c:hname.h http_.c:http.h http_socket_.c:http_socket.h http_ssl_.c:http_ssl.h http_transport_.c:http_transport.h import_.c:import.h info_.c:info.h json_.c:json.h json_artifact_.c:json_artifact.h json_branch_.c:json_branch.h json_config_.c:json_config.h json_diff_.c:json_diff.h json_dir_.c:json_dir.h json_finfo_.c:json_finfo.h json_login_.c:json_login.h json_query_.c:json_query.h json_report_.c:json_report.h json_status_.c:json_status.h json_tag_.c:json_tag.h json_timeline_.c:json_timeline.h json_user_.c:json_user.h json_wiki_.c:json_wiki.h leaf_.c:leaf.h loadctrl_.c:loadctrl.h login_.c:login.h lookslike_.c:lookslike.h main_.c:main.h manifest_.c:manifest.h markdown_.c:markdown.h markdown_html_.c:markdown_html.h md5_.c:md5.h merge_.c:merge.h merge3_.c:merge3.h moderate_.c:moderate.h name_.c:name.h path_.c:path.h piechart_.c:piechart.h pivot_.c:pivot.h popen_.c:popen.h pqueue_.c:pqueue.h printf_.c:printf.h publish_.c:publish.h purge_.c:purge.h rebuild_.c:rebuild.h regexp_.c:regexp.h report_.c:report.h rss_.c:rss.h schema_.c:schema.h search_.c:search.h security_audit_.c:security_audit.h setup_.c:setup.h setupuser_.c:setupuser.h sha1_.c:sha1.h sha1hard_.c:sha1hard.h sha3_.c:sha3.h shun_.c:shun.h sitemap_.c:sitemap.h skins_.c:skins.h smtp_.c:smtp.h sqlcmd_.c:sqlcmd.h stash_.c:stash.h stat_.c:stat.h statrep_.c:statrep.h style_.c:style.h sync_.c:sync.h tag_.c:tag.h tar_.c:tar.h th_main_.c:th_main.h timeline_.c:timeline.h tkt_.c:tkt.h tktsetup_.c:tktsetup.h undo_.c:undo.h unicode_.c:unicode.h unversioned_.c:unversioned.h update_.c:update.h url_.c:url.h user_.c:user.h utf8_.c:utf8.h util_.c:util.h verify_.c:verify.h vfile_.c:vfile.h webmail_.c:webmail.h wiki_.c:wiki.h wikiformat_.c:wikiformat.h winfile_.c:winfile.h winhttp_.c:winhttp.h wysiwyg_.c:wysiwyg.h xfer_.c:xfer.h xfersetup_.c:xfersetup.h zip_.c:zip.h $(SRCDIR)\sqlite3.h $(SRCDIR)\th.h VERSION.h $(SRCDIR)\cson_amalgamation.h @copy /Y nul: headers |
Changes to win/Makefile.mingw.
︙ | ︙ | |||
69 70 71 72 73 74 75 | #### Enable relative paths in external diff/gdiff # # FOSSIL_ENABLE_EXEC_REL_PATHS = 1 #### Enable legacy treatment of mv/rm (skip checkout files) # | | | 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 | #### Enable relative paths in external diff/gdiff # # FOSSIL_ENABLE_EXEC_REL_PATHS = 1 #### Enable legacy treatment of mv/rm (skip checkout files) # FOSSIL_ENABLE_LEGACY_MV_RM = 1 #### Enable TH1 scripts in embedded documentation files # # FOSSIL_ENABLE_TH1_DOCS = 1 #### Enable hooks for commands and web pages via TH1 # |
︙ | ︙ | |||
172 173 174 175 176 177 178 | endif #### The directories where the OpenSSL include and library files are located. # The recommended usage here is to use the Sysinternals junction tool # to create a hard link between an "openssl-1.x" sub-directory of the # Fossil source code directory and the target OpenSSL source directory. # | | | 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 | endif #### The directories where the OpenSSL include and library files are located. # The recommended usage here is to use the Sysinternals junction tool # to create a hard link between an "openssl-1.x" sub-directory of the # Fossil source code directory and the target OpenSSL source directory. # OPENSSLDIR = $(SRCDIR)/../compat/openssl-1.1.1-pre8 OPENSSLINCDIR = $(OPENSSLDIR)/include OPENSSLLIBDIR = $(OPENSSLDIR) #### Either the directory where the Tcl library is installed or the Tcl # source code directory resides (depending on the value of the macro # FOSSIL_TCL_SOURCE). If this points to the Tcl install directory, # this directory must have "include" and "lib" sub-directories. If |
︙ | ︙ | |||
405 406 407 408 409 410 411 412 413 414 415 416 417 418 | LIB += -lkernel32 -lws2_32 else LIB += -lnetapi32 -lkernel32 -luser32 -ladvapi32 -lws2_32 endif else LIB += -lkernel32 -lws2_32 endif #### Tcl shell for use in running the fossil test suite. This is only # used for testing. # TCLSH = tclsh #### Nullsoft installer MakeNSIS location | > > > > | 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 | LIB += -lkernel32 -lws2_32 else LIB += -lnetapi32 -lkernel32 -luser32 -ladvapi32 -lws2_32 endif else LIB += -lkernel32 -lws2_32 endif #### Library required for DNS lookups. # LIB += -ldnsapi #### Tcl shell for use in running the fossil test suite. This is only # used for testing. # TCLSH = tclsh #### Nullsoft installer MakeNSIS location |
︙ | ︙ | |||
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 | XBCC = $(BCC) $(CFLAGS) XTCC = $(TCC) $(CFLAGS) -I. -I$(SRCDIR) SRC = \ $(SRCDIR)/add.c \ $(SRCDIR)/allrepo.c \ $(SRCDIR)/attach.c \ $(SRCDIR)/bag.c \ $(SRCDIR)/bisect.c \ $(SRCDIR)/blob.c \ $(SRCDIR)/branch.c \ $(SRCDIR)/browse.c \ $(SRCDIR)/builtin.c \ $(SRCDIR)/bundle.c \ $(SRCDIR)/cache.c \ $(SRCDIR)/captcha.c \ $(SRCDIR)/cgi.c \ $(SRCDIR)/checkin.c \ $(SRCDIR)/checkout.c \ $(SRCDIR)/clearsign.c \ $(SRCDIR)/clone.c \ $(SRCDIR)/comformat.c \ $(SRCDIR)/configure.c \ $(SRCDIR)/content.c \ $(SRCDIR)/cookies.c \ $(SRCDIR)/db.c \ $(SRCDIR)/delta.c \ $(SRCDIR)/deltacmd.c \ $(SRCDIR)/descendants.c \ $(SRCDIR)/diff.c \ $(SRCDIR)/diffcmd.c \ $(SRCDIR)/dispatch.c \ $(SRCDIR)/doc.c \ $(SRCDIR)/encode.c \ $(SRCDIR)/etag.c \ $(SRCDIR)/event.c \ $(SRCDIR)/export.c \ $(SRCDIR)/file.c \ $(SRCDIR)/finfo.c \ $(SRCDIR)/foci.c \ $(SRCDIR)/fshell.c \ $(SRCDIR)/fusefs.c \ $(SRCDIR)/glob.c \ $(SRCDIR)/graph.c \ $(SRCDIR)/gzip.c \ $(SRCDIR)/hname.c \ $(SRCDIR)/http.c \ | > > > > | 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 | XBCC = $(BCC) $(CFLAGS) XTCC = $(TCC) $(CFLAGS) -I. -I$(SRCDIR) SRC = \ $(SRCDIR)/add.c \ $(SRCDIR)/allrepo.c \ $(SRCDIR)/attach.c \ $(SRCDIR)/backoffice.c \ $(SRCDIR)/bag.c \ $(SRCDIR)/bisect.c \ $(SRCDIR)/blob.c \ $(SRCDIR)/branch.c \ $(SRCDIR)/browse.c \ $(SRCDIR)/builtin.c \ $(SRCDIR)/bundle.c \ $(SRCDIR)/cache.c \ $(SRCDIR)/capabilities.c \ $(SRCDIR)/captcha.c \ $(SRCDIR)/cgi.c \ $(SRCDIR)/checkin.c \ $(SRCDIR)/checkout.c \ $(SRCDIR)/clearsign.c \ $(SRCDIR)/clone.c \ $(SRCDIR)/comformat.c \ $(SRCDIR)/configure.c \ $(SRCDIR)/content.c \ $(SRCDIR)/cookies.c \ $(SRCDIR)/db.c \ $(SRCDIR)/delta.c \ $(SRCDIR)/deltacmd.c \ $(SRCDIR)/descendants.c \ $(SRCDIR)/diff.c \ $(SRCDIR)/diffcmd.c \ $(SRCDIR)/dispatch.c \ $(SRCDIR)/doc.c \ $(SRCDIR)/email.c \ $(SRCDIR)/encode.c \ $(SRCDIR)/etag.c \ $(SRCDIR)/event.c \ $(SRCDIR)/export.c \ $(SRCDIR)/file.c \ $(SRCDIR)/finfo.c \ $(SRCDIR)/foci.c \ $(SRCDIR)/forum.c \ $(SRCDIR)/fshell.c \ $(SRCDIR)/fusefs.c \ $(SRCDIR)/glob.c \ $(SRCDIR)/graph.c \ $(SRCDIR)/gzip.c \ $(SRCDIR)/hname.c \ $(SRCDIR)/http.c \ |
︙ | ︙ | |||
522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 | $(SRCDIR)/regexp.c \ $(SRCDIR)/report.c \ $(SRCDIR)/rss.c \ $(SRCDIR)/schema.c \ $(SRCDIR)/search.c \ $(SRCDIR)/security_audit.c \ $(SRCDIR)/setup.c \ $(SRCDIR)/sha1.c \ $(SRCDIR)/sha1hard.c \ $(SRCDIR)/sha3.c \ $(SRCDIR)/shun.c \ $(SRCDIR)/sitemap.c \ $(SRCDIR)/skins.c \ $(SRCDIR)/sqlcmd.c \ $(SRCDIR)/stash.c \ $(SRCDIR)/stat.c \ $(SRCDIR)/statrep.c \ $(SRCDIR)/style.c \ $(SRCDIR)/sync.c \ $(SRCDIR)/tag.c \ | > > | 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 | $(SRCDIR)/regexp.c \ $(SRCDIR)/report.c \ $(SRCDIR)/rss.c \ $(SRCDIR)/schema.c \ $(SRCDIR)/search.c \ $(SRCDIR)/security_audit.c \ $(SRCDIR)/setup.c \ $(SRCDIR)/setupuser.c \ $(SRCDIR)/sha1.c \ $(SRCDIR)/sha1hard.c \ $(SRCDIR)/sha3.c \ $(SRCDIR)/shun.c \ $(SRCDIR)/sitemap.c \ $(SRCDIR)/skins.c \ $(SRCDIR)/smtp.c \ $(SRCDIR)/sqlcmd.c \ $(SRCDIR)/stash.c \ $(SRCDIR)/stat.c \ $(SRCDIR)/statrep.c \ $(SRCDIR)/style.c \ $(SRCDIR)/sync.c \ $(SRCDIR)/tag.c \ |
︙ | ︙ | |||
550 551 552 553 554 555 556 557 558 559 560 561 562 563 | $(SRCDIR)/update.c \ $(SRCDIR)/url.c \ $(SRCDIR)/user.c \ $(SRCDIR)/utf8.c \ $(SRCDIR)/util.c \ $(SRCDIR)/verify.c \ $(SRCDIR)/vfile.c \ $(SRCDIR)/wiki.c \ $(SRCDIR)/wikiformat.c \ $(SRCDIR)/winfile.c \ $(SRCDIR)/winhttp.c \ $(SRCDIR)/wysiwyg.c \ $(SRCDIR)/xfer.c \ $(SRCDIR)/xfersetup.c \ | > | 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 | $(SRCDIR)/update.c \ $(SRCDIR)/url.c \ $(SRCDIR)/user.c \ $(SRCDIR)/utf8.c \ $(SRCDIR)/util.c \ $(SRCDIR)/verify.c \ $(SRCDIR)/vfile.c \ $(SRCDIR)/webmail.c \ $(SRCDIR)/wiki.c \ $(SRCDIR)/wikiformat.c \ $(SRCDIR)/winfile.c \ $(SRCDIR)/winhttp.c \ $(SRCDIR)/wysiwyg.c \ $(SRCDIR)/xfer.c \ $(SRCDIR)/xfersetup.c \ |
︙ | ︙ | |||
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 | $(SRCDIR)/../skins/rounded1/header.txt \ $(SRCDIR)/../skins/xekri/css.txt \ $(SRCDIR)/../skins/xekri/details.txt \ $(SRCDIR)/../skins/xekri/footer.txt \ $(SRCDIR)/../skins/xekri/header.txt \ $(SRCDIR)/ci_edit.js \ $(SRCDIR)/diff.tcl \ $(SRCDIR)/graph.js \ $(SRCDIR)/href.js \ $(SRCDIR)/login.js \ $(SRCDIR)/markdown.md \ $(SRCDIR)/menu.js \ $(SRCDIR)/sbsdiff.js \ $(SRCDIR)/scroll.js \ $(SRCDIR)/skin.js \ $(SRCDIR)/sorttable.js \ $(SRCDIR)/tree.js \ $(SRCDIR)/useredit.js \ $(SRCDIR)/wiki.wiki TRANS_SRC = \ $(OBJDIR)/add_.c \ $(OBJDIR)/allrepo_.c \ $(OBJDIR)/attach_.c \ $(OBJDIR)/bag_.c \ $(OBJDIR)/bisect_.c \ $(OBJDIR)/blob_.c \ $(OBJDIR)/branch_.c \ $(OBJDIR)/browse_.c \ $(OBJDIR)/builtin_.c \ $(OBJDIR)/bundle_.c \ $(OBJDIR)/cache_.c \ $(OBJDIR)/captcha_.c \ $(OBJDIR)/cgi_.c \ $(OBJDIR)/checkin_.c \ $(OBJDIR)/checkout_.c \ $(OBJDIR)/clearsign_.c \ $(OBJDIR)/clone_.c \ $(OBJDIR)/comformat_.c \ $(OBJDIR)/configure_.c \ $(OBJDIR)/content_.c \ $(OBJDIR)/cookies_.c \ $(OBJDIR)/db_.c \ $(OBJDIR)/delta_.c \ $(OBJDIR)/deltacmd_.c \ $(OBJDIR)/descendants_.c \ $(OBJDIR)/diff_.c \ $(OBJDIR)/diffcmd_.c \ $(OBJDIR)/dispatch_.c \ $(OBJDIR)/doc_.c \ $(OBJDIR)/encode_.c \ $(OBJDIR)/etag_.c \ $(OBJDIR)/event_.c \ $(OBJDIR)/export_.c \ $(OBJDIR)/file_.c \ $(OBJDIR)/finfo_.c \ $(OBJDIR)/foci_.c \ $(OBJDIR)/fshell_.c \ $(OBJDIR)/fusefs_.c \ $(OBJDIR)/glob_.c \ $(OBJDIR)/graph_.c \ $(OBJDIR)/gzip_.c \ $(OBJDIR)/hname_.c \ $(OBJDIR)/http_.c \ | > > > > > | 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 | $(SRCDIR)/../skins/rounded1/header.txt \ $(SRCDIR)/../skins/xekri/css.txt \ $(SRCDIR)/../skins/xekri/details.txt \ $(SRCDIR)/../skins/xekri/footer.txt \ $(SRCDIR)/../skins/xekri/header.txt \ $(SRCDIR)/ci_edit.js \ $(SRCDIR)/diff.tcl \ $(SRCDIR)/forum.js \ $(SRCDIR)/graph.js \ $(SRCDIR)/href.js \ $(SRCDIR)/login.js \ $(SRCDIR)/markdown.md \ $(SRCDIR)/menu.js \ $(SRCDIR)/sbsdiff.js \ $(SRCDIR)/scroll.js \ $(SRCDIR)/skin.js \ $(SRCDIR)/sorttable.js \ $(SRCDIR)/tree.js \ $(SRCDIR)/useredit.js \ $(SRCDIR)/wiki.wiki TRANS_SRC = \ $(OBJDIR)/add_.c \ $(OBJDIR)/allrepo_.c \ $(OBJDIR)/attach_.c \ $(OBJDIR)/backoffice_.c \ $(OBJDIR)/bag_.c \ $(OBJDIR)/bisect_.c \ $(OBJDIR)/blob_.c \ $(OBJDIR)/branch_.c \ $(OBJDIR)/browse_.c \ $(OBJDIR)/builtin_.c \ $(OBJDIR)/bundle_.c \ $(OBJDIR)/cache_.c \ $(OBJDIR)/capabilities_.c \ $(OBJDIR)/captcha_.c \ $(OBJDIR)/cgi_.c \ $(OBJDIR)/checkin_.c \ $(OBJDIR)/checkout_.c \ $(OBJDIR)/clearsign_.c \ $(OBJDIR)/clone_.c \ $(OBJDIR)/comformat_.c \ $(OBJDIR)/configure_.c \ $(OBJDIR)/content_.c \ $(OBJDIR)/cookies_.c \ $(OBJDIR)/db_.c \ $(OBJDIR)/delta_.c \ $(OBJDIR)/deltacmd_.c \ $(OBJDIR)/descendants_.c \ $(OBJDIR)/diff_.c \ $(OBJDIR)/diffcmd_.c \ $(OBJDIR)/dispatch_.c \ $(OBJDIR)/doc_.c \ $(OBJDIR)/email_.c \ $(OBJDIR)/encode_.c \ $(OBJDIR)/etag_.c \ $(OBJDIR)/event_.c \ $(OBJDIR)/export_.c \ $(OBJDIR)/file_.c \ $(OBJDIR)/finfo_.c \ $(OBJDIR)/foci_.c \ $(OBJDIR)/forum_.c \ $(OBJDIR)/fshell_.c \ $(OBJDIR)/fusefs_.c \ $(OBJDIR)/glob_.c \ $(OBJDIR)/graph_.c \ $(OBJDIR)/gzip_.c \ $(OBJDIR)/hname_.c \ $(OBJDIR)/http_.c \ |
︙ | ︙ | |||
723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 | $(OBJDIR)/regexp_.c \ $(OBJDIR)/report_.c \ $(OBJDIR)/rss_.c \ $(OBJDIR)/schema_.c \ $(OBJDIR)/search_.c \ $(OBJDIR)/security_audit_.c \ $(OBJDIR)/setup_.c \ $(OBJDIR)/sha1_.c \ $(OBJDIR)/sha1hard_.c \ $(OBJDIR)/sha3_.c \ $(OBJDIR)/shun_.c \ $(OBJDIR)/sitemap_.c \ $(OBJDIR)/skins_.c \ $(OBJDIR)/sqlcmd_.c \ $(OBJDIR)/stash_.c \ $(OBJDIR)/stat_.c \ $(OBJDIR)/statrep_.c \ $(OBJDIR)/style_.c \ $(OBJDIR)/sync_.c \ $(OBJDIR)/tag_.c \ | > > | 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 | $(OBJDIR)/regexp_.c \ $(OBJDIR)/report_.c \ $(OBJDIR)/rss_.c \ $(OBJDIR)/schema_.c \ $(OBJDIR)/search_.c \ $(OBJDIR)/security_audit_.c \ $(OBJDIR)/setup_.c \ $(OBJDIR)/setupuser_.c \ $(OBJDIR)/sha1_.c \ $(OBJDIR)/sha1hard_.c \ $(OBJDIR)/sha3_.c \ $(OBJDIR)/shun_.c \ $(OBJDIR)/sitemap_.c \ $(OBJDIR)/skins_.c \ $(OBJDIR)/smtp_.c \ $(OBJDIR)/sqlcmd_.c \ $(OBJDIR)/stash_.c \ $(OBJDIR)/stat_.c \ $(OBJDIR)/statrep_.c \ $(OBJDIR)/style_.c \ $(OBJDIR)/sync_.c \ $(OBJDIR)/tag_.c \ |
︙ | ︙ | |||
751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 | $(OBJDIR)/update_.c \ $(OBJDIR)/url_.c \ $(OBJDIR)/user_.c \ $(OBJDIR)/utf8_.c \ $(OBJDIR)/util_.c \ $(OBJDIR)/verify_.c \ $(OBJDIR)/vfile_.c \ $(OBJDIR)/wiki_.c \ $(OBJDIR)/wikiformat_.c \ $(OBJDIR)/winfile_.c \ $(OBJDIR)/winhttp_.c \ $(OBJDIR)/wysiwyg_.c \ $(OBJDIR)/xfer_.c \ $(OBJDIR)/xfersetup_.c \ $(OBJDIR)/zip_.c OBJ = \ $(OBJDIR)/add.o \ $(OBJDIR)/allrepo.o \ $(OBJDIR)/attach.o \ $(OBJDIR)/bag.o \ $(OBJDIR)/bisect.o \ $(OBJDIR)/blob.o \ $(OBJDIR)/branch.o \ $(OBJDIR)/browse.o \ $(OBJDIR)/builtin.o \ $(OBJDIR)/bundle.o \ $(OBJDIR)/cache.o \ $(OBJDIR)/captcha.o \ $(OBJDIR)/cgi.o \ $(OBJDIR)/checkin.o \ $(OBJDIR)/checkout.o \ $(OBJDIR)/clearsign.o \ $(OBJDIR)/clone.o \ $(OBJDIR)/comformat.o \ $(OBJDIR)/configure.o \ $(OBJDIR)/content.o \ $(OBJDIR)/cookies.o \ $(OBJDIR)/db.o \ $(OBJDIR)/delta.o \ $(OBJDIR)/deltacmd.o \ $(OBJDIR)/descendants.o \ $(OBJDIR)/diff.o \ $(OBJDIR)/diffcmd.o \ $(OBJDIR)/dispatch.o \ $(OBJDIR)/doc.o \ $(OBJDIR)/encode.o \ $(OBJDIR)/etag.o \ $(OBJDIR)/event.o \ $(OBJDIR)/export.o \ $(OBJDIR)/file.o \ $(OBJDIR)/finfo.o \ $(OBJDIR)/foci.o \ $(OBJDIR)/fshell.o \ $(OBJDIR)/fusefs.o \ $(OBJDIR)/glob.o \ $(OBJDIR)/graph.o \ $(OBJDIR)/gzip.o \ $(OBJDIR)/hname.o \ $(OBJDIR)/http.o \ | > > > > > | 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 | $(OBJDIR)/update_.c \ $(OBJDIR)/url_.c \ $(OBJDIR)/user_.c \ $(OBJDIR)/utf8_.c \ $(OBJDIR)/util_.c \ $(OBJDIR)/verify_.c \ $(OBJDIR)/vfile_.c \ $(OBJDIR)/webmail_.c \ $(OBJDIR)/wiki_.c \ $(OBJDIR)/wikiformat_.c \ $(OBJDIR)/winfile_.c \ $(OBJDIR)/winhttp_.c \ $(OBJDIR)/wysiwyg_.c \ $(OBJDIR)/xfer_.c \ $(OBJDIR)/xfersetup_.c \ $(OBJDIR)/zip_.c OBJ = \ $(OBJDIR)/add.o \ $(OBJDIR)/allrepo.o \ $(OBJDIR)/attach.o \ $(OBJDIR)/backoffice.o \ $(OBJDIR)/bag.o \ $(OBJDIR)/bisect.o \ $(OBJDIR)/blob.o \ $(OBJDIR)/branch.o \ $(OBJDIR)/browse.o \ $(OBJDIR)/builtin.o \ $(OBJDIR)/bundle.o \ $(OBJDIR)/cache.o \ $(OBJDIR)/capabilities.o \ $(OBJDIR)/captcha.o \ $(OBJDIR)/cgi.o \ $(OBJDIR)/checkin.o \ $(OBJDIR)/checkout.o \ $(OBJDIR)/clearsign.o \ $(OBJDIR)/clone.o \ $(OBJDIR)/comformat.o \ $(OBJDIR)/configure.o \ $(OBJDIR)/content.o \ $(OBJDIR)/cookies.o \ $(OBJDIR)/db.o \ $(OBJDIR)/delta.o \ $(OBJDIR)/deltacmd.o \ $(OBJDIR)/descendants.o \ $(OBJDIR)/diff.o \ $(OBJDIR)/diffcmd.o \ $(OBJDIR)/dispatch.o \ $(OBJDIR)/doc.o \ $(OBJDIR)/email.o \ $(OBJDIR)/encode.o \ $(OBJDIR)/etag.o \ $(OBJDIR)/event.o \ $(OBJDIR)/export.o \ $(OBJDIR)/file.o \ $(OBJDIR)/finfo.o \ $(OBJDIR)/foci.o \ $(OBJDIR)/forum.o \ $(OBJDIR)/fshell.o \ $(OBJDIR)/fusefs.o \ $(OBJDIR)/glob.o \ $(OBJDIR)/graph.o \ $(OBJDIR)/gzip.o \ $(OBJDIR)/hname.o \ $(OBJDIR)/http.o \ |
︙ | ︙ | |||
853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 | $(OBJDIR)/regexp.o \ $(OBJDIR)/report.o \ $(OBJDIR)/rss.o \ $(OBJDIR)/schema.o \ $(OBJDIR)/search.o \ $(OBJDIR)/security_audit.o \ $(OBJDIR)/setup.o \ $(OBJDIR)/sha1.o \ $(OBJDIR)/sha1hard.o \ $(OBJDIR)/sha3.o \ $(OBJDIR)/shun.o \ $(OBJDIR)/sitemap.o \ $(OBJDIR)/skins.o \ $(OBJDIR)/sqlcmd.o \ $(OBJDIR)/stash.o \ $(OBJDIR)/stat.o \ $(OBJDIR)/statrep.o \ $(OBJDIR)/style.o \ $(OBJDIR)/sync.o \ $(OBJDIR)/tag.o \ | > > | 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 | $(OBJDIR)/regexp.o \ $(OBJDIR)/report.o \ $(OBJDIR)/rss.o \ $(OBJDIR)/schema.o \ $(OBJDIR)/search.o \ $(OBJDIR)/security_audit.o \ $(OBJDIR)/setup.o \ $(OBJDIR)/setupuser.o \ $(OBJDIR)/sha1.o \ $(OBJDIR)/sha1hard.o \ $(OBJDIR)/sha3.o \ $(OBJDIR)/shun.o \ $(OBJDIR)/sitemap.o \ $(OBJDIR)/skins.o \ $(OBJDIR)/smtp.o \ $(OBJDIR)/sqlcmd.o \ $(OBJDIR)/stash.o \ $(OBJDIR)/stat.o \ $(OBJDIR)/statrep.o \ $(OBJDIR)/style.o \ $(OBJDIR)/sync.o \ $(OBJDIR)/tag.o \ |
︙ | ︙ | |||
881 882 883 884 885 886 887 888 889 890 891 892 893 894 | $(OBJDIR)/update.o \ $(OBJDIR)/url.o \ $(OBJDIR)/user.o \ $(OBJDIR)/utf8.o \ $(OBJDIR)/util.o \ $(OBJDIR)/verify.o \ $(OBJDIR)/vfile.o \ $(OBJDIR)/wiki.o \ $(OBJDIR)/wikiformat.o \ $(OBJDIR)/winfile.o \ $(OBJDIR)/winhttp.o \ $(OBJDIR)/wysiwyg.o \ $(OBJDIR)/xfer.o \ $(OBJDIR)/xfersetup.o \ | > | 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 | $(OBJDIR)/update.o \ $(OBJDIR)/url.o \ $(OBJDIR)/user.o \ $(OBJDIR)/utf8.o \ $(OBJDIR)/util.o \ $(OBJDIR)/verify.o \ $(OBJDIR)/vfile.o \ $(OBJDIR)/webmail.o \ $(OBJDIR)/wiki.o \ $(OBJDIR)/wikiformat.o \ $(OBJDIR)/winfile.o \ $(OBJDIR)/winhttp.o \ $(OBJDIR)/wysiwyg.o \ $(OBJDIR)/xfer.o \ $(OBJDIR)/xfersetup.o \ |
︙ | ︙ | |||
951 952 953 954 955 956 957 | $(CP) $(SRCDIR)/../win/fossil.exe.manifest $(OBJDIR) endif $(RCC) $(OBJDIR)/fossil.rc -o $(OBJDIR)/fossil.o install: $(OBJDIR) $(APPNAME) ifdef USE_WINDOWS $(MKDIR) $(subst /,\,$(INSTALLDIR)) | | | | 977 978 979 980 981 982 983 984 985 986 987 988 989 990 991 992 993 994 | $(CP) $(SRCDIR)/../win/fossil.exe.manifest $(OBJDIR) endif $(RCC) $(OBJDIR)/fossil.rc -o $(OBJDIR)/fossil.o install: $(OBJDIR) $(APPNAME) ifdef USE_WINDOWS $(MKDIR) $(subst /,\,$(INSTALLDIR)) $(CP) $(subst /,\,$(APPNAME)) $(subst /,\,$(INSTALLDIR)) else $(MKDIR) $(INSTALLDIR) $(CP) $(APPNAME) $(INSTALLDIR) endif $(OBJDIR): ifdef USE_WINDOWS $(MKDIR) $(subst /,\,$(OBJDIR)) else $(MKDIR) $(OBJDIR) |
︙ | ︙ | |||
1113 1114 1115 1116 1117 1118 1119 1120 1121 1122 1123 1124 1125 1126 1127 1128 1129 1130 1131 1132 1133 1134 1135 1136 1137 1138 1139 1140 1141 1142 1143 1144 1145 1146 1147 1148 1149 1150 1151 1152 1153 1154 1155 1156 1157 1158 1159 | $(OBJDIR)/builtin_data.h: $(MKBUILTIN) $(EXTRA_FILES) $(MKBUILTIN) --prefix $(SRCDIR)/ $(EXTRA_FILES) >$@ $(OBJDIR)/headers: $(OBJDIR)/page_index.h $(OBJDIR)/builtin_data.h $(OBJDIR)/default_css.h $(MAKEHEADERS) $(OBJDIR)/VERSION.h $(MAKEHEADERS) $(OBJDIR)/add_.c:$(OBJDIR)/add.h \ $(OBJDIR)/allrepo_.c:$(OBJDIR)/allrepo.h \ $(OBJDIR)/attach_.c:$(OBJDIR)/attach.h \ $(OBJDIR)/bag_.c:$(OBJDIR)/bag.h \ $(OBJDIR)/bisect_.c:$(OBJDIR)/bisect.h \ $(OBJDIR)/blob_.c:$(OBJDIR)/blob.h \ $(OBJDIR)/branch_.c:$(OBJDIR)/branch.h \ $(OBJDIR)/browse_.c:$(OBJDIR)/browse.h \ $(OBJDIR)/builtin_.c:$(OBJDIR)/builtin.h \ $(OBJDIR)/bundle_.c:$(OBJDIR)/bundle.h \ $(OBJDIR)/cache_.c:$(OBJDIR)/cache.h \ $(OBJDIR)/captcha_.c:$(OBJDIR)/captcha.h \ $(OBJDIR)/cgi_.c:$(OBJDIR)/cgi.h \ $(OBJDIR)/checkin_.c:$(OBJDIR)/checkin.h \ $(OBJDIR)/checkout_.c:$(OBJDIR)/checkout.h \ $(OBJDIR)/clearsign_.c:$(OBJDIR)/clearsign.h \ $(OBJDIR)/clone_.c:$(OBJDIR)/clone.h \ $(OBJDIR)/comformat_.c:$(OBJDIR)/comformat.h \ $(OBJDIR)/configure_.c:$(OBJDIR)/configure.h \ $(OBJDIR)/content_.c:$(OBJDIR)/content.h \ $(OBJDIR)/cookies_.c:$(OBJDIR)/cookies.h \ $(OBJDIR)/db_.c:$(OBJDIR)/db.h \ $(OBJDIR)/delta_.c:$(OBJDIR)/delta.h \ $(OBJDIR)/deltacmd_.c:$(OBJDIR)/deltacmd.h \ $(OBJDIR)/descendants_.c:$(OBJDIR)/descendants.h \ $(OBJDIR)/diff_.c:$(OBJDIR)/diff.h \ $(OBJDIR)/diffcmd_.c:$(OBJDIR)/diffcmd.h \ $(OBJDIR)/dispatch_.c:$(OBJDIR)/dispatch.h \ $(OBJDIR)/doc_.c:$(OBJDIR)/doc.h \ $(OBJDIR)/encode_.c:$(OBJDIR)/encode.h \ $(OBJDIR)/etag_.c:$(OBJDIR)/etag.h \ $(OBJDIR)/event_.c:$(OBJDIR)/event.h \ $(OBJDIR)/export_.c:$(OBJDIR)/export.h \ $(OBJDIR)/file_.c:$(OBJDIR)/file.h \ $(OBJDIR)/finfo_.c:$(OBJDIR)/finfo.h \ $(OBJDIR)/foci_.c:$(OBJDIR)/foci.h \ $(OBJDIR)/fshell_.c:$(OBJDIR)/fshell.h \ $(OBJDIR)/fusefs_.c:$(OBJDIR)/fusefs.h \ $(OBJDIR)/glob_.c:$(OBJDIR)/glob.h \ $(OBJDIR)/graph_.c:$(OBJDIR)/graph.h \ $(OBJDIR)/gzip_.c:$(OBJDIR)/gzip.h \ $(OBJDIR)/hname_.c:$(OBJDIR)/hname.h \ $(OBJDIR)/http_.c:$(OBJDIR)/http.h \ | > > > > | 1139 1140 1141 1142 1143 1144 1145 1146 1147 1148 1149 1150 1151 1152 1153 1154 1155 1156 1157 1158 1159 1160 1161 1162 1163 1164 1165 1166 1167 1168 1169 1170 1171 1172 1173 1174 1175 1176 1177 1178 1179 1180 1181 1182 1183 1184 1185 1186 1187 1188 1189 | $(OBJDIR)/builtin_data.h: $(MKBUILTIN) $(EXTRA_FILES) $(MKBUILTIN) --prefix $(SRCDIR)/ $(EXTRA_FILES) >$@ $(OBJDIR)/headers: $(OBJDIR)/page_index.h $(OBJDIR)/builtin_data.h $(OBJDIR)/default_css.h $(MAKEHEADERS) $(OBJDIR)/VERSION.h $(MAKEHEADERS) $(OBJDIR)/add_.c:$(OBJDIR)/add.h \ $(OBJDIR)/allrepo_.c:$(OBJDIR)/allrepo.h \ $(OBJDIR)/attach_.c:$(OBJDIR)/attach.h \ $(OBJDIR)/backoffice_.c:$(OBJDIR)/backoffice.h \ $(OBJDIR)/bag_.c:$(OBJDIR)/bag.h \ $(OBJDIR)/bisect_.c:$(OBJDIR)/bisect.h \ $(OBJDIR)/blob_.c:$(OBJDIR)/blob.h \ $(OBJDIR)/branch_.c:$(OBJDIR)/branch.h \ $(OBJDIR)/browse_.c:$(OBJDIR)/browse.h \ $(OBJDIR)/builtin_.c:$(OBJDIR)/builtin.h \ $(OBJDIR)/bundle_.c:$(OBJDIR)/bundle.h \ $(OBJDIR)/cache_.c:$(OBJDIR)/cache.h \ $(OBJDIR)/capabilities_.c:$(OBJDIR)/capabilities.h \ $(OBJDIR)/captcha_.c:$(OBJDIR)/captcha.h \ $(OBJDIR)/cgi_.c:$(OBJDIR)/cgi.h \ $(OBJDIR)/checkin_.c:$(OBJDIR)/checkin.h \ $(OBJDIR)/checkout_.c:$(OBJDIR)/checkout.h \ $(OBJDIR)/clearsign_.c:$(OBJDIR)/clearsign.h \ $(OBJDIR)/clone_.c:$(OBJDIR)/clone.h \ $(OBJDIR)/comformat_.c:$(OBJDIR)/comformat.h \ $(OBJDIR)/configure_.c:$(OBJDIR)/configure.h \ $(OBJDIR)/content_.c:$(OBJDIR)/content.h \ $(OBJDIR)/cookies_.c:$(OBJDIR)/cookies.h \ $(OBJDIR)/db_.c:$(OBJDIR)/db.h \ $(OBJDIR)/delta_.c:$(OBJDIR)/delta.h \ $(OBJDIR)/deltacmd_.c:$(OBJDIR)/deltacmd.h \ $(OBJDIR)/descendants_.c:$(OBJDIR)/descendants.h \ $(OBJDIR)/diff_.c:$(OBJDIR)/diff.h \ $(OBJDIR)/diffcmd_.c:$(OBJDIR)/diffcmd.h \ $(OBJDIR)/dispatch_.c:$(OBJDIR)/dispatch.h \ $(OBJDIR)/doc_.c:$(OBJDIR)/doc.h \ $(OBJDIR)/email_.c:$(OBJDIR)/email.h \ $(OBJDIR)/encode_.c:$(OBJDIR)/encode.h \ $(OBJDIR)/etag_.c:$(OBJDIR)/etag.h \ $(OBJDIR)/event_.c:$(OBJDIR)/event.h \ $(OBJDIR)/export_.c:$(OBJDIR)/export.h \ $(OBJDIR)/file_.c:$(OBJDIR)/file.h \ $(OBJDIR)/finfo_.c:$(OBJDIR)/finfo.h \ $(OBJDIR)/foci_.c:$(OBJDIR)/foci.h \ $(OBJDIR)/forum_.c:$(OBJDIR)/forum.h \ $(OBJDIR)/fshell_.c:$(OBJDIR)/fshell.h \ $(OBJDIR)/fusefs_.c:$(OBJDIR)/fusefs.h \ $(OBJDIR)/glob_.c:$(OBJDIR)/glob.h \ $(OBJDIR)/graph_.c:$(OBJDIR)/graph.h \ $(OBJDIR)/gzip_.c:$(OBJDIR)/gzip.h \ $(OBJDIR)/hname_.c:$(OBJDIR)/hname.h \ $(OBJDIR)/http_.c:$(OBJDIR)/http.h \ |
︙ | ︙ | |||
1202 1203 1204 1205 1206 1207 1208 1209 1210 1211 1212 1213 1214 1215 1216 1217 1218 1219 1220 1221 | $(OBJDIR)/regexp_.c:$(OBJDIR)/regexp.h \ $(OBJDIR)/report_.c:$(OBJDIR)/report.h \ $(OBJDIR)/rss_.c:$(OBJDIR)/rss.h \ $(OBJDIR)/schema_.c:$(OBJDIR)/schema.h \ $(OBJDIR)/search_.c:$(OBJDIR)/search.h \ $(OBJDIR)/security_audit_.c:$(OBJDIR)/security_audit.h \ $(OBJDIR)/setup_.c:$(OBJDIR)/setup.h \ $(OBJDIR)/sha1_.c:$(OBJDIR)/sha1.h \ $(OBJDIR)/sha1hard_.c:$(OBJDIR)/sha1hard.h \ $(OBJDIR)/sha3_.c:$(OBJDIR)/sha3.h \ $(OBJDIR)/shun_.c:$(OBJDIR)/shun.h \ $(OBJDIR)/sitemap_.c:$(OBJDIR)/sitemap.h \ $(OBJDIR)/skins_.c:$(OBJDIR)/skins.h \ $(OBJDIR)/sqlcmd_.c:$(OBJDIR)/sqlcmd.h \ $(OBJDIR)/stash_.c:$(OBJDIR)/stash.h \ $(OBJDIR)/stat_.c:$(OBJDIR)/stat.h \ $(OBJDIR)/statrep_.c:$(OBJDIR)/statrep.h \ $(OBJDIR)/style_.c:$(OBJDIR)/style.h \ $(OBJDIR)/sync_.c:$(OBJDIR)/sync.h \ $(OBJDIR)/tag_.c:$(OBJDIR)/tag.h \ | > > | 1232 1233 1234 1235 1236 1237 1238 1239 1240 1241 1242 1243 1244 1245 1246 1247 1248 1249 1250 1251 1252 1253 | $(OBJDIR)/regexp_.c:$(OBJDIR)/regexp.h \ $(OBJDIR)/report_.c:$(OBJDIR)/report.h \ $(OBJDIR)/rss_.c:$(OBJDIR)/rss.h \ $(OBJDIR)/schema_.c:$(OBJDIR)/schema.h \ $(OBJDIR)/search_.c:$(OBJDIR)/search.h \ $(OBJDIR)/security_audit_.c:$(OBJDIR)/security_audit.h \ $(OBJDIR)/setup_.c:$(OBJDIR)/setup.h \ $(OBJDIR)/setupuser_.c:$(OBJDIR)/setupuser.h \ $(OBJDIR)/sha1_.c:$(OBJDIR)/sha1.h \ $(OBJDIR)/sha1hard_.c:$(OBJDIR)/sha1hard.h \ $(OBJDIR)/sha3_.c:$(OBJDIR)/sha3.h \ $(OBJDIR)/shun_.c:$(OBJDIR)/shun.h \ $(OBJDIR)/sitemap_.c:$(OBJDIR)/sitemap.h \ $(OBJDIR)/skins_.c:$(OBJDIR)/skins.h \ $(OBJDIR)/smtp_.c:$(OBJDIR)/smtp.h \ $(OBJDIR)/sqlcmd_.c:$(OBJDIR)/sqlcmd.h \ $(OBJDIR)/stash_.c:$(OBJDIR)/stash.h \ $(OBJDIR)/stat_.c:$(OBJDIR)/stat.h \ $(OBJDIR)/statrep_.c:$(OBJDIR)/statrep.h \ $(OBJDIR)/style_.c:$(OBJDIR)/style.h \ $(OBJDIR)/sync_.c:$(OBJDIR)/sync.h \ $(OBJDIR)/tag_.c:$(OBJDIR)/tag.h \ |
︙ | ︙ | |||
1230 1231 1232 1233 1234 1235 1236 1237 1238 1239 1240 1241 1242 1243 | $(OBJDIR)/update_.c:$(OBJDIR)/update.h \ $(OBJDIR)/url_.c:$(OBJDIR)/url.h \ $(OBJDIR)/user_.c:$(OBJDIR)/user.h \ $(OBJDIR)/utf8_.c:$(OBJDIR)/utf8.h \ $(OBJDIR)/util_.c:$(OBJDIR)/util.h \ $(OBJDIR)/verify_.c:$(OBJDIR)/verify.h \ $(OBJDIR)/vfile_.c:$(OBJDIR)/vfile.h \ $(OBJDIR)/wiki_.c:$(OBJDIR)/wiki.h \ $(OBJDIR)/wikiformat_.c:$(OBJDIR)/wikiformat.h \ $(OBJDIR)/winfile_.c:$(OBJDIR)/winfile.h \ $(OBJDIR)/winhttp_.c:$(OBJDIR)/winhttp.h \ $(OBJDIR)/wysiwyg_.c:$(OBJDIR)/wysiwyg.h \ $(OBJDIR)/xfer_.c:$(OBJDIR)/xfer.h \ $(OBJDIR)/xfersetup_.c:$(OBJDIR)/xfersetup.h \ | > | 1262 1263 1264 1265 1266 1267 1268 1269 1270 1271 1272 1273 1274 1275 1276 | $(OBJDIR)/update_.c:$(OBJDIR)/update.h \ $(OBJDIR)/url_.c:$(OBJDIR)/url.h \ $(OBJDIR)/user_.c:$(OBJDIR)/user.h \ $(OBJDIR)/utf8_.c:$(OBJDIR)/utf8.h \ $(OBJDIR)/util_.c:$(OBJDIR)/util.h \ $(OBJDIR)/verify_.c:$(OBJDIR)/verify.h \ $(OBJDIR)/vfile_.c:$(OBJDIR)/vfile.h \ $(OBJDIR)/webmail_.c:$(OBJDIR)/webmail.h \ $(OBJDIR)/wiki_.c:$(OBJDIR)/wiki.h \ $(OBJDIR)/wikiformat_.c:$(OBJDIR)/wikiformat.h \ $(OBJDIR)/winfile_.c:$(OBJDIR)/winfile.h \ $(OBJDIR)/winhttp_.c:$(OBJDIR)/winhttp.h \ $(OBJDIR)/wysiwyg_.c:$(OBJDIR)/wysiwyg.h \ $(OBJDIR)/xfer_.c:$(OBJDIR)/xfer.h \ $(OBJDIR)/xfersetup_.c:$(OBJDIR)/xfersetup.h \ |
︙ | ︙ | |||
1270 1271 1272 1273 1274 1275 1276 1277 1278 1279 1280 1281 1282 1283 | $(OBJDIR)/attach_.c: $(SRCDIR)/attach.c $(TRANSLATE) $(TRANSLATE) $(SRCDIR)/attach.c >$@ $(OBJDIR)/attach.o: $(OBJDIR)/attach_.c $(OBJDIR)/attach.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/attach.o -c $(OBJDIR)/attach_.c $(OBJDIR)/attach.h: $(OBJDIR)/headers $(OBJDIR)/bag_.c: $(SRCDIR)/bag.c $(TRANSLATE) $(TRANSLATE) $(SRCDIR)/bag.c >$@ $(OBJDIR)/bag.o: $(OBJDIR)/bag_.c $(OBJDIR)/bag.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/bag.o -c $(OBJDIR)/bag_.c | > > > > > > > > | 1303 1304 1305 1306 1307 1308 1309 1310 1311 1312 1313 1314 1315 1316 1317 1318 1319 1320 1321 1322 1323 1324 | $(OBJDIR)/attach_.c: $(SRCDIR)/attach.c $(TRANSLATE) $(TRANSLATE) $(SRCDIR)/attach.c >$@ $(OBJDIR)/attach.o: $(OBJDIR)/attach_.c $(OBJDIR)/attach.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/attach.o -c $(OBJDIR)/attach_.c $(OBJDIR)/attach.h: $(OBJDIR)/headers $(OBJDIR)/backoffice_.c: $(SRCDIR)/backoffice.c $(TRANSLATE) $(TRANSLATE) $(SRCDIR)/backoffice.c >$@ $(OBJDIR)/backoffice.o: $(OBJDIR)/backoffice_.c $(OBJDIR)/backoffice.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/backoffice.o -c $(OBJDIR)/backoffice_.c $(OBJDIR)/backoffice.h: $(OBJDIR)/headers $(OBJDIR)/bag_.c: $(SRCDIR)/bag.c $(TRANSLATE) $(TRANSLATE) $(SRCDIR)/bag.c >$@ $(OBJDIR)/bag.o: $(OBJDIR)/bag_.c $(OBJDIR)/bag.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/bag.o -c $(OBJDIR)/bag_.c |
︙ | ︙ | |||
1334 1335 1336 1337 1338 1339 1340 1341 1342 1343 1344 1345 1346 1347 | $(OBJDIR)/cache_.c: $(SRCDIR)/cache.c $(TRANSLATE) $(TRANSLATE) $(SRCDIR)/cache.c >$@ $(OBJDIR)/cache.o: $(OBJDIR)/cache_.c $(OBJDIR)/cache.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/cache.o -c $(OBJDIR)/cache_.c $(OBJDIR)/cache.h: $(OBJDIR)/headers $(OBJDIR)/captcha_.c: $(SRCDIR)/captcha.c $(TRANSLATE) $(TRANSLATE) $(SRCDIR)/captcha.c >$@ $(OBJDIR)/captcha.o: $(OBJDIR)/captcha_.c $(OBJDIR)/captcha.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/captcha.o -c $(OBJDIR)/captcha_.c | > > > > > > > > | 1375 1376 1377 1378 1379 1380 1381 1382 1383 1384 1385 1386 1387 1388 1389 1390 1391 1392 1393 1394 1395 1396 | $(OBJDIR)/cache_.c: $(SRCDIR)/cache.c $(TRANSLATE) $(TRANSLATE) $(SRCDIR)/cache.c >$@ $(OBJDIR)/cache.o: $(OBJDIR)/cache_.c $(OBJDIR)/cache.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/cache.o -c $(OBJDIR)/cache_.c $(OBJDIR)/cache.h: $(OBJDIR)/headers $(OBJDIR)/capabilities_.c: $(SRCDIR)/capabilities.c $(TRANSLATE) $(TRANSLATE) $(SRCDIR)/capabilities.c >$@ $(OBJDIR)/capabilities.o: $(OBJDIR)/capabilities_.c $(OBJDIR)/capabilities.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/capabilities.o -c $(OBJDIR)/capabilities_.c $(OBJDIR)/capabilities.h: $(OBJDIR)/headers $(OBJDIR)/captcha_.c: $(SRCDIR)/captcha.c $(TRANSLATE) $(TRANSLATE) $(SRCDIR)/captcha.c >$@ $(OBJDIR)/captcha.o: $(OBJDIR)/captcha_.c $(OBJDIR)/captcha.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/captcha.o -c $(OBJDIR)/captcha_.c |
︙ | ︙ | |||
1478 1479 1480 1481 1482 1483 1484 1485 1486 1487 1488 1489 1490 1491 | $(OBJDIR)/doc_.c: $(SRCDIR)/doc.c $(TRANSLATE) $(TRANSLATE) $(SRCDIR)/doc.c >$@ $(OBJDIR)/doc.o: $(OBJDIR)/doc_.c $(OBJDIR)/doc.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/doc.o -c $(OBJDIR)/doc_.c $(OBJDIR)/doc.h: $(OBJDIR)/headers $(OBJDIR)/encode_.c: $(SRCDIR)/encode.c $(TRANSLATE) $(TRANSLATE) $(SRCDIR)/encode.c >$@ $(OBJDIR)/encode.o: $(OBJDIR)/encode_.c $(OBJDIR)/encode.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/encode.o -c $(OBJDIR)/encode_.c | > > > > > > > > | 1527 1528 1529 1530 1531 1532 1533 1534 1535 1536 1537 1538 1539 1540 1541 1542 1543 1544 1545 1546 1547 1548 | $(OBJDIR)/doc_.c: $(SRCDIR)/doc.c $(TRANSLATE) $(TRANSLATE) $(SRCDIR)/doc.c >$@ $(OBJDIR)/doc.o: $(OBJDIR)/doc_.c $(OBJDIR)/doc.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/doc.o -c $(OBJDIR)/doc_.c $(OBJDIR)/doc.h: $(OBJDIR)/headers $(OBJDIR)/email_.c: $(SRCDIR)/email.c $(TRANSLATE) $(TRANSLATE) $(SRCDIR)/email.c >$@ $(OBJDIR)/email.o: $(OBJDIR)/email_.c $(OBJDIR)/email.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/email.o -c $(OBJDIR)/email_.c $(OBJDIR)/email.h: $(OBJDIR)/headers $(OBJDIR)/encode_.c: $(SRCDIR)/encode.c $(TRANSLATE) $(TRANSLATE) $(SRCDIR)/encode.c >$@ $(OBJDIR)/encode.o: $(OBJDIR)/encode_.c $(OBJDIR)/encode.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/encode.o -c $(OBJDIR)/encode_.c |
︙ | ︙ | |||
1534 1535 1536 1537 1538 1539 1540 1541 1542 1543 1544 1545 1546 1547 | $(OBJDIR)/foci_.c: $(SRCDIR)/foci.c $(TRANSLATE) $(TRANSLATE) $(SRCDIR)/foci.c >$@ $(OBJDIR)/foci.o: $(OBJDIR)/foci_.c $(OBJDIR)/foci.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/foci.o -c $(OBJDIR)/foci_.c $(OBJDIR)/foci.h: $(OBJDIR)/headers $(OBJDIR)/fshell_.c: $(SRCDIR)/fshell.c $(TRANSLATE) $(TRANSLATE) $(SRCDIR)/fshell.c >$@ $(OBJDIR)/fshell.o: $(OBJDIR)/fshell_.c $(OBJDIR)/fshell.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/fshell.o -c $(OBJDIR)/fshell_.c | > > > > > > > > | 1591 1592 1593 1594 1595 1596 1597 1598 1599 1600 1601 1602 1603 1604 1605 1606 1607 1608 1609 1610 1611 1612 | $(OBJDIR)/foci_.c: $(SRCDIR)/foci.c $(TRANSLATE) $(TRANSLATE) $(SRCDIR)/foci.c >$@ $(OBJDIR)/foci.o: $(OBJDIR)/foci_.c $(OBJDIR)/foci.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/foci.o -c $(OBJDIR)/foci_.c $(OBJDIR)/foci.h: $(OBJDIR)/headers $(OBJDIR)/forum_.c: $(SRCDIR)/forum.c $(TRANSLATE) $(TRANSLATE) $(SRCDIR)/forum.c >$@ $(OBJDIR)/forum.o: $(OBJDIR)/forum_.c $(OBJDIR)/forum.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/forum.o -c $(OBJDIR)/forum_.c $(OBJDIR)/forum.h: $(OBJDIR)/headers $(OBJDIR)/fshell_.c: $(SRCDIR)/fshell.c $(TRANSLATE) $(TRANSLATE) $(SRCDIR)/fshell.c >$@ $(OBJDIR)/fshell.o: $(OBJDIR)/fshell_.c $(OBJDIR)/fshell.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/fshell.o -c $(OBJDIR)/fshell_.c |
︙ | ︙ | |||
1982 1983 1984 1985 1986 1987 1988 1989 1990 1991 1992 1993 1994 1995 | $(OBJDIR)/setup_.c: $(SRCDIR)/setup.c $(TRANSLATE) $(TRANSLATE) $(SRCDIR)/setup.c >$@ $(OBJDIR)/setup.o: $(OBJDIR)/setup_.c $(OBJDIR)/setup.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/setup.o -c $(OBJDIR)/setup_.c $(OBJDIR)/setup.h: $(OBJDIR)/headers $(OBJDIR)/sha1_.c: $(SRCDIR)/sha1.c $(TRANSLATE) $(TRANSLATE) $(SRCDIR)/sha1.c >$@ $(OBJDIR)/sha1.o: $(OBJDIR)/sha1_.c $(OBJDIR)/sha1.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/sha1.o -c $(OBJDIR)/sha1_.c | > > > > > > > > | 2047 2048 2049 2050 2051 2052 2053 2054 2055 2056 2057 2058 2059 2060 2061 2062 2063 2064 2065 2066 2067 2068 | $(OBJDIR)/setup_.c: $(SRCDIR)/setup.c $(TRANSLATE) $(TRANSLATE) $(SRCDIR)/setup.c >$@ $(OBJDIR)/setup.o: $(OBJDIR)/setup_.c $(OBJDIR)/setup.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/setup.o -c $(OBJDIR)/setup_.c $(OBJDIR)/setup.h: $(OBJDIR)/headers $(OBJDIR)/setupuser_.c: $(SRCDIR)/setupuser.c $(TRANSLATE) $(TRANSLATE) $(SRCDIR)/setupuser.c >$@ $(OBJDIR)/setupuser.o: $(OBJDIR)/setupuser_.c $(OBJDIR)/setupuser.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/setupuser.o -c $(OBJDIR)/setupuser_.c $(OBJDIR)/setupuser.h: $(OBJDIR)/headers $(OBJDIR)/sha1_.c: $(SRCDIR)/sha1.c $(TRANSLATE) $(TRANSLATE) $(SRCDIR)/sha1.c >$@ $(OBJDIR)/sha1.o: $(OBJDIR)/sha1_.c $(OBJDIR)/sha1.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/sha1.o -c $(OBJDIR)/sha1_.c |
︙ | ︙ | |||
2030 2031 2032 2033 2034 2035 2036 2037 2038 2039 2040 2041 2042 2043 | $(OBJDIR)/skins_.c: $(SRCDIR)/skins.c $(TRANSLATE) $(TRANSLATE) $(SRCDIR)/skins.c >$@ $(OBJDIR)/skins.o: $(OBJDIR)/skins_.c $(OBJDIR)/skins.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/skins.o -c $(OBJDIR)/skins_.c $(OBJDIR)/skins.h: $(OBJDIR)/headers $(OBJDIR)/sqlcmd_.c: $(SRCDIR)/sqlcmd.c $(TRANSLATE) $(TRANSLATE) $(SRCDIR)/sqlcmd.c >$@ $(OBJDIR)/sqlcmd.o: $(OBJDIR)/sqlcmd_.c $(OBJDIR)/sqlcmd.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/sqlcmd.o -c $(OBJDIR)/sqlcmd_.c | > > > > > > > > | 2103 2104 2105 2106 2107 2108 2109 2110 2111 2112 2113 2114 2115 2116 2117 2118 2119 2120 2121 2122 2123 2124 | $(OBJDIR)/skins_.c: $(SRCDIR)/skins.c $(TRANSLATE) $(TRANSLATE) $(SRCDIR)/skins.c >$@ $(OBJDIR)/skins.o: $(OBJDIR)/skins_.c $(OBJDIR)/skins.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/skins.o -c $(OBJDIR)/skins_.c $(OBJDIR)/skins.h: $(OBJDIR)/headers $(OBJDIR)/smtp_.c: $(SRCDIR)/smtp.c $(TRANSLATE) $(TRANSLATE) $(SRCDIR)/smtp.c >$@ $(OBJDIR)/smtp.o: $(OBJDIR)/smtp_.c $(OBJDIR)/smtp.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/smtp.o -c $(OBJDIR)/smtp_.c $(OBJDIR)/smtp.h: $(OBJDIR)/headers $(OBJDIR)/sqlcmd_.c: $(SRCDIR)/sqlcmd.c $(TRANSLATE) $(TRANSLATE) $(SRCDIR)/sqlcmd.c >$@ $(OBJDIR)/sqlcmd.o: $(OBJDIR)/sqlcmd_.c $(OBJDIR)/sqlcmd.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/sqlcmd.o -c $(OBJDIR)/sqlcmd_.c |
︙ | ︙ | |||
2206 2207 2208 2209 2210 2211 2212 2213 2214 2215 2216 2217 2218 2219 | $(OBJDIR)/vfile_.c: $(SRCDIR)/vfile.c $(TRANSLATE) $(TRANSLATE) $(SRCDIR)/vfile.c >$@ $(OBJDIR)/vfile.o: $(OBJDIR)/vfile_.c $(OBJDIR)/vfile.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/vfile.o -c $(OBJDIR)/vfile_.c $(OBJDIR)/vfile.h: $(OBJDIR)/headers $(OBJDIR)/wiki_.c: $(SRCDIR)/wiki.c $(TRANSLATE) $(TRANSLATE) $(SRCDIR)/wiki.c >$@ $(OBJDIR)/wiki.o: $(OBJDIR)/wiki_.c $(OBJDIR)/wiki.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/wiki.o -c $(OBJDIR)/wiki_.c | > > > > > > > > | 2287 2288 2289 2290 2291 2292 2293 2294 2295 2296 2297 2298 2299 2300 2301 2302 2303 2304 2305 2306 2307 2308 | $(OBJDIR)/vfile_.c: $(SRCDIR)/vfile.c $(TRANSLATE) $(TRANSLATE) $(SRCDIR)/vfile.c >$@ $(OBJDIR)/vfile.o: $(OBJDIR)/vfile_.c $(OBJDIR)/vfile.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/vfile.o -c $(OBJDIR)/vfile_.c $(OBJDIR)/vfile.h: $(OBJDIR)/headers $(OBJDIR)/webmail_.c: $(SRCDIR)/webmail.c $(TRANSLATE) $(TRANSLATE) $(SRCDIR)/webmail.c >$@ $(OBJDIR)/webmail.o: $(OBJDIR)/webmail_.c $(OBJDIR)/webmail.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/webmail.o -c $(OBJDIR)/webmail_.c $(OBJDIR)/webmail.h: $(OBJDIR)/headers $(OBJDIR)/wiki_.c: $(SRCDIR)/wiki.c $(TRANSLATE) $(TRANSLATE) $(SRCDIR)/wiki.c >$@ $(OBJDIR)/wiki.o: $(OBJDIR)/wiki_.c $(OBJDIR)/wiki.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/wiki.o -c $(OBJDIR)/wiki_.c |
︙ | ︙ | |||
2332 2333 2334 2335 2336 2337 2338 | -DSQLITE_HAVE_ZLIB \ -DSQLITE_INTROSPECTION_PRAGMAS \ -DSQLITE_ENABLE_DBPAGE_VTAB \ -Dmain=sqlite3_shell \ -DSQLITE_SHELL_IS_UTF8=1 \ -DSQLITE_OMIT_LOAD_EXTENSION=1 \ -DUSE_SYSTEM_SQLITE=$(USE_SYSTEM_SQLITE) \ | | > | 2421 2422 2423 2424 2425 2426 2427 2428 2429 2430 2431 2432 2433 2434 2435 2436 | -DSQLITE_HAVE_ZLIB \ -DSQLITE_INTROSPECTION_PRAGMAS \ -DSQLITE_ENABLE_DBPAGE_VTAB \ -Dmain=sqlite3_shell \ -DSQLITE_SHELL_IS_UTF8=1 \ -DSQLITE_OMIT_LOAD_EXTENSION=1 \ -DUSE_SYSTEM_SQLITE=$(USE_SYSTEM_SQLITE) \ -DSQLITE_SHELL_DBNAME_PROC=sqlcmd_get_dbname \ -DSQLITE_SHELL_INIT_PROC=sqlcmd_init_proc \ -Daccess=file_access \ -Dsystem=fossil_system \ -Dgetenv=fossil_getenv \ -Dfopen=fossil_fopen MINIZ_OPTIONS = -DMINIZ_NO_STDIO \ -DMINIZ_NO_TIME \ |
︙ | ︙ |
Changes to win/Makefile.mingw.mistachkin.
︙ | ︙ | |||
172 173 174 175 176 177 178 | endif #### The directories where the OpenSSL include and library files are located. # The recommended usage here is to use the Sysinternals junction tool # to create a hard link between an "openssl-1.x" sub-directory of the # Fossil source code directory and the target OpenSSL source directory. # | | | 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 | endif #### The directories where the OpenSSL include and library files are located. # The recommended usage here is to use the Sysinternals junction tool # to create a hard link between an "openssl-1.x" sub-directory of the # Fossil source code directory and the target OpenSSL source directory. # OPENSSLDIR = $(SRCDIR)/../compat/openssl-1.1.1-pre9 OPENSSLINCDIR = $(OPENSSLDIR)/include OPENSSLLIBDIR = $(OPENSSLDIR) #### Either the directory where the Tcl library is installed or the Tcl # source code directory resides (depending on the value of the macro # FOSSIL_TCL_SOURCE). If this points to the Tcl install directory, # this directory must have "include" and "lib" sub-directories. If |
︙ | ︙ | |||
405 406 407 408 409 410 411 412 413 414 415 416 417 418 | LIB += -lkernel32 -lws2_32 else LIB += -lnetapi32 -lkernel32 -luser32 -ladvapi32 -lws2_32 endif else LIB += -lkernel32 -lws2_32 endif #### Tcl shell for use in running the fossil test suite. This is only # used for testing. # TCLSH = tclsh #### Nullsoft installer MakeNSIS location | > > > > | 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 | LIB += -lkernel32 -lws2_32 else LIB += -lnetapi32 -lkernel32 -luser32 -ladvapi32 -lws2_32 endif else LIB += -lkernel32 -lws2_32 endif #### Library required for DNS lookups. # LIB += -ldnsapi #### Tcl shell for use in running the fossil test suite. This is only # used for testing. # TCLSH = tclsh #### Nullsoft installer MakeNSIS location |
︙ | ︙ | |||
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 | XBCC = $(BCC) $(CFLAGS) XTCC = $(TCC) $(CFLAGS) -I. -I$(SRCDIR) SRC = \ $(SRCDIR)/add.c \ $(SRCDIR)/allrepo.c \ $(SRCDIR)/attach.c \ $(SRCDIR)/bag.c \ $(SRCDIR)/bisect.c \ $(SRCDIR)/blob.c \ $(SRCDIR)/branch.c \ $(SRCDIR)/browse.c \ $(SRCDIR)/builtin.c \ $(SRCDIR)/bundle.c \ $(SRCDIR)/cache.c \ $(SRCDIR)/captcha.c \ $(SRCDIR)/cgi.c \ $(SRCDIR)/checkin.c \ $(SRCDIR)/checkout.c \ $(SRCDIR)/clearsign.c \ $(SRCDIR)/clone.c \ $(SRCDIR)/comformat.c \ $(SRCDIR)/configure.c \ $(SRCDIR)/content.c \ $(SRCDIR)/cookies.c \ $(SRCDIR)/db.c \ $(SRCDIR)/delta.c \ $(SRCDIR)/deltacmd.c \ $(SRCDIR)/descendants.c \ $(SRCDIR)/diff.c \ $(SRCDIR)/diffcmd.c \ $(SRCDIR)/dispatch.c \ $(SRCDIR)/doc.c \ $(SRCDIR)/encode.c \ $(SRCDIR)/etag.c \ $(SRCDIR)/event.c \ $(SRCDIR)/export.c \ $(SRCDIR)/file.c \ $(SRCDIR)/finfo.c \ $(SRCDIR)/foci.c \ $(SRCDIR)/fshell.c \ $(SRCDIR)/fusefs.c \ $(SRCDIR)/glob.c \ $(SRCDIR)/graph.c \ $(SRCDIR)/gzip.c \ $(SRCDIR)/hname.c \ $(SRCDIR)/http.c \ | > > > > | 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 | XBCC = $(BCC) $(CFLAGS) XTCC = $(TCC) $(CFLAGS) -I. -I$(SRCDIR) SRC = \ $(SRCDIR)/add.c \ $(SRCDIR)/allrepo.c \ $(SRCDIR)/attach.c \ $(SRCDIR)/backoffice.c \ $(SRCDIR)/bag.c \ $(SRCDIR)/bisect.c \ $(SRCDIR)/blob.c \ $(SRCDIR)/branch.c \ $(SRCDIR)/browse.c \ $(SRCDIR)/builtin.c \ $(SRCDIR)/bundle.c \ $(SRCDIR)/cache.c \ $(SRCDIR)/capabilities.c \ $(SRCDIR)/captcha.c \ $(SRCDIR)/cgi.c \ $(SRCDIR)/checkin.c \ $(SRCDIR)/checkout.c \ $(SRCDIR)/clearsign.c \ $(SRCDIR)/clone.c \ $(SRCDIR)/comformat.c \ $(SRCDIR)/configure.c \ $(SRCDIR)/content.c \ $(SRCDIR)/cookies.c \ $(SRCDIR)/db.c \ $(SRCDIR)/delta.c \ $(SRCDIR)/deltacmd.c \ $(SRCDIR)/descendants.c \ $(SRCDIR)/diff.c \ $(SRCDIR)/diffcmd.c \ $(SRCDIR)/dispatch.c \ $(SRCDIR)/doc.c \ $(SRCDIR)/email.c \ $(SRCDIR)/encode.c \ $(SRCDIR)/etag.c \ $(SRCDIR)/event.c \ $(SRCDIR)/export.c \ $(SRCDIR)/file.c \ $(SRCDIR)/finfo.c \ $(SRCDIR)/foci.c \ $(SRCDIR)/forum.c \ $(SRCDIR)/fshell.c \ $(SRCDIR)/fusefs.c \ $(SRCDIR)/glob.c \ $(SRCDIR)/graph.c \ $(SRCDIR)/gzip.c \ $(SRCDIR)/hname.c \ $(SRCDIR)/http.c \ |
︙ | ︙ | |||
528 529 530 531 532 533 534 535 536 537 538 539 540 541 | $(SRCDIR)/setup.c \ $(SRCDIR)/sha1.c \ $(SRCDIR)/sha1hard.c \ $(SRCDIR)/sha3.c \ $(SRCDIR)/shun.c \ $(SRCDIR)/sitemap.c \ $(SRCDIR)/skins.c \ $(SRCDIR)/sqlcmd.c \ $(SRCDIR)/stash.c \ $(SRCDIR)/stat.c \ $(SRCDIR)/statrep.c \ $(SRCDIR)/style.c \ $(SRCDIR)/sync.c \ $(SRCDIR)/tag.c \ | > | 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 | $(SRCDIR)/setup.c \ $(SRCDIR)/sha1.c \ $(SRCDIR)/sha1hard.c \ $(SRCDIR)/sha3.c \ $(SRCDIR)/shun.c \ $(SRCDIR)/sitemap.c \ $(SRCDIR)/skins.c \ $(SRCDIR)/smtp.c \ $(SRCDIR)/sqlcmd.c \ $(SRCDIR)/stash.c \ $(SRCDIR)/stat.c \ $(SRCDIR)/statrep.c \ $(SRCDIR)/style.c \ $(SRCDIR)/sync.c \ $(SRCDIR)/tag.c \ |
︙ | ︙ | |||
550 551 552 553 554 555 556 557 558 559 560 561 562 563 | $(SRCDIR)/update.c \ $(SRCDIR)/url.c \ $(SRCDIR)/user.c \ $(SRCDIR)/utf8.c \ $(SRCDIR)/util.c \ $(SRCDIR)/verify.c \ $(SRCDIR)/vfile.c \ $(SRCDIR)/wiki.c \ $(SRCDIR)/wikiformat.c \ $(SRCDIR)/winfile.c \ $(SRCDIR)/winhttp.c \ $(SRCDIR)/wysiwyg.c \ $(SRCDIR)/xfer.c \ $(SRCDIR)/xfersetup.c \ | > | 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 | $(SRCDIR)/update.c \ $(SRCDIR)/url.c \ $(SRCDIR)/user.c \ $(SRCDIR)/utf8.c \ $(SRCDIR)/util.c \ $(SRCDIR)/verify.c \ $(SRCDIR)/vfile.c \ $(SRCDIR)/webmail.c \ $(SRCDIR)/wiki.c \ $(SRCDIR)/wikiformat.c \ $(SRCDIR)/winfile.c \ $(SRCDIR)/winhttp.c \ $(SRCDIR)/wysiwyg.c \ $(SRCDIR)/xfer.c \ $(SRCDIR)/xfersetup.c \ |
︙ | ︙ | |||
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 | $(SRCDIR)/../skins/rounded1/header.txt \ $(SRCDIR)/../skins/xekri/css.txt \ $(SRCDIR)/../skins/xekri/details.txt \ $(SRCDIR)/../skins/xekri/footer.txt \ $(SRCDIR)/../skins/xekri/header.txt \ $(SRCDIR)/ci_edit.js \ $(SRCDIR)/diff.tcl \ $(SRCDIR)/graph.js \ $(SRCDIR)/href.js \ $(SRCDIR)/login.js \ $(SRCDIR)/markdown.md \ $(SRCDIR)/menu.js \ $(SRCDIR)/sbsdiff.js \ $(SRCDIR)/scroll.js \ $(SRCDIR)/skin.js \ $(SRCDIR)/sorttable.js \ $(SRCDIR)/tree.js \ $(SRCDIR)/useredit.js \ $(SRCDIR)/wiki.wiki TRANS_SRC = \ $(OBJDIR)/add_.c \ $(OBJDIR)/allrepo_.c \ $(OBJDIR)/attach_.c \ $(OBJDIR)/bag_.c \ $(OBJDIR)/bisect_.c \ $(OBJDIR)/blob_.c \ $(OBJDIR)/branch_.c \ $(OBJDIR)/browse_.c \ $(OBJDIR)/builtin_.c \ $(OBJDIR)/bundle_.c \ $(OBJDIR)/cache_.c \ $(OBJDIR)/captcha_.c \ $(OBJDIR)/cgi_.c \ $(OBJDIR)/checkin_.c \ $(OBJDIR)/checkout_.c \ $(OBJDIR)/clearsign_.c \ $(OBJDIR)/clone_.c \ $(OBJDIR)/comformat_.c \ $(OBJDIR)/configure_.c \ $(OBJDIR)/content_.c \ $(OBJDIR)/cookies_.c \ $(OBJDIR)/db_.c \ $(OBJDIR)/delta_.c \ $(OBJDIR)/deltacmd_.c \ $(OBJDIR)/descendants_.c \ $(OBJDIR)/diff_.c \ $(OBJDIR)/diffcmd_.c \ $(OBJDIR)/dispatch_.c \ $(OBJDIR)/doc_.c \ $(OBJDIR)/encode_.c \ $(OBJDIR)/etag_.c \ $(OBJDIR)/event_.c \ $(OBJDIR)/export_.c \ $(OBJDIR)/file_.c \ $(OBJDIR)/finfo_.c \ $(OBJDIR)/foci_.c \ $(OBJDIR)/fshell_.c \ $(OBJDIR)/fusefs_.c \ $(OBJDIR)/glob_.c \ $(OBJDIR)/graph_.c \ $(OBJDIR)/gzip_.c \ $(OBJDIR)/hname_.c \ $(OBJDIR)/http_.c \ | > > > > > | 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 | $(SRCDIR)/../skins/rounded1/header.txt \ $(SRCDIR)/../skins/xekri/css.txt \ $(SRCDIR)/../skins/xekri/details.txt \ $(SRCDIR)/../skins/xekri/footer.txt \ $(SRCDIR)/../skins/xekri/header.txt \ $(SRCDIR)/ci_edit.js \ $(SRCDIR)/diff.tcl \ $(SRCDIR)/forum.js \ $(SRCDIR)/graph.js \ $(SRCDIR)/href.js \ $(SRCDIR)/login.js \ $(SRCDIR)/markdown.md \ $(SRCDIR)/menu.js \ $(SRCDIR)/sbsdiff.js \ $(SRCDIR)/scroll.js \ $(SRCDIR)/skin.js \ $(SRCDIR)/sorttable.js \ $(SRCDIR)/tree.js \ $(SRCDIR)/useredit.js \ $(SRCDIR)/wiki.wiki TRANS_SRC = \ $(OBJDIR)/add_.c \ $(OBJDIR)/allrepo_.c \ $(OBJDIR)/attach_.c \ $(OBJDIR)/backoffice_.c \ $(OBJDIR)/bag_.c \ $(OBJDIR)/bisect_.c \ $(OBJDIR)/blob_.c \ $(OBJDIR)/branch_.c \ $(OBJDIR)/browse_.c \ $(OBJDIR)/builtin_.c \ $(OBJDIR)/bundle_.c \ $(OBJDIR)/cache_.c \ $(OBJDIR)/capabilities_.c \ $(OBJDIR)/captcha_.c \ $(OBJDIR)/cgi_.c \ $(OBJDIR)/checkin_.c \ $(OBJDIR)/checkout_.c \ $(OBJDIR)/clearsign_.c \ $(OBJDIR)/clone_.c \ $(OBJDIR)/comformat_.c \ $(OBJDIR)/configure_.c \ $(OBJDIR)/content_.c \ $(OBJDIR)/cookies_.c \ $(OBJDIR)/db_.c \ $(OBJDIR)/delta_.c \ $(OBJDIR)/deltacmd_.c \ $(OBJDIR)/descendants_.c \ $(OBJDIR)/diff_.c \ $(OBJDIR)/diffcmd_.c \ $(OBJDIR)/dispatch_.c \ $(OBJDIR)/doc_.c \ $(OBJDIR)/email_.c \ $(OBJDIR)/encode_.c \ $(OBJDIR)/etag_.c \ $(OBJDIR)/event_.c \ $(OBJDIR)/export_.c \ $(OBJDIR)/file_.c \ $(OBJDIR)/finfo_.c \ $(OBJDIR)/foci_.c \ $(OBJDIR)/forum_.c \ $(OBJDIR)/fshell_.c \ $(OBJDIR)/fusefs_.c \ $(OBJDIR)/glob_.c \ $(OBJDIR)/graph_.c \ $(OBJDIR)/gzip_.c \ $(OBJDIR)/hname_.c \ $(OBJDIR)/http_.c \ |
︙ | ︙ | |||
729 730 731 732 733 734 735 736 737 738 739 740 741 742 | $(OBJDIR)/setup_.c \ $(OBJDIR)/sha1_.c \ $(OBJDIR)/sha1hard_.c \ $(OBJDIR)/sha3_.c \ $(OBJDIR)/shun_.c \ $(OBJDIR)/sitemap_.c \ $(OBJDIR)/skins_.c \ $(OBJDIR)/sqlcmd_.c \ $(OBJDIR)/stash_.c \ $(OBJDIR)/stat_.c \ $(OBJDIR)/statrep_.c \ $(OBJDIR)/style_.c \ $(OBJDIR)/sync_.c \ $(OBJDIR)/tag_.c \ | > | 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 | $(OBJDIR)/setup_.c \ $(OBJDIR)/sha1_.c \ $(OBJDIR)/sha1hard_.c \ $(OBJDIR)/sha3_.c \ $(OBJDIR)/shun_.c \ $(OBJDIR)/sitemap_.c \ $(OBJDIR)/skins_.c \ $(OBJDIR)/smtp_.c \ $(OBJDIR)/sqlcmd_.c \ $(OBJDIR)/stash_.c \ $(OBJDIR)/stat_.c \ $(OBJDIR)/statrep_.c \ $(OBJDIR)/style_.c \ $(OBJDIR)/sync_.c \ $(OBJDIR)/tag_.c \ |
︙ | ︙ | |||
751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 | $(OBJDIR)/update_.c \ $(OBJDIR)/url_.c \ $(OBJDIR)/user_.c \ $(OBJDIR)/utf8_.c \ $(OBJDIR)/util_.c \ $(OBJDIR)/verify_.c \ $(OBJDIR)/vfile_.c \ $(OBJDIR)/wiki_.c \ $(OBJDIR)/wikiformat_.c \ $(OBJDIR)/winfile_.c \ $(OBJDIR)/winhttp_.c \ $(OBJDIR)/wysiwyg_.c \ $(OBJDIR)/xfer_.c \ $(OBJDIR)/xfersetup_.c \ $(OBJDIR)/zip_.c OBJ = \ $(OBJDIR)/add.o \ $(OBJDIR)/allrepo.o \ $(OBJDIR)/attach.o \ $(OBJDIR)/bag.o \ $(OBJDIR)/bisect.o \ $(OBJDIR)/blob.o \ $(OBJDIR)/branch.o \ $(OBJDIR)/browse.o \ $(OBJDIR)/builtin.o \ $(OBJDIR)/bundle.o \ $(OBJDIR)/cache.o \ $(OBJDIR)/captcha.o \ $(OBJDIR)/cgi.o \ $(OBJDIR)/checkin.o \ $(OBJDIR)/checkout.o \ $(OBJDIR)/clearsign.o \ $(OBJDIR)/clone.o \ $(OBJDIR)/comformat.o \ $(OBJDIR)/configure.o \ $(OBJDIR)/content.o \ $(OBJDIR)/cookies.o \ $(OBJDIR)/db.o \ $(OBJDIR)/delta.o \ $(OBJDIR)/deltacmd.o \ $(OBJDIR)/descendants.o \ $(OBJDIR)/diff.o \ $(OBJDIR)/diffcmd.o \ $(OBJDIR)/dispatch.o \ $(OBJDIR)/doc.o \ $(OBJDIR)/encode.o \ $(OBJDIR)/etag.o \ $(OBJDIR)/event.o \ $(OBJDIR)/export.o \ $(OBJDIR)/file.o \ $(OBJDIR)/finfo.o \ $(OBJDIR)/foci.o \ $(OBJDIR)/fshell.o \ $(OBJDIR)/fusefs.o \ $(OBJDIR)/glob.o \ $(OBJDIR)/graph.o \ $(OBJDIR)/gzip.o \ $(OBJDIR)/hname.o \ $(OBJDIR)/http.o \ | > > > > > | 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 | $(OBJDIR)/update_.c \ $(OBJDIR)/url_.c \ $(OBJDIR)/user_.c \ $(OBJDIR)/utf8_.c \ $(OBJDIR)/util_.c \ $(OBJDIR)/verify_.c \ $(OBJDIR)/vfile_.c \ $(OBJDIR)/webmail_.c \ $(OBJDIR)/wiki_.c \ $(OBJDIR)/wikiformat_.c \ $(OBJDIR)/winfile_.c \ $(OBJDIR)/winhttp_.c \ $(OBJDIR)/wysiwyg_.c \ $(OBJDIR)/xfer_.c \ $(OBJDIR)/xfersetup_.c \ $(OBJDIR)/zip_.c OBJ = \ $(OBJDIR)/add.o \ $(OBJDIR)/allrepo.o \ $(OBJDIR)/attach.o \ $(OBJDIR)/backoffice.o \ $(OBJDIR)/bag.o \ $(OBJDIR)/bisect.o \ $(OBJDIR)/blob.o \ $(OBJDIR)/branch.o \ $(OBJDIR)/browse.o \ $(OBJDIR)/builtin.o \ $(OBJDIR)/bundle.o \ $(OBJDIR)/cache.o \ $(OBJDIR)/capabilities.o \ $(OBJDIR)/captcha.o \ $(OBJDIR)/cgi.o \ $(OBJDIR)/checkin.o \ $(OBJDIR)/checkout.o \ $(OBJDIR)/clearsign.o \ $(OBJDIR)/clone.o \ $(OBJDIR)/comformat.o \ $(OBJDIR)/configure.o \ $(OBJDIR)/content.o \ $(OBJDIR)/cookies.o \ $(OBJDIR)/db.o \ $(OBJDIR)/delta.o \ $(OBJDIR)/deltacmd.o \ $(OBJDIR)/descendants.o \ $(OBJDIR)/diff.o \ $(OBJDIR)/diffcmd.o \ $(OBJDIR)/dispatch.o \ $(OBJDIR)/doc.o \ $(OBJDIR)/email.o \ $(OBJDIR)/encode.o \ $(OBJDIR)/etag.o \ $(OBJDIR)/event.o \ $(OBJDIR)/export.o \ $(OBJDIR)/file.o \ $(OBJDIR)/finfo.o \ $(OBJDIR)/foci.o \ $(OBJDIR)/forum.o \ $(OBJDIR)/fshell.o \ $(OBJDIR)/fusefs.o \ $(OBJDIR)/glob.o \ $(OBJDIR)/graph.o \ $(OBJDIR)/gzip.o \ $(OBJDIR)/hname.o \ $(OBJDIR)/http.o \ |
︙ | ︙ | |||
859 860 861 862 863 864 865 866 867 868 869 870 871 872 | $(OBJDIR)/setup.o \ $(OBJDIR)/sha1.o \ $(OBJDIR)/sha1hard.o \ $(OBJDIR)/sha3.o \ $(OBJDIR)/shun.o \ $(OBJDIR)/sitemap.o \ $(OBJDIR)/skins.o \ $(OBJDIR)/sqlcmd.o \ $(OBJDIR)/stash.o \ $(OBJDIR)/stat.o \ $(OBJDIR)/statrep.o \ $(OBJDIR)/style.o \ $(OBJDIR)/sync.o \ $(OBJDIR)/tag.o \ | > | 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 | $(OBJDIR)/setup.o \ $(OBJDIR)/sha1.o \ $(OBJDIR)/sha1hard.o \ $(OBJDIR)/sha3.o \ $(OBJDIR)/shun.o \ $(OBJDIR)/sitemap.o \ $(OBJDIR)/skins.o \ $(OBJDIR)/smtp.o \ $(OBJDIR)/sqlcmd.o \ $(OBJDIR)/stash.o \ $(OBJDIR)/stat.o \ $(OBJDIR)/statrep.o \ $(OBJDIR)/style.o \ $(OBJDIR)/sync.o \ $(OBJDIR)/tag.o \ |
︙ | ︙ | |||
881 882 883 884 885 886 887 888 889 890 891 892 893 894 | $(OBJDIR)/update.o \ $(OBJDIR)/url.o \ $(OBJDIR)/user.o \ $(OBJDIR)/utf8.o \ $(OBJDIR)/util.o \ $(OBJDIR)/verify.o \ $(OBJDIR)/vfile.o \ $(OBJDIR)/wiki.o \ $(OBJDIR)/wikiformat.o \ $(OBJDIR)/winfile.o \ $(OBJDIR)/winhttp.o \ $(OBJDIR)/wysiwyg.o \ $(OBJDIR)/xfer.o \ $(OBJDIR)/xfersetup.o \ | > | 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 | $(OBJDIR)/update.o \ $(OBJDIR)/url.o \ $(OBJDIR)/user.o \ $(OBJDIR)/utf8.o \ $(OBJDIR)/util.o \ $(OBJDIR)/verify.o \ $(OBJDIR)/vfile.o \ $(OBJDIR)/webmail.o \ $(OBJDIR)/wiki.o \ $(OBJDIR)/wikiformat.o \ $(OBJDIR)/winfile.o \ $(OBJDIR)/winhttp.o \ $(OBJDIR)/wysiwyg.o \ $(OBJDIR)/xfer.o \ $(OBJDIR)/xfersetup.o \ |
︙ | ︙ | |||
951 952 953 954 955 956 957 | $(CP) $(SRCDIR)/../win/fossil.exe.manifest $(OBJDIR) endif $(RCC) $(OBJDIR)/fossil.rc -o $(OBJDIR)/fossil.o install: $(OBJDIR) $(APPNAME) ifdef USE_WINDOWS $(MKDIR) $(subst /,\,$(INSTALLDIR)) | | | | 974 975 976 977 978 979 980 981 982 983 984 985 986 987 988 989 990 991 | $(CP) $(SRCDIR)/../win/fossil.exe.manifest $(OBJDIR) endif $(RCC) $(OBJDIR)/fossil.rc -o $(OBJDIR)/fossil.o install: $(OBJDIR) $(APPNAME) ifdef USE_WINDOWS $(MKDIR) $(subst /,\,$(INSTALLDIR)) $(CP) $(subst /,\,$(APPNAME)) $(subst /,\,$(INSTALLDIR)) else $(MKDIR) $(INSTALLDIR) $(CP) $(APPNAME) $(INSTALLDIR) endif $(OBJDIR): ifdef USE_WINDOWS $(MKDIR) $(subst /,\,$(OBJDIR)) else $(MKDIR) $(OBJDIR) |
︙ | ︙ | |||
1113 1114 1115 1116 1117 1118 1119 1120 1121 1122 1123 1124 1125 1126 1127 1128 1129 1130 1131 1132 1133 1134 1135 1136 1137 1138 1139 1140 1141 1142 1143 1144 1145 1146 1147 1148 1149 1150 1151 1152 1153 1154 1155 1156 1157 1158 1159 | $(OBJDIR)/builtin_data.h: $(MKBUILTIN) $(EXTRA_FILES) $(MKBUILTIN) --prefix $(SRCDIR)/ $(EXTRA_FILES) >$@ $(OBJDIR)/headers: $(OBJDIR)/page_index.h $(OBJDIR)/builtin_data.h $(OBJDIR)/default_css.h $(MAKEHEADERS) $(OBJDIR)/VERSION.h $(MAKEHEADERS) $(OBJDIR)/add_.c:$(OBJDIR)/add.h \ $(OBJDIR)/allrepo_.c:$(OBJDIR)/allrepo.h \ $(OBJDIR)/attach_.c:$(OBJDIR)/attach.h \ $(OBJDIR)/bag_.c:$(OBJDIR)/bag.h \ $(OBJDIR)/bisect_.c:$(OBJDIR)/bisect.h \ $(OBJDIR)/blob_.c:$(OBJDIR)/blob.h \ $(OBJDIR)/branch_.c:$(OBJDIR)/branch.h \ $(OBJDIR)/browse_.c:$(OBJDIR)/browse.h \ $(OBJDIR)/builtin_.c:$(OBJDIR)/builtin.h \ $(OBJDIR)/bundle_.c:$(OBJDIR)/bundle.h \ $(OBJDIR)/cache_.c:$(OBJDIR)/cache.h \ $(OBJDIR)/captcha_.c:$(OBJDIR)/captcha.h \ $(OBJDIR)/cgi_.c:$(OBJDIR)/cgi.h \ $(OBJDIR)/checkin_.c:$(OBJDIR)/checkin.h \ $(OBJDIR)/checkout_.c:$(OBJDIR)/checkout.h \ $(OBJDIR)/clearsign_.c:$(OBJDIR)/clearsign.h \ $(OBJDIR)/clone_.c:$(OBJDIR)/clone.h \ $(OBJDIR)/comformat_.c:$(OBJDIR)/comformat.h \ $(OBJDIR)/configure_.c:$(OBJDIR)/configure.h \ $(OBJDIR)/content_.c:$(OBJDIR)/content.h \ $(OBJDIR)/cookies_.c:$(OBJDIR)/cookies.h \ $(OBJDIR)/db_.c:$(OBJDIR)/db.h \ $(OBJDIR)/delta_.c:$(OBJDIR)/delta.h \ $(OBJDIR)/deltacmd_.c:$(OBJDIR)/deltacmd.h \ $(OBJDIR)/descendants_.c:$(OBJDIR)/descendants.h \ $(OBJDIR)/diff_.c:$(OBJDIR)/diff.h \ $(OBJDIR)/diffcmd_.c:$(OBJDIR)/diffcmd.h \ $(OBJDIR)/dispatch_.c:$(OBJDIR)/dispatch.h \ $(OBJDIR)/doc_.c:$(OBJDIR)/doc.h \ $(OBJDIR)/encode_.c:$(OBJDIR)/encode.h \ $(OBJDIR)/etag_.c:$(OBJDIR)/etag.h \ $(OBJDIR)/event_.c:$(OBJDIR)/event.h \ $(OBJDIR)/export_.c:$(OBJDIR)/export.h \ $(OBJDIR)/file_.c:$(OBJDIR)/file.h \ $(OBJDIR)/finfo_.c:$(OBJDIR)/finfo.h \ $(OBJDIR)/foci_.c:$(OBJDIR)/foci.h \ $(OBJDIR)/fshell_.c:$(OBJDIR)/fshell.h \ $(OBJDIR)/fusefs_.c:$(OBJDIR)/fusefs.h \ $(OBJDIR)/glob_.c:$(OBJDIR)/glob.h \ $(OBJDIR)/graph_.c:$(OBJDIR)/graph.h \ $(OBJDIR)/gzip_.c:$(OBJDIR)/gzip.h \ $(OBJDIR)/hname_.c:$(OBJDIR)/hname.h \ $(OBJDIR)/http_.c:$(OBJDIR)/http.h \ | > > > > | 1136 1137 1138 1139 1140 1141 1142 1143 1144 1145 1146 1147 1148 1149 1150 1151 1152 1153 1154 1155 1156 1157 1158 1159 1160 1161 1162 1163 1164 1165 1166 1167 1168 1169 1170 1171 1172 1173 1174 1175 1176 1177 1178 1179 1180 1181 1182 1183 1184 1185 1186 | $(OBJDIR)/builtin_data.h: $(MKBUILTIN) $(EXTRA_FILES) $(MKBUILTIN) --prefix $(SRCDIR)/ $(EXTRA_FILES) >$@ $(OBJDIR)/headers: $(OBJDIR)/page_index.h $(OBJDIR)/builtin_data.h $(OBJDIR)/default_css.h $(MAKEHEADERS) $(OBJDIR)/VERSION.h $(MAKEHEADERS) $(OBJDIR)/add_.c:$(OBJDIR)/add.h \ $(OBJDIR)/allrepo_.c:$(OBJDIR)/allrepo.h \ $(OBJDIR)/attach_.c:$(OBJDIR)/attach.h \ $(OBJDIR)/backoffice_.c:$(OBJDIR)/backoffice.h \ $(OBJDIR)/bag_.c:$(OBJDIR)/bag.h \ $(OBJDIR)/bisect_.c:$(OBJDIR)/bisect.h \ $(OBJDIR)/blob_.c:$(OBJDIR)/blob.h \ $(OBJDIR)/branch_.c:$(OBJDIR)/branch.h \ $(OBJDIR)/browse_.c:$(OBJDIR)/browse.h \ $(OBJDIR)/builtin_.c:$(OBJDIR)/builtin.h \ $(OBJDIR)/bundle_.c:$(OBJDIR)/bundle.h \ $(OBJDIR)/cache_.c:$(OBJDIR)/cache.h \ $(OBJDIR)/capabilities_.c:$(OBJDIR)/capabilities.h \ $(OBJDIR)/captcha_.c:$(OBJDIR)/captcha.h \ $(OBJDIR)/cgi_.c:$(OBJDIR)/cgi.h \ $(OBJDIR)/checkin_.c:$(OBJDIR)/checkin.h \ $(OBJDIR)/checkout_.c:$(OBJDIR)/checkout.h \ $(OBJDIR)/clearsign_.c:$(OBJDIR)/clearsign.h \ $(OBJDIR)/clone_.c:$(OBJDIR)/clone.h \ $(OBJDIR)/comformat_.c:$(OBJDIR)/comformat.h \ $(OBJDIR)/configure_.c:$(OBJDIR)/configure.h \ $(OBJDIR)/content_.c:$(OBJDIR)/content.h \ $(OBJDIR)/cookies_.c:$(OBJDIR)/cookies.h \ $(OBJDIR)/db_.c:$(OBJDIR)/db.h \ $(OBJDIR)/delta_.c:$(OBJDIR)/delta.h \ $(OBJDIR)/deltacmd_.c:$(OBJDIR)/deltacmd.h \ $(OBJDIR)/descendants_.c:$(OBJDIR)/descendants.h \ $(OBJDIR)/diff_.c:$(OBJDIR)/diff.h \ $(OBJDIR)/diffcmd_.c:$(OBJDIR)/diffcmd.h \ $(OBJDIR)/dispatch_.c:$(OBJDIR)/dispatch.h \ $(OBJDIR)/doc_.c:$(OBJDIR)/doc.h \ $(OBJDIR)/email_.c:$(OBJDIR)/email.h \ $(OBJDIR)/encode_.c:$(OBJDIR)/encode.h \ $(OBJDIR)/etag_.c:$(OBJDIR)/etag.h \ $(OBJDIR)/event_.c:$(OBJDIR)/event.h \ $(OBJDIR)/export_.c:$(OBJDIR)/export.h \ $(OBJDIR)/file_.c:$(OBJDIR)/file.h \ $(OBJDIR)/finfo_.c:$(OBJDIR)/finfo.h \ $(OBJDIR)/foci_.c:$(OBJDIR)/foci.h \ $(OBJDIR)/forum_.c:$(OBJDIR)/forum.h \ $(OBJDIR)/fshell_.c:$(OBJDIR)/fshell.h \ $(OBJDIR)/fusefs_.c:$(OBJDIR)/fusefs.h \ $(OBJDIR)/glob_.c:$(OBJDIR)/glob.h \ $(OBJDIR)/graph_.c:$(OBJDIR)/graph.h \ $(OBJDIR)/gzip_.c:$(OBJDIR)/gzip.h \ $(OBJDIR)/hname_.c:$(OBJDIR)/hname.h \ $(OBJDIR)/http_.c:$(OBJDIR)/http.h \ |
︙ | ︙ | |||
1208 1209 1210 1211 1212 1213 1214 1215 1216 1217 1218 1219 1220 1221 | $(OBJDIR)/setup_.c:$(OBJDIR)/setup.h \ $(OBJDIR)/sha1_.c:$(OBJDIR)/sha1.h \ $(OBJDIR)/sha1hard_.c:$(OBJDIR)/sha1hard.h \ $(OBJDIR)/sha3_.c:$(OBJDIR)/sha3.h \ $(OBJDIR)/shun_.c:$(OBJDIR)/shun.h \ $(OBJDIR)/sitemap_.c:$(OBJDIR)/sitemap.h \ $(OBJDIR)/skins_.c:$(OBJDIR)/skins.h \ $(OBJDIR)/sqlcmd_.c:$(OBJDIR)/sqlcmd.h \ $(OBJDIR)/stash_.c:$(OBJDIR)/stash.h \ $(OBJDIR)/stat_.c:$(OBJDIR)/stat.h \ $(OBJDIR)/statrep_.c:$(OBJDIR)/statrep.h \ $(OBJDIR)/style_.c:$(OBJDIR)/style.h \ $(OBJDIR)/sync_.c:$(OBJDIR)/sync.h \ $(OBJDIR)/tag_.c:$(OBJDIR)/tag.h \ | > | 1235 1236 1237 1238 1239 1240 1241 1242 1243 1244 1245 1246 1247 1248 1249 | $(OBJDIR)/setup_.c:$(OBJDIR)/setup.h \ $(OBJDIR)/sha1_.c:$(OBJDIR)/sha1.h \ $(OBJDIR)/sha1hard_.c:$(OBJDIR)/sha1hard.h \ $(OBJDIR)/sha3_.c:$(OBJDIR)/sha3.h \ $(OBJDIR)/shun_.c:$(OBJDIR)/shun.h \ $(OBJDIR)/sitemap_.c:$(OBJDIR)/sitemap.h \ $(OBJDIR)/skins_.c:$(OBJDIR)/skins.h \ $(OBJDIR)/smtp_.c:$(OBJDIR)/smtp.h \ $(OBJDIR)/sqlcmd_.c:$(OBJDIR)/sqlcmd.h \ $(OBJDIR)/stash_.c:$(OBJDIR)/stash.h \ $(OBJDIR)/stat_.c:$(OBJDIR)/stat.h \ $(OBJDIR)/statrep_.c:$(OBJDIR)/statrep.h \ $(OBJDIR)/style_.c:$(OBJDIR)/style.h \ $(OBJDIR)/sync_.c:$(OBJDIR)/sync.h \ $(OBJDIR)/tag_.c:$(OBJDIR)/tag.h \ |
︙ | ︙ | |||
1230 1231 1232 1233 1234 1235 1236 1237 1238 1239 1240 1241 1242 1243 | $(OBJDIR)/update_.c:$(OBJDIR)/update.h \ $(OBJDIR)/url_.c:$(OBJDIR)/url.h \ $(OBJDIR)/user_.c:$(OBJDIR)/user.h \ $(OBJDIR)/utf8_.c:$(OBJDIR)/utf8.h \ $(OBJDIR)/util_.c:$(OBJDIR)/util.h \ $(OBJDIR)/verify_.c:$(OBJDIR)/verify.h \ $(OBJDIR)/vfile_.c:$(OBJDIR)/vfile.h \ $(OBJDIR)/wiki_.c:$(OBJDIR)/wiki.h \ $(OBJDIR)/wikiformat_.c:$(OBJDIR)/wikiformat.h \ $(OBJDIR)/winfile_.c:$(OBJDIR)/winfile.h \ $(OBJDIR)/winhttp_.c:$(OBJDIR)/winhttp.h \ $(OBJDIR)/wysiwyg_.c:$(OBJDIR)/wysiwyg.h \ $(OBJDIR)/xfer_.c:$(OBJDIR)/xfer.h \ $(OBJDIR)/xfersetup_.c:$(OBJDIR)/xfersetup.h \ | > | 1258 1259 1260 1261 1262 1263 1264 1265 1266 1267 1268 1269 1270 1271 1272 | $(OBJDIR)/update_.c:$(OBJDIR)/update.h \ $(OBJDIR)/url_.c:$(OBJDIR)/url.h \ $(OBJDIR)/user_.c:$(OBJDIR)/user.h \ $(OBJDIR)/utf8_.c:$(OBJDIR)/utf8.h \ $(OBJDIR)/util_.c:$(OBJDIR)/util.h \ $(OBJDIR)/verify_.c:$(OBJDIR)/verify.h \ $(OBJDIR)/vfile_.c:$(OBJDIR)/vfile.h \ $(OBJDIR)/webmail_.c:$(OBJDIR)/webmail.h \ $(OBJDIR)/wiki_.c:$(OBJDIR)/wiki.h \ $(OBJDIR)/wikiformat_.c:$(OBJDIR)/wikiformat.h \ $(OBJDIR)/winfile_.c:$(OBJDIR)/winfile.h \ $(OBJDIR)/winhttp_.c:$(OBJDIR)/winhttp.h \ $(OBJDIR)/wysiwyg_.c:$(OBJDIR)/wysiwyg.h \ $(OBJDIR)/xfer_.c:$(OBJDIR)/xfer.h \ $(OBJDIR)/xfersetup_.c:$(OBJDIR)/xfersetup.h \ |
︙ | ︙ | |||
1270 1271 1272 1273 1274 1275 1276 1277 1278 1279 1280 1281 1282 1283 | $(OBJDIR)/attach_.c: $(SRCDIR)/attach.c $(TRANSLATE) $(TRANSLATE) $(SRCDIR)/attach.c >$@ $(OBJDIR)/attach.o: $(OBJDIR)/attach_.c $(OBJDIR)/attach.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/attach.o -c $(OBJDIR)/attach_.c $(OBJDIR)/attach.h: $(OBJDIR)/headers $(OBJDIR)/bag_.c: $(SRCDIR)/bag.c $(TRANSLATE) $(TRANSLATE) $(SRCDIR)/bag.c >$@ $(OBJDIR)/bag.o: $(OBJDIR)/bag_.c $(OBJDIR)/bag.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/bag.o -c $(OBJDIR)/bag_.c | > > > > > > > > | 1299 1300 1301 1302 1303 1304 1305 1306 1307 1308 1309 1310 1311 1312 1313 1314 1315 1316 1317 1318 1319 1320 | $(OBJDIR)/attach_.c: $(SRCDIR)/attach.c $(TRANSLATE) $(TRANSLATE) $(SRCDIR)/attach.c >$@ $(OBJDIR)/attach.o: $(OBJDIR)/attach_.c $(OBJDIR)/attach.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/attach.o -c $(OBJDIR)/attach_.c $(OBJDIR)/attach.h: $(OBJDIR)/headers $(OBJDIR)/backoffice_.c: $(SRCDIR)/backoffice.c $(TRANSLATE) $(TRANSLATE) $(SRCDIR)/backoffice.c >$@ $(OBJDIR)/backoffice.o: $(OBJDIR)/backoffice_.c $(OBJDIR)/backoffice.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/backoffice.o -c $(OBJDIR)/backoffice_.c $(OBJDIR)/backoffice.h: $(OBJDIR)/headers $(OBJDIR)/bag_.c: $(SRCDIR)/bag.c $(TRANSLATE) $(TRANSLATE) $(SRCDIR)/bag.c >$@ $(OBJDIR)/bag.o: $(OBJDIR)/bag_.c $(OBJDIR)/bag.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/bag.o -c $(OBJDIR)/bag_.c |
︙ | ︙ | |||
1334 1335 1336 1337 1338 1339 1340 1341 1342 1343 1344 1345 1346 1347 | $(OBJDIR)/cache_.c: $(SRCDIR)/cache.c $(TRANSLATE) $(TRANSLATE) $(SRCDIR)/cache.c >$@ $(OBJDIR)/cache.o: $(OBJDIR)/cache_.c $(OBJDIR)/cache.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/cache.o -c $(OBJDIR)/cache_.c $(OBJDIR)/cache.h: $(OBJDIR)/headers $(OBJDIR)/captcha_.c: $(SRCDIR)/captcha.c $(TRANSLATE) $(TRANSLATE) $(SRCDIR)/captcha.c >$@ $(OBJDIR)/captcha.o: $(OBJDIR)/captcha_.c $(OBJDIR)/captcha.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/captcha.o -c $(OBJDIR)/captcha_.c | > > > > > > > > | 1371 1372 1373 1374 1375 1376 1377 1378 1379 1380 1381 1382 1383 1384 1385 1386 1387 1388 1389 1390 1391 1392 | $(OBJDIR)/cache_.c: $(SRCDIR)/cache.c $(TRANSLATE) $(TRANSLATE) $(SRCDIR)/cache.c >$@ $(OBJDIR)/cache.o: $(OBJDIR)/cache_.c $(OBJDIR)/cache.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/cache.o -c $(OBJDIR)/cache_.c $(OBJDIR)/cache.h: $(OBJDIR)/headers $(OBJDIR)/capabilities_.c: $(SRCDIR)/capabilities.c $(TRANSLATE) $(TRANSLATE) $(SRCDIR)/capabilities.c >$@ $(OBJDIR)/capabilities.o: $(OBJDIR)/capabilities_.c $(OBJDIR)/capabilities.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/capabilities.o -c $(OBJDIR)/capabilities_.c $(OBJDIR)/capabilities.h: $(OBJDIR)/headers $(OBJDIR)/captcha_.c: $(SRCDIR)/captcha.c $(TRANSLATE) $(TRANSLATE) $(SRCDIR)/captcha.c >$@ $(OBJDIR)/captcha.o: $(OBJDIR)/captcha_.c $(OBJDIR)/captcha.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/captcha.o -c $(OBJDIR)/captcha_.c |
︙ | ︙ | |||
1478 1479 1480 1481 1482 1483 1484 1485 1486 1487 1488 1489 1490 1491 | $(OBJDIR)/doc_.c: $(SRCDIR)/doc.c $(TRANSLATE) $(TRANSLATE) $(SRCDIR)/doc.c >$@ $(OBJDIR)/doc.o: $(OBJDIR)/doc_.c $(OBJDIR)/doc.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/doc.o -c $(OBJDIR)/doc_.c $(OBJDIR)/doc.h: $(OBJDIR)/headers $(OBJDIR)/encode_.c: $(SRCDIR)/encode.c $(TRANSLATE) $(TRANSLATE) $(SRCDIR)/encode.c >$@ $(OBJDIR)/encode.o: $(OBJDIR)/encode_.c $(OBJDIR)/encode.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/encode.o -c $(OBJDIR)/encode_.c | > > > > > > > > | 1523 1524 1525 1526 1527 1528 1529 1530 1531 1532 1533 1534 1535 1536 1537 1538 1539 1540 1541 1542 1543 1544 | $(OBJDIR)/doc_.c: $(SRCDIR)/doc.c $(TRANSLATE) $(TRANSLATE) $(SRCDIR)/doc.c >$@ $(OBJDIR)/doc.o: $(OBJDIR)/doc_.c $(OBJDIR)/doc.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/doc.o -c $(OBJDIR)/doc_.c $(OBJDIR)/doc.h: $(OBJDIR)/headers $(OBJDIR)/email_.c: $(SRCDIR)/email.c $(TRANSLATE) $(TRANSLATE) $(SRCDIR)/email.c >$@ $(OBJDIR)/email.o: $(OBJDIR)/email_.c $(OBJDIR)/email.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/email.o -c $(OBJDIR)/email_.c $(OBJDIR)/email.h: $(OBJDIR)/headers $(OBJDIR)/encode_.c: $(SRCDIR)/encode.c $(TRANSLATE) $(TRANSLATE) $(SRCDIR)/encode.c >$@ $(OBJDIR)/encode.o: $(OBJDIR)/encode_.c $(OBJDIR)/encode.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/encode.o -c $(OBJDIR)/encode_.c |
︙ | ︙ | |||
1534 1535 1536 1537 1538 1539 1540 1541 1542 1543 1544 1545 1546 1547 | $(OBJDIR)/foci_.c: $(SRCDIR)/foci.c $(TRANSLATE) $(TRANSLATE) $(SRCDIR)/foci.c >$@ $(OBJDIR)/foci.o: $(OBJDIR)/foci_.c $(OBJDIR)/foci.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/foci.o -c $(OBJDIR)/foci_.c $(OBJDIR)/foci.h: $(OBJDIR)/headers $(OBJDIR)/fshell_.c: $(SRCDIR)/fshell.c $(TRANSLATE) $(TRANSLATE) $(SRCDIR)/fshell.c >$@ $(OBJDIR)/fshell.o: $(OBJDIR)/fshell_.c $(OBJDIR)/fshell.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/fshell.o -c $(OBJDIR)/fshell_.c | > > > > > > > > | 1587 1588 1589 1590 1591 1592 1593 1594 1595 1596 1597 1598 1599 1600 1601 1602 1603 1604 1605 1606 1607 1608 | $(OBJDIR)/foci_.c: $(SRCDIR)/foci.c $(TRANSLATE) $(TRANSLATE) $(SRCDIR)/foci.c >$@ $(OBJDIR)/foci.o: $(OBJDIR)/foci_.c $(OBJDIR)/foci.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/foci.o -c $(OBJDIR)/foci_.c $(OBJDIR)/foci.h: $(OBJDIR)/headers $(OBJDIR)/forum_.c: $(SRCDIR)/forum.c $(TRANSLATE) $(TRANSLATE) $(SRCDIR)/forum.c >$@ $(OBJDIR)/forum.o: $(OBJDIR)/forum_.c $(OBJDIR)/forum.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/forum.o -c $(OBJDIR)/forum_.c $(OBJDIR)/forum.h: $(OBJDIR)/headers $(OBJDIR)/fshell_.c: $(SRCDIR)/fshell.c $(TRANSLATE) $(TRANSLATE) $(SRCDIR)/fshell.c >$@ $(OBJDIR)/fshell.o: $(OBJDIR)/fshell_.c $(OBJDIR)/fshell.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/fshell.o -c $(OBJDIR)/fshell_.c |
︙ | ︙ | |||
2030 2031 2032 2033 2034 2035 2036 2037 2038 2039 2040 2041 2042 2043 | $(OBJDIR)/skins_.c: $(SRCDIR)/skins.c $(TRANSLATE) $(TRANSLATE) $(SRCDIR)/skins.c >$@ $(OBJDIR)/skins.o: $(OBJDIR)/skins_.c $(OBJDIR)/skins.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/skins.o -c $(OBJDIR)/skins_.c $(OBJDIR)/skins.h: $(OBJDIR)/headers $(OBJDIR)/sqlcmd_.c: $(SRCDIR)/sqlcmd.c $(TRANSLATE) $(TRANSLATE) $(SRCDIR)/sqlcmd.c >$@ $(OBJDIR)/sqlcmd.o: $(OBJDIR)/sqlcmd_.c $(OBJDIR)/sqlcmd.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/sqlcmd.o -c $(OBJDIR)/sqlcmd_.c | > > > > > > > > | 2091 2092 2093 2094 2095 2096 2097 2098 2099 2100 2101 2102 2103 2104 2105 2106 2107 2108 2109 2110 2111 2112 | $(OBJDIR)/skins_.c: $(SRCDIR)/skins.c $(TRANSLATE) $(TRANSLATE) $(SRCDIR)/skins.c >$@ $(OBJDIR)/skins.o: $(OBJDIR)/skins_.c $(OBJDIR)/skins.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/skins.o -c $(OBJDIR)/skins_.c $(OBJDIR)/skins.h: $(OBJDIR)/headers $(OBJDIR)/smtp_.c: $(SRCDIR)/smtp.c $(TRANSLATE) $(TRANSLATE) $(SRCDIR)/smtp.c >$@ $(OBJDIR)/smtp.o: $(OBJDIR)/smtp_.c $(OBJDIR)/smtp.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/smtp.o -c $(OBJDIR)/smtp_.c $(OBJDIR)/smtp.h: $(OBJDIR)/headers $(OBJDIR)/sqlcmd_.c: $(SRCDIR)/sqlcmd.c $(TRANSLATE) $(TRANSLATE) $(SRCDIR)/sqlcmd.c >$@ $(OBJDIR)/sqlcmd.o: $(OBJDIR)/sqlcmd_.c $(OBJDIR)/sqlcmd.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/sqlcmd.o -c $(OBJDIR)/sqlcmd_.c |
︙ | ︙ | |||
2206 2207 2208 2209 2210 2211 2212 2213 2214 2215 2216 2217 2218 2219 | $(OBJDIR)/vfile_.c: $(SRCDIR)/vfile.c $(TRANSLATE) $(TRANSLATE) $(SRCDIR)/vfile.c >$@ $(OBJDIR)/vfile.o: $(OBJDIR)/vfile_.c $(OBJDIR)/vfile.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/vfile.o -c $(OBJDIR)/vfile_.c $(OBJDIR)/vfile.h: $(OBJDIR)/headers $(OBJDIR)/wiki_.c: $(SRCDIR)/wiki.c $(TRANSLATE) $(TRANSLATE) $(SRCDIR)/wiki.c >$@ $(OBJDIR)/wiki.o: $(OBJDIR)/wiki_.c $(OBJDIR)/wiki.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/wiki.o -c $(OBJDIR)/wiki_.c | > > > > > > > > | 2275 2276 2277 2278 2279 2280 2281 2282 2283 2284 2285 2286 2287 2288 2289 2290 2291 2292 2293 2294 2295 2296 | $(OBJDIR)/vfile_.c: $(SRCDIR)/vfile.c $(TRANSLATE) $(TRANSLATE) $(SRCDIR)/vfile.c >$@ $(OBJDIR)/vfile.o: $(OBJDIR)/vfile_.c $(OBJDIR)/vfile.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/vfile.o -c $(OBJDIR)/vfile_.c $(OBJDIR)/vfile.h: $(OBJDIR)/headers $(OBJDIR)/webmail_.c: $(SRCDIR)/webmail.c $(TRANSLATE) $(TRANSLATE) $(SRCDIR)/webmail.c >$@ $(OBJDIR)/webmail.o: $(OBJDIR)/webmail_.c $(OBJDIR)/webmail.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/webmail.o -c $(OBJDIR)/webmail_.c $(OBJDIR)/webmail.h: $(OBJDIR)/headers $(OBJDIR)/wiki_.c: $(SRCDIR)/wiki.c $(TRANSLATE) $(TRANSLATE) $(SRCDIR)/wiki.c >$@ $(OBJDIR)/wiki.o: $(OBJDIR)/wiki_.c $(OBJDIR)/wiki.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/wiki.o -c $(OBJDIR)/wiki_.c |
︙ | ︙ | |||
2332 2333 2334 2335 2336 2337 2338 | -DSQLITE_HAVE_ZLIB \ -DSQLITE_INTROSPECTION_PRAGMAS \ -DSQLITE_ENABLE_DBPAGE_VTAB \ -Dmain=sqlite3_shell \ -DSQLITE_SHELL_IS_UTF8=1 \ -DSQLITE_OMIT_LOAD_EXTENSION=1 \ -DUSE_SYSTEM_SQLITE=$(USE_SYSTEM_SQLITE) \ | | > | 2409 2410 2411 2412 2413 2414 2415 2416 2417 2418 2419 2420 2421 2422 2423 2424 | -DSQLITE_HAVE_ZLIB \ -DSQLITE_INTROSPECTION_PRAGMAS \ -DSQLITE_ENABLE_DBPAGE_VTAB \ -Dmain=sqlite3_shell \ -DSQLITE_SHELL_IS_UTF8=1 \ -DSQLITE_OMIT_LOAD_EXTENSION=1 \ -DUSE_SYSTEM_SQLITE=$(USE_SYSTEM_SQLITE) \ -DSQLITE_SHELL_DBNAME_PROC=sqlcmd_get_dbname \ -DSQLITE_SHELL_INIT_PROC=sqlcmd_init_proc \ -Daccess=file_access \ -Dsystem=fossil_system \ -Dgetenv=fossil_getenv \ -Dfopen=fossil_fopen MINIZ_OPTIONS = -DMINIZ_NO_STDIO \ -DMINIZ_NO_TIME \ |
︙ | ︙ |
Changes to win/Makefile.msc.
︙ | ︙ | |||
57 58 59 60 61 62 63 | # Enable the JSON API? !ifndef FOSSIL_ENABLE_JSON FOSSIL_ENABLE_JSON = 0 !endif # Enable legacy treatment of the mv/rm commands? !ifndef FOSSIL_ENABLE_LEGACY_MV_RM | | | 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 | # Enable the JSON API? !ifndef FOSSIL_ENABLE_JSON FOSSIL_ENABLE_JSON = 0 !endif # Enable legacy treatment of the mv/rm commands? !ifndef FOSSIL_ENABLE_LEGACY_MV_RM FOSSIL_ENABLE_LEGACY_MV_RM = 1 !endif # Enable use of miniz instead of zlib? !ifndef FOSSIL_ENABLE_MINIZ FOSSIL_ENABLE_MINIZ = 0 !endif |
︙ | ︙ | |||
96 97 98 99 100 101 102 | # Enable support for the SQLite Encryption Extension? !ifndef USE_SEE USE_SEE = 0 !endif !if $(FOSSIL_ENABLE_SSL)!=0 | | | 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 | # Enable support for the SQLite Encryption Extension? !ifndef USE_SEE USE_SEE = 0 !endif !if $(FOSSIL_ENABLE_SSL)!=0 SSLDIR = $(B)\compat\openssl-1.1.1-pre8 SSLINCDIR = $(SSLDIR)\inc32 !if $(FOSSIL_DYNAMIC_BUILD)!=0 SSLLIBDIR = $(SSLDIR)\out32dll !else SSLLIBDIR = $(SSLDIR)\out32 !endif SSLLFLAGS = /nologo /opt:ref /debug |
︙ | ︙ | |||
243 244 245 246 247 248 249 | CFLAGS = $(CFLAGS) $(CRTFLAGS) /O2 !endif BCC = $(CC) $(CFLAGS) TCC = $(CC) /c $(CFLAGS) $(MSCDEF) $(INCL) RCC = $(RC) /D_WIN32 /D_MSC_VER $(MSCDEF) $(INCL) MTC = mt | | | 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 | CFLAGS = $(CFLAGS) $(CRTFLAGS) /O2 !endif BCC = $(CC) $(CFLAGS) TCC = $(CC) /c $(CFLAGS) $(MSCDEF) $(INCL) RCC = $(RC) /D_WIN32 /D_MSC_VER $(MSCDEF) $(INCL) MTC = mt LIBS = ws2_32.lib advapi32.lib dnsapi.lib LIBDIR = !if $(FOSSIL_DYNAMIC_BUILD)!=0 TCC = $(TCC) /DFOSSIL_DYNAMIC_BUILD=1 RCC = $(RCC) /DFOSSIL_DYNAMIC_BUILD=1 !endif |
︙ | ︙ | |||
365 366 367 368 369 370 371 | /DSQLITE_HAVE_ZLIB \ /DSQLITE_INTROSPECTION_PRAGMAS \ /DSQLITE_ENABLE_DBPAGE_VTAB \ /Dmain=sqlite3_shell \ /DSQLITE_SHELL_IS_UTF8=1 \ /DSQLITE_OMIT_LOAD_EXTENSION=1 \ /DUSE_SYSTEM_SQLITE=$(USE_SYSTEM_SQLITE) \ | | > > > > > | 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 | /DSQLITE_HAVE_ZLIB \ /DSQLITE_INTROSPECTION_PRAGMAS \ /DSQLITE_ENABLE_DBPAGE_VTAB \ /Dmain=sqlite3_shell \ /DSQLITE_SHELL_IS_UTF8=1 \ /DSQLITE_OMIT_LOAD_EXTENSION=1 \ /DUSE_SYSTEM_SQLITE=$(USE_SYSTEM_SQLITE) \ /DSQLITE_SHELL_DBNAME_PROC=sqlcmd_get_dbname \ /DSQLITE_SHELL_INIT_PROC=sqlcmd_init_proc \ /Daccess=file_access \ /Dsystem=fossil_system \ /Dgetenv=fossil_getenv \ /Dfopen=fossil_fopen MINIZ_OPTIONS = /DMINIZ_NO_STDIO \ /DMINIZ_NO_TIME \ /DMINIZ_NO_ARCHIVE_APIS SRC = add_.c \ allrepo_.c \ attach_.c \ backoffice_.c \ bag_.c \ bisect_.c \ blob_.c \ branch_.c \ browse_.c \ builtin_.c \ bundle_.c \ cache_.c \ capabilities_.c \ captcha_.c \ cgi_.c \ checkin_.c \ checkout_.c \ clearsign_.c \ clone_.c \ comformat_.c \ configure_.c \ content_.c \ cookies_.c \ db_.c \ delta_.c \ deltacmd_.c \ descendants_.c \ diff_.c \ diffcmd_.c \ dispatch_.c \ doc_.c \ email_.c \ encode_.c \ etag_.c \ event_.c \ export_.c \ file_.c \ finfo_.c \ foci_.c \ forum_.c \ fshell_.c \ fusefs_.c \ glob_.c \ graph_.c \ gzip_.c \ hname_.c \ http_.c \ |
︙ | ︙ | |||
467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 | regexp_.c \ report_.c \ rss_.c \ schema_.c \ search_.c \ security_audit_.c \ setup_.c \ sha1_.c \ sha1hard_.c \ sha3_.c \ shun_.c \ sitemap_.c \ skins_.c \ sqlcmd_.c \ stash_.c \ stat_.c \ statrep_.c \ style_.c \ sync_.c \ tag_.c \ | > > | 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 | regexp_.c \ report_.c \ rss_.c \ schema_.c \ search_.c \ security_audit_.c \ setup_.c \ setupuser_.c \ sha1_.c \ sha1hard_.c \ sha3_.c \ shun_.c \ sitemap_.c \ skins_.c \ smtp_.c \ sqlcmd_.c \ stash_.c \ stat_.c \ statrep_.c \ style_.c \ sync_.c \ tag_.c \ |
︙ | ︙ | |||
495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 | update_.c \ url_.c \ user_.c \ utf8_.c \ util_.c \ verify_.c \ vfile_.c \ wiki_.c \ wikiformat_.c \ winfile_.c \ winhttp_.c \ wysiwyg_.c \ xfer_.c \ xfersetup_.c \ zip_.c | > | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | > > > > > | 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 | update_.c \ url_.c \ user_.c \ utf8_.c \ util_.c \ verify_.c \ vfile_.c \ webmail_.c \ wiki_.c \ wikiformat_.c \ winfile_.c \ winhttp_.c \ wysiwyg_.c \ xfer_.c \ xfersetup_.c \ zip_.c EXTRA_FILES = $(SRCDIR)\..\skins\aht\details.txt \ $(SRCDIR)\..\skins\ardoise\css.txt \ $(SRCDIR)\..\skins\ardoise\details.txt \ $(SRCDIR)\..\skins\ardoise\footer.txt \ $(SRCDIR)\..\skins\ardoise\header.txt \ $(SRCDIR)\..\skins\black_and_white\css.txt \ $(SRCDIR)\..\skins\black_and_white\details.txt \ $(SRCDIR)\..\skins\black_and_white\footer.txt \ $(SRCDIR)\..\skins\black_and_white\header.txt \ $(SRCDIR)\..\skins\blitz\css.txt \ $(SRCDIR)\..\skins\blitz\details.txt \ $(SRCDIR)\..\skins\blitz\footer.txt \ $(SRCDIR)\..\skins\blitz\header.txt \ $(SRCDIR)\..\skins\blitz\ticket.txt \ $(SRCDIR)\..\skins\blitz_no_logo\css.txt \ $(SRCDIR)\..\skins\blitz_no_logo\details.txt \ $(SRCDIR)\..\skins\blitz_no_logo\footer.txt \ $(SRCDIR)\..\skins\blitz_no_logo\header.txt \ $(SRCDIR)\..\skins\blitz_no_logo\ticket.txt \ $(SRCDIR)\..\skins\bootstrap\css.txt \ $(SRCDIR)\..\skins\bootstrap\details.txt \ $(SRCDIR)\..\skins\bootstrap\footer.txt \ $(SRCDIR)\..\skins\bootstrap\header.txt \ $(SRCDIR)\..\skins\default\css.txt \ $(SRCDIR)\..\skins\default\details.txt \ $(SRCDIR)\..\skins\default\footer.txt \ $(SRCDIR)\..\skins\default\header.txt \ $(SRCDIR)\..\skins\eagle\css.txt \ $(SRCDIR)\..\skins\eagle\details.txt \ $(SRCDIR)\..\skins\eagle\footer.txt \ $(SRCDIR)\..\skins\eagle\header.txt \ $(SRCDIR)\..\skins\enhanced1\css.txt \ $(SRCDIR)\..\skins\enhanced1\details.txt \ $(SRCDIR)\..\skins\enhanced1\footer.txt \ $(SRCDIR)\..\skins\enhanced1\header.txt \ $(SRCDIR)\..\skins\khaki\css.txt \ $(SRCDIR)\..\skins\khaki\details.txt \ $(SRCDIR)\..\skins\khaki\footer.txt \ $(SRCDIR)\..\skins\khaki\header.txt \ $(SRCDIR)\..\skins\original\css.txt \ $(SRCDIR)\..\skins\original\details.txt \ $(SRCDIR)\..\skins\original\footer.txt \ $(SRCDIR)\..\skins\original\header.txt \ $(SRCDIR)\..\skins\plain_gray\css.txt \ $(SRCDIR)\..\skins\plain_gray\details.txt \ $(SRCDIR)\..\skins\plain_gray\footer.txt \ $(SRCDIR)\..\skins\plain_gray\header.txt \ $(SRCDIR)\..\skins\rounded1\css.txt \ $(SRCDIR)\..\skins\rounded1\details.txt \ $(SRCDIR)\..\skins\rounded1\footer.txt \ $(SRCDIR)\..\skins\rounded1\header.txt \ $(SRCDIR)\..\skins\xekri\css.txt \ $(SRCDIR)\..\skins\xekri\details.txt \ $(SRCDIR)\..\skins\xekri\footer.txt \ $(SRCDIR)\..\skins\xekri\header.txt \ $(SRCDIR)\ci_edit.js \ $(SRCDIR)\diff.tcl \ $(SRCDIR)\forum.js \ $(SRCDIR)\graph.js \ $(SRCDIR)\href.js \ $(SRCDIR)\login.js \ $(SRCDIR)\markdown.md \ $(SRCDIR)\menu.js \ $(SRCDIR)\sbsdiff.js \ $(SRCDIR)\scroll.js \ $(SRCDIR)\skin.js \ $(SRCDIR)\sorttable.js \ $(SRCDIR)\tree.js \ $(SRCDIR)\useredit.js \ $(SRCDIR)\wiki.wiki OBJ = $(OX)\add$O \ $(OX)\allrepo$O \ $(OX)\attach$O \ $(OX)\backoffice$O \ $(OX)\bag$O \ $(OX)\bisect$O \ $(OX)\blob$O \ $(OX)\branch$O \ $(OX)\browse$O \ $(OX)\builtin$O \ $(OX)\bundle$O \ $(OX)\cache$O \ $(OX)\capabilities$O \ $(OX)\captcha$O \ $(OX)\cgi$O \ $(OX)\checkin$O \ $(OX)\checkout$O \ $(OX)\clearsign$O \ $(OX)\clone$O \ $(OX)\comformat$O \ $(OX)\configure$O \ $(OX)\content$O \ $(OX)\cookies$O \ $(OX)\cson_amalgamation$O \ $(OX)\db$O \ $(OX)\delta$O \ $(OX)\deltacmd$O \ $(OX)\descendants$O \ $(OX)\diff$O \ $(OX)\diffcmd$O \ $(OX)\dispatch$O \ $(OX)\doc$O \ $(OX)\email$O \ $(OX)\encode$O \ $(OX)\etag$O \ $(OX)\event$O \ $(OX)\export$O \ $(OX)\file$O \ $(OX)\finfo$O \ $(OX)\foci$O \ $(OX)\forum$O \ $(OX)\fshell$O \ $(OX)\fusefs$O \ $(OX)\glob$O \ $(OX)\graph$O \ $(OX)\gzip$O \ $(OX)\hname$O \ $(OX)\http$O \ |
︙ | ︙ | |||
667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 | $(OX)\regexp$O \ $(OX)\report$O \ $(OX)\rss$O \ $(OX)\schema$O \ $(OX)\search$O \ $(OX)\security_audit$O \ $(OX)\setup$O \ $(OX)\sha1$O \ $(OX)\sha1hard$O \ $(OX)\sha3$O \ $(OX)\shell$O \ $(OX)\shun$O \ $(OX)\sitemap$O \ $(OX)\skins$O \ $(OX)\sqlcmd$O \ $(OX)\sqlite3$O \ $(OX)\stash$O \ $(OX)\stat$O \ $(OX)\statrep$O \ $(OX)\style$O \ $(OX)\sync$O \ | > > | 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 | $(OX)\regexp$O \ $(OX)\report$O \ $(OX)\rss$O \ $(OX)\schema$O \ $(OX)\search$O \ $(OX)\security_audit$O \ $(OX)\setup$O \ $(OX)\setupuser$O \ $(OX)\sha1$O \ $(OX)\sha1hard$O \ $(OX)\sha3$O \ $(OX)\shell$O \ $(OX)\shun$O \ $(OX)\sitemap$O \ $(OX)\skins$O \ $(OX)\smtp$O \ $(OX)\sqlcmd$O \ $(OX)\sqlite3$O \ $(OX)\stash$O \ $(OX)\stat$O \ $(OX)\statrep$O \ $(OX)\style$O \ $(OX)\sync$O \ |
︙ | ︙ | |||
700 701 702 703 704 705 706 707 708 709 710 711 712 713 | $(OX)\update$O \ $(OX)\url$O \ $(OX)\user$O \ $(OX)\utf8$O \ $(OX)\util$O \ $(OX)\verify$O \ $(OX)\vfile$O \ $(OX)\wiki$O \ $(OX)\wikiformat$O \ $(OX)\winfile$O \ $(OX)\winhttp$O \ $(OX)\wysiwyg$O \ $(OX)\xfer$O \ $(OX)\xfersetup$O \ | > | 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 | $(OX)\update$O \ $(OX)\url$O \ $(OX)\user$O \ $(OX)\utf8$O \ $(OX)\util$O \ $(OX)\verify$O \ $(OX)\vfile$O \ $(OX)\webmail$O \ $(OX)\wiki$O \ $(OX)\wikiformat$O \ $(OX)\winfile$O \ $(OX)\winhttp$O \ $(OX)\wysiwyg$O \ $(OX)\xfer$O \ $(OX)\xfersetup$O \ |
︙ | ︙ | |||
766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 | if exist $@.manifest \ $(MTC) -nologo -manifest $@.manifest -outputresource:$@;1 $(OX)\linkopts: $B\win\Makefile.msc echo $(OX)\add.obj > $@ echo $(OX)\allrepo.obj >> $@ echo $(OX)\attach.obj >> $@ echo $(OX)\bag.obj >> $@ echo $(OX)\bisect.obj >> $@ echo $(OX)\blob.obj >> $@ echo $(OX)\branch.obj >> $@ echo $(OX)\browse.obj >> $@ echo $(OX)\builtin.obj >> $@ echo $(OX)\bundle.obj >> $@ echo $(OX)\cache.obj >> $@ echo $(OX)\captcha.obj >> $@ echo $(OX)\cgi.obj >> $@ echo $(OX)\checkin.obj >> $@ echo $(OX)\checkout.obj >> $@ echo $(OX)\clearsign.obj >> $@ echo $(OX)\clone.obj >> $@ echo $(OX)\comformat.obj >> $@ echo $(OX)\configure.obj >> $@ echo $(OX)\content.obj >> $@ echo $(OX)\cookies.obj >> $@ echo $(OX)\cson_amalgamation.obj >> $@ echo $(OX)\db.obj >> $@ echo $(OX)\delta.obj >> $@ echo $(OX)\deltacmd.obj >> $@ echo $(OX)\descendants.obj >> $@ echo $(OX)\diff.obj >> $@ echo $(OX)\diffcmd.obj >> $@ echo $(OX)\dispatch.obj >> $@ echo $(OX)\doc.obj >> $@ echo $(OX)\encode.obj >> $@ echo $(OX)\etag.obj >> $@ echo $(OX)\event.obj >> $@ echo $(OX)\export.obj >> $@ echo $(OX)\file.obj >> $@ echo $(OX)\finfo.obj >> $@ echo $(OX)\foci.obj >> $@ echo $(OX)\fshell.obj >> $@ echo $(OX)\fusefs.obj >> $@ echo $(OX)\glob.obj >> $@ echo $(OX)\graph.obj >> $@ echo $(OX)\gzip.obj >> $@ echo $(OX)\hname.obj >> $@ echo $(OX)\http.obj >> $@ | > > > > | 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 | if exist $@.manifest \ $(MTC) -nologo -manifest $@.manifest -outputresource:$@;1 $(OX)\linkopts: $B\win\Makefile.msc echo $(OX)\add.obj > $@ echo $(OX)\allrepo.obj >> $@ echo $(OX)\attach.obj >> $@ echo $(OX)\backoffice.obj >> $@ echo $(OX)\bag.obj >> $@ echo $(OX)\bisect.obj >> $@ echo $(OX)\blob.obj >> $@ echo $(OX)\branch.obj >> $@ echo $(OX)\browse.obj >> $@ echo $(OX)\builtin.obj >> $@ echo $(OX)\bundle.obj >> $@ echo $(OX)\cache.obj >> $@ echo $(OX)\capabilities.obj >> $@ echo $(OX)\captcha.obj >> $@ echo $(OX)\cgi.obj >> $@ echo $(OX)\checkin.obj >> $@ echo $(OX)\checkout.obj >> $@ echo $(OX)\clearsign.obj >> $@ echo $(OX)\clone.obj >> $@ echo $(OX)\comformat.obj >> $@ echo $(OX)\configure.obj >> $@ echo $(OX)\content.obj >> $@ echo $(OX)\cookies.obj >> $@ echo $(OX)\cson_amalgamation.obj >> $@ echo $(OX)\db.obj >> $@ echo $(OX)\delta.obj >> $@ echo $(OX)\deltacmd.obj >> $@ echo $(OX)\descendants.obj >> $@ echo $(OX)\diff.obj >> $@ echo $(OX)\diffcmd.obj >> $@ echo $(OX)\dispatch.obj >> $@ echo $(OX)\doc.obj >> $@ echo $(OX)\email.obj >> $@ echo $(OX)\encode.obj >> $@ echo $(OX)\etag.obj >> $@ echo $(OX)\event.obj >> $@ echo $(OX)\export.obj >> $@ echo $(OX)\file.obj >> $@ echo $(OX)\finfo.obj >> $@ echo $(OX)\foci.obj >> $@ echo $(OX)\forum.obj >> $@ echo $(OX)\fshell.obj >> $@ echo $(OX)\fusefs.obj >> $@ echo $(OX)\glob.obj >> $@ echo $(OX)\graph.obj >> $@ echo $(OX)\gzip.obj >> $@ echo $(OX)\hname.obj >> $@ echo $(OX)\http.obj >> $@ |
︙ | ︙ | |||
856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 | echo $(OX)\regexp.obj >> $@ echo $(OX)\report.obj >> $@ echo $(OX)\rss.obj >> $@ echo $(OX)\schema.obj >> $@ echo $(OX)\search.obj >> $@ echo $(OX)\security_audit.obj >> $@ echo $(OX)\setup.obj >> $@ echo $(OX)\sha1.obj >> $@ echo $(OX)\sha1hard.obj >> $@ echo $(OX)\sha3.obj >> $@ echo $(OX)\shell.obj >> $@ echo $(OX)\shun.obj >> $@ echo $(OX)\sitemap.obj >> $@ echo $(OX)\skins.obj >> $@ echo $(OX)\sqlcmd.obj >> $@ echo $(OX)\sqlite3.obj >> $@ echo $(OX)\stash.obj >> $@ echo $(OX)\stat.obj >> $@ echo $(OX)\statrep.obj >> $@ echo $(OX)\style.obj >> $@ echo $(OX)\sync.obj >> $@ | > > | 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 | echo $(OX)\regexp.obj >> $@ echo $(OX)\report.obj >> $@ echo $(OX)\rss.obj >> $@ echo $(OX)\schema.obj >> $@ echo $(OX)\search.obj >> $@ echo $(OX)\security_audit.obj >> $@ echo $(OX)\setup.obj >> $@ echo $(OX)\setupuser.obj >> $@ echo $(OX)\sha1.obj >> $@ echo $(OX)\sha1hard.obj >> $@ echo $(OX)\sha3.obj >> $@ echo $(OX)\shell.obj >> $@ echo $(OX)\shun.obj >> $@ echo $(OX)\sitemap.obj >> $@ echo $(OX)\skins.obj >> $@ echo $(OX)\smtp.obj >> $@ echo $(OX)\sqlcmd.obj >> $@ echo $(OX)\sqlite3.obj >> $@ echo $(OX)\stash.obj >> $@ echo $(OX)\stat.obj >> $@ echo $(OX)\statrep.obj >> $@ echo $(OX)\style.obj >> $@ echo $(OX)\sync.obj >> $@ |
︙ | ︙ | |||
889 890 891 892 893 894 895 896 897 898 899 900 901 902 | echo $(OX)\update.obj >> $@ echo $(OX)\url.obj >> $@ echo $(OX)\user.obj >> $@ echo $(OX)\utf8.obj >> $@ echo $(OX)\util.obj >> $@ echo $(OX)\verify.obj >> $@ echo $(OX)\vfile.obj >> $@ echo $(OX)\wiki.obj >> $@ echo $(OX)\wikiformat.obj >> $@ echo $(OX)\winfile.obj >> $@ echo $(OX)\winhttp.obj >> $@ echo $(OX)\wysiwyg.obj >> $@ echo $(OX)\xfer.obj >> $@ echo $(OX)\xfersetup.obj >> $@ | > | 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 | echo $(OX)\update.obj >> $@ echo $(OX)\url.obj >> $@ echo $(OX)\user.obj >> $@ echo $(OX)\utf8.obj >> $@ echo $(OX)\util.obj >> $@ echo $(OX)\verify.obj >> $@ echo $(OX)\vfile.obj >> $@ echo $(OX)\webmail.obj >> $@ echo $(OX)\wiki.obj >> $@ echo $(OX)\wikiformat.obj >> $@ echo $(OX)\winfile.obj >> $@ echo $(OX)\winhttp.obj >> $@ echo $(OX)\wysiwyg.obj >> $@ echo $(OX)\xfer.obj >> $@ echo $(OX)\xfersetup.obj >> $@ |
︙ | ︙ | |||
1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 | translate$E $** > $@ $(OX)\attach$O : attach_.c attach.h $(TCC) /Fo$@ -c attach_.c attach_.c : $(SRCDIR)\attach.c translate$E $** > $@ $(OX)\bag$O : bag_.c bag.h $(TCC) /Fo$@ -c bag_.c bag_.c : $(SRCDIR)\bag.c translate$E $** > $@ | > > > > > > | 1055 1056 1057 1058 1059 1060 1061 1062 1063 1064 1065 1066 1067 1068 1069 1070 1071 1072 1073 1074 | translate$E $** > $@ $(OX)\attach$O : attach_.c attach.h $(TCC) /Fo$@ -c attach_.c attach_.c : $(SRCDIR)\attach.c translate$E $** > $@ $(OX)\backoffice$O : backoffice_.c backoffice.h $(TCC) /Fo$@ -c backoffice_.c backoffice_.c : $(SRCDIR)\backoffice.c translate$E $** > $@ $(OX)\bag$O : bag_.c bag.h $(TCC) /Fo$@ -c bag_.c bag_.c : $(SRCDIR)\bag.c translate$E $** > $@ |
︙ | ︙ | |||
1080 1081 1082 1083 1084 1085 1086 1087 1088 1089 1090 1091 1092 1093 | translate$E $** > $@ $(OX)\cache$O : cache_.c cache.h $(TCC) /Fo$@ -c cache_.c cache_.c : $(SRCDIR)\cache.c translate$E $** > $@ $(OX)\captcha$O : captcha_.c captcha.h $(TCC) /Fo$@ -c captcha_.c captcha_.c : $(SRCDIR)\captcha.c translate$E $** > $@ | > > > > > > | 1109 1110 1111 1112 1113 1114 1115 1116 1117 1118 1119 1120 1121 1122 1123 1124 1125 1126 1127 1128 | translate$E $** > $@ $(OX)\cache$O : cache_.c cache.h $(TCC) /Fo$@ -c cache_.c cache_.c : $(SRCDIR)\cache.c translate$E $** > $@ $(OX)\capabilities$O : capabilities_.c capabilities.h $(TCC) /Fo$@ -c capabilities_.c capabilities_.c : $(SRCDIR)\capabilities.c translate$E $** > $@ $(OX)\captcha$O : captcha_.c captcha.h $(TCC) /Fo$@ -c captcha_.c captcha_.c : $(SRCDIR)\captcha.c translate$E $** > $@ |
︙ | ︙ | |||
1188 1189 1190 1191 1192 1193 1194 1195 1196 1197 1198 1199 1200 1201 | translate$E $** > $@ $(OX)\doc$O : doc_.c doc.h $(TCC) /Fo$@ -c doc_.c doc_.c : $(SRCDIR)\doc.c translate$E $** > $@ $(OX)\encode$O : encode_.c encode.h $(TCC) /Fo$@ -c encode_.c encode_.c : $(SRCDIR)\encode.c translate$E $** > $@ | > > > > > > | 1223 1224 1225 1226 1227 1228 1229 1230 1231 1232 1233 1234 1235 1236 1237 1238 1239 1240 1241 1242 | translate$E $** > $@ $(OX)\doc$O : doc_.c doc.h $(TCC) /Fo$@ -c doc_.c doc_.c : $(SRCDIR)\doc.c translate$E $** > $@ $(OX)\email$O : email_.c email.h $(TCC) /Fo$@ -c email_.c email_.c : $(SRCDIR)\email.c translate$E $** > $@ $(OX)\encode$O : encode_.c encode.h $(TCC) /Fo$@ -c encode_.c encode_.c : $(SRCDIR)\encode.c translate$E $** > $@ |
︙ | ︙ | |||
1230 1231 1232 1233 1234 1235 1236 1237 1238 1239 1240 1241 1242 1243 | translate$E $** > $@ $(OX)\foci$O : foci_.c foci.h $(TCC) /Fo$@ -c foci_.c foci_.c : $(SRCDIR)\foci.c translate$E $** > $@ $(OX)\fshell$O : fshell_.c fshell.h $(TCC) /Fo$@ -c fshell_.c fshell_.c : $(SRCDIR)\fshell.c translate$E $** > $@ | > > > > > > | 1271 1272 1273 1274 1275 1276 1277 1278 1279 1280 1281 1282 1283 1284 1285 1286 1287 1288 1289 1290 | translate$E $** > $@ $(OX)\foci$O : foci_.c foci.h $(TCC) /Fo$@ -c foci_.c foci_.c : $(SRCDIR)\foci.c translate$E $** > $@ $(OX)\forum$O : forum_.c forum.h $(TCC) /Fo$@ -c forum_.c forum_.c : $(SRCDIR)\forum.c translate$E $** > $@ $(OX)\fshell$O : fshell_.c fshell.h $(TCC) /Fo$@ -c fshell_.c fshell_.c : $(SRCDIR)\fshell.c translate$E $** > $@ |
︙ | ︙ | |||
1566 1567 1568 1569 1570 1571 1572 1573 1574 1575 1576 1577 1578 1579 | translate$E $** > $@ $(OX)\setup$O : setup_.c setup.h $(TCC) /Fo$@ -c setup_.c setup_.c : $(SRCDIR)\setup.c translate$E $** > $@ $(OX)\sha1$O : sha1_.c sha1.h $(TCC) /Fo$@ -c sha1_.c sha1_.c : $(SRCDIR)\sha1.c translate$E $** > $@ | > > > > > > | 1613 1614 1615 1616 1617 1618 1619 1620 1621 1622 1623 1624 1625 1626 1627 1628 1629 1630 1631 1632 | translate$E $** > $@ $(OX)\setup$O : setup_.c setup.h $(TCC) /Fo$@ -c setup_.c setup_.c : $(SRCDIR)\setup.c translate$E $** > $@ $(OX)\setupuser$O : setupuser_.c setupuser.h $(TCC) /Fo$@ -c setupuser_.c setupuser_.c : $(SRCDIR)\setupuser.c translate$E $** > $@ $(OX)\sha1$O : sha1_.c sha1.h $(TCC) /Fo$@ -c sha1_.c sha1_.c : $(SRCDIR)\sha1.c translate$E $** > $@ |
︙ | ︙ | |||
1602 1603 1604 1605 1606 1607 1608 1609 1610 1611 1612 1613 1614 1615 | translate$E $** > $@ $(OX)\skins$O : skins_.c skins.h $(TCC) /Fo$@ -c skins_.c skins_.c : $(SRCDIR)\skins.c translate$E $** > $@ $(OX)\sqlcmd$O : sqlcmd_.c sqlcmd.h $(TCC) /Fo$@ -c sqlcmd_.c sqlcmd_.c : $(SRCDIR)\sqlcmd.c translate$E $** > $@ | > > > > > > | 1655 1656 1657 1658 1659 1660 1661 1662 1663 1664 1665 1666 1667 1668 1669 1670 1671 1672 1673 1674 | translate$E $** > $@ $(OX)\skins$O : skins_.c skins.h $(TCC) /Fo$@ -c skins_.c skins_.c : $(SRCDIR)\skins.c translate$E $** > $@ $(OX)\smtp$O : smtp_.c smtp.h $(TCC) /Fo$@ -c smtp_.c smtp_.c : $(SRCDIR)\smtp.c translate$E $** > $@ $(OX)\sqlcmd$O : sqlcmd_.c sqlcmd.h $(TCC) /Fo$@ -c sqlcmd_.c sqlcmd_.c : $(SRCDIR)\sqlcmd.c translate$E $** > $@ |
︙ | ︙ | |||
1734 1735 1736 1737 1738 1739 1740 1741 1742 1743 1744 1745 1746 1747 | translate$E $** > $@ $(OX)\vfile$O : vfile_.c vfile.h $(TCC) /Fo$@ -c vfile_.c vfile_.c : $(SRCDIR)\vfile.c translate$E $** > $@ $(OX)\wiki$O : wiki_.c wiki.h $(TCC) /Fo$@ -c wiki_.c wiki_.c : $(SRCDIR)\wiki.c translate$E $** > $@ | > > > > > > | 1793 1794 1795 1796 1797 1798 1799 1800 1801 1802 1803 1804 1805 1806 1807 1808 1809 1810 1811 1812 | translate$E $** > $@ $(OX)\vfile$O : vfile_.c vfile.h $(TCC) /Fo$@ -c vfile_.c vfile_.c : $(SRCDIR)\vfile.c translate$E $** > $@ $(OX)\webmail$O : webmail_.c webmail.h $(TCC) /Fo$@ -c webmail_.c webmail_.c : $(SRCDIR)\webmail.c translate$E $** > $@ $(OX)\wiki$O : wiki_.c wiki.h $(TCC) /Fo$@ -c wiki_.c wiki_.c : $(SRCDIR)\wiki.c translate$E $** > $@ |
︙ | ︙ | |||
1790 1791 1792 1793 1794 1795 1796 1797 1798 1799 1800 1801 1802 1803 1804 1805 1806 1807 1808 1809 1810 1811 1812 1813 1814 1815 1816 1817 1818 1819 1820 1821 1822 1823 1824 1825 1826 1827 1828 1829 1830 1831 1832 1833 1834 1835 1836 | fossil.res : $B\win\fossil.rc $(RCC) /fo $@ $** headers: makeheaders$E page_index.h builtin_data.h default_css.h VERSION.h makeheaders$E add_.c:add.h \ allrepo_.c:allrepo.h \ attach_.c:attach.h \ bag_.c:bag.h \ bisect_.c:bisect.h \ blob_.c:blob.h \ branch_.c:branch.h \ browse_.c:browse.h \ builtin_.c:builtin.h \ bundle_.c:bundle.h \ cache_.c:cache.h \ captcha_.c:captcha.h \ cgi_.c:cgi.h \ checkin_.c:checkin.h \ checkout_.c:checkout.h \ clearsign_.c:clearsign.h \ clone_.c:clone.h \ comformat_.c:comformat.h \ configure_.c:configure.h \ content_.c:content.h \ cookies_.c:cookies.h \ db_.c:db.h \ delta_.c:delta.h \ deltacmd_.c:deltacmd.h \ descendants_.c:descendants.h \ diff_.c:diff.h \ diffcmd_.c:diffcmd.h \ dispatch_.c:dispatch.h \ doc_.c:doc.h \ encode_.c:encode.h \ etag_.c:etag.h \ event_.c:event.h \ export_.c:export.h \ file_.c:file.h \ finfo_.c:finfo.h \ foci_.c:foci.h \ fshell_.c:fshell.h \ fusefs_.c:fusefs.h \ glob_.c:glob.h \ graph_.c:graph.h \ gzip_.c:gzip.h \ hname_.c:hname.h \ http_.c:http.h \ | > > > > | 1855 1856 1857 1858 1859 1860 1861 1862 1863 1864 1865 1866 1867 1868 1869 1870 1871 1872 1873 1874 1875 1876 1877 1878 1879 1880 1881 1882 1883 1884 1885 1886 1887 1888 1889 1890 1891 1892 1893 1894 1895 1896 1897 1898 1899 1900 1901 1902 1903 1904 1905 | fossil.res : $B\win\fossil.rc $(RCC) /fo $@ $** headers: makeheaders$E page_index.h builtin_data.h default_css.h VERSION.h makeheaders$E add_.c:add.h \ allrepo_.c:allrepo.h \ attach_.c:attach.h \ backoffice_.c:backoffice.h \ bag_.c:bag.h \ bisect_.c:bisect.h \ blob_.c:blob.h \ branch_.c:branch.h \ browse_.c:browse.h \ builtin_.c:builtin.h \ bundle_.c:bundle.h \ cache_.c:cache.h \ capabilities_.c:capabilities.h \ captcha_.c:captcha.h \ cgi_.c:cgi.h \ checkin_.c:checkin.h \ checkout_.c:checkout.h \ clearsign_.c:clearsign.h \ clone_.c:clone.h \ comformat_.c:comformat.h \ configure_.c:configure.h \ content_.c:content.h \ cookies_.c:cookies.h \ db_.c:db.h \ delta_.c:delta.h \ deltacmd_.c:deltacmd.h \ descendants_.c:descendants.h \ diff_.c:diff.h \ diffcmd_.c:diffcmd.h \ dispatch_.c:dispatch.h \ doc_.c:doc.h \ email_.c:email.h \ encode_.c:encode.h \ etag_.c:etag.h \ event_.c:event.h \ export_.c:export.h \ file_.c:file.h \ finfo_.c:finfo.h \ foci_.c:foci.h \ forum_.c:forum.h \ fshell_.c:fshell.h \ fusefs_.c:fusefs.h \ glob_.c:glob.h \ graph_.c:graph.h \ gzip_.c:gzip.h \ hname_.c:hname.h \ http_.c:http.h \ |
︙ | ︙ | |||
1879 1880 1881 1882 1883 1884 1885 1886 1887 1888 1889 1890 1891 1892 1893 1894 1895 1896 1897 1898 | regexp_.c:regexp.h \ report_.c:report.h \ rss_.c:rss.h \ schema_.c:schema.h \ search_.c:search.h \ security_audit_.c:security_audit.h \ setup_.c:setup.h \ sha1_.c:sha1.h \ sha1hard_.c:sha1hard.h \ sha3_.c:sha3.h \ shun_.c:shun.h \ sitemap_.c:sitemap.h \ skins_.c:skins.h \ sqlcmd_.c:sqlcmd.h \ stash_.c:stash.h \ stat_.c:stat.h \ statrep_.c:statrep.h \ style_.c:style.h \ sync_.c:sync.h \ tag_.c:tag.h \ | > > | 1948 1949 1950 1951 1952 1953 1954 1955 1956 1957 1958 1959 1960 1961 1962 1963 1964 1965 1966 1967 1968 1969 | regexp_.c:regexp.h \ report_.c:report.h \ rss_.c:rss.h \ schema_.c:schema.h \ search_.c:search.h \ security_audit_.c:security_audit.h \ setup_.c:setup.h \ setupuser_.c:setupuser.h \ sha1_.c:sha1.h \ sha1hard_.c:sha1hard.h \ sha3_.c:sha3.h \ shun_.c:shun.h \ sitemap_.c:sitemap.h \ skins_.c:skins.h \ smtp_.c:smtp.h \ sqlcmd_.c:sqlcmd.h \ stash_.c:stash.h \ stat_.c:stat.h \ statrep_.c:statrep.h \ style_.c:style.h \ sync_.c:sync.h \ tag_.c:tag.h \ |
︙ | ︙ | |||
1907 1908 1909 1910 1911 1912 1913 1914 1915 1916 1917 1918 1919 1920 1921 1922 1923 1924 1925 1926 | update_.c:update.h \ url_.c:url.h \ user_.c:user.h \ utf8_.c:utf8.h \ util_.c:util.h \ verify_.c:verify.h \ vfile_.c:vfile.h \ wiki_.c:wiki.h \ wikiformat_.c:wikiformat.h \ winfile_.c:winfile.h \ winhttp_.c:winhttp.h \ wysiwyg_.c:wysiwyg.h \ xfer_.c:xfer.h \ xfersetup_.c:xfersetup.h \ zip_.c:zip.h \ $(SRCDIR)\sqlite3.h \ $(SRCDIR)\th.h \ VERSION.h \ $(SRCDIR)\cson_amalgamation.h @copy /Y nul: headers | > | 1978 1979 1980 1981 1982 1983 1984 1985 1986 1987 1988 1989 1990 1991 1992 1993 1994 1995 1996 1997 1998 | update_.c:update.h \ url_.c:url.h \ user_.c:user.h \ utf8_.c:utf8.h \ util_.c:util.h \ verify_.c:verify.h \ vfile_.c:vfile.h \ webmail_.c:webmail.h \ wiki_.c:wiki.h \ wikiformat_.c:wikiformat.h \ winfile_.c:winfile.h \ winhttp_.c:winhttp.h \ wysiwyg_.c:wysiwyg.h \ xfer_.c:xfer.h \ xfersetup_.c:xfersetup.h \ zip_.c:zip.h \ $(SRCDIR)\sqlite3.h \ $(SRCDIR)\th.h \ VERSION.h \ $(SRCDIR)\cson_amalgamation.h @copy /Y nul: headers |
Changes to win/include/dirent.h.
︙ | ︙ | |||
295 296 297 298 299 300 301 | static int _wclosedir (_WDIR *dirp); static void rewinddir (DIR* dirp); static void _wrewinddir (_WDIR* dirp); static int scandir (const char *dirname, struct dirent ***namelist, int (*filter)(const struct dirent*), | | | 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 | static int _wclosedir (_WDIR *dirp); static void rewinddir (DIR* dirp); static void _wrewinddir (_WDIR* dirp); static int scandir (const char *dirname, struct dirent ***namelist, int (*filter)(const struct dirent*), int (*compare)(const struct dirent**, const struct dirent**)); static int alphasort (const struct dirent **a, const struct dirent **b); static int versionsort (const struct dirent **a, const struct dirent **b); /* For compatibility with Symbian */ |
︙ | ︙ | |||
363 364 365 366 367 368 369 | dirp->handle = INVALID_HANDLE_VALUE; dirp->patt = NULL; dirp->cached = 0; /* Compute the length of full path plus zero terminator * * Note that on WinRT there's no way to convert relative paths | | | | 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 | dirp->handle = INVALID_HANDLE_VALUE; dirp->patt = NULL; dirp->cached = 0; /* Compute the length of full path plus zero terminator * * Note that on WinRT there's no way to convert relative paths * into absolute paths, so just assume it is an absolute path. */ # if defined(WINAPI_FAMILY) && (WINAPI_FAMILY == WINAPI_FAMILY_PHONE_APP) n = wcslen(dirname); # else n = GetFullPathNameW (dirname, 0, NULL, NULL); # endif /* Allocate room for absolute directory name and search pattern */ dirp->patt = (wchar_t*) malloc (sizeof (wchar_t) * n + 16); if (dirp->patt) { /* * Convert relative directory name to an absolute one. This * allows rewinddir() to function correctly even when current * working directory is changed between opendir() and rewinddir(). * * Note that on WinRT there's no way to convert relative paths * into absolute paths, so just assume it is an absolute path. */ # if defined(WINAPI_FAMILY) && (WINAPI_FAMILY == WINAPI_FAMILY_PHONE_APP) wcsncpy_s(dirp->patt, n+1, dirname, n); # else n = GetFullPathNameW (dirname, n, dirp->patt, NULL); # endif if (n > 0) { |
︙ | ︙ | |||
448 449 450 451 452 453 454 | return dirp; } /* * Read next directory entry. * | | | 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 | return dirp; } /* * Read next directory entry. * * Returns pointer to static directory entry which may be overwritten by * subsequent calls to _wreaddir(). */ static struct _wdirent* _wreaddir( _WDIR *dirp) { struct _wdirent *entry; |
︙ | ︙ | |||
639 640 641 642 643 644 645 | } else if (dirp->handle != INVALID_HANDLE_VALUE) { /* Get the next directory entry from stream */ if (FindNextFileW (dirp->handle, &dirp->data) != FALSE) { /* Got a file */ p = &dirp->data; } else { | | | 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 | } else if (dirp->handle != INVALID_HANDLE_VALUE) { /* Get the next directory entry from stream */ if (FindNextFileW (dirp->handle, &dirp->data) != FALSE) { /* Got a file */ p = &dirp->data; } else { /* The very last entry has been processed or an error occurred */ FindClose (dirp->handle); dirp->handle = INVALID_HANDLE_VALUE; p = NULL; } } else { |
︙ | ︙ | |||
738 739 740 741 742 743 744 | /* Return pointer to statically allocated directory entry */ return entry; } /* * Read next directory entry into called-allocated buffer. * | | | 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 | /* Return pointer to statically allocated directory entry */ return entry; } /* * Read next directory entry into called-allocated buffer. * * Returns zero on success. If the end of directory stream is reached, then * sets result to NULL and returns zero. */ static int readdir_r( DIR *dirp, struct dirent *entry, struct dirent **result) |
︙ | ︙ | |||
800 801 802 803 804 805 806 | entry->d_off = 0; entry->d_reclen = sizeof (struct dirent); } else { /* * Cannot convert file name to multi-byte string so construct | | | 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 | entry->d_off = 0; entry->d_reclen = sizeof (struct dirent); } else { /* * Cannot convert file name to multi-byte string so construct * an erroneous directory entry and return that. Note that * we cannot return NULL as that would stop the processing * of directory entries completely. */ entry->d_name[0] = '?'; entry->d_name[1] = '\0'; entry->d_namlen = 1; entry->d_type = DT_UNKNOWN; |
︙ | ︙ | |||
873 874 875 876 877 878 879 | * Scan directory for entries. */ static int scandir( const char *dirname, struct dirent ***namelist, int (*filter)(const struct dirent*), | | | 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 | * Scan directory for entries. */ static int scandir( const char *dirname, struct dirent ***namelist, int (*filter)(const struct dirent*), int (*compare)(const struct dirent**, const struct dirent**)) { struct dirent **files = NULL; size_t size = 0; size_t allocated = 0; const size_t init_size = 1; DIR *dir = NULL; struct dirent *entry; |
︙ | ︙ | |||
933 934 935 936 937 938 939 | break; } } /* Read directory entry to temporary area */ if (readdir_r (dir, tmp, &entry) == /*OK*/0) { | | | 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 | break; } } /* Read directory entry to temporary area */ if (readdir_r (dir, tmp, &entry) == /*OK*/0) { /* Did we get an entry? */ if (entry != NULL) { int pass; /* Determine whether to include the entry in result */ if (filter) { /* Let the filter function decide */ pass = filter (tmp); |
︙ | ︙ | |||
961 962 963 964 965 966 967 | } else { /* * End of directory stream reached => sort entries and * exit. */ | | > | 961 962 963 964 965 966 967 968 969 970 971 972 973 974 975 976 | } else { /* * End of directory stream reached => sort entries and * exit. */ qsort (files, size, sizeof (void*), (int (*) (const void*, const void*)) compare); break; } } else { /* Error reading directory entry */ result = /*Error*/ -1; |
︙ | ︙ | |||
1056 1057 1058 1059 1060 1061 1062 | if (wcstr && sizeInWords) { if (n >= sizeInWords) { n = sizeInWords - 1; } wcstr[n] = 0; } | | | 1057 1058 1059 1060 1061 1062 1063 1064 1065 1066 1067 1068 1069 1070 1071 | if (wcstr && sizeInWords) { if (n >= sizeInWords) { n = sizeInWords - 1; } wcstr[n] = 0; } /* Length of resulting multi-byte string WITH zero terminator */ if (pReturnValue) { *pReturnValue = n + 1; } /* Success */ error = 0; |
︙ | ︙ |
Added 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. |
Added www/backoffice.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 | Backoffice ========== This is technical documentation about the internal workings of Fossil. Ordinary Fossil users do not need to know about anything covered by this document. The information here is intended for people who want to enhance or extend the Fossil code, or who just want a deeper understanding of the internal workings of Fossil. What Is The Backoffice ---------------------- The backoffice is a mechanism used by a [Fossil server](/doc/trunk/www/server.wiki) to do low-priority background work that is not directly related to the user interface. Here are some examples of the kinds of work that backoffice performs: 1. Sending email alerts and notifications 2. Sending out daily digests of email notifications 3. Other background email handling chores 4. Automatic syncing of peer repositories 5. Repository maintenance and optimization (As of 2018-08-07, only items 1 and 2 have actually been implemented.) The idea is that the backoffice handles behind-the-scenes work that does not have tight latency requirements. When Backoffice Runs -------------------- A backoffice process is usually launched automatically by a webpage request. After each webpage is generated, Fossil checks to see if any backoffice work needs to be done. If there is work to do, and no other process is already assigned to do the work, then a new backoffice process is started to do the work. This happens for every webpage, regardless of how that webpage is launched, and regardless of the purpose of the webpage. This also happens on the server for "[fossil sync](/help?cmd=sync)" and [fossil clone](/help?cmd=clone)" commands which are implemented as web requests - albeit requests that the human user never sees. Web requests can arrive at the Fossil server via direct TCP/IP (for example when Fossil is started using commands like "[fossil server](/help?cmd=server)") or via [CGI](/doc/trunk/www/server.wiki) or [SCGI](/doc/trunk/www/scgi.wiki) or via SSH. A backoffice process might be started regardless of the origin of the request. The backoffice is not a daemon. Each backoffice process runs for a short while and then exits. This helps keep Fossil easy to manage, since there are no daemons to start and stop. To upgrade Fossil to a new version, you simply replace the older "fossil" executable with the newer one, and the backoffice processes will (within a minute or so) start using the new one. (Upgrading the executable on Windows is more complicated, since on Windows it is not possible to replace an executable file that is in active use. But Windows users probably already know this.) The backoffice is serialized and rate limited. No more than a single backoffice process will be running at once, and backoffice runs will not occur more frequently than once every 60 seconds. If a Fossil server is idle, then no backoffice processes will be running. That means there are no extra processes sitting around taking up memory and process table slots for seldom accessed repositories. The backoffice is an on-demand system. A busy repository will usually have a backoffice running at all times. But an infrequently accessed repository will only have backoffice processes running for a minute or two following the most recent access. Manually Running The Backoffice ------------------------------- The automatic backoffice runs are sufficient for most installations. However, the daily digest of email notifications is handled by the backoffice. If a Fossil server can sometimes go more than a day without being accessed, then the automatic backoffice will never run, and the daily digest might not go out until somebody does visit a webpage. If this is a problem, an adminstrator can set up a cron job to periodically run: > fossil backoffice -R _REPOSITORY_ That command will cause backoffice processing to occur immediately. Note that this is almost never necessary for an internet-facing Fossil repository, since most repositories will get multiple accesses per day from random robots, which will be sufficient to kick off the daily digest emails. And even for a private server, if there is very little traffic, then the daily digests are probably a no-op anyhow and won't be missed. How Backoffice Is Implemented ----------------------------- The backoffice is implemented by the "backoffice.c" source file. Serialization and rate limiting is handled by a single entry in the repository database CONFIG table named "backoffice". This entry is called "the lease". The value of the lease is a text string representing four integers, which are respectively: 1. The process id of the "current" backoffice process 2. The lease expiration time of the current backoffice process 3. The process id of the "next" backoffice process 4. The lease expiration time for the next backoffice process Times are expressed in seconds since 1970. A process id of zero means "no process". Sometimes the process id will be non-zero even if there is no corresponding process. Fossil knows how to figure out whether or not a process still exists. You can print out a decoded copy of the current backoffice lease using this command: > fossil test-backoffice-lease -R _REPOSITORY_ If a system has been idle for a long time, then there will be no backoffice processes. (Either the process id entries in the lease will be zero, or there will exist no process associated with the process id.) When a new web request comes in, the system sees that no backoffice process is active and so it kicks off a separate process to run backoffice. The new backoffice process becomes the "current" process. It sets a lease expiration time for itself to be 60 seconds in the future. Then it does the backoffice processing and exits. Note that usually the backoffice process will exit long before its lease expires. That is ok. The lease is there to limit the rate at which backoffice processes run. If a new backoffice process starts up and sees that the "current" lease has yet to expire, the new process makes itself the "next" backoffice process and sets its expiration time to be 60 seconds past the expiration time of the "current" backoffice process. The "next" process then puts itself to sleep until the "current" lease expires. After the "current" lease expires and the "current" process has itself exited, then the "next" process promotes itself to the new "current" process. It sets the current lease expiration to be 60 seconds in the future, runs whatever backoffice work is needed, then exits. If a new backoffice process starts up and finds that there is already a "current" lease and a "next" process, it exits without doing anything. This should happen only rarely, since the lease information is checked prior to spawning the backoffice process, so a conflict will only happen in a race. Because the "backoffice" entry of the CONFIG table is in the repository database, access to the lease is serialized. The lease prevents more than one backoffice process from running at a time. It prevents backoffice processes from running more frequently than once every 60 seconds. And, it guarantees (assuming processes are not killed out-of-band) that every web request will be followed within 60 seconds by a backoffice run. Debugging The Backoffice ------------------------ The backoffice should "just work". It should not require administrator attention. However, if you suspect that something is not working right, there are some debugging aids. We have already mentioned the command that shows the backoffice lease for a repository: > fossil test-backoffice-lease -R _REPOSITORY_ Running that command every few seconds should show what is going on with backoffice processing in a particular repository. There are also two settings that control backoffice behavior. The "backoffice-nodelay" setting prevents the "next" process from taking a lease and sleeping. If "backoffice-nodelay" is set, that causes all backoffice processes to exit either immediately or after doing whatever backoffice works needs to be done. If something is going wrong and backoffice leases are causing delays in webpage processing, then setting "backoffice-nodelay" to true can work around the problem until the bug can be fixed. The "backoffice-logfile" setting is the name of a log file onto which is appended a short message everything a backoffice process actually starts to do the backoffice work. This log file can be used to verify that backoffice really is running, if there is any doubt. Most installations should leave "backoffice-nodelay" off and leave "backoffice-logfile" unset or set to an empty string. |
Changes to www/build.wiki.
︙ | ︙ | |||
134 135 136 137 138 139 140 | file "<b>win\buildmsvc.bat</b>" may be used and it will attempt to detect and use the latest installed version of MSVC.<br><br>To enable the optional <a href="https://www.openssl.org/">OpenSSL</a> support, first <a href="https://www.openssl.org/source/">download the official source code for OpenSSL</a> and extract it to an appropriately named "<b>openssl-X.Y.ZA</b>" subdirectory within the local [/tree?ci=trunk&name=compat | compat] directory (e.g. | | | 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 | file "<b>win\buildmsvc.bat</b>" may be used and it will attempt to detect and use the latest installed version of MSVC.<br><br>To enable the optional <a href="https://www.openssl.org/">OpenSSL</a> support, first <a href="https://www.openssl.org/source/">download the official source code for OpenSSL</a> and extract it to an appropriately named "<b>openssl-X.Y.ZA</b>" subdirectory within the local [/tree?ci=trunk&name=compat | compat] directory (e.g. "<b>compat/openssl-1.1.1-pre9</b>"), then make sure that some recent <a href="http://www.perl.org/">Perl</a> binaries are installed locally, and finally run one of the following commands: <blockquote><pre> nmake /f Makefile.msc FOSSIL_ENABLE_SSL=1 FOSSIL_BUILD_SSL=1 PERLDIR=C:\full\path\to\Perl\bin </pre></blockquote> <blockquote><pre> buildmsvc.bat FOSSIL_ENABLE_SSL=1 FOSSIL_BUILD_SSL=1 PERLDIR=C:\full\path\to\Perl\bin |
︙ | ︙ |
Changes to www/changes.wiki.
1 2 3 4 5 6 7 8 9 | <title>Change Log</title> <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 | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 | <title>Change Log</title> <a name='v2_7'></a> <h2>Changes for Version 2.7 (2018-??-??)</h2> * Add support for [./alerts.md|email alerts]. * Add support for forums. * Added new user capabilities letters needed to support alerts and forum. Formerly, user capabilities were letters from [a-z], but with the enhancements, the supply of lower case letters was exhausted. User capabilities are now letters in [a-zA-Z0-9]. * Added 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 <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 |
︙ | ︙ |
Changes to www/customskin.md.
︙ | ︙ | |||
89 90 91 92 93 94 95 | When cloning a repository, the skin of new repository is initialized to the skin of the repository from which it was cloned. Header And Footer Processing ---------------------------- | | | 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 | When cloning a repository, the skin of new repository is initialized to the skin of the repository from which it was cloned. Header And Footer Processing ---------------------------- The header.txt and footer.txt files of a skin are merely the HTML text of the header and footer. Except, before being prepended and appended to the content, the header and footer text are run through a [TH1 interpreter](./th1.md) that might adjust the text as follows: * All text within <th1>...</th1> is elided from the output and that text is instead run as a TH1 script. That TH1 script has the opportunity to insert new text in place of itself, |
︙ | ︙ |
Added 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> → 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 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> → 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> → 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> → 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> → 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/env-opts.md.
︙ | ︙ | |||
117 118 119 120 121 122 123 124 125 126 127 128 129 130 | `FOSSIL_HOME`, `LOCALAPPDATA` (Windows), `APPDATA` (Windows), `HOMEDRIVE` and `HOMEPATH` (Windows, used together), and `HOME` is used as the location of the `~/.fossil` file. `EDITOR`: Name the editor to use for check-in and stash comments. Overridden by the local or global `editor` setting or the `VISUAL` environment variable. `FOSSIL_FORCE_TICKET_MODERATION`: If set, *ALL* changes for tickets will be required to go through moderation (even those performed by the local interactive user via the command line). This can be useful for local (or remote) testing of the moderation subsystem and its impact on the contents and status of tickets. | > > > > | 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 | `FOSSIL_HOME`, `LOCALAPPDATA` (Windows), `APPDATA` (Windows), `HOMEDRIVE` and `HOMEPATH` (Windows, used together), and `HOME` is used as the location of the `~/.fossil` file. `EDITOR`: Name the editor to use for check-in and stash comments. Overridden by the local or global `editor` setting or the `VISUAL` environment variable. `FOSSIL_BREAK`: If set, an opportunity will be created to attach a debugger to the Fossil process prior to any significant work being performed. `FOSSIL_FORCE_TICKET_MODERATION`: If set, *ALL* changes for tickets will be required to go through moderation (even those performed by the local interactive user via the command line). This can be useful for local (or remote) testing of the moderation subsystem and its impact on the contents and status of tickets. |
︙ | ︙ |
Changes to www/fileformat.wiki.
︙ | ︙ | |||
69 70 71 72 73 74 75 76 77 | <li> [#manifest | Manifests] </li> <li> [#cluster | Clusters] </li> <li> [#ctrl | Control Artifacts] </li> <li> [#wikichng | Wiki Pages] </li> <li> [#tktchng | Ticket Changes] </li> <li> [#attachment | Attachments] </li> <li> [#event | TechNotes] </li> </ul> | > | | 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 | <li> [#manifest | Manifests] </li> <li> [#cluster | Clusters] </li> <li> [#ctrl | Control Artifacts] </li> <li> [#wikichng | Wiki Pages] </li> <li> [#tktchng | Ticket Changes] </li> <li> [#attachment | Attachments] </li> <li> [#event | TechNotes] </li> <li> [#forum | Forum Posts] </li> </ul> These eight structural artifact types are described in subsections below. Structural artifacts are ASCII text. The artifact may be PGP clearsigned. After removal of the PGP clearsign header and suffix (if any) a structural artifact consists of one or more "cards" separated by a single newline (ASCII: 0x0a) character. Each card begins with a single character "card type". Zero or more arguments may follow the card type. All arguments are separated from each other |
︙ | ︙ | |||
492 493 494 495 496 497 498 | where the technote is displayed) and a unique identifier for the technote. When there are multiple artifacts with the same technote-id, the one with the most recent D card is the only one used. The technote-id must be a 40-character lower-case hexadecimal string. The optional N card specifies the mimetype of the text of the technote that is contained in the W card. If the N card is omitted, then the | | | 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 | where the technote is displayed) and a unique identifier for the technote. When there are multiple artifacts with the same technote-id, the one with the most recent D card is the only one used. The technote-id must be a 40-character lower-case hexadecimal string. The optional N card specifies the mimetype of the text of the technote that is contained in the W card. If the N card is omitted, then the W card text mimetype is assumed to be text/x-fossil-wiki, which is the Fossil wiki format. The optional P card specifies a prior technote with the same technote-id from which the current technote is an edit. The P card is a hint to the system that it might be space efficient to store one technote as a delta of the other. |
︙ | ︙ | |||
523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 | A single W card provides wiki text for the document associated with the technote. The format of the W card is exactly the same as for a [#wikichng | wiki artifact]. The Z card is the required checksum over the rest of the artifact. <a name="summary"></a> <h2>3.0 Card Summary</h2> The following table summarizes the various kinds of cards that appear on Fossil artifacts. A blank entry means that combination of card and artifact is not legal. A number or range of numbers indicates the number of times a card may (or must) appear in the corresponding artifact type. e.g. a value of 1 indicates a required unique card and 1+ indicates that one or more such cards are required. <table border=1 width="100%"> <tr> <th rowspan=2 valign=bottom>Card Format</th> | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | > > | > < > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 | A single W card provides wiki text for the document associated with the technote. The format of the W card is exactly the same as for a [#wikichng | wiki artifact]. The Z card is the required checksum over the rest of the artifact. <a name="forum"></a> <h3>2.8 Forum Posts</h3> Forum posts are intended as a mechanism for users and developers to discuss a project. Forum posts are like messages on a mailing list. The following cards are allowed on an forum post artifact: <blockquote> <b>D</b> <i>time-and-date-stamp</i><br /> <b>G</b> <i>thread-root</i><br /> <b>H</b> <i>thread-title</i><br /> <b>I</b> <i>in-reply-to</i><br /> <b>N</b> <i>mimetype</i><br /> <b>P</b> <i>parent-artifact-id</i><br /> <b>U</b> <i>user-name</i><br /> <b>W</b> <i>size</i> <b>\n</b> <i>text</i> <b>\n</b><br /> <b>Z</b> <i>checksum</i> </blockquote> Every forum post must have either one I card and one G card or one H card. Forum posts are organized into topic threads. The initial post for a thread (the root post) has an H card giving the title or subject for that thread. The argument to the H card is a string in the same format as a comment string in a C card. All follow-up posts have an I card that indicates which prior post in the same thread the current forum post is replying to, and a G card specifying the root post for the entire thread. The argument to G and I cards is the artifact hash for the prior forum post to which the card refers. In theory, it is sufficient for follow-up posts to have only an I card, since the G card value could be computed by following a chain of I cards. However, the G card is required in order to associate the artifact with a forum thread in the case where an intermediate artifact in the I card chain is shunned or otherwise becomes unreadable. A single D card is required to give the date and time when the forum post was created. The optional N card specifies the mimetype of the text of the technote that is contained in the W card. If the N card is omitted, then the W card text mimetype is assumed to be text/x-fossil-wiki, which is the Fossil wiki format. The optional P card specifies a prior forum post for which this forum post is an edit. For display purposes, only the child post is shown, though the historical post is retained as a record. If P cards are used and there exist multiple versions of the same forum post, then I cards for other artifacts refer to whichever version of the post was current at the time the reply was made, but G cards refer to the initial, unedited root post for the thread. Thus, following the chain of I cards back to the root of the thread may land on a different post than the one given in the G card. However, following the chain of I cards back to the thread root, then following P cards back to the initial version of the thread root must give the same artifact as is provided by the G card, otherwise the artifact containing the G card is considered invalid and should be ignored. In general, P cards may contain multiple arguments, indicating a merge. But since forum posts cannot be merged, the P card of a forum post may only contain a single argument. The U card gives name of the user who entered the forum post. A single W card provides wiki text for the forum post. The format of the W card is exactly the same as for a [#wikichng | wiki artifact]. The Z card is the required checksum over the rest of the artifact. <a name="summary"></a> <h2>3.0 Card Summary</h2> The following table summarizes the various kinds of cards that appear on Fossil artifacts. A blank entry means that combination of card and artifact is not legal. A number or range of numbers indicates the number of times a card may (or must) appear in the corresponding artifact type. e.g. a value of 1 indicates a required unique card and 1+ indicates that one or more such cards are required. <table border=1 width="100%"> <tr> <th rowspan=2 valign=bottom>Card Format</th> <th colspan=8>Used By</th> </tr> <tr> <th>Manifest</th> <th>Cluster</th> <th>Control</th> <th>Wiki</th> <th>Ticket</th> <th>Attachment</th> <th>Technote</th> <th>Forum</th> </tr> <tr> <td><b>A</b> <i>filename</i> <i>target</i> ?<i>source</i>?</td> <td> </td> <td> </td> <td> </td> <td> </td> <td> </td> <td align=center><b>1</b></td> <td> </td> <td> </td> </tr> <tr> <td><b>B</b> <i>baseline</i></td> <td align=center><b>0-1</b></td> <td> </td> <td> </td> <td> </td> <td> </td> <td> </td> <td> </td> <td> </td> </tr> <tr> <td><b>C</b> <i>comment-text</i></td> <td align=center><b>1</b></td> <td> </td> <td> </td> <td> </td> <td> </td> <td align=center><b>0-1</b></td> <td align=center><b>0-1</b></td> <td> </td> </tr> <tr> <td><b>D</b> <i>date-time-stamp</i></td> <td align=center><b>1</b></td> <td> </td> <td align=center><b>1</b></td> <td align=center><b>1</b></td> <td align=center><b>1</b></td> <td align=center><b>1</b></td> <td align=center><b>1</b></td> <td align=center><b>1</b></td> </tr> <tr> <td><b>E</b> <i>technote-time technote-id</i></td> <td> </td> <td> </td> <td> </td> <td> </td> <td> </td> <td> </td> <td align=center><b>1</b></td> <td> </td> </tr> <tr> <td><b>F</b> <i>filename</i> ?<i>uuid</i>? ?<i>permissions</i>? ?<i>oldname</i>?</td> <td align=center><b>0+</b></td> <td> </td> <td> </td> <td> </td> <td> </td> <td> </td> <td> </td> <td> </td> </tr> <tr> <td><b>G</b> <i>thread-root</i></td> <td> </td> <td> </td> <td> </td> <td> </td> <td> </td> <td> </td> <td> </td> <td align=center><b>0-1</b></td> </tr> <tr> <td><b>H</b> <i>thread-title</i></td> <td> </td> <td> </td> <td> </td> <td> </td> <td> </td> <td> </td> <td> </td> <td align=center><b>0-1</b></td> </tr> <tr> <td><b>I</b> <i>in-reply-to</i></td> <td> </td> <td> </td> <td> </td> <td> </td> <td> </td> <td> </td> <td> </td> <td align=center><b>0-1</b></td> </tr> <tr> <td><b>J</b> <i>name</i> ?<i>value</i>?</td> <td> </td> <td> </td> <td> </td> <td> </td> <td align=center><b>1+</b></td> <td> </td> <td> </td> <td> </td> </tr> <tr> <td><b>K</b> <i>ticket-uuid</i></td> <td> </td> <td> </td> <td> </td> <td> </td> <td align=center><b>1</b></td> <td> </td> <td> </td> <td> </td> </tr> <tr> <td><b>L</b> <i>wiki-title</i></td> <td> </td> <td> </td> <td> </td> <td align=center><b>1</b></td> <td> </td> <td> </td> <td> </td> <td> </td> </tr> <tr> <td><b>M</b> <i>uuid</i></td> <td> </td> <td align=center><b>1+</b></td> <td> </td> <td> </td> <td> </td> <td> </td> <td> </td> <td> </td> </tr> <tr> <td><b>N</b> <i>mimetype</i></td> <td align=center><b>0-1</b></td> <td> </td> <td> </td> <td align=center><b>0-1</b></td> <td> </td> <td align=center><b>0-1</b></td> <td align=center><b>0-1</b></td> <td align=center><b>0-1</b></td> </tr> <tr> <td><b>P</b> <i>uuid ...</i></td> <td align=center><b>0-1</b></td> <td> </td> <td> </td> <td align=center><b>0-1</b></td> <td> </td> <td> </td> <td align=center><b>0-1</b></td> <td align=center><b>0-1</b></td> </tr> <tr> <td><b>Q</b> (<b>+</b>|<b>-</b>)<i>uuid</i> ?<i>uuid</i>?</td> <td align=center><b>0+</b></td> <td> </td> <td> </td> <td> </td> <td> </td> <td> </td> <td> </td> <td> </td> </tr> <tr> <td><b>R</b> <i>md5sum</i></td> <td align=center><b>0-1</b></td> <td> </td> <td> </td> <td> </td> <td> </td> <td> </td> <td> </td> <td> </td> <tr> <td><b>T</b> (<b>+</b>|<b>*</b>|<b>-</b>)<i>tagname</i> <i>uuid</i> ?<i>value</i>?</td> <td align=center><b>0+</b></td> <td> </td> <td align=center><b>1+</b></td> <td> </td> <td> </td> <td> </td> <td align=center><b>0+</b></td> <td> </td> </tr> <tr> <td><b>U</b> <i>username</i></td> <td align=center><b>1</b></td> <td> </td> <td align=center><b>1</b></td> <td align=center><b>1</b></td> <td align=center><b>1</b></td> <td align=center><b>0-1</b></td> <td align=center><b>0-1</b></td> <td align=center><b>1</b></td> </tr> <tr> <td><b>W</b> <i>size</i> <b>\n</b> <i>text</i> <b>\n</b></td> <td> </td> <td> </td> <td> </td> <td align=center><b>1</b></td> <td> </td> <td> </td> <td align=center><b>1</b></td> <td align=center><b>1</b></td> </tr> <tr> <td><b>Z</b> <i>md5sum</i></td> <td align=center><b>1</b></td> <td align=center><b>1</b></td> <td align=center><b>1</b></td> <td align=center><b>1</b></td> <td align=center><b>1</b></td> <td align=center><b>1</b></td> <td align=center><b>1</b></td> <td align=center><b>1</b></td> |
︙ | ︙ |
Added www/forum.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 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 | <title>Fossil Forums</title> <h2>Introduction</h2> As of Fossil 2.7, Fossil includes a built-in discussion forum feature. Any project complex enough to benefit from being managed by Fossil and which has more than one user can probably also benefit from having a discussion forum. Even if your project has a discussion forum already, there are many benefits to using Fossil's built-in forum feature, some of which you cannot get by using third-party alternatives: * <b>Easy to Administer:</b> Third-party discussion forum and mailing list software tends to be difficult to install, set up, and administer. The Fossil forum feature aims to be as close to zero-configuration as is practical. * <b>Malefactor Resistant:</b> Because Fossil accepts forum posts only via the web UI, it is inherently [./antibot.wiki | protected against bots]. * <b>Distributed and Tamper-Proof:</b> Posts are stored in the Fossil repository using the same [./fileformat.wiki | block chain technology] that Fossil uses to store your check-ins, wiki documents, etc. Posts sync to cloned repositories in a tamper-proof fashion. * <b>Space Efficient:</b> Because of Fossil's [./delta_format.wiki | delta compression technology], discussions add little to the size of a cloned repository. Ten years of the SQLite project's discussions — averaging about 2 dozen posts per day — compress down to [https://fossil-scm.org/forum/forumpost/9b6f3f36bdb | just 35 MB of space] in a Fossil forum repository. * <b>Built-in Full-Text Search:</b> Fossil forums use [https://sqlite.org/fts3.html | SQLite's powerful FTS4 engine] to handle searches. If your project currently uses a mailing list for discussions, this means you are no longer reliant upon third-party mailing list archive services to provide a useful search engine for your discussions. If you are running a private Fossil repository, you may not even have the <em>option</em> of delegating this useful service to a third-party; Fossil provides this service out of the box. * <b>One Result Per Matching Post:</b> When you search the forum archives via the Fossil web interface, you get only one result for each matching post. When you search for project information via a standard web search engine, you might get a result from the project site's own mail archive plus one from Nabble, one from Gmane, one from The Mail Archive... * <b>Search Off-Line:</b> Because Fossil is a [./concepts.wiki | distributed version control system], project members can search your forum archive while disconnected from the network where the central Fossil instance runs. Your past discussions are potentially just as valuable as a wiki document or checkin comment: there is no good reason why you should have to wait to get back on the Internet or back to the office before you can search for past posts. * <b>Contribute Off-Line:</b> Fossil forum posts work like any other insertion into the repository, so a user can create new threads and reply to existing ones while off-line, then sync their contributions to the server they cloned from when back on-line. Yes, you can post to the forum from inside a tent, miles from the nearest wifi router or cellular data tower. * <b>Interlink with Other Fossil-Managed Artifacts:</b> Because forum posts are normal Fossil artifacts, you can interlink them with other Fossil artifacts using short internal links: link to forum threads from a [./tickets.wiki | ticket], link to a wiki document from a forum post, etc. * <b>Durable Links:</b> Once you create a valid internal artifact link in Fossil, it <em>remains</em> valid, durably. With third-party forum software and mailing list search engines, your links are only valid until the third-party component changes its URL scheme or disappears from the web. * <b>Role-Based Access Control:</b> The forum uses the same [https://en.wikipedia.org/wiki/Role-based_access_control | RBAC system] that Fossil uses to control all other repository accesses. The Fossil forum feature simply adds several new fine-grained capability bits to the existing system. * <b>Enduring, Open File Format:</b> Since Fossil has an [./fileformat.wiki | open and well-documented file format], your discussion archives are truly that: <em>archives</em>. You are no longer dependent on the lifetime and business model of a third-party piece of software or service. Should you choose to stop using Fossil, you can easily extract your discussion traffic for transfer to another system. * <b>Lightweight Markup:</b> Posts can be marked up using Fossil's existing [/md_rules | Markdown] and [/wiki_rules | Wiki] markup processors. No longer must you choose between two bad options: to 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 for normal Fossil repository logins. There are several dedicated forum-related capability bits you can grant a user: * <b>Read Forum</b> (<tt>2</tt>): The user may read forum posts. * <b>Write Forum</b> (<tt>3</tt>): The user may create new forum threads, reply to existing threads, and edit their own posts. New posts are held for moderation, and they are marked to prevent them from being included in clone and sync operations. * <b>WriteTrusted Forum</b> (<tt>4</tt>): Same as <b>Write Forum</b> except that forum updates bypass the [#moderation | moderation and private artifact restrictions]. * <b>Moderate Forum</b> (<tt>5</tt>): User gets buttons on posts which allow them to either reject or approve posts held for moderation. User also gets access to a page (<tt>/modreq</tt>) 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 → Access and enable the "Allow users to register themselves" setting. You may also wish to give users in the <tt>anonymous</tt> category the Read Forum (2) and Write Forum (3) capabilities: this allows people to post without creating an account simply by solving [./antibot.wiki | a simple CAPTCHA]. For a private repository, you probably won't want to give the <tt>anonymous</tt> user any forum access, but you may wish to give the Read Forum capability (2) to users in the <tt>reader</tt> category. 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. You must give at least one user or user category the Email Alerts capability (7), else the only people able to sign themselves up for email notifications are those with the Setup or Admin capability. Those users could sign others up via Admin → Notification, but you probably want to give this capability to one of the user categories. Give it to <tt>nobody</tt> if you want anyone to sign up without any restrictions. Give it to <tt>anonymous</tt> if you want the user to solve a simple CAPTCHA before signing up. 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 these capabilities to every user on your Fossil repository.) By following this advice, you should not need to tediously add capabilities to individual accounts except in atypical cases, such as to grant the Moderate Forum capability (5) to an uncommonly highly-trusted user. <h3 id="skin">Skin Setup</h3> If you create a new Fossil repository with version 2.7 or newer, its default skin is already set up correctly for typical forum configurations. Those upgrading existing repositories will need to edit the Header part of their existing Fossil skin in Admin → Skins, adding something like this 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 you're using the stock skin or something sufficiently close, the changes may work with your existing skin as-is. Otherwise, you might need to adjust some things, such as the background color used for the selected forum post: <verbatim> div.forumSel { background-color: rgba(0, 0, 0, 0.05); } </verbatim> That overrides the default — a hard-coded light cyan — with a 95% transparent black overlay instead, which simply darkens your skin's normal background color underneath the selected post. That should work with almost any background color except for very dark background colors. For dark skins, an inverse of the above trick will work better: <verbatim> div.forumSel { background-color: rgba(255, 255, 255, 0.05); } </verbatim> That overlays the background with 5% white to lighten it slightly. <h3 id="search">Enable Forum Search</h3> One of the underlying assumptions of the forum feature is that you will want to be able to search the forum archives, so the <tt>/forum</tt> page always includes a search box. Since that depends on search being enabled on the Fossil repository, Fossil warns that search is disabled until you go into Admin → Search and enable the "Search Forum" setting. You may want to enable some of the other Fossil search features while you're in there. All of this does come at some CPU and I/O cost, which is why it's disabled by default. <h3 id="sso">Single Sign-On</h3> If you choose to host your discussion forums within the same repository as your project's other Fossil-managed content, you inherently have a single sign-on system. Contrast third-party mailing list and forum software where you either end up with two separate user tables and permission sets, or you must go to significant effort to integrate the two login systems. You may instead choose to host your forums in a Fossil repository separate from your project's main Fossil repository. A good reason to do this is that you have a public project where very few of those participating in the forum have special capability bits for project assets managed by Fossil, so you wish to segregate the two user sets. Yet, what of the users who will have logins on both repositories? Some users will be trusted with access to the project's main Fossil repository, and these users will probably also participate in the 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 → 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 → 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:" * create a new post * reply to an existing post * edit a post or reply When a person with the normal <b>Write Forum</b> capability (<tt>3</tt>) updates the forum, Fossil saves the update in its block chain, but this update is impermanent because of two other table updates made at the same time: <ol> <li>Fossil saves the update artifact's ID in its <tt>private</tt> table, preventing Fossil from sending such artifacts to any of the repository's clones. (This is the same mechanism behind [./private.wiki | private branches].)</li> <li>Fossil also adds a reference to that artifact in the <tt>modreq</tt> table, which backs the moderation feature. This is what causes Fossil to leave out the Reply button when rendering that post's HTML in the forum's web interface.</li> </ol> When a moderator approves an update, Fossil deletes these table entries, making the update semi-permanent. This changes how Fossil renders the HTML for that update. It also means the artifact will now sync to clones, if the sync is done by a user with <b>Check-Out</b> capability (<tt>o</tt>). When a forum user edits a moderator-approved artifact, what actually happens under the hood is that Fossil writes another artifact to the repository which refers to the original version as its parent, causing Fossil UI to present the new version instead of the original. The original version remains in the repository, just as with historical checkins. The parent must remain in the repository for referential integrity purposes. When you "Delete" a moderator-approved post or reply through Fossil UI, it's actually an edit with blank replacement content. The only way to truly delete such artifacts is through [./shunning.wiki | shunning]. When a user with <b>WriteTrusted Forum</b> capability (<tt>4</tt>) updates the forum, it happens in the same way except that Fossil skips the <tt>private</tt> and <tt>modreq</tt> table insertions. When a moderator rejects an update, that artifact is unceremoniously removed from the tip of the block chain. This is safe because Fossil prevents replies to a reply or post awaiting moderator approval, so referential integrity cannot be harmed. Rejecting an edit is even safer, since the original post remains behind, so that replies continue to refer to that original post. <h2 id="mod-user">Using the Moderation Feature</h2> Having described all of the work that Fossil performs under the hood on behalf of its users, we can now give the short list of work left for the repository's administrators and moderators: <ol> <li>Add the <b>Moderate Forum</b> capability (<tt>5</tt>) to any of your users who should have this ability. You don't need to do this for any user with Setup (<tt>s</tt>) or Admin (<tt>a</tt>) capability; it's [http://fossil-scm.org/index.html/artifact/b16221ffb736caa2?ln=1246-1257 | already included].</li> <li>When someone updates the forum, an entry will appear in the timeline if the type filter is set to "Forum" or "Any Type". If that user has only the <b>Write Forum</b> capability (<tt>3</tt>), any other user with the <b>Moderate Forum</b> capability (<tt>5</tt>) will see a conditional link appear at the top of the main forum page: "Moderation Requests". Clicking this takes the moderator to the <tt>/modreq</tt> page. A moderator may wish to keep the main forum page open in a browser tab, reloading it occasionally to see when the "Moderation Requests" link reappears.</li> <li>A moderator viewing an update pending moderation sees two buttons at the bottom, "Approve" and "Reject" in place of the "Delete" button that the post's creator sees. Beware that both actions are durable and have no undo. Be careful!</li> </ol> |
Changes to www/fossil-v-git.wiki.
1 2 3 4 5 6 7 8 9 10 11 12 | <title>Fossil Versus Git</title> <h2>1.0 Don't Stress!</h2> If you start out using one DVCS and later decide you like the other better, you can easily [./inout.wiki | move your content]¹. Fossil and [http://git-scm.com | Git] are very similar in many respects, but they also have important differences. See the table below for a high-level summary and the text that follows for more details. | | | | | > | > > < < | | 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 | <title>Fossil Versus Git</title> <h2>1.0 Don't Stress!</h2> If you start out using one DVCS and later decide you like the other better, you can easily [./inout.wiki | move your content]¹. Fossil and [http://git-scm.com | Git] are very similar in many respects, but they also have important differences. See the table below for a high-level summary and the text that follows for more details. Keep in mind that you are reading this on a Fossil website, and though we try to be fair, the information here might be biased in favor of Fossil. Ask around for second opinions from people who have used <em>both</em> Fossil and Git. ¹<small><i>Git does not support wiki, tickets, or tech-notes, so those elements will not transfer when exporting from Fossil to Git.</i></small> <h2>2.0 Differences Between Fossil And Git</h2> Differences between Fossil and Git are summarized by the following table, with further description in the text that follows. <blockquote><table border=1 cellpadding=5 align=center> <tr><th width="50%">GIT</th><th width="50%">FOSSIL</th></tr> <tr><td>File versioning only</td> <td>Versioning, Tickets, Wiki, and Technotes</td></tr> <tr><td>Ad-hoc, pile-of-files key/value database</td> <td>Relational SQL database</td></tr> <tr><td>Bazaar-style development</td><td>Cathedral-style development</td></tr> <tr><td>Designed for Linux development</td> <td>Designed for SQLite development</td></tr> <tr><td>Lots of little tools</td><td>Stand-alone executable</td></tr> <tr><td>One check-out per repository</td> <td>Many check-outs per repository</td></tr> <tr><td>Remembers what you should have done</td> <td>Remembers what you actually did</td></tr> <tr><td>GPL</td><td>BSD</td></tr> </table></blockquote> <h3>2.1 Feature Set</h3> Git provides file versioning services only, whereas Fossil adds integrated [./wikitheory.wiki | wiki], [./bugtheory.wiki | ticketing & bug tracking], [./embeddeddoc.wiki | embedded documentation], and [./event.wiki | Technical notes]. These additional capabilities are available for Git as 3rd-party and/or |
︙ | ︙ | |||
61 62 63 64 65 66 67 | the stand-alone Fossil executable together with a 2-line CGI script suffice to instantiate a full-featured developer website. To accomplish the same using Git requires locating, installing, configuring, integrating, and managing a wide assortment of separate tools. Standing up a developer website using Fossil can be done in minutes, whereas doing the same using Git requires hours or days. | | | 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 | the stand-alone Fossil executable together with a 2-line CGI script suffice to instantiate a full-featured developer website. To accomplish the same using Git requires locating, installing, configuring, integrating, and managing a wide assortment of separate tools. Standing up a developer website using Fossil can be done in minutes, whereas doing the same using Git requires hours or days. <h3>2.2 Database</h3> The baseline data structures for Fossil and Git are the same (modulo formatting details). Both systems store check-ins as immutable objects referencing their immediate ancestors and named by a cryptographic hash of the check-in content. The difference is that Git stores its objects as individual files |
︙ | ︙ | |||
105 106 107 108 109 110 111 | The ease with which check-ins can be located and queried in Fossil has resulted in a huge variety of reports and status screens ([./webpage-ex.md|examples]) that show project state in ways that help developers maintain enhanced awareness and comprehension and avoid errors. | | | 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 | The ease with which check-ins can be located and queried in Fossil has resulted in a huge variety of reports and status screens ([./webpage-ex.md|examples]) that show project state in ways that help developers maintain enhanced awareness and comprehension and avoid errors. <h3>2.3 Cathedral vs. Bazaar</h3> Fossil and Git promote different development styles. Git promotes a "bazaar" development style in which numerous anonymous developers make small and sometimes haphazard contributions. Fossil promotes a "cathedral" development model in which the project is closely supervised by an highly engaged architect and implemented by a clique of developers. |
︙ | ︙ | |||
135 136 137 138 139 140 141 | Work in progress from one developer is readily visible to all other developers and to the project leader, well before the code is ready to integrate. Fossil places a lot of emphasis on reporting the state of the project, and the changes underway by all developers, so that all developers and especially the project leader can maintain a better mental picture of what is happening, and better situational awareness. | | | | | | | | 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 | Work in progress from one developer is readily visible to all other developers and to the project leader, well before the code is ready to integrate. Fossil places a lot of emphasis on reporting the state of the project, and the changes underway by all developers, so that all developers and especially the project leader can maintain a better mental picture of what is happening, and better situational awareness. <h3>2.4 Linux vs. SQLite</h3> Git was specifically designed to support the development of Linux. Fossil was specifically designed to support the development of SQLite. Both SQLite and Linux are important pieces of software. SQLite is found on far more systems than Linux. (Almost every Linux system uses SQLite, but there are many non-Linux systems such as iPhones, PlayStations, and Windows PCs that use SQLite.) On the other hand, for those systems that do use Linux, Linux is a far more important component. Linux uses a bazaar-style development model. There are thousands and thousands of contributors, most of whom do not know each others names. Git is designed for this scenario. SQLite uses cathedral-style development. 95% of the code in SQLite comes from just three programmers, 64% from just the lead developer. And all SQLite developers know each other well and interact daily. Fossil is designed for this development model. <h3>2.5 Lots of little tools vs. Self-contained system</h3> Git consists of many small tools, each doing one small part of the job, which can be recombined (by experts) to perform powerful operations. Git has a lot of complexity and many dependencies and requires an "installer" script or program to get it running. Fossil is a single self-contained stand-alone executable with hardly any dependencies. Fossil can be (and often is) run inside a minimally configured chroot jail. To install Fossil, one merely puts the executable on $PATH. The designer of Git says that the unix philosophy is to have lots of small tools that collaborate to get the job done. The designer of Fossil says that the unix philosophy is "it just works". Both individuals have written their DVCSes to reflect their own view of the "unix philosophy". <h3>2.6 One vs. Many Check-outs per Repository</h3> A "repository" in Git is a pile-of-files in the ".git" subdirectory of a single check-out. The check-out and the repository are inseperable. With Fossil, a "repository" is a single SQLite database file that can be stored anywhere. There can be multiple active check-outs from the same repository, perhaps open on different branches or on different snapshots of the same branch. Long-running tests or builds can be running in one check-out while changes are being committed in another. <h3>2.7 What you should have done vs. What you actually did</h3> Git puts a lot of emphasis on maintaining a "clean" check-in history. Extraneous and experimental branches by individual developers often never make it into the main repository. And branches are often rebased before being pushed, to make it appear as if development had been linear. Git strives to record what the development of a project should have looked like had there been no mistakes. Fossil, in contrast, puts more emphasis on recording exactly what happened, including all of the messy errors, dead-ends, experimental branches, and so forth. One might argue that this makes the history of a Fossil project "messy". But another point of view is that this makes the history "accurate". In actual practice, the superior reporting tools available in Fossil mean that the added "mess" is not a factor. One commentator has mused that Git records history according to the victors, whereas Fossil records history as it actually happened. <h3>2.8 GPL vs. BSD</h3> Git is covered by the GPL license whereas Fossil is covered by a two-clause BSD license. Consider the difference between GPL and BSD licenses: GPL is designed to make writing easier at the expense of making reading harder. BSD is designed to make reading easier at the expense of making writing harder. To a first approximation, the GPL license grants the right to read source code to anyone who promises to give back enhancements. In other words, the act of reading GPL source code (a prerequiste for making changes) implies acceptance of the license which requires updates to be contributed back under the same license. (The details are more complex, but the foregoing captures the essence of the idea.) A big advantage of the GPL |
︙ | ︙ | |||
243 244 245 246 247 248 249 | implementations themselves, not to the projects which the systems store. Nevertheless, one can see a more GPL-oriented world-view in Git and a more BSD-oriented world-view in Fossil. Git encourages anonymous contributions and siloed development, which are hallmarks of the GPL/bazaar approach to software, whereas Fossil encourages a more tightly collaborative, cliquish, cathedral-style approach more typical of BSD-licensed projects. | | | | 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 | implementations themselves, not to the projects which the systems store. Nevertheless, one can see a more GPL-oriented world-view in Git and a more BSD-oriented world-view in Fossil. Git encourages anonymous contributions and siloed development, which are hallmarks of the GPL/bazaar approach to software, whereas Fossil encourages a more tightly collaborative, cliquish, cathedral-style approach more typical of BSD-licensed projects. <h2>3.0 Missing Features</h2> Most of the capabilities found in Git are also available in Fossil and the other way around. For example, both systems have local check-outs, remote repositories, push/pull/sync, bisect capabilities, and a "stash". Both systems store project history as a directed acyclic graph (DAG) of immutable check-in objects. But there are a few capabilities in one system that are missing from the other. <h3>3.1 Features found in Fossil but missing from Git</h3> * <b>The ability to show descendents of a check-in.</b> Both Git and Fossil can easily find the ancestors of a check-in. But only Fossil shows the descendents. (It is possible to find the descendents of a check-in in Git using the log, but that is sufficiently difficult that nobody ever actually does it.) |
︙ | ︙ | |||
293 294 295 296 297 298 299 | * <b>The [/help?cmd=ui|fossil ui] command</b> Fossil supports an integrated web interface. Some of the same features are available using third-party add-ons for Git, but they do not provide nearly as many features and they are not nearly as convenient to use. | | | | 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 | * <b>The [/help?cmd=ui|fossil ui] command</b> Fossil supports an integrated web interface. Some of the same features are available using third-party add-ons for Git, but they do not provide nearly as many features and they are not nearly as convenient to use. <h3>3.2 Features found in Git but missing from Fossil</h3> * <b>Rebase</b> Because of its emphasis on recording history exactly as it happened, rather than as we would have liked it to happen, Fossil deliberately does not provide a "rebase" command. One can rebase manually in Fossil, with sufficient perserverence, but it is not something that can be done with a single command. * <b>Push or pull a single branch</b> The [/help?cmd=push|fossil push], [/help?cmd=pull|fossil pull], and [/help?cmd=sync|fossil sync] commands do not provide the capability to push or pull individual branches. Pushing and pulling in Fossil is all or nothing. This is in keeping with Fossil's emphasis on maintaining a complete record and on sharing everything between all developers. |
Changes to www/hacker-howto.wiki.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | <title>Fossil Hackers How-To</title> The following links are of interest to programmers who want to modify or enhance Fossil. Ordinary users can safely ignore this information. * [./build.wiki | How To Compile And Install Fossil] * [./customskin.md | Theming Fossil] * [./makefile.wiki | The Fossil Build Process] * [./tech_overview.wiki | A Technical Overview of Fossil] * [./adding_code.wiki | Adding Features To Fossil] * [./contribute.wiki|Contributing Code Or Enhancements To The Fossil Project] * [./style.wiki | Coding Style Guidelines] * [./checkin.wiki | Pre-checkin Checklist] * [../test/release-checklist.wiki | Release Checklist] | > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | <title>Fossil Hackers How-To</title> The following links are of interest to programmers who want to modify or enhance Fossil. Ordinary users can safely ignore this information. * [./build.wiki | How To Compile And Install Fossil] * [./customskin.md | Theming Fossil] * [./makefile.wiki | The Fossil Build Process] * [./tech_overview.wiki | A Technical Overview of Fossil] * [./adding_code.wiki | Adding Features To Fossil] * [./contribute.wiki|Contributing Code Or Enhancements To The Fossil Project] * [./fileformat.wiki|Fossil Artifact File Format] * [./style.wiki | Coding Style Guidelines] * [./checkin.wiki | Pre-checkin Checklist] * [../test/release-checklist.wiki | Release Checklist] * [./backoffice.md | The "backoffice" subsystem] |
Changes to www/index.wiki.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | <title>Home</title> <h3>What Is Fossil?</h3> <div style='width:200px;float:right;border:2px solid #446979;padding:10px;margin:0px 10px;'> <ul> <li> [/uv/download.html | Download] <li> [./quickstart.wiki | Quick Start] <li> [./build.wiki | Install] <li> [../COPYRIGHT-BSD2.txt | License] <li> [./faq.wiki | FAQ] <li> [./changes.wiki | Change Log] <li> [./hacker-howto.wiki | Hacker How-To] <li> [./hints.wiki | Tip & Hints] <li> [./permutedindex.html | Documentation Index] | > | < < < | < | | > > > | | 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 | <title>Home</title> <h3>What Is Fossil?</h3> <div style='width:200px;float:right;border:2px solid #446979;padding:10px;margin:0px 10px;'> <ul> <li> [/uv/download.html | Download] <li> [./quickstart.wiki | Quick Start] <li> [./build.wiki | Install] <li> [../COPYRIGHT-BSD2.txt | License] <li> [./faq.wiki | FAQ] <li> [./changes.wiki | Change Log] <li> [./hacker-howto.wiki | Hacker How-To] <li> [./fossil-v-git.wiki | Fossil vs. Git] <li> [./hints.wiki | Tip & Hints] <li> [./permutedindex.html | Documentation Index] <li> [https://fossil-scm.org/forum | Forum ] <li> [http://www.mail-archive.com/fossil-users@lists.fossil-scm.org | Mailing list archives] </ul> <img src="fossil3.gif" align="center"> </div> <p>Fossil is a simple, high-reliability, distributed software configuration management system with these advanced features: 1. <b>Integrated Bug Tracking, Wiki, Forum, and Technotes</b> - In addition to doing [./concepts.wiki | distributed version control] like Git and Mercurial, Fossil also supports [./bugtheory.wiki | bug tracking], [./wikitheory.wiki | wiki], [./forum.wiki | forum], and [./event.wiki | technotes]. 2. <b>Built-in Web Interface</b> - Fossil has a built-in, [https://fossil-scm.org/skins/index.html | themeable], and intuitive [./webui.wiki | web interface] with a rich variety of information pages ([./webpage-ex.md|examples]) promoting situational awareness. This entire website is just a running instance of Fossil. The pages you see here are all [./wikitheory.wiki | wiki] or [./embeddeddoc.wiki | embedded documentation] or (in the case of the [/uv/download.html|download] page) |
︙ | ︙ | |||
91 92 93 94 95 96 97 | [./quotes.wiki | Quotes] about Fossil and other DVCSes. * [./faq.wiki | Frequently Asked Questions] * The [./concepts.wiki | concepts] behind Fossil. [./whyusefossil.wiki#definitions | Another viewpoint]. * [./quickstart.wiki | Quick Start] guide to using Fossil. * [./qandc.wiki | Questions & Criticisms] directed at Fossil. * [./build.wiki | Compiling and Installing] | < < < < | 91 92 93 94 95 96 97 98 99 100 101 102 103 104 | [./quotes.wiki | Quotes] about Fossil and other DVCSes. * [./faq.wiki | Frequently Asked Questions] * The [./concepts.wiki | concepts] behind Fossil. [./whyusefossil.wiki#definitions | Another viewpoint]. * [./quickstart.wiki | Quick Start] guide to using Fossil. * [./qandc.wiki | Questions & Criticisms] directed at Fossil. * [./build.wiki | Compiling and Installing] * Fossil supports [./embeddeddoc.wiki | embedded documentation] that is versioned along with project source code. * Fossil uses an [./fileformat.wiki | enduring file format] that is designed to be readable, searchable, and extensible by people not yet born. * A tutorial on [./branching.wiki | branching], what it means and how to do it using Fossil. |
︙ | ︙ |
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} antibot.wiki {Defense against Spiders and Bots} 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} checkin_names.wiki {Check-in And Version Names} checkin.wiki {Check-in Checklist} | > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | #!/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} checkin_names.wiki {Check-in And Version Names} checkin.wiki {Check-in Checklist} |
︙ | ︙ | |||
117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 | <li> <a href='quickstart.wiki'>Quick-start Guide</a> <li> <a href='faq.wiki'>FAQ</a> <li> <a href='build.wiki'>Compiling and installing Fossil</a> <li> <a href='../COPYRIGHT-BSD2.txt'>License</a> <li> <a href='http://www.fossil-scm.org/schimpf-book/home'>Jim Schimpf's book</a> <li> <a href='$ROOT/help'>List of commands, web-pages, and settings</a> </ul> <a name="pindex"></a> <h2>Permuted Index:</h2> <ul>} foreach entry $permindex { foreach {title file bold} $entry break if {$bold} {set title <b>$title</b>} if {[string match /* $file]} {set file ../../..$file} puts $out "<li><a href=\"$file\">$title</a></li>" } puts $out "</ul></div>" | > | 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 | <li> <a href='quickstart.wiki'>Quick-start Guide</a> <li> <a href='faq.wiki'>FAQ</a> <li> <a href='build.wiki'>Compiling and installing Fossil</a> <li> <a href='../COPYRIGHT-BSD2.txt'>License</a> <li> <a href='http://www.fossil-scm.org/schimpf-book/home'>Jim Schimpf's book</a> <li> <a href='$ROOT/help'>List of commands, web-pages, and settings</a> <li> <a href='hacker-howto.wiki'>Hacker How-To</a> </ul> <a name="pindex"></a> <h2>Permuted Index:</h2> <ul>} foreach entry $permindex { foreach {title file bold} $entry break if {$bold} {set title <b>$title</b>} if {[string match /* $file]} {set file ../../..$file} puts $out "<li><a href=\"$file\">$title</a></li>" } puts $out "</ul></div>" |
Changes to www/permutedindex.html.
︙ | ︙ | |||
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 | <li> <a href='quickstart.wiki'>Quick-start Guide</a> <li> <a href='faq.wiki'>FAQ</a> <li> <a href='build.wiki'>Compiling and installing Fossil</a> <li> <a href='../COPYRIGHT-BSD2.txt'>License</a> <li> <a href='http://www.fossil-scm.org/schimpf-book/home'>Jim Schimpf's book</a> <li> <a href='$ROOT/help'>List of commands, web-pages, and settings</a> </ul> <a name="pindex"></a> <h2>Permuted Index:</h2> <ul> <li><a href="fiveminutes.wiki">5 Minutes as a Single User — Up and Running in</a></li> <li><a href="fossil-from-msvc.wiki">2010 IDE — 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 — Contributor License</a></li> <li><a href="delta_encoder_algorithm.wiki">Algorithm — Fossil Delta Encoding</a></li> <li><a href="blame.wiki">Algorithm Of Fossil — The Annotate/Blame</a></li> <li><a href="blame.wiki">Annotate/Blame Algorithm Of Fossil — The</a></li> <li><a href="customskin.md">Appearance of Web Pages — Theming: Customizing The</a></li> <li><a href="faq.wiki">Asked Questions — Frequently</a></li> <li><a href="password.wiki">Authentication — Password Management And</a></li> <li><a href="whyusefossil.wiki"><b>Benefits Of Version Control</b></a></li> <li><a href="hashpolicy.wiki">Between SHA1 and SHA3-256 — Hash Policy: Choosing</a></li> <li><a href="antibot.wiki">Bots — Defense against Spiders and</a></li> <li><a href="private.wiki">Branches — Creating, Syncing, and Deleting Private</a></li> <li><a href="branching.wiki"><b>Branching, Forking, Merging, and Tagging</b></a></li> <li><a href="bugtheory.wiki"><b>Bug Tracking In Fossil</b></a></li> <li><a href="makefile.wiki">Build Process — The Fossil</a></li> | > > > | 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 | <li> <a href='quickstart.wiki'>Quick-start Guide</a> <li> <a href='faq.wiki'>FAQ</a> <li> <a href='build.wiki'>Compiling and installing Fossil</a> <li> <a href='../COPYRIGHT-BSD2.txt'>License</a> <li> <a href='http://www.fossil-scm.org/schimpf-book/home'>Jim Schimpf's book</a> <li> <a href='$ROOT/help'>List of commands, web-pages, and settings</a> <li> <a href='hacker-howto.wiki'>Hacker How-To</a> </ul> <a name="pindex"></a> <h2>Permuted Index:</h2> <ul> <li><a href="fiveminutes.wiki">5 Minutes as a Single User — Up and Running in</a></li> <li><a href="fossil-from-msvc.wiki">2010 IDE — 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 — Contributor License</a></li> <li><a href="alerts.md">Alerts And Notification — Email</a></li> <li><a href="delta_encoder_algorithm.wiki">Algorithm — Fossil Delta Encoding</a></li> <li><a href="blame.wiki">Algorithm Of Fossil — The Annotate/Blame</a></li> <li><a href="blame.wiki">Annotate/Blame Algorithm Of Fossil — The</a></li> <li><a href="customskin.md">Appearance of Web Pages — Theming: Customizing The</a></li> <li><a href="faq.wiki">Asked Questions — Frequently</a></li> <li><a href="password.wiki">Authentication — Password Management And</a></li> <li><a href="backoffice.md">Backoffice mechanism of Fossil — The</a></li> <li><a href="whyusefossil.wiki"><b>Benefits Of Version Control</b></a></li> <li><a href="hashpolicy.wiki">Between SHA1 and SHA3-256 — Hash Policy: Choosing</a></li> <li><a href="antibot.wiki">Bots — Defense against Spiders and</a></li> <li><a href="private.wiki">Branches — Creating, Syncing, and Deleting Private</a></li> <li><a href="branching.wiki"><b>Branching, Forking, Merging, and Tagging</b></a></li> <li><a href="bugtheory.wiki"><b>Bug Tracking In Fossil</b></a></li> <li><a href="makefile.wiki">Build Process — The Fossil</a></li> |
︙ | ︙ | |||
73 74 75 76 77 78 79 80 81 82 83 84 85 86 | <li><a href="tech_overview.wiki">Design And Implementation Of Fossil — A Technical Overview Of The</a></li> <li><a href="theory1.wiki">Design Of The Fossil DVCS — Thoughts On The</a></li> <li><a href="embeddeddoc.wiki">Documentation — Embedded Project</a></li> <li><a href="contribute.wiki">Documentation To The Fossil Project — Contributing Code or</a></li> <li><a href="aboutdownload.wiki">Download Page Works — How The</a></li> <li><a href="theory1.wiki">DVCS — Thoughts On The Design Of The Fossil</a></li> <li><a href="quotes.wiki">DVCSes in General — Quotes: What People Are Saying About Fossil, Git, and</a></li> <li><a href="embeddeddoc.wiki"><b>Embedded Project Documentation</b></a></li> <li><a href="delta_encoder_algorithm.wiki">Encoding Algorithm — Fossil Delta</a></li> <li><a href="encryptedrepos.wiki">Encrypted Repositories — 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 — Webpage</a></li> <li><a href="inout.wiki">Export To And From Git — Import And</a></li> | > | 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 | <li><a href="tech_overview.wiki">Design And Implementation Of Fossil — A Technical Overview Of The</a></li> <li><a href="theory1.wiki">Design Of The Fossil DVCS — Thoughts On The</a></li> <li><a href="embeddeddoc.wiki">Documentation — Embedded Project</a></li> <li><a href="contribute.wiki">Documentation To The Fossil Project — Contributing Code or</a></li> <li><a href="aboutdownload.wiki">Download Page Works — How The</a></li> <li><a href="theory1.wiki">DVCS — Thoughts On The Design Of The Fossil</a></li> <li><a href="quotes.wiki">DVCSes in General — 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 — Fossil Delta</a></li> <li><a href="encryptedrepos.wiki">Encrypted Repositories — 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 — Webpage</a></li> <li><a href="inout.wiki">Export To And From Git — Import And</a></li> |
︙ | ︙ | |||
137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 | <li><a href="webui.wiki">Interface — The Fossil Web</a></li> <li><a href="th1.md">Language — The TH1 Scripting</a></li> <li><a href="copyright-release.html">License Agreement — Contributor</a></li> <li><a href="../../../help"><b>Lists of Commands and Webpages</b></a></li> <li><a href="password.wiki">Management And Authentication — Password</a></li> <li><a href="../../../sitemap">Map — Site</a></li> <li><a href="../../../md_rules"><b>Markdown Formatting Rules</b></a></li> <li><a href="branching.wiki">Merging, and Tagging — Branching, Forking,</a></li> <li><a href="fossil-from-msvc.wiki">Microsoft Express 2010 IDE — Integrating Fossil in the</a></li> <li><a href="fiveminutes.wiki">Minutes as a Single User — Up and Running in 5</a></li> <li><a href="globs.md">Name Glob Patterns — File</a></li> <li><a href="checkin_names.wiki">Names — Check-in And Version</a></li> <li><a href="adding_code.wiki">New Features To Fossil — Adding</a></li> <li><a href="newrepo.wiki">New Fossil Repository — How To Create A</a></li> <li><a href="foss-cklist.wiki">Open-Source Projects — Checklist For Successful</a></li> <li><a href="pop.wiki">Operation — Principles Of</a></li> <li><a href="env-opts.md">Options — Environment Variables and Global</a></li> <li><a href="tech_overview.wiki">Overview Of The Design And Implementation Of Fossil — A Technical</a></li> <li><a href="index.wiki">Page — Home</a></li> <li><a href="aboutdownload.wiki">Page Works — How The Download</a></li> <li><a href="customskin.md">Pages — Theming: Customizing The Appearance of Web</a></li> | > > | 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 | <li><a href="webui.wiki">Interface — The Fossil Web</a></li> <li><a href="th1.md">Language — The TH1 Scripting</a></li> <li><a href="copyright-release.html">License Agreement — Contributor</a></li> <li><a href="../../../help"><b>Lists of Commands and Webpages</b></a></li> <li><a href="password.wiki">Management And Authentication — Password</a></li> <li><a href="../../../sitemap">Map — Site</a></li> <li><a href="../../../md_rules"><b>Markdown Formatting Rules</b></a></li> <li><a href="backoffice.md">mechanism of Fossil — The Backoffice</a></li> <li><a href="branching.wiki">Merging, and Tagging — Branching, Forking,</a></li> <li><a href="fossil-from-msvc.wiki">Microsoft Express 2010 IDE — Integrating Fossil in the</a></li> <li><a href="fiveminutes.wiki">Minutes as a Single User — Up and Running in 5</a></li> <li><a href="globs.md">Name Glob Patterns — File</a></li> <li><a href="checkin_names.wiki">Names — Check-in And Version</a></li> <li><a href="adding_code.wiki">New Features To Fossil — Adding</a></li> <li><a href="newrepo.wiki">New Fossil Repository — How To Create A</a></li> <li><a href="alerts.md">Notification — Email Alerts And</a></li> <li><a href="foss-cklist.wiki">Open-Source Projects — Checklist For Successful</a></li> <li><a href="pop.wiki">Operation — Principles Of</a></li> <li><a href="env-opts.md">Options — Environment Variables and Global</a></li> <li><a href="tech_overview.wiki">Overview Of The Design And Implementation Of Fossil — A Technical</a></li> <li><a href="index.wiki">Page — Home</a></li> <li><a href="aboutdownload.wiki">Page Works — How The Download</a></li> <li><a href="customskin.md">Pages — Theming: Customizing The Appearance of Web</a></li> |
︙ | ︙ | |||
204 205 206 207 208 209 210 211 212 213 214 215 216 217 | <li><a href="private.wiki">Syncing, and Deleting Private Branches — Creating,</a></li> <li><a href="custom_ticket.wiki">System — Customizing The Ticket</a></li> <li><a href="tickets.wiki">System — The Fossil Ticket</a></li> <li><a href="branching.wiki">Tagging — Branching, Forking, Merging, and</a></li> <li><a href="tech_overview.wiki">Technical Overview Of The Design And Implementation Of Fossil — A</a></li> <li><a href="../test/release-checklist.wiki">Testing Checklist — Pre-Release</a></li> <li><a href="th1.md">TH1 Scripting Language — The</a></li> <li><a href="blame.wiki"><b>The Annotate/Blame Algorithm Of Fossil</b></a></li> <li><a href="makefile.wiki"><b>The Fossil Build Process</b></a></li> <li><a href="sync.wiki"><b>The Fossil Sync Protocol</b></a></li> <li><a href="tickets.wiki"><b>The Fossil Ticket System</b></a></li> <li><a href="webui.wiki"><b>The Fossil Web Interface</b></a></li> <li><a href="th1.md"><b>The TH1 Scripting Language</b></a></li> <li><a href="customskin.md"><b>Theming: Customizing The Appearance of Web Pages</b></a></li> | > | 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 | <li><a href="private.wiki">Syncing, and Deleting Private Branches — Creating,</a></li> <li><a href="custom_ticket.wiki">System — Customizing The Ticket</a></li> <li><a href="tickets.wiki">System — The Fossil Ticket</a></li> <li><a href="branching.wiki">Tagging — Branching, Forking, Merging, and</a></li> <li><a href="tech_overview.wiki">Technical Overview Of The Design And Implementation Of Fossil — A</a></li> <li><a href="../test/release-checklist.wiki">Testing Checklist — Pre-Release</a></li> <li><a href="th1.md">TH1 Scripting Language — The</a></li> <li><a href="backoffice.md"><b>The "Backoffice" mechanism of Fossil</b></a></li> <li><a href="blame.wiki"><b>The Annotate/Blame Algorithm Of Fossil</b></a></li> <li><a href="makefile.wiki"><b>The Fossil Build Process</b></a></li> <li><a href="sync.wiki"><b>The Fossil Sync Protocol</b></a></li> <li><a href="tickets.wiki"><b>The Fossil Ticket System</b></a></li> <li><a href="webui.wiki"><b>The Fossil Web Interface</b></a></li> <li><a href="th1.md"><b>The TH1 Scripting Language</b></a></li> <li><a href="customskin.md"><b>Theming: Customizing The Appearance of Web Pages</b></a></li> |
︙ | ︙ |
Changes to www/stats.wiki.
1 2 3 4 5 6 7 8 9 | <title>Fossil Performance</title> <h1 align="center">Performance Statistics</h1> The questions will inevitably arise: How does Fossil perform? Does it use a lot of disk space or bandwidth? Is it scalable? In an attempt to answers these questions, this report looks at several projects that use fossil for configuration management and examines how well they are working. The following table is a summary of the results. | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | 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 | <title>Fossil Performance</title> <h1 align="center">Performance Statistics</h1> The questions will inevitably arise: How does Fossil perform? Does it use a lot of disk space or bandwidth? Is it scalable? In an attempt to answers these questions, this report looks at several projects that use fossil for configuration management and examines how well they are working. The following table is a summary of the results. (Last updated on 2018-06-04.) Explanation and analysis follows the table. <table border=1> <tr> <th>Project</th> <th>Number Of Artifacts</th> <th>Number Of Check-ins</th> <th>Project Duration<br>(as of 2018-06-04)</th> <th>Uncompressed Size</th> <th>Repository Size</th> <th>Compression Ratio</th> <th>Clone Bandwidth</th> </tr> <tr align="center"> <td>[http://www.sqlite.org/src/timeline | SQLite] <td>77492 <td>20686 <td>6580 days<br>18.02 years <td>5.6 GB <td>70.0 MB <td>80:1 <td>51.1 MB </tr> <tr align="center"> <td>[http://core.tcl.tk/tcl/timeline | TCL] <td>161991 <td>23146 <td>7375 days<br>20.19 years <td>8.0 GB <td>222.0 MB <td>36:1 <td>150.5 MB </tr> <tr align="center"> <td>[/timeline | Fossil] <td>39148 <td>11266 <td>3971 days<br>10.87 years <td>3.8 GB <td>42.0 MB <td>90:1 <td>27.4 MB </tr> <tr align="center"> <td>[http://www.sqlite.org/slt/timeline | SLT] <td>2384 <td>169 <td>3474 days<br>9.51 years <td>2.1 GB <td>145.9 MB <td>14:1 <td>143.4 MB </tr> <tr align="center"> <td>[http://www.sqlite.org/th3.html | TH3] <td>12406 <td>3718 <td>3539 days<br>9.69 years <td>544 MB <td>18.0 MB <td>30:1 <td>14.7 MB </tr> <tr align="center"> <td>[http://www.sqlite.org/docsrc/timeline | SQLite Docs] <td>8752 <td>2783 <td>3857 days<br>10.56 years <td>349.9 MB <td>16.3 MB <td>21:1 <td>13.57 MB </tr> </table> <h2>Measured Attributes</h2> In Fossil, every version of every file, every wiki page, every change to |
︙ | ︙ | |||
127 128 129 130 131 132 133 | <h2>Analysis And Supplemental Data</h2> Perhaps the two most interesting datapoints in the above table are SQLite and SLT. SQLite is a long-running project with long revision chains. Some of the files in SQLite have been edited over a thousand times. Each of these edits is stored as a delta, and hence the SQLite project | | | 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 | <h2>Analysis And Supplemental Data</h2> Perhaps the two most interesting datapoints in the above table are SQLite and SLT. SQLite is a long-running project with long revision chains. Some of the files in SQLite have been edited over a thousand times. Each of these edits is stored as a delta, and hence the SQLite project gets excellent 80:1 compression. SLT, on the other hand, consists of many large (megabyte-sized) SQL scripts that have one or maybe two edits each. There is very little delta compression occurring and so the overall repository compression ratio is much lower. Note also that quite a bit more bandwidth is required to clone SLT than SQLite. For the first nine years of its development, SQLite was versioned by CVS. The resulting CVS repository measured over 320MB in size. So, the |
︙ | ︙ |
Changes to www/sync.wiki.
︙ | ︙ | |||
496 497 498 499 500 501 502 | <p>The reqconfig card is normally sent in response to the "fossil configuration pull" command. The format is as follows: <blockquote> <b>reqconfig</b> <i>configuration-name</i> </blockquote> | | > < > > > > | > < > > > > > < > > > > > | 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 | <p>The reqconfig card is normally sent in response to the "fossil configuration pull" command. The format is as follows: <blockquote> <b>reqconfig</b> <i>configuration-name</i> </blockquote> <p>As of 2018-06-04, the configuration-name must be one of the following values: <table border=0 align="center"> <tr><td valign="top"> <ul> <li> css <li> header <li> footer <li> details <li> logo-mimetype <li> logo-image <li> background-mimetype <li> background-image <li> index-page <li> timeline-block-markup <li> timeline-max-comment <li> timeline-plaintext <li> adunit <li> adunit-omit-if-admin <li> adunit-omit-if-user <ul></td><td valign="top"><ul> <li> th1-docs <li> th1-hooks <li> th1-setup <li> tcl <li> tcl-setup <li> project-name <li> short-project-name <li> project-description <li> index-page <li> manifest <li> binary-glob <li> clean-glob <li> ignore-glob <li> keep-glob <li> crlf-glob <ul></td><td valign="top"><ul> <li> crnl-glob <li> encoding-glob <li> empty-dirs <li> allow-symlinks <li> dotfiles <li> parent-project-code <li> parent-projet-name <li> hash-policy <li> mv-rm-files <li> ticket-table <li> ticket-common <li> ticket-change <li> ticket-newpage <li> ticket-viewpage <li> ticket-editpage <ul></td><td valign="top"><ul> <li> ticket-reportlist <li> ticket-report-template <li> ticket-key-template <li> ticket-title-expr <li> ticket-closed-expr <li> xfer-common-script <li> xfer-push-script <li> xfer-commit-script <li> xfer-ticket-script <li> @reportfmt <li> @user <li> @concealed <li> @shun </ul></td></tr> </table> |
︙ | ︙ | |||
670 671 672 673 674 675 676 | </ol> <h3>3.12 Comment Cards</h3> <p>Any card that begins with "#" (ASCII 0x23) is a comment card and is silently ignored.</p> | | > > > > > > > > > | 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 | </ol> <h3>3.12 Comment Cards</h3> <p>Any card that begins with "#" (ASCII 0x23) is a comment card and is silently ignored.</p> <h3>3.13 Message and Error Cards</h3> <p>If the server discovers anything wrong with a request, it generates an error card in its reply. When the client sees the error card, it displays an error message to the user and aborts the sync operation. An error card looks like this:</p> <blockquote> <b>error</b> <i>error-message</i> </blockquote> <p>The error message is English text that is encoded in order to be a single token. A space (ASCII 0x20) is represented as "\s" (ASCII 0x5C, 0x73). A newline (ASCII 0x0a) is "\n" (ASCII 0x6C, x6E). A backslash (ASCII 0x5C) is represented as two backslashes "\\". Apart from space and newline, no other whitespace characters nor any unprintable characters are allowed in the error message.</p> <p>The server can also send a message card that also prints a message on the client console, but which is not an error: <blockquote> <b>message</b> <i>message-text</i> </blockquote> <p>The message-text uses the same format as an error message. <h3>3.14 Unknown Cards</h3> <p>If either the client or the server sees a card that is not described above, then it generates an error and aborts.</p> <h2>4.0 Phantoms And Clusters</h2> |
︙ | ︙ | |||
911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 | <li> <b>gimme</b> <i>artifact-id</i> <li> <b>uvgimme</b> <i>name</i> <li> <b>cookie</b> <i>cookie-text</i> <li> <b>reqconfig</b> <i>parameter-name</i> <li> <b>config</b> <i>parameter-name size</i> <b>\n</b> <i>content</i> <li> <b>pragma</b> <i>name</i> <i>value...</i> <li> <b>error</b> <i>error-message</i> <li> <b>#</b> <i>arbitrary-text...</i> </ul> <li>Phantoms are artifacts that a repository knows exist but does not possess. <li>Clusters are artifacts that contain IDs of other artifacts. <li>Clusters are created automatically on the server during a pull. <li>Repositories keep track of all artifacts that are not named in any cluster and send igot messages for those artifacts. <li>Repositories keep track of all the phantoms they hold and send gimme messages for those artifacts. </ol> | > | 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 | <li> <b>gimme</b> <i>artifact-id</i> <li> <b>uvgimme</b> <i>name</i> <li> <b>cookie</b> <i>cookie-text</i> <li> <b>reqconfig</b> <i>parameter-name</i> <li> <b>config</b> <i>parameter-name size</i> <b>\n</b> <i>content</i> <li> <b>pragma</b> <i>name</i> <i>value...</i> <li> <b>error</b> <i>error-message</i> <li> <b>message</b> <i>text-messate</i> <li> <b>#</b> <i>arbitrary-text...</i> </ul> <li>Phantoms are artifacts that a repository knows exist but does not possess. <li>Clusters are artifacts that contain IDs of other artifacts. <li>Clusters are created automatically on the server during a pull. <li>Repositories keep track of all artifacts that are not named in any cluster and send igot messages for those artifacts. <li>Repositories keep track of all the phantoms they hold and send gimme messages for those artifacts. </ol> |