Fossil SCM

Sync with trunk.

florian 2025-08-21 12:07 diff-word-wrap merge
Commit a0377ebb9d1bb4740606d9b63742c072aef042973e2a36a7f51f82da59dbd213
80 files changed +89 -59 +218 -154 +6 -1 +1 -1 -1 +12 -8 +37 -9 +1 -1 +2 -2 +1 -1 +2 -2 +2 -2 +1 +5 -1 +19 -22 +10 +30 -8 +30 -8 +17 -11 +17 -11 +1 -1 +1 +1 +1 -1 +5 -5 +2 -1 +5 -5 +6 -6 +9 -11 +4 -8 +2 -7 +3 -4 -1 +2 -2 +1 -1 +16 -12 +51 -22 +1 -1 +1 -1 +3 -3 +3 -3 +33 -9 +1 -2 +1 -1 +131 -142 +46 -22 +12 +1 -1 +2 -2 +2 -2 +3 -3 +1 +5 -5 +55 -62 +3 -3 +1 -1 +11 -1 +21 -62 +2 -2 +1 +1 -1 +2 -2 +4 -2 +4 -4 +4 -4 +11 +5 -5 +6 -5 +42 +2 -2 +1 -1 +1 -1 +55 -13 +2 -1 +1 +10 -4 +12 +10 +3 +28 -16
+89 -59
--- extsrc/shell.c
+++ extsrc/shell.c
@@ -122,10 +122,13 @@
122122
typedef sqlite3_int64 i64;
123123
typedef sqlite3_uint64 u64;
124124
typedef unsigned char u8;
125125
#include <ctype.h>
126126
#include <stdarg.h>
127
+#ifndef _WIN32
128
+# include <sys/time.h>
129
+#endif
127130
128131
#if !defined(_WIN32) && !defined(WIN32)
129132
# include <signal.h>
130133
# if !defined(__RTP__) && !defined(_WRS_KERNEL) && !defined(SQLITE_WASI)
131134
# include <pwd.h>
@@ -646,24 +649,27 @@
646649
if( a==0 ) a = "";
647650
if( b==0 ) b = "";
648651
return strncmp(a,b,n);
649652
}
650653
651
-/* Return the current wall-clock time */
654
+/* Return the current wall-clock time in microseconds since the
655
+** Unix epoch (1970-01-01T00:00:00Z)
656
+*/
652657
static sqlite3_int64 timeOfDay(void){
653
- static sqlite3_vfs *clockVfs = 0;
654
- sqlite3_int64 t;
655
- if( clockVfs==0 ) clockVfs = sqlite3_vfs_find(0);
656
- if( clockVfs==0 ) return 0; /* Never actually happens */
657
- if( clockVfs->iVersion>=2 && clockVfs->xCurrentTimeInt64!=0 ){
658
- clockVfs->xCurrentTimeInt64(clockVfs, &t);
659
- }else{
660
- double r;
661
- clockVfs->xCurrentTime(clockVfs, &r);
662
- t = (sqlite3_int64)(r*86400000.0);
663
- }
658
+#if defined(_WIN32)
659
+ sqlite3_uint64 t;
660
+ FILETIME tm;
661
+ GetSystemTimePreciseAsFileTime(&tm);
662
+ t = ((u64)tm.dwHighDateTime<<32) | (u64)tm.dwLowDateTime;
663
+ t += 116444736000000000LL;
664
+ t /= 10;
664665
return t;
666
+#else
667
+ struct timeval sNow;
668
+ (void)gettimeofday(&sNow,0);
669
+ return ((i64)sNow.tv_sec)*1000000 + sNow.tv_usec;
670
+#endif
665671
}
666672
667673
#if !defined(_WIN32) && !defined(WIN32) && !defined(__minux)
668674
#include <sys/time.h>
669675
#include <sys/resource.h>
@@ -704,12 +710,12 @@
704710
static void endTimer(FILE *out){
705711
if( enableTimer ){
706712
sqlite3_int64 iEnd = timeOfDay();
707713
struct rusage sEnd;
708714
getrusage(RUSAGE_SELF, &sEnd);
709
- sqlite3_fprintf(out, "Run Time: real %.3f user %f sys %f\n",
710
- (iEnd - iBegin)*0.001,
715
+ sqlite3_fprintf(out, "Run Time: real %.6f user %f sys %f\n",
716
+ (iEnd - iBegin)*0.000001,
711717
timeDiff(&sBegin.ru_utime, &sEnd.ru_utime),
712718
timeDiff(&sBegin.ru_stime, &sEnd.ru_stime));
713719
}
714720
}
715721
@@ -783,12 +789,12 @@
783789
static void endTimer(FILE *out){
784790
if( enableTimer && getProcessTimesAddr){
785791
FILETIME ftCreation, ftExit, ftKernelEnd, ftUserEnd;
786792
sqlite3_int64 ftWallEnd = timeOfDay();
787793
getProcessTimesAddr(hProcess,&ftCreation,&ftExit,&ftKernelEnd,&ftUserEnd);
788
- sqlite3_fprintf(out, "Run Time: real %.3f user %f sys %f\n",
789
- (ftWallEnd - ftWallBegin)*0.001,
794
+ sqlite3_fprintf(out, "Run Time: real %.6f user %f sys %f\n",
795
+ (ftWallEnd - ftWallBegin)*0.000001,
790796
timeDiff(&ftUserBegin, &ftUserEnd),
791797
timeDiff(&ftKernelBegin, &ftKernelEnd));
792798
}
793799
}
794800
@@ -32888,76 +32894,95 @@
3288832894
3288932895
return home_dir;
3289032896
}
3289132897
3289232898
/*
32893
-** On non-Windows platforms, look for $XDG_CONFIG_HOME.
32894
-** If ${XDG_CONFIG_HOME}/sqlite3/sqliterc is found, return
32895
-** the path to it. If there is no $(XDG_CONFIG_HOME) then
32896
-** look for $(HOME)/.config/sqlite3/sqliterc and if found
32897
-** return that. If none of these are found, return 0.
32899
+** On non-Windows platforms, look for:
32900
+**
32901
+** - ${zEnvVar}/${zBaseName}
32902
+** - ${HOME}/${zSubdir}/${zBaseName}
32903
+**
32904
+** $zEnvVar is intended to be the name of an XDG_... environment
32905
+** variable, e.g. XDG_CONFIG_HOME or XDG_STATE_HOME. If zEnvVar is
32906
+** NULL or getenv(zEnvVar) is NULL then fall back to the second
32907
+** option. If the selected option is not found in the filesystem,
32908
+** return 0.
32909
+**
32910
+** zSubdir may be NULL or empty, in which case ${HOME}/${zBaseName}
32911
+** becomes the fallback.
32912
+**
32913
+** Both zSubdir and zBaseName may contain subdirectory parts. zSubdir
32914
+** will conventionally be ".config" or ".local/state", which, not
32915
+** coincidentally, is the typical subdir of the corresponding XDG_...
32916
+** var with the XDG var's $HOME prefix.
3289832917
**
32899
-** The string returned is obtained from sqlite3_malloc() and
32900
-** should be freed by the caller.
32918
+** The returned string is obtained from sqlite3_malloc() and should be
32919
+** sqlite3_free()'d by the caller.
3290132920
*/
32902
-static char *find_xdg_config(void){
32921
+static char *find_xdg_file(const char *zEnvVar, const char *zSubdir,
32922
+ const char *zBaseName){
3290332923
#if defined(_WIN32) || defined(WIN32) || defined(_WIN32_WCE) \
3290432924
|| defined(__RTP__) || defined(_WRS_KERNEL)
3290532925
return 0;
3290632926
#else
32907
- char *zConfig = 0;
32908
- const char *zXdgHome;
32909
-
32910
- zXdgHome = getenv("XDG_CONFIG_HOME");
32911
- if( zXdgHome==0 ){
32912
- const char *zHome = getenv("HOME");
32913
- if( zHome==0 ) return 0;
32914
- zConfig = sqlite3_mprintf("%s/.config/sqlite3/sqliterc", zHome);
32927
+ char *zConfigFile = 0;
32928
+ const char *zXdgDir;
32929
+
32930
+ zXdgDir = zEnvVar ? getenv(zEnvVar) : 0;
32931
+ if( zXdgDir ){
32932
+ zConfigFile = sqlite3_mprintf("%s/%s", zXdgDir, zBaseName);
3291532933
}else{
32916
- zConfig = sqlite3_mprintf("%s/sqlite3/sqliterc", zXdgHome);
32917
- }
32918
- shell_check_oom(zConfig);
32919
- if( access(zConfig,0)!=0 ){
32920
- sqlite3_free(zConfig);
32921
- zConfig = 0;
32922
- }
32923
- return zConfig;
32934
+ const char * zHome = find_home_dir(0);
32935
+ if( zHome==0 ) return 0;
32936
+ zConfigFile = (zSubdir && *zSubdir)
32937
+ ? sqlite3_mprintf("%s/%s/%s", zHome, zSubdir, zBaseName)
32938
+ : sqlite3_mprintf("%s/%s", zHome, zBaseName);
32939
+ }
32940
+ shell_check_oom(zConfigFile);
32941
+ if( access(zConfigFile,0)!=0 ){
32942
+ sqlite3_free(zConfigFile);
32943
+ zConfigFile = 0;
32944
+ }
32945
+ return zConfigFile;
3292432946
#endif
3292532947
}
3292632948
3292732949
/*
32928
-** Read input from the file given by sqliterc_override. Or if that
32929
-** parameter is NULL, take input from the first of find_xdg_config()
32930
-** or ~/.sqliterc which is found.
32950
+** Read input from the file sqliterc_override. If that parameter is
32951
+** NULL, take it from find_xdg_file(), if found, or fall back to
32952
+** ~/.sqliterc.
3293132953
**
32932
-** Returns the number of errors.
32954
+** Failure to read the config is only considered a failure if
32955
+** sqliterc_override is not NULL, in which case this function may emit
32956
+** a warning or, if ::bail_on_error is true, fail fatally if the file
32957
+** named by sqliterc_override is not found.
3293332958
*/
3293432959
static void process_sqliterc(
3293532960
ShellState *p, /* Configuration data */
3293632961
const char *sqliterc_override /* Name of config file. NULL to use default */
3293732962
){
3293832963
char *home_dir = NULL;
32939
- const char *sqliterc = sqliterc_override;
32940
- char *zBuf = 0;
32964
+ char *sqliterc = (char*)sqliterc_override;
3294132965
FILE *inSaved = p->in;
3294232966
int savedLineno = p->lineno;
3294332967
3294432968
if( sqliterc == NULL ){
32945
- sqliterc = zBuf = find_xdg_config();
32969
+ sqliterc = find_xdg_file("XDG_CONFIG_HOME",
32970
+ ".config",
32971
+ "sqlite3/sqliterc");
3294632972
}
3294732973
if( sqliterc == NULL ){
3294832974
home_dir = find_home_dir(0);
3294932975
if( home_dir==0 ){
3295032976
eputz("-- warning: cannot find home directory;"
3295132977
" cannot read ~/.sqliterc\n");
3295232978
return;
3295332979
}
32954
- zBuf = sqlite3_mprintf("%s/.sqliterc",home_dir);
32955
- shell_check_oom(zBuf);
32956
- sqliterc = zBuf;
32980
+ sqliterc = sqlite3_mprintf("%s/.sqliterc",home_dir);
32981
+ shell_check_oom(sqliterc);
3295732982
}
32958
- p->in = sqlite3_fopen(sqliterc,"rb");
32983
+ p->in = sqliterc ? sqlite3_fopen(sqliterc,"rb") : 0;
3295932984
if( p->in ){
3296032985
if( stdin_is_interactive ){
3296132986
sqlite3_fprintf(stderr,"-- Loading resources from %s\n", sqliterc);
3296232987
}
3296332988
if( process_input(p) && bail_on_error ) exit(1);
@@ -32966,11 +32991,13 @@
3296632991
sqlite3_fprintf(stderr,"cannot open: \"%s\"\n", sqliterc);
3296732992
if( bail_on_error ) exit(1);
3296832993
}
3296932994
p->in = inSaved;
3297032995
p->lineno = savedLineno;
32971
- sqlite3_free(zBuf);
32996
+ if( sqliterc != sqliterc_override ){
32997
+ sqlite3_free(sqliterc);
32998
+ }
3297232999
}
3297333000
3297433001
/*
3297533002
** Show available command line options
3297633003
*/
@@ -33734,11 +33761,10 @@
3373433761
/* Run commands received from standard input
3373533762
*/
3373633763
if( stdin_is_interactive ){
3373733764
char *zHome;
3373833765
char *zHistory;
33739
- int nHistory;
3374033766
sqlite3_fprintf(stdout,
3374133767
"SQLite version %s %.19s\n" /*extra-version-info*/
3374233768
"Enter \".help\" for usage hints.\n",
3374333769
sqlite3_libversion(), sqlite3_sourceid());
3374433770
if( warnInmemoryDb ){
@@ -33747,15 +33773,19 @@
3374733773
sputz(stdout, ".\nUse \".open FILENAME\" to reopen on a"
3374833774
" persistent database.\n");
3374933775
}
3375033776
zHistory = getenv("SQLITE_HISTORY");
3375133777
if( zHistory ){
33752
- zHistory = strdup(zHistory);
33753
- }else if( (zHome = find_home_dir(0))!=0 ){
33754
- nHistory = strlen30(zHome) + 20;
33755
- if( (zHistory = malloc(nHistory))!=0 ){
33756
- sqlite3_snprintf(nHistory, zHistory,"%s/.sqlite_history", zHome);
33778
+ zHistory = sqlite3_mprintf("%s", zHistory);
33779
+ shell_check_oom(zHistory);
33780
+ }else{
33781
+ zHistory = find_xdg_file("XDG_STATE_HOME",
33782
+ ".local/state",
33783
+ "sqlite_history");
33784
+ if( 0==zHistory && (zHome = find_home_dir(0))!=0 ){
33785
+ zHistory = sqlite3_mprintf("%s/.sqlite_history", zHome);
33786
+ shell_check_oom(zHistory);
3375733787
}
3375833788
}
3375933789
if( zHistory ){ shell_read_history(zHistory); }
3376033790
#if (HAVE_READLINE || HAVE_EDITLINE) && !defined(SQLITE_OMIT_READLINE_COMPLETION)
3376133791
rl_attempted_completion_function = readline_completion;
@@ -33767,11 +33797,11 @@
3376733797
data.in = 0;
3376833798
rc = process_input(&data);
3376933799
if( zHistory ){
3377033800
shell_stifle_history(2000);
3377133801
shell_write_history(zHistory);
33772
- free(zHistory);
33802
+ sqlite3_free(zHistory);
3377333803
}
3377433804
}else{
3377533805
data.in = stdin;
3377633806
rc = process_input(&data);
3377733807
}
3377833808
--- extsrc/shell.c
+++ extsrc/shell.c
@@ -122,10 +122,13 @@
122 typedef sqlite3_int64 i64;
123 typedef sqlite3_uint64 u64;
124 typedef unsigned char u8;
125 #include <ctype.h>
126 #include <stdarg.h>
 
 
 
127
128 #if !defined(_WIN32) && !defined(WIN32)
129 # include <signal.h>
130 # if !defined(__RTP__) && !defined(_WRS_KERNEL) && !defined(SQLITE_WASI)
131 # include <pwd.h>
@@ -646,24 +649,27 @@
646 if( a==0 ) a = "";
647 if( b==0 ) b = "";
648 return strncmp(a,b,n);
649 }
650
651 /* Return the current wall-clock time */
 
 
652 static sqlite3_int64 timeOfDay(void){
653 static sqlite3_vfs *clockVfs = 0;
654 sqlite3_int64 t;
655 if( clockVfs==0 ) clockVfs = sqlite3_vfs_find(0);
656 if( clockVfs==0 ) return 0; /* Never actually happens */
657 if( clockVfs->iVersion>=2 && clockVfs->xCurrentTimeInt64!=0 ){
658 clockVfs->xCurrentTimeInt64(clockVfs, &t);
659 }else{
660 double r;
661 clockVfs->xCurrentTime(clockVfs, &r);
662 t = (sqlite3_int64)(r*86400000.0);
663 }
664 return t;
 
 
 
 
 
665 }
666
667 #if !defined(_WIN32) && !defined(WIN32) && !defined(__minux)
668 #include <sys/time.h>
669 #include <sys/resource.h>
@@ -704,12 +710,12 @@
704 static void endTimer(FILE *out){
705 if( enableTimer ){
706 sqlite3_int64 iEnd = timeOfDay();
707 struct rusage sEnd;
708 getrusage(RUSAGE_SELF, &sEnd);
709 sqlite3_fprintf(out, "Run Time: real %.3f user %f sys %f\n",
710 (iEnd - iBegin)*0.001,
711 timeDiff(&sBegin.ru_utime, &sEnd.ru_utime),
712 timeDiff(&sBegin.ru_stime, &sEnd.ru_stime));
713 }
714 }
715
@@ -783,12 +789,12 @@
783 static void endTimer(FILE *out){
784 if( enableTimer && getProcessTimesAddr){
785 FILETIME ftCreation, ftExit, ftKernelEnd, ftUserEnd;
786 sqlite3_int64 ftWallEnd = timeOfDay();
787 getProcessTimesAddr(hProcess,&ftCreation,&ftExit,&ftKernelEnd,&ftUserEnd);
788 sqlite3_fprintf(out, "Run Time: real %.3f user %f sys %f\n",
789 (ftWallEnd - ftWallBegin)*0.001,
790 timeDiff(&ftUserBegin, &ftUserEnd),
791 timeDiff(&ftKernelBegin, &ftKernelEnd));
792 }
793 }
794
@@ -32888,76 +32894,95 @@
32888
32889 return home_dir;
32890 }
32891
32892 /*
32893 ** On non-Windows platforms, look for $XDG_CONFIG_HOME.
32894 ** If ${XDG_CONFIG_HOME}/sqlite3/sqliterc is found, return
32895 ** the path to it. If there is no $(XDG_CONFIG_HOME) then
32896 ** look for $(HOME)/.config/sqlite3/sqliterc and if found
32897 ** return that. If none of these are found, return 0.
 
 
 
 
 
 
 
 
 
 
 
 
 
32898 **
32899 ** The string returned is obtained from sqlite3_malloc() and
32900 ** should be freed by the caller.
32901 */
32902 static char *find_xdg_config(void){
 
32903 #if defined(_WIN32) || defined(WIN32) || defined(_WIN32_WCE) \
32904 || defined(__RTP__) || defined(_WRS_KERNEL)
32905 return 0;
32906 #else
32907 char *zConfig = 0;
32908 const char *zXdgHome;
32909
32910 zXdgHome = getenv("XDG_CONFIG_HOME");
32911 if( zXdgHome==0 ){
32912 const char *zHome = getenv("HOME");
32913 if( zHome==0 ) return 0;
32914 zConfig = sqlite3_mprintf("%s/.config/sqlite3/sqliterc", zHome);
32915 }else{
32916 zConfig = sqlite3_mprintf("%s/sqlite3/sqliterc", zXdgHome);
32917 }
32918 shell_check_oom(zConfig);
32919 if( access(zConfig,0)!=0 ){
32920 sqlite3_free(zConfig);
32921 zConfig = 0;
32922 }
32923 return zConfig;
 
 
 
 
32924 #endif
32925 }
32926
32927 /*
32928 ** Read input from the file given by sqliterc_override. Or if that
32929 ** parameter is NULL, take input from the first of find_xdg_config()
32930 ** or ~/.sqliterc which is found.
32931 **
32932 ** Returns the number of errors.
 
 
 
32933 */
32934 static void process_sqliterc(
32935 ShellState *p, /* Configuration data */
32936 const char *sqliterc_override /* Name of config file. NULL to use default */
32937 ){
32938 char *home_dir = NULL;
32939 const char *sqliterc = sqliterc_override;
32940 char *zBuf = 0;
32941 FILE *inSaved = p->in;
32942 int savedLineno = p->lineno;
32943
32944 if( sqliterc == NULL ){
32945 sqliterc = zBuf = find_xdg_config();
 
 
32946 }
32947 if( sqliterc == NULL ){
32948 home_dir = find_home_dir(0);
32949 if( home_dir==0 ){
32950 eputz("-- warning: cannot find home directory;"
32951 " cannot read ~/.sqliterc\n");
32952 return;
32953 }
32954 zBuf = sqlite3_mprintf("%s/.sqliterc",home_dir);
32955 shell_check_oom(zBuf);
32956 sqliterc = zBuf;
32957 }
32958 p->in = sqlite3_fopen(sqliterc,"rb");
32959 if( p->in ){
32960 if( stdin_is_interactive ){
32961 sqlite3_fprintf(stderr,"-- Loading resources from %s\n", sqliterc);
32962 }
32963 if( process_input(p) && bail_on_error ) exit(1);
@@ -32966,11 +32991,13 @@
32966 sqlite3_fprintf(stderr,"cannot open: \"%s\"\n", sqliterc);
32967 if( bail_on_error ) exit(1);
32968 }
32969 p->in = inSaved;
32970 p->lineno = savedLineno;
32971 sqlite3_free(zBuf);
 
 
32972 }
32973
32974 /*
32975 ** Show available command line options
32976 */
@@ -33734,11 +33761,10 @@
33734 /* Run commands received from standard input
33735 */
33736 if( stdin_is_interactive ){
33737 char *zHome;
33738 char *zHistory;
33739 int nHistory;
33740 sqlite3_fprintf(stdout,
33741 "SQLite version %s %.19s\n" /*extra-version-info*/
33742 "Enter \".help\" for usage hints.\n",
33743 sqlite3_libversion(), sqlite3_sourceid());
33744 if( warnInmemoryDb ){
@@ -33747,15 +33773,19 @@
33747 sputz(stdout, ".\nUse \".open FILENAME\" to reopen on a"
33748 " persistent database.\n");
33749 }
33750 zHistory = getenv("SQLITE_HISTORY");
33751 if( zHistory ){
33752 zHistory = strdup(zHistory);
33753 }else if( (zHome = find_home_dir(0))!=0 ){
33754 nHistory = strlen30(zHome) + 20;
33755 if( (zHistory = malloc(nHistory))!=0 ){
33756 sqlite3_snprintf(nHistory, zHistory,"%s/.sqlite_history", zHome);
 
 
 
 
33757 }
33758 }
33759 if( zHistory ){ shell_read_history(zHistory); }
33760 #if (HAVE_READLINE || HAVE_EDITLINE) && !defined(SQLITE_OMIT_READLINE_COMPLETION)
33761 rl_attempted_completion_function = readline_completion;
@@ -33767,11 +33797,11 @@
33767 data.in = 0;
33768 rc = process_input(&data);
33769 if( zHistory ){
33770 shell_stifle_history(2000);
33771 shell_write_history(zHistory);
33772 free(zHistory);
33773 }
33774 }else{
33775 data.in = stdin;
33776 rc = process_input(&data);
33777 }
33778
--- extsrc/shell.c
+++ extsrc/shell.c
@@ -122,10 +122,13 @@
122 typedef sqlite3_int64 i64;
123 typedef sqlite3_uint64 u64;
124 typedef unsigned char u8;
125 #include <ctype.h>
126 #include <stdarg.h>
127 #ifndef _WIN32
128 # include <sys/time.h>
129 #endif
130
131 #if !defined(_WIN32) && !defined(WIN32)
132 # include <signal.h>
133 # if !defined(__RTP__) && !defined(_WRS_KERNEL) && !defined(SQLITE_WASI)
134 # include <pwd.h>
@@ -646,24 +649,27 @@
649 if( a==0 ) a = "";
650 if( b==0 ) b = "";
651 return strncmp(a,b,n);
652 }
653
654 /* Return the current wall-clock time in microseconds since the
655 ** Unix epoch (1970-01-01T00:00:00Z)
656 */
657 static sqlite3_int64 timeOfDay(void){
658 #if defined(_WIN32)
659 sqlite3_uint64 t;
660 FILETIME tm;
661 GetSystemTimePreciseAsFileTime(&tm);
662 t = ((u64)tm.dwHighDateTime<<32) | (u64)tm.dwLowDateTime;
663 t += 116444736000000000LL;
664 t /= 10;
 
 
 
 
665 return t;
666 #else
667 struct timeval sNow;
668 (void)gettimeofday(&sNow,0);
669 return ((i64)sNow.tv_sec)*1000000 + sNow.tv_usec;
670 #endif
671 }
672
673 #if !defined(_WIN32) && !defined(WIN32) && !defined(__minux)
674 #include <sys/time.h>
675 #include <sys/resource.h>
@@ -704,12 +710,12 @@
710 static void endTimer(FILE *out){
711 if( enableTimer ){
712 sqlite3_int64 iEnd = timeOfDay();
713 struct rusage sEnd;
714 getrusage(RUSAGE_SELF, &sEnd);
715 sqlite3_fprintf(out, "Run Time: real %.6f user %f sys %f\n",
716 (iEnd - iBegin)*0.000001,
717 timeDiff(&sBegin.ru_utime, &sEnd.ru_utime),
718 timeDiff(&sBegin.ru_stime, &sEnd.ru_stime));
719 }
720 }
721
@@ -783,12 +789,12 @@
789 static void endTimer(FILE *out){
790 if( enableTimer && getProcessTimesAddr){
791 FILETIME ftCreation, ftExit, ftKernelEnd, ftUserEnd;
792 sqlite3_int64 ftWallEnd = timeOfDay();
793 getProcessTimesAddr(hProcess,&ftCreation,&ftExit,&ftKernelEnd,&ftUserEnd);
794 sqlite3_fprintf(out, "Run Time: real %.6f user %f sys %f\n",
795 (ftWallEnd - ftWallBegin)*0.000001,
796 timeDiff(&ftUserBegin, &ftUserEnd),
797 timeDiff(&ftKernelBegin, &ftKernelEnd));
798 }
799 }
800
@@ -32888,76 +32894,95 @@
32894
32895 return home_dir;
32896 }
32897
32898 /*
32899 ** On non-Windows platforms, look for:
32900 **
32901 ** - ${zEnvVar}/${zBaseName}
32902 ** - ${HOME}/${zSubdir}/${zBaseName}
32903 **
32904 ** $zEnvVar is intended to be the name of an XDG_... environment
32905 ** variable, e.g. XDG_CONFIG_HOME or XDG_STATE_HOME. If zEnvVar is
32906 ** NULL or getenv(zEnvVar) is NULL then fall back to the second
32907 ** option. If the selected option is not found in the filesystem,
32908 ** return 0.
32909 **
32910 ** zSubdir may be NULL or empty, in which case ${HOME}/${zBaseName}
32911 ** becomes the fallback.
32912 **
32913 ** Both zSubdir and zBaseName may contain subdirectory parts. zSubdir
32914 ** will conventionally be ".config" or ".local/state", which, not
32915 ** coincidentally, is the typical subdir of the corresponding XDG_...
32916 ** var with the XDG var's $HOME prefix.
32917 **
32918 ** The returned string is obtained from sqlite3_malloc() and should be
32919 ** sqlite3_free()'d by the caller.
32920 */
32921 static char *find_xdg_file(const char *zEnvVar, const char *zSubdir,
32922 const char *zBaseName){
32923 #if defined(_WIN32) || defined(WIN32) || defined(_WIN32_WCE) \
32924 || defined(__RTP__) || defined(_WRS_KERNEL)
32925 return 0;
32926 #else
32927 char *zConfigFile = 0;
32928 const char *zXdgDir;
32929
32930 zXdgDir = zEnvVar ? getenv(zEnvVar) : 0;
32931 if( zXdgDir ){
32932 zConfigFile = sqlite3_mprintf("%s/%s", zXdgDir, zBaseName);
 
 
32933 }else{
32934 const char * zHome = find_home_dir(0);
32935 if( zHome==0 ) return 0;
32936 zConfigFile = (zSubdir && *zSubdir)
32937 ? sqlite3_mprintf("%s/%s/%s", zHome, zSubdir, zBaseName)
32938 : sqlite3_mprintf("%s/%s", zHome, zBaseName);
32939 }
32940 shell_check_oom(zConfigFile);
32941 if( access(zConfigFile,0)!=0 ){
32942 sqlite3_free(zConfigFile);
32943 zConfigFile = 0;
32944 }
32945 return zConfigFile;
32946 #endif
32947 }
32948
32949 /*
32950 ** Read input from the file sqliterc_override. If that parameter is
32951 ** NULL, take it from find_xdg_file(), if found, or fall back to
32952 ** ~/.sqliterc.
32953 **
32954 ** Failure to read the config is only considered a failure if
32955 ** sqliterc_override is not NULL, in which case this function may emit
32956 ** a warning or, if ::bail_on_error is true, fail fatally if the file
32957 ** named by sqliterc_override is not found.
32958 */
32959 static void process_sqliterc(
32960 ShellState *p, /* Configuration data */
32961 const char *sqliterc_override /* Name of config file. NULL to use default */
32962 ){
32963 char *home_dir = NULL;
32964 char *sqliterc = (char*)sqliterc_override;
 
32965 FILE *inSaved = p->in;
32966 int savedLineno = p->lineno;
32967
32968 if( sqliterc == NULL ){
32969 sqliterc = find_xdg_file("XDG_CONFIG_HOME",
32970 ".config",
32971 "sqlite3/sqliterc");
32972 }
32973 if( sqliterc == NULL ){
32974 home_dir = find_home_dir(0);
32975 if( home_dir==0 ){
32976 eputz("-- warning: cannot find home directory;"
32977 " cannot read ~/.sqliterc\n");
32978 return;
32979 }
32980 sqliterc = sqlite3_mprintf("%s/.sqliterc",home_dir);
32981 shell_check_oom(sqliterc);
 
32982 }
32983 p->in = sqliterc ? sqlite3_fopen(sqliterc,"rb") : 0;
32984 if( p->in ){
32985 if( stdin_is_interactive ){
32986 sqlite3_fprintf(stderr,"-- Loading resources from %s\n", sqliterc);
32987 }
32988 if( process_input(p) && bail_on_error ) exit(1);
@@ -32966,11 +32991,13 @@
32991 sqlite3_fprintf(stderr,"cannot open: \"%s\"\n", sqliterc);
32992 if( bail_on_error ) exit(1);
32993 }
32994 p->in = inSaved;
32995 p->lineno = savedLineno;
32996 if( sqliterc != sqliterc_override ){
32997 sqlite3_free(sqliterc);
32998 }
32999 }
33000
33001 /*
33002 ** Show available command line options
33003 */
@@ -33734,11 +33761,10 @@
33761 /* Run commands received from standard input
33762 */
33763 if( stdin_is_interactive ){
33764 char *zHome;
33765 char *zHistory;
 
33766 sqlite3_fprintf(stdout,
33767 "SQLite version %s %.19s\n" /*extra-version-info*/
33768 "Enter \".help\" for usage hints.\n",
33769 sqlite3_libversion(), sqlite3_sourceid());
33770 if( warnInmemoryDb ){
@@ -33747,15 +33773,19 @@
33773 sputz(stdout, ".\nUse \".open FILENAME\" to reopen on a"
33774 " persistent database.\n");
33775 }
33776 zHistory = getenv("SQLITE_HISTORY");
33777 if( zHistory ){
33778 zHistory = sqlite3_mprintf("%s", zHistory);
33779 shell_check_oom(zHistory);
33780 }else{
33781 zHistory = find_xdg_file("XDG_STATE_HOME",
33782 ".local/state",
33783 "sqlite_history");
33784 if( 0==zHistory && (zHome = find_home_dir(0))!=0 ){
33785 zHistory = sqlite3_mprintf("%s/.sqlite_history", zHome);
33786 shell_check_oom(zHistory);
33787 }
33788 }
33789 if( zHistory ){ shell_read_history(zHistory); }
33790 #if (HAVE_READLINE || HAVE_EDITLINE) && !defined(SQLITE_OMIT_READLINE_COMPLETION)
33791 rl_attempted_completion_function = readline_completion;
@@ -33767,11 +33797,11 @@
33797 data.in = 0;
33798 rc = process_input(&data);
33799 if( zHistory ){
33800 shell_stifle_history(2000);
33801 shell_write_history(zHistory);
33802 sqlite3_free(zHistory);
33803 }
33804 }else{
33805 data.in = stdin;
33806 rc = process_input(&data);
33807 }
33808
+218 -154
--- extsrc/sqlite3.c
+++ extsrc/sqlite3.c
@@ -16,11 +16,11 @@
1616
** if you want a wrapper to interface SQLite with your choice of programming
1717
** language. The code for the "sqlite3" command-line shell is also in a
1818
** separate file. This file contains only code for the core SQLite library.
1919
**
2020
** The content in this amalgamation comes from Fossil check-in
21
-** 9f184f8dfa5ef6d57e10376adc30e0060ced with changes in files:
21
+** cf7163f82ca380958a79350473b2c5a2cebd with changes in files:
2222
**
2323
**
2424
*/
2525
#ifndef SQLITE_AMALGAMATION
2626
#define SQLITE_CORE 1
@@ -465,11 +465,11 @@
465465
** [sqlite3_libversion_number()], [sqlite3_sourceid()],
466466
** [sqlite_version()] and [sqlite_source_id()].
467467
*/
468468
#define SQLITE_VERSION "3.51.0"
469469
#define SQLITE_VERSION_NUMBER 3051000
470
-#define SQLITE_SOURCE_ID "2025-07-15 19:00:01 9f184f8dfa5ef6d57e10376adc30e0060ceda07d283c23dfdfe3dbdd6608f839"
470
+#define SQLITE_SOURCE_ID "2025-07-30 16:17:14 cf7163f82ca380958a79350473b2c5a2cebda7496d6d575fa2835c362010fea1"
471471
472472
/*
473473
** CAPI3REF: Run-Time Library Version Numbers
474474
** KEYWORDS: sqlite3_version sqlite3_sourceid
475475
**
@@ -814,10 +814,13 @@
814814
** [sqlite3_extended_errcode()].
815815
*/
816816
#define SQLITE_ERROR_MISSING_COLLSEQ (SQLITE_ERROR | (1<<8))
817817
#define SQLITE_ERROR_RETRY (SQLITE_ERROR | (2<<8))
818818
#define SQLITE_ERROR_SNAPSHOT (SQLITE_ERROR | (3<<8))
819
+#define SQLITE_ERROR_RESERVESIZE (SQLITE_ERROR | (4<<8))
820
+#define SQLITE_ERROR_KEY (SQLITE_ERROR | (5<<8))
821
+#define SQLITE_ERROR_UNABLE (SQLITE_ERROR | (6<<8))
819822
#define SQLITE_IOERR_READ (SQLITE_IOERR | (1<<8))
820823
#define SQLITE_IOERR_SHORT_READ (SQLITE_IOERR | (2<<8))
821824
#define SQLITE_IOERR_WRITE (SQLITE_IOERR | (3<<8))
822825
#define SQLITE_IOERR_FSYNC (SQLITE_IOERR | (4<<8))
823826
#define SQLITE_IOERR_DIR_FSYNC (SQLITE_IOERR | (5<<8))
@@ -848,10 +851,12 @@
848851
#define SQLITE_IOERR_COMMIT_ATOMIC (SQLITE_IOERR | (30<<8))
849852
#define SQLITE_IOERR_ROLLBACK_ATOMIC (SQLITE_IOERR | (31<<8))
850853
#define SQLITE_IOERR_DATA (SQLITE_IOERR | (32<<8))
851854
#define SQLITE_IOERR_CORRUPTFS (SQLITE_IOERR | (33<<8))
852855
#define SQLITE_IOERR_IN_PAGE (SQLITE_IOERR | (34<<8))
856
+#define SQLITE_IOERR_BADKEY (SQLITE_IOERR | (35<<8))
857
+#define SQLITE_IOERR_CODEC (SQLITE_IOERR | (36<<8))
853858
#define SQLITE_LOCKED_SHAREDCACHE (SQLITE_LOCKED | (1<<8))
854859
#define SQLITE_LOCKED_VTAB (SQLITE_LOCKED | (2<<8))
855860
#define SQLITE_BUSY_RECOVERY (SQLITE_BUSY | (1<<8))
856861
#define SQLITE_BUSY_SNAPSHOT (SQLITE_BUSY | (2<<8))
857862
#define SQLITE_BUSY_TIMEOUT (SQLITE_BUSY | (3<<8))
@@ -19503,10 +19508,11 @@
1950319508
AggInfo *pAggInfo; /* Used by TK_AGG_COLUMN and TK_AGG_FUNCTION */
1950419509
union {
1950519510
Table *pTab; /* TK_COLUMN: Table containing column. Can be NULL
1950619511
** for a column of an index on an expression */
1950719512
Window *pWin; /* EP_WinFunc: Window/Filter defn for a function */
19513
+ int nReg; /* TK_NULLS: Number of registers to NULL out */
1950819514
struct { /* TK_IN, TK_SELECT, and TK_EXISTS */
1950919515
int iAddr; /* Subroutine entry address */
1951019516
int regReturn; /* Register used to hold return address */
1951119517
} sub;
1951219518
} y;
@@ -21540,10 +21546,11 @@
2154021546
SQLITE_PRIVATE void sqlite3ExprCodeGeneratedColumn(Parse*, Table*, Column*, int);
2154121547
#endif
2154221548
SQLITE_PRIVATE void sqlite3ExprCodeCopy(Parse*, Expr*, int);
2154321549
SQLITE_PRIVATE void sqlite3ExprCodeFactorable(Parse*, Expr*, int);
2154421550
SQLITE_PRIVATE int sqlite3ExprCodeRunJustOnce(Parse*, Expr*, int);
21551
+SQLITE_PRIVATE void sqlite3ExprNullRegisterRange(Parse*, int, int);
2154521552
SQLITE_PRIVATE int sqlite3ExprCodeTemp(Parse*, Expr*, int*);
2154621553
SQLITE_PRIVATE int sqlite3ExprCodeTarget(Parse*, Expr*, int);
2154721554
SQLITE_PRIVATE int sqlite3ExprCodeExprList(Parse*, ExprList*, int, int, u8);
2154821555
#define SQLITE_ECEL_DUP 0x01 /* Deep, not shallow copies */
2154921556
#define SQLITE_ECEL_FACTOR 0x02 /* Factor out constant terms */
@@ -24366,13 +24373,15 @@
2436624373
SQLITE_PRIVATE void sqlite3VdbeMemAboutToChange(Vdbe*,Mem*);
2436724374
SQLITE_PRIVATE int sqlite3VdbeCheckMemInvariants(Mem*);
2436824375
#endif
2436924376
2437024377
#ifndef SQLITE_OMIT_FOREIGN_KEY
24371
-SQLITE_PRIVATE int sqlite3VdbeCheckFk(Vdbe *, int);
24378
+SQLITE_PRIVATE int sqlite3VdbeCheckFkImmediate(Vdbe*);
24379
+SQLITE_PRIVATE int sqlite3VdbeCheckFkDeferred(Vdbe*);
2437224380
#else
24373
-# define sqlite3VdbeCheckFk(p,i) 0
24381
+# define sqlite3VdbeCheckFkImmediate(p) 0
24382
+# define sqlite3VdbeCheckFkDeferred(p) 0
2437424383
#endif
2437524384
2437624385
#ifdef SQLITE_DEBUG
2437724386
SQLITE_PRIVATE void sqlite3VdbePrintSql(Vdbe*);
2437824387
SQLITE_PRIVATE void sqlite3VdbeMemPrettyPrint(Mem *pMem, StrAccum *pStr);
@@ -32252,11 +32261,25 @@
3225232261
length = sqlite3Strlen30(bufpt);
3225332262
break;
3225432263
}
3225532264
}
3225632265
if( s.sign=='-' ){
32257
- prefix = '-';
32266
+ if( flag_alternateform
32267
+ && !flag_prefix
32268
+ && xtype==etFLOAT
32269
+ && s.iDP<=iRound
32270
+ ){
32271
+ /* Suppress the minus sign if all of the following are true:
32272
+ ** * The value displayed is zero
32273
+ ** * The '#' flag is used
32274
+ ** * The '+' flag is not used, and
32275
+ ** * The format is %f
32276
+ */
32277
+ prefix = 0;
32278
+ }else{
32279
+ prefix = '-';
32280
+ }
3225832281
}else{
3225932282
prefix = flag_prefix;
3226032283
}
3226132284
3226232285
exp = s.iDP-1;
@@ -51192,10 +51215,107 @@
5119251215
** on allocation size granularity boundaries.
5119351216
** During sqlite3_os_init() we do a GetSystemInfo()
5119451217
** to get the granularity size.
5119551218
*/
5119651219
static SYSTEM_INFO winSysInfo;
51220
+
51221
+/*
51222
+** Convert a UTF-8 filename into whatever form the underlying
51223
+** operating system wants filenames in. Space to hold the result
51224
+** is obtained from malloc and must be freed by the calling
51225
+** function
51226
+**
51227
+** On Cygwin, 3 possible input forms are accepted:
51228
+** - If the filename starts with "<drive>:/" or "<drive>:\",
51229
+** it is converted to UTF-16 as-is.
51230
+** - If the filename contains '/', it is assumed to be a
51231
+** Cygwin absolute path, it is converted to a win32
51232
+** absolute path in UTF-16.
51233
+** - Otherwise it must be a filename only, the win32 filename
51234
+** is returned in UTF-16.
51235
+** Note: If the function cygwin_conv_path() fails, only
51236
+** UTF-8 -> UTF-16 conversion will be done. This can only
51237
+** happen when the file path >32k, in which case winUtf8ToUnicode()
51238
+** will fail too.
51239
+*/
51240
+static void *winConvertFromUtf8Filename(const char *zFilename){
51241
+ void *zConverted = 0;
51242
+ if( osIsNT() ){
51243
+#ifdef __CYGWIN__
51244
+ int nChar;
51245
+ LPWSTR zWideFilename;
51246
+
51247
+ if( osCygwin_conv_path && !(winIsDriveLetterAndColon(zFilename)
51248
+ && winIsDirSep(zFilename[2])) ){
51249
+ i64 nByte;
51250
+ int convertflag = CCP_POSIX_TO_WIN_W;
51251
+ if( !strchr(zFilename, '/') ) convertflag |= CCP_RELATIVE;
51252
+ nByte = (i64)osCygwin_conv_path(convertflag,
51253
+ zFilename, 0, 0);
51254
+ if( nByte>0 ){
51255
+ zConverted = sqlite3MallocZero(12+(u64)nByte);
51256
+ if ( zConverted==0 ){
51257
+ return zConverted;
51258
+ }
51259
+ zWideFilename = zConverted;
51260
+ /* Filenames should be prefixed, except when converted
51261
+ * full path already starts with "\\?\". */
51262
+ if( osCygwin_conv_path(convertflag, zFilename,
51263
+ zWideFilename+4, nByte)==0 ){
51264
+ if( (convertflag&CCP_RELATIVE) ){
51265
+ memmove(zWideFilename, zWideFilename+4, nByte);
51266
+ }else if( memcmp(zWideFilename+4, L"\\\\", 4) ){
51267
+ memcpy(zWideFilename, L"\\\\?\\", 8);
51268
+ }else if( zWideFilename[6]!='?' ){
51269
+ memmove(zWideFilename+6, zWideFilename+4, nByte);
51270
+ memcpy(zWideFilename, L"\\\\?\\UNC", 14);
51271
+ }else{
51272
+ memmove(zWideFilename, zWideFilename+4, nByte);
51273
+ }
51274
+ return zConverted;
51275
+ }
51276
+ sqlite3_free(zConverted);
51277
+ }
51278
+ }
51279
+ nChar = osMultiByteToWideChar(CP_UTF8, 0, zFilename, -1, NULL, 0);
51280
+ if( nChar==0 ){
51281
+ return 0;
51282
+ }
51283
+ zWideFilename = sqlite3MallocZero( nChar*sizeof(WCHAR)+12 );
51284
+ if( zWideFilename==0 ){
51285
+ return 0;
51286
+ }
51287
+ nChar = osMultiByteToWideChar(CP_UTF8, 0, zFilename, -1,
51288
+ zWideFilename, nChar);
51289
+ if( nChar==0 ){
51290
+ sqlite3_free(zWideFilename);
51291
+ zWideFilename = 0;
51292
+ }else if( nChar>MAX_PATH
51293
+ && winIsDriveLetterAndColon(zFilename)
51294
+ && winIsDirSep(zFilename[2]) ){
51295
+ memmove(zWideFilename+4, zWideFilename, nChar*sizeof(WCHAR));
51296
+ zWideFilename[2] = '\\';
51297
+ memcpy(zWideFilename, L"\\\\?\\", 8);
51298
+ }else if( nChar>MAX_PATH
51299
+ && winIsDirSep(zFilename[0]) && winIsDirSep(zFilename[1])
51300
+ && zFilename[2] != '?' ){
51301
+ memmove(zWideFilename+6, zWideFilename, nChar*sizeof(WCHAR));
51302
+ memcpy(zWideFilename, L"\\\\?\\UNC", 14);
51303
+ }
51304
+ zConverted = zWideFilename;
51305
+#else
51306
+ zConverted = winUtf8ToUnicode(zFilename);
51307
+#endif /* __CYGWIN__ */
51308
+ }
51309
+#if defined(SQLITE_WIN32_HAS_ANSI) && defined(_WIN32)
51310
+ else{
51311
+ zConverted = winUtf8ToMbcs(zFilename, osAreFileApisANSI());
51312
+ }
51313
+#endif
51314
+ /* caller will handle out of memory */
51315
+ return zConverted;
51316
+}
5119751317
5119851318
#ifndef SQLITE_OMIT_WAL
5119951319
5120051320
/*
5120151321
** Helper functions to obtain and relinquish the global mutex. The
@@ -51387,107 +51507,10 @@
5138751507
5138851508
return rc;
5138951509
}
5139051510
5139151511
51392
-/*
51393
-** Convert a UTF-8 filename into whatever form the underlying
51394
-** operating system wants filenames in. Space to hold the result
51395
-** is obtained from malloc and must be freed by the calling
51396
-** function
51397
-**
51398
-** On Cygwin, 3 possible input forms are accepted:
51399
-** - If the filename starts with "<drive>:/" or "<drive>:\",
51400
-** it is converted to UTF-16 as-is.
51401
-** - If the filename contains '/', it is assumed to be a
51402
-** Cygwin absolute path, it is converted to a win32
51403
-** absolute path in UTF-16.
51404
-** - Otherwise it must be a filename only, the win32 filename
51405
-** is returned in UTF-16.
51406
-** Note: If the function cygwin_conv_path() fails, only
51407
-** UTF-8 -> UTF-16 conversion will be done. This can only
51408
-** happen when the file path >32k, in which case winUtf8ToUnicode()
51409
-** will fail too.
51410
-*/
51411
-static void *winConvertFromUtf8Filename(const char *zFilename){
51412
- void *zConverted = 0;
51413
- if( osIsNT() ){
51414
-#ifdef __CYGWIN__
51415
- int nChar;
51416
- LPWSTR zWideFilename;
51417
-
51418
- if( osCygwin_conv_path && !(winIsDriveLetterAndColon(zFilename)
51419
- && winIsDirSep(zFilename[2])) ){
51420
- i64 nByte;
51421
- int convertflag = CCP_POSIX_TO_WIN_W;
51422
- if( !strchr(zFilename, '/') ) convertflag |= CCP_RELATIVE;
51423
- nByte = (i64)osCygwin_conv_path(convertflag,
51424
- zFilename, 0, 0);
51425
- if( nByte>0 ){
51426
- zConverted = sqlite3MallocZero(12+(u64)nByte);
51427
- if ( zConverted==0 ){
51428
- return zConverted;
51429
- }
51430
- zWideFilename = zConverted;
51431
- /* Filenames should be prefixed, except when converted
51432
- * full path already starts with "\\?\". */
51433
- if( osCygwin_conv_path(convertflag, zFilename,
51434
- zWideFilename+4, nByte)==0 ){
51435
- if( (convertflag&CCP_RELATIVE) ){
51436
- memmove(zWideFilename, zWideFilename+4, nByte);
51437
- }else if( memcmp(zWideFilename+4, L"\\\\", 4) ){
51438
- memcpy(zWideFilename, L"\\\\?\\", 8);
51439
- }else if( zWideFilename[6]!='?' ){
51440
- memmove(zWideFilename+6, zWideFilename+4, nByte);
51441
- memcpy(zWideFilename, L"\\\\?\\UNC", 14);
51442
- }else{
51443
- memmove(zWideFilename, zWideFilename+4, nByte);
51444
- }
51445
- return zConverted;
51446
- }
51447
- sqlite3_free(zConverted);
51448
- }
51449
- }
51450
- nChar = osMultiByteToWideChar(CP_UTF8, 0, zFilename, -1, NULL, 0);
51451
- if( nChar==0 ){
51452
- return 0;
51453
- }
51454
- zWideFilename = sqlite3MallocZero( nChar*sizeof(WCHAR)+12 );
51455
- if( zWideFilename==0 ){
51456
- return 0;
51457
- }
51458
- nChar = osMultiByteToWideChar(CP_UTF8, 0, zFilename, -1,
51459
- zWideFilename, nChar);
51460
- if( nChar==0 ){
51461
- sqlite3_free(zWideFilename);
51462
- zWideFilename = 0;
51463
- }else if( nChar>MAX_PATH
51464
- && winIsDriveLetterAndColon(zFilename)
51465
- && winIsDirSep(zFilename[2]) ){
51466
- memmove(zWideFilename+4, zWideFilename, nChar*sizeof(WCHAR));
51467
- zWideFilename[2] = '\\';
51468
- memcpy(zWideFilename, L"\\\\?\\", 8);
51469
- }else if( nChar>MAX_PATH
51470
- && winIsDirSep(zFilename[0]) && winIsDirSep(zFilename[1])
51471
- && zFilename[2] != '?' ){
51472
- memmove(zWideFilename+6, zWideFilename, nChar*sizeof(WCHAR));
51473
- memcpy(zWideFilename, L"\\\\?\\UNC", 14);
51474
- }
51475
- zConverted = zWideFilename;
51476
-#else
51477
- zConverted = winUtf8ToUnicode(zFilename);
51478
-#endif /* __CYGWIN__ */
51479
- }
51480
-#if defined(SQLITE_WIN32_HAS_ANSI) && defined(_WIN32)
51481
- else{
51482
- zConverted = winUtf8ToMbcs(zFilename, osAreFileApisANSI());
51483
- }
51484
-#endif
51485
- /* caller will handle out of memory */
51486
- return zConverted;
51487
-}
51488
-
5148951512
/*
5149051513
** This function is used to open a handle on a *-shm file.
5149151514
**
5149251515
** If SQLITE_ENABLE_SETLK_TIMEOUT is defined at build time, then the file
5149351516
** is opened with FILE_FLAG_OVERLAPPED specified. If not, it is not.
@@ -89078,14 +89101,16 @@
8907889101
** simple case then too.
8907989102
*/
8908089103
if( 0==sqlite3Strlen30(sqlite3BtreeGetFilename(db->aDb[0].pBt))
8908189104
|| nTrans<=1
8908289105
){
89083
- for(i=0; rc==SQLITE_OK && i<db->nDb; i++){
89084
- Btree *pBt = db->aDb[i].pBt;
89085
- if( pBt ){
89086
- rc = sqlite3BtreeCommitPhaseOne(pBt, 0);
89106
+ if( needXcommit ){
89107
+ for(i=0; rc==SQLITE_OK && i<db->nDb; i++){
89108
+ Btree *pBt = db->aDb[i].pBt;
89109
+ if( sqlite3BtreeTxnState(pBt)>=SQLITE_TXN_WRITE ){
89110
+ rc = sqlite3BtreeCommitPhaseOne(pBt, 0);
89111
+ }
8908789112
}
8908889113
}
8908989114
8909089115
/* Do the commit only if all databases successfully complete phase 1.
8909189116
** If one of the BtreeCommitPhaseOne() calls fails, this indicates an
@@ -89092,11 +89117,13 @@
8909289117
** IO error while deleting or truncating a journal file. It is unlikely,
8909389118
** but could happen. In this case abandon processing and return the error.
8909489119
*/
8909589120
for(i=0; rc==SQLITE_OK && i<db->nDb; i++){
8909689121
Btree *pBt = db->aDb[i].pBt;
89097
- if( pBt ){
89122
+ int txn = sqlite3BtreeTxnState(pBt);
89123
+ if( txn!=SQLITE_TXN_NONE ){
89124
+ assert( needXcommit || txn==SQLITE_TXN_READ );
8909889125
rc = sqlite3BtreeCommitPhaseTwo(pBt, 0);
8909989126
}
8910089127
}
8910189128
if( rc==SQLITE_OK ){
8910289129
sqlite3VtabCommit(db);
@@ -89347,32 +89374,35 @@
8934789374
return SQLITE_OK;
8934889375
}
8934989376
8935089377
8935189378
/*
89352
-** This function is called when a transaction opened by the database
89379
+** These functions are called when a transaction opened by the database
8935389380
** handle associated with the VM passed as an argument is about to be
89354
-** committed. If there are outstanding deferred foreign key constraint
89355
-** violations, return SQLITE_ERROR. Otherwise, SQLITE_OK.
89381
+** committed. If there are outstanding foreign key constraint violations
89382
+** return an error code. Otherwise, SQLITE_OK.
8935689383
**
8935789384
** If there are outstanding FK violations and this function returns
89358
-** SQLITE_ERROR, set the result of the VM to SQLITE_CONSTRAINT_FOREIGNKEY
89359
-** and write an error message to it. Then return SQLITE_ERROR.
89385
+** non-zero, set the result of the VM to SQLITE_CONSTRAINT_FOREIGNKEY
89386
+** and write an error message to it.
8936089387
*/
8936189388
#ifndef SQLITE_OMIT_FOREIGN_KEY
89362
-SQLITE_PRIVATE int sqlite3VdbeCheckFk(Vdbe *p, int deferred){
89389
+static SQLITE_NOINLINE int vdbeFkError(Vdbe *p){
89390
+ p->rc = SQLITE_CONSTRAINT_FOREIGNKEY;
89391
+ p->errorAction = OE_Abort;
89392
+ sqlite3VdbeError(p, "FOREIGN KEY constraint failed");
89393
+ if( (p->prepFlags & SQLITE_PREPARE_SAVESQL)==0 ) return SQLITE_ERROR;
89394
+ return SQLITE_CONSTRAINT_FOREIGNKEY;
89395
+}
89396
+SQLITE_PRIVATE int sqlite3VdbeCheckFkImmediate(Vdbe *p){
89397
+ if( p->nFkConstraint==0 ) return SQLITE_OK;
89398
+ return vdbeFkError(p);
89399
+}
89400
+SQLITE_PRIVATE int sqlite3VdbeCheckFkDeferred(Vdbe *p){
8936389401
sqlite3 *db = p->db;
89364
- if( (deferred && (db->nDeferredCons+db->nDeferredImmCons)>0)
89365
- || (!deferred && p->nFkConstraint>0)
89366
- ){
89367
- p->rc = SQLITE_CONSTRAINT_FOREIGNKEY;
89368
- p->errorAction = OE_Abort;
89369
- sqlite3VdbeError(p, "FOREIGN KEY constraint failed");
89370
- if( (p->prepFlags & SQLITE_PREPARE_SAVESQL)==0 ) return SQLITE_ERROR;
89371
- return SQLITE_CONSTRAINT_FOREIGNKEY;
89372
- }
89373
- return SQLITE_OK;
89402
+ if( (db->nDeferredCons+db->nDeferredImmCons)==0 ) return SQLITE_OK;
89403
+ return vdbeFkError(p);
8937489404
}
8937589405
#endif
8937689406
8937789407
/*
8937889408
** This routine is called the when a VDBE tries to halt. If the VDBE
@@ -89462,11 +89492,11 @@
8946289492
}
8946389493
}
8946489494
8946589495
/* Check for immediate foreign key violations. */
8946689496
if( p->rc==SQLITE_OK || (p->errorAction==OE_Fail && !isSpecialError) ){
89467
- (void)sqlite3VdbeCheckFk(p, 0);
89497
+ (void)sqlite3VdbeCheckFkImmediate(p);
8946889498
}
8946989499
8947089500
/* If the auto-commit flag is set and this is the only active writer
8947189501
** VM, then we do either a commit or rollback of the current transaction.
8947289502
**
@@ -89476,11 +89506,11 @@
8947689506
if( !sqlite3VtabInSync(db)
8947789507
&& db->autoCommit
8947889508
&& db->nVdbeWrite==(p->readOnly==0)
8947989509
){
8948089510
if( p->rc==SQLITE_OK || (p->errorAction==OE_Fail && !isSpecialError) ){
89481
- rc = sqlite3VdbeCheckFk(p, 1);
89511
+ rc = sqlite3VdbeCheckFkDeferred(p);
8948289512
if( rc!=SQLITE_OK ){
8948389513
if( NEVER(p->readOnly) ){
8948489514
sqlite3VdbeLeave(p);
8948589515
return SQLITE_ERROR;
8948689516
}
@@ -90341,19 +90371,19 @@
9034190371
/* pMem->flags = 0; // sqlite3VdbeSerialGet() will set this for us */
9034290372
pMem->szMalloc = 0;
9034390373
pMem->z = 0;
9034490374
sqlite3VdbeSerialGet(&aKey[d], serial_type, pMem);
9034590375
d += sqlite3VdbeSerialTypeLen(serial_type);
90346
- pMem++;
9034790376
if( (++u)>=p->nField ) break;
90377
+ pMem++;
9034890378
}
9034990379
if( d>(u32)nKey && u ){
9035090380
assert( CORRUPT_DB );
9035190381
/* In a corrupt record entry, the last pMem might have been set up using
9035290382
** uninitialized memory. Overwrite its value with NULL, to prevent
9035390383
** warnings from MSAN. */
90354
- sqlite3VdbeMemSetNull(pMem-1);
90384
+ sqlite3VdbeMemSetNull(pMem-(u<p->nField));
9035590385
}
9035690386
testcase( u == pKeyInfo->nKeyField + 1 );
9035790387
testcase( u < pKeyInfo->nKeyField + 1 );
9035890388
assert( u<=pKeyInfo->nKeyField + 1 );
9035990389
p->nField = u;
@@ -90520,10 +90550,36 @@
9052090550
** Both *pMem1 and *pMem2 contain string values. Compare the two values
9052190551
** using the collation sequence pColl. As usual, return a negative , zero
9052290552
** or positive value if *pMem1 is less than, equal to or greater than
9052390553
** *pMem2, respectively. Similar in spirit to "rc = (*pMem1) - (*pMem2);".
9052490554
*/
90555
+static SQLITE_NOINLINE int vdbeCompareMemStringWithEncodingChange(
90556
+ const Mem *pMem1,
90557
+ const Mem *pMem2,
90558
+ const CollSeq *pColl,
90559
+ u8 *prcErr /* If an OOM occurs, set to SQLITE_NOMEM */
90560
+){
90561
+ int rc;
90562
+ const void *v1, *v2;
90563
+ Mem c1;
90564
+ Mem c2;
90565
+ sqlite3VdbeMemInit(&c1, pMem1->db, MEM_Null);
90566
+ sqlite3VdbeMemInit(&c2, pMem1->db, MEM_Null);
90567
+ sqlite3VdbeMemShallowCopy(&c1, pMem1, MEM_Ephem);
90568
+ sqlite3VdbeMemShallowCopy(&c2, pMem2, MEM_Ephem);
90569
+ v1 = sqlite3ValueText((sqlite3_value*)&c1, pColl->enc);
90570
+ v2 = sqlite3ValueText((sqlite3_value*)&c2, pColl->enc);
90571
+ if( (v1==0 || v2==0) ){
90572
+ if( prcErr ) *prcErr = SQLITE_NOMEM_BKPT;
90573
+ rc = 0;
90574
+ }else{
90575
+ rc = pColl->xCmp(pColl->pUser, c1.n, v1, c2.n, v2);
90576
+ }
90577
+ sqlite3VdbeMemReleaseMalloc(&c1);
90578
+ sqlite3VdbeMemReleaseMalloc(&c2);
90579
+ return rc;
90580
+}
9052590581
static int vdbeCompareMemString(
9052690582
const Mem *pMem1,
9052790583
const Mem *pMem2,
9052890584
const CollSeq *pColl,
9052990585
u8 *prcErr /* If an OOM occurs, set to SQLITE_NOMEM */
@@ -90531,29 +90587,11 @@
9053190587
if( pMem1->enc==pColl->enc ){
9053290588
/* The strings are already in the correct encoding. Call the
9053390589
** comparison function directly */
9053490590
return pColl->xCmp(pColl->pUser,pMem1->n,pMem1->z,pMem2->n,pMem2->z);
9053590591
}else{
90536
- int rc;
90537
- const void *v1, *v2;
90538
- Mem c1;
90539
- Mem c2;
90540
- sqlite3VdbeMemInit(&c1, pMem1->db, MEM_Null);
90541
- sqlite3VdbeMemInit(&c2, pMem1->db, MEM_Null);
90542
- sqlite3VdbeMemShallowCopy(&c1, pMem1, MEM_Ephem);
90543
- sqlite3VdbeMemShallowCopy(&c2, pMem2, MEM_Ephem);
90544
- v1 = sqlite3ValueText((sqlite3_value*)&c1, pColl->enc);
90545
- v2 = sqlite3ValueText((sqlite3_value*)&c2, pColl->enc);
90546
- if( (v1==0 || v2==0) ){
90547
- if( prcErr ) *prcErr = SQLITE_NOMEM_BKPT;
90548
- rc = 0;
90549
- }else{
90550
- rc = pColl->xCmp(pColl->pUser, c1.n, v1, c2.n, v2);
90551
- }
90552
- sqlite3VdbeMemReleaseMalloc(&c1);
90553
- sqlite3VdbeMemReleaseMalloc(&c2);
90554
- return rc;
90592
+ return vdbeCompareMemStringWithEncodingChange(pMem1,pMem2,pColl,prcErr);
9055590593
}
9055690594
}
9055790595
9055890596
/*
9055990597
** The input pBlob is guaranteed to be a Blob that is not marked
@@ -96256,11 +96294,11 @@
9625696294
** exits. This opcode is used to raise foreign key constraint errors prior
9625796295
** to returning results such as a row change count or the result of a
9625896296
** RETURNING clause.
9625996297
*/
9626096298
case OP_FkCheck: {
96261
- if( (rc = sqlite3VdbeCheckFk(p,0))!=SQLITE_OK ){
96299
+ if( (rc = sqlite3VdbeCheckFkImmediate(p))!=SQLITE_OK ){
9626296300
goto abort_due_to_error;
9626396301
}
9626496302
break;
9626596303
}
9626696304
@@ -98440,11 +98478,11 @@
9844098478
** and this is a RELEASE command, then the current transaction
9844198479
** is committed.
9844298480
*/
9844398481
int isTransaction = pSavepoint->pNext==0 && db->isTransactionSavepoint;
9844498482
if( isTransaction && p1==SAVEPOINT_RELEASE ){
98445
- if( (rc = sqlite3VdbeCheckFk(p, 1))!=SQLITE_OK ){
98483
+ if( (rc = sqlite3VdbeCheckFkDeferred(p))!=SQLITE_OK ){
9844698484
goto vdbe_return;
9844798485
}
9844898486
db->autoCommit = 1;
9844998487
if( sqlite3VdbeHalt(p)==SQLITE_BUSY ){
9845098488
p->pc = (int)(pOp - aOp);
@@ -98558,11 +98596,11 @@
9855898596
*/
9855998597
sqlite3VdbeError(p, "cannot commit transaction - "
9856098598
"SQL statements in progress");
9856198599
rc = SQLITE_BUSY;
9856298600
goto abort_due_to_error;
98563
- }else if( (rc = sqlite3VdbeCheckFk(p, 1))!=SQLITE_OK ){
98601
+ }else if( (rc = sqlite3VdbeCheckFkDeferred(p))!=SQLITE_OK ){
9856498602
goto vdbe_return;
9856598603
}else{
9856698604
db->autoCommit = (u8)desiredAutoCommit;
9856798605
}
9856898606
if( sqlite3VdbeHalt(p)==SQLITE_BUSY ){
@@ -109256,12 +109294,12 @@
109256109294
assert( ((X)&~(NC_IsCheck|NC_PartIdx|NC_IdxExpr|NC_GenCol))==0 ); \
109257109295
if( ((N)->ncFlags & (X))!=0 ) notValidImpl(P,N,M,E,R);
109258109296
109259109297
/*
109260109298
** Expression p should encode a floating point value between 1.0 and 0.0.
109261
-** Return 1024 times this value. Or return -1 if p is not a floating point
109262
-** value between 1.0 and 0.0.
109299
+** Return 134,217,728 (2^27) times this value. Or return -1 if p is not
109300
+** a floating point value between 1.0 and 0.0.
109263109301
*/
109264109302
static int exprProbability(Expr *p){
109265109303
double r = -1.0;
109266109304
if( p->op!=TK_FLOAT ) return -1;
109267109305
assert( !ExprHasProperty(p, EP_IntValue) );
@@ -115633,10 +115671,16 @@
115633115671
case TK_STRING: {
115634115672
assert( !ExprHasProperty(pExpr, EP_IntValue) );
115635115673
sqlite3VdbeLoadString(v, target, pExpr->u.zToken);
115636115674
return target;
115637115675
}
115676
+ case TK_NULLS: {
115677
+ /* Set a range of registers to NULL. pExpr->y.nReg registers starting
115678
+ ** with target */
115679
+ sqlite3VdbeAddOp3(v, OP_Null, 0, target, target + pExpr->y.nReg - 1);
115680
+ return target;
115681
+ }
115638115682
default: {
115639115683
/* Make NULL the default case so that if a bug causes an illegal
115640115684
** Expr node to be passed into this function, it will be handled
115641115685
** sanely and not crash. But keep the assert() to bring the problem
115642115686
** to the attention of the developers. */
@@ -116341,10 +116385,29 @@
116341116385
}
116342116386
pParse->pConstExpr = p;
116343116387
}
116344116388
return regDest;
116345116389
}
116390
+
116391
+/*
116392
+** Make arrangements to invoke OP_Null on a range of registers
116393
+** during initialization.
116394
+*/
116395
+SQLITE_PRIVATE SQLITE_NOINLINE void sqlite3ExprNullRegisterRange(
116396
+ Parse *pParse, /* Parsing context */
116397
+ int iReg, /* First register to set to NULL */
116398
+ int nReg /* Number of sequential registers to NULL out */
116399
+){
116400
+ u8 okConstFactor = pParse->okConstFactor;
116401
+ Expr t;
116402
+ memset(&t, 0, sizeof(t));
116403
+ t.op = TK_NULLS;
116404
+ t.y.nReg = nReg;
116405
+ pParse->okConstFactor = 1;
116406
+ sqlite3ExprCodeRunJustOnce(pParse, &t, iReg);
116407
+ pParse->okConstFactor = okConstFactor;
116408
+}
116346116409
116347116410
/*
116348116411
** Generate code to evaluate an expression and store the results
116349116412
** into a register. Return the register number where the results
116350116413
** are stored.
@@ -152746,10 +152809,11 @@
152746152809
Select *pSub = pWhere->x.pSelect;
152747152810
Expr *pSubWhere = pSub->pWhere;
152748152811
if( pSub->pSrc->nSrc==1
152749152812
&& (pSub->selFlags & SF_Aggregate)==0
152750152813
&& !pSub->pSrc->a[0].fg.isSubquery
152814
+ && pSub->pLimit==0
152751152815
){
152752152816
memset(pWhere, 0, sizeof(*pWhere));
152753152817
pWhere->op = TK_INTEGER;
152754152818
pWhere->u.iValue = 1;
152755152819
ExprSetProperty(pWhere, EP_IntValue);
@@ -153766,10 +153830,11 @@
153766153830
iBMem = pParse->nMem + 1;
153767153831
pParse->nMem += pGroupBy->nExpr;
153768153832
sqlite3VdbeAddOp2(v, OP_Integer, 0, iAbortFlag);
153769153833
VdbeComment((v, "clear abort flag"));
153770153834
sqlite3VdbeAddOp3(v, OP_Null, 0, iAMem, iAMem+pGroupBy->nExpr-1);
153835
+ sqlite3ExprNullRegisterRange(pParse, iAMem, pGroupBy->nExpr);
153771153836
153772153837
/* Begin a loop that will extract all source rows in GROUP BY order.
153773153838
** This might involve two separate loops with an OP_Sort in between, or
153774153839
** it might be a single loop that uses an index to extract information
153775153840
** in the right order to begin with.
@@ -169084,10 +169149,11 @@
169084169149
if( pProbe->bNoQuery ) continue;
169085169150
rSize = pProbe->aiRowLogEst[0];
169086169151
pNew->u.btree.nEq = 0;
169087169152
pNew->u.btree.nBtm = 0;
169088169153
pNew->u.btree.nTop = 0;
169154
+ pNew->u.btree.nDistinctCol = 0;
169089169155
pNew->nSkip = 0;
169090169156
pNew->nLTerm = 0;
169091169157
pNew->iSortIdx = 0;
169092169158
pNew->rSetup = 0;
169093169159
pNew->prereq = mPrereq;
@@ -170152,12 +170218,10 @@
170152170218
&& ((wctrlFlags&(WHERE_DISTINCTBY|WHERE_SORTBYGROUP))!=WHERE_DISTINCTBY)
170153170219
){
170154170220
obSat = obDone;
170155170221
}
170156170222
break;
170157
- }else if( wctrlFlags & WHERE_DISTINCTBY ){
170158
- pLoop->u.btree.nDistinctCol = 0;
170159170223
}
170160170224
iCur = pWInfo->pTabList->a[pLoop->iTab].iCursor;
170161170225
170162170226
/* Mark off any ORDER BY term X that is a column in the table of
170163170227
** the current loop for which there is term in the WHERE
@@ -258113,11 +258177,11 @@
258113258177
int nArg, /* Number of args */
258114258178
sqlite3_value **apUnused /* Function arguments */
258115258179
){
258116258180
assert( nArg==0 );
258117258181
UNUSED_PARAM2(nArg, apUnused);
258118
- sqlite3_result_text(pCtx, "fts5: 2025-07-15 19:00:01 9f184f8dfa5ef6d57e10376adc30e0060ceda07d283c23dfdfe3dbdd6608f839", -1, SQLITE_TRANSIENT);
258182
+ sqlite3_result_text(pCtx, "fts5: 2025-07-30 16:17:14 cf7163f82ca380958a79350473b2c5a2cebda7496d6d575fa2835c362010fea1", -1, SQLITE_TRANSIENT);
258119258183
}
258120258184
258121258185
/*
258122258186
** Implementation of fts5_locale(LOCALE, TEXT) function.
258123258187
**
258124258188
--- extsrc/sqlite3.c
+++ extsrc/sqlite3.c
@@ -16,11 +16,11 @@
16 ** if you want a wrapper to interface SQLite with your choice of programming
17 ** language. The code for the "sqlite3" command-line shell is also in a
18 ** separate file. This file contains only code for the core SQLite library.
19 **
20 ** The content in this amalgamation comes from Fossil check-in
21 ** 9f184f8dfa5ef6d57e10376adc30e0060ced with changes in files:
22 **
23 **
24 */
25 #ifndef SQLITE_AMALGAMATION
26 #define SQLITE_CORE 1
@@ -465,11 +465,11 @@
465 ** [sqlite3_libversion_number()], [sqlite3_sourceid()],
466 ** [sqlite_version()] and [sqlite_source_id()].
467 */
468 #define SQLITE_VERSION "3.51.0"
469 #define SQLITE_VERSION_NUMBER 3051000
470 #define SQLITE_SOURCE_ID "2025-07-15 19:00:01 9f184f8dfa5ef6d57e10376adc30e0060ceda07d283c23dfdfe3dbdd6608f839"
471
472 /*
473 ** CAPI3REF: Run-Time Library Version Numbers
474 ** KEYWORDS: sqlite3_version sqlite3_sourceid
475 **
@@ -814,10 +814,13 @@
814 ** [sqlite3_extended_errcode()].
815 */
816 #define SQLITE_ERROR_MISSING_COLLSEQ (SQLITE_ERROR | (1<<8))
817 #define SQLITE_ERROR_RETRY (SQLITE_ERROR | (2<<8))
818 #define SQLITE_ERROR_SNAPSHOT (SQLITE_ERROR | (3<<8))
 
 
 
819 #define SQLITE_IOERR_READ (SQLITE_IOERR | (1<<8))
820 #define SQLITE_IOERR_SHORT_READ (SQLITE_IOERR | (2<<8))
821 #define SQLITE_IOERR_WRITE (SQLITE_IOERR | (3<<8))
822 #define SQLITE_IOERR_FSYNC (SQLITE_IOERR | (4<<8))
823 #define SQLITE_IOERR_DIR_FSYNC (SQLITE_IOERR | (5<<8))
@@ -848,10 +851,12 @@
848 #define SQLITE_IOERR_COMMIT_ATOMIC (SQLITE_IOERR | (30<<8))
849 #define SQLITE_IOERR_ROLLBACK_ATOMIC (SQLITE_IOERR | (31<<8))
850 #define SQLITE_IOERR_DATA (SQLITE_IOERR | (32<<8))
851 #define SQLITE_IOERR_CORRUPTFS (SQLITE_IOERR | (33<<8))
852 #define SQLITE_IOERR_IN_PAGE (SQLITE_IOERR | (34<<8))
 
 
853 #define SQLITE_LOCKED_SHAREDCACHE (SQLITE_LOCKED | (1<<8))
854 #define SQLITE_LOCKED_VTAB (SQLITE_LOCKED | (2<<8))
855 #define SQLITE_BUSY_RECOVERY (SQLITE_BUSY | (1<<8))
856 #define SQLITE_BUSY_SNAPSHOT (SQLITE_BUSY | (2<<8))
857 #define SQLITE_BUSY_TIMEOUT (SQLITE_BUSY | (3<<8))
@@ -19503,10 +19508,11 @@
19503 AggInfo *pAggInfo; /* Used by TK_AGG_COLUMN and TK_AGG_FUNCTION */
19504 union {
19505 Table *pTab; /* TK_COLUMN: Table containing column. Can be NULL
19506 ** for a column of an index on an expression */
19507 Window *pWin; /* EP_WinFunc: Window/Filter defn for a function */
 
19508 struct { /* TK_IN, TK_SELECT, and TK_EXISTS */
19509 int iAddr; /* Subroutine entry address */
19510 int regReturn; /* Register used to hold return address */
19511 } sub;
19512 } y;
@@ -21540,10 +21546,11 @@
21540 SQLITE_PRIVATE void sqlite3ExprCodeGeneratedColumn(Parse*, Table*, Column*, int);
21541 #endif
21542 SQLITE_PRIVATE void sqlite3ExprCodeCopy(Parse*, Expr*, int);
21543 SQLITE_PRIVATE void sqlite3ExprCodeFactorable(Parse*, Expr*, int);
21544 SQLITE_PRIVATE int sqlite3ExprCodeRunJustOnce(Parse*, Expr*, int);
 
21545 SQLITE_PRIVATE int sqlite3ExprCodeTemp(Parse*, Expr*, int*);
21546 SQLITE_PRIVATE int sqlite3ExprCodeTarget(Parse*, Expr*, int);
21547 SQLITE_PRIVATE int sqlite3ExprCodeExprList(Parse*, ExprList*, int, int, u8);
21548 #define SQLITE_ECEL_DUP 0x01 /* Deep, not shallow copies */
21549 #define SQLITE_ECEL_FACTOR 0x02 /* Factor out constant terms */
@@ -24366,13 +24373,15 @@
24366 SQLITE_PRIVATE void sqlite3VdbeMemAboutToChange(Vdbe*,Mem*);
24367 SQLITE_PRIVATE int sqlite3VdbeCheckMemInvariants(Mem*);
24368 #endif
24369
24370 #ifndef SQLITE_OMIT_FOREIGN_KEY
24371 SQLITE_PRIVATE int sqlite3VdbeCheckFk(Vdbe *, int);
 
24372 #else
24373 # define sqlite3VdbeCheckFk(p,i) 0
 
24374 #endif
24375
24376 #ifdef SQLITE_DEBUG
24377 SQLITE_PRIVATE void sqlite3VdbePrintSql(Vdbe*);
24378 SQLITE_PRIVATE void sqlite3VdbeMemPrettyPrint(Mem *pMem, StrAccum *pStr);
@@ -32252,11 +32261,25 @@
32252 length = sqlite3Strlen30(bufpt);
32253 break;
32254 }
32255 }
32256 if( s.sign=='-' ){
32257 prefix = '-';
 
 
 
 
 
 
 
 
 
 
 
 
 
 
32258 }else{
32259 prefix = flag_prefix;
32260 }
32261
32262 exp = s.iDP-1;
@@ -51192,10 +51215,107 @@
51192 ** on allocation size granularity boundaries.
51193 ** During sqlite3_os_init() we do a GetSystemInfo()
51194 ** to get the granularity size.
51195 */
51196 static SYSTEM_INFO winSysInfo;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
51197
51198 #ifndef SQLITE_OMIT_WAL
51199
51200 /*
51201 ** Helper functions to obtain and relinquish the global mutex. The
@@ -51387,107 +51507,10 @@
51387
51388 return rc;
51389 }
51390
51391
51392 /*
51393 ** Convert a UTF-8 filename into whatever form the underlying
51394 ** operating system wants filenames in. Space to hold the result
51395 ** is obtained from malloc and must be freed by the calling
51396 ** function
51397 **
51398 ** On Cygwin, 3 possible input forms are accepted:
51399 ** - If the filename starts with "<drive>:/" or "<drive>:\",
51400 ** it is converted to UTF-16 as-is.
51401 ** - If the filename contains '/', it is assumed to be a
51402 ** Cygwin absolute path, it is converted to a win32
51403 ** absolute path in UTF-16.
51404 ** - Otherwise it must be a filename only, the win32 filename
51405 ** is returned in UTF-16.
51406 ** Note: If the function cygwin_conv_path() fails, only
51407 ** UTF-8 -> UTF-16 conversion will be done. This can only
51408 ** happen when the file path >32k, in which case winUtf8ToUnicode()
51409 ** will fail too.
51410 */
51411 static void *winConvertFromUtf8Filename(const char *zFilename){
51412 void *zConverted = 0;
51413 if( osIsNT() ){
51414 #ifdef __CYGWIN__
51415 int nChar;
51416 LPWSTR zWideFilename;
51417
51418 if( osCygwin_conv_path && !(winIsDriveLetterAndColon(zFilename)
51419 && winIsDirSep(zFilename[2])) ){
51420 i64 nByte;
51421 int convertflag = CCP_POSIX_TO_WIN_W;
51422 if( !strchr(zFilename, '/') ) convertflag |= CCP_RELATIVE;
51423 nByte = (i64)osCygwin_conv_path(convertflag,
51424 zFilename, 0, 0);
51425 if( nByte>0 ){
51426 zConverted = sqlite3MallocZero(12+(u64)nByte);
51427 if ( zConverted==0 ){
51428 return zConverted;
51429 }
51430 zWideFilename = zConverted;
51431 /* Filenames should be prefixed, except when converted
51432 * full path already starts with "\\?\". */
51433 if( osCygwin_conv_path(convertflag, zFilename,
51434 zWideFilename+4, nByte)==0 ){
51435 if( (convertflag&CCP_RELATIVE) ){
51436 memmove(zWideFilename, zWideFilename+4, nByte);
51437 }else if( memcmp(zWideFilename+4, L"\\\\", 4) ){
51438 memcpy(zWideFilename, L"\\\\?\\", 8);
51439 }else if( zWideFilename[6]!='?' ){
51440 memmove(zWideFilename+6, zWideFilename+4, nByte);
51441 memcpy(zWideFilename, L"\\\\?\\UNC", 14);
51442 }else{
51443 memmove(zWideFilename, zWideFilename+4, nByte);
51444 }
51445 return zConverted;
51446 }
51447 sqlite3_free(zConverted);
51448 }
51449 }
51450 nChar = osMultiByteToWideChar(CP_UTF8, 0, zFilename, -1, NULL, 0);
51451 if( nChar==0 ){
51452 return 0;
51453 }
51454 zWideFilename = sqlite3MallocZero( nChar*sizeof(WCHAR)+12 );
51455 if( zWideFilename==0 ){
51456 return 0;
51457 }
51458 nChar = osMultiByteToWideChar(CP_UTF8, 0, zFilename, -1,
51459 zWideFilename, nChar);
51460 if( nChar==0 ){
51461 sqlite3_free(zWideFilename);
51462 zWideFilename = 0;
51463 }else if( nChar>MAX_PATH
51464 && winIsDriveLetterAndColon(zFilename)
51465 && winIsDirSep(zFilename[2]) ){
51466 memmove(zWideFilename+4, zWideFilename, nChar*sizeof(WCHAR));
51467 zWideFilename[2] = '\\';
51468 memcpy(zWideFilename, L"\\\\?\\", 8);
51469 }else if( nChar>MAX_PATH
51470 && winIsDirSep(zFilename[0]) && winIsDirSep(zFilename[1])
51471 && zFilename[2] != '?' ){
51472 memmove(zWideFilename+6, zWideFilename, nChar*sizeof(WCHAR));
51473 memcpy(zWideFilename, L"\\\\?\\UNC", 14);
51474 }
51475 zConverted = zWideFilename;
51476 #else
51477 zConverted = winUtf8ToUnicode(zFilename);
51478 #endif /* __CYGWIN__ */
51479 }
51480 #if defined(SQLITE_WIN32_HAS_ANSI) && defined(_WIN32)
51481 else{
51482 zConverted = winUtf8ToMbcs(zFilename, osAreFileApisANSI());
51483 }
51484 #endif
51485 /* caller will handle out of memory */
51486 return zConverted;
51487 }
51488
51489 /*
51490 ** This function is used to open a handle on a *-shm file.
51491 **
51492 ** If SQLITE_ENABLE_SETLK_TIMEOUT is defined at build time, then the file
51493 ** is opened with FILE_FLAG_OVERLAPPED specified. If not, it is not.
@@ -89078,14 +89101,16 @@
89078 ** simple case then too.
89079 */
89080 if( 0==sqlite3Strlen30(sqlite3BtreeGetFilename(db->aDb[0].pBt))
89081 || nTrans<=1
89082 ){
89083 for(i=0; rc==SQLITE_OK && i<db->nDb; i++){
89084 Btree *pBt = db->aDb[i].pBt;
89085 if( pBt ){
89086 rc = sqlite3BtreeCommitPhaseOne(pBt, 0);
 
 
89087 }
89088 }
89089
89090 /* Do the commit only if all databases successfully complete phase 1.
89091 ** If one of the BtreeCommitPhaseOne() calls fails, this indicates an
@@ -89092,11 +89117,13 @@
89092 ** IO error while deleting or truncating a journal file. It is unlikely,
89093 ** but could happen. In this case abandon processing and return the error.
89094 */
89095 for(i=0; rc==SQLITE_OK && i<db->nDb; i++){
89096 Btree *pBt = db->aDb[i].pBt;
89097 if( pBt ){
 
 
89098 rc = sqlite3BtreeCommitPhaseTwo(pBt, 0);
89099 }
89100 }
89101 if( rc==SQLITE_OK ){
89102 sqlite3VtabCommit(db);
@@ -89347,32 +89374,35 @@
89347 return SQLITE_OK;
89348 }
89349
89350
89351 /*
89352 ** This function is called when a transaction opened by the database
89353 ** handle associated with the VM passed as an argument is about to be
89354 ** committed. If there are outstanding deferred foreign key constraint
89355 ** violations, return SQLITE_ERROR. Otherwise, SQLITE_OK.
89356 **
89357 ** If there are outstanding FK violations and this function returns
89358 ** SQLITE_ERROR, set the result of the VM to SQLITE_CONSTRAINT_FOREIGNKEY
89359 ** and write an error message to it. Then return SQLITE_ERROR.
89360 */
89361 #ifndef SQLITE_OMIT_FOREIGN_KEY
89362 SQLITE_PRIVATE int sqlite3VdbeCheckFk(Vdbe *p, int deferred){
 
 
 
 
 
 
 
 
 
 
 
89363 sqlite3 *db = p->db;
89364 if( (deferred && (db->nDeferredCons+db->nDeferredImmCons)>0)
89365 || (!deferred && p->nFkConstraint>0)
89366 ){
89367 p->rc = SQLITE_CONSTRAINT_FOREIGNKEY;
89368 p->errorAction = OE_Abort;
89369 sqlite3VdbeError(p, "FOREIGN KEY constraint failed");
89370 if( (p->prepFlags & SQLITE_PREPARE_SAVESQL)==0 ) return SQLITE_ERROR;
89371 return SQLITE_CONSTRAINT_FOREIGNKEY;
89372 }
89373 return SQLITE_OK;
89374 }
89375 #endif
89376
89377 /*
89378 ** This routine is called the when a VDBE tries to halt. If the VDBE
@@ -89462,11 +89492,11 @@
89462 }
89463 }
89464
89465 /* Check for immediate foreign key violations. */
89466 if( p->rc==SQLITE_OK || (p->errorAction==OE_Fail && !isSpecialError) ){
89467 (void)sqlite3VdbeCheckFk(p, 0);
89468 }
89469
89470 /* If the auto-commit flag is set and this is the only active writer
89471 ** VM, then we do either a commit or rollback of the current transaction.
89472 **
@@ -89476,11 +89506,11 @@
89476 if( !sqlite3VtabInSync(db)
89477 && db->autoCommit
89478 && db->nVdbeWrite==(p->readOnly==0)
89479 ){
89480 if( p->rc==SQLITE_OK || (p->errorAction==OE_Fail && !isSpecialError) ){
89481 rc = sqlite3VdbeCheckFk(p, 1);
89482 if( rc!=SQLITE_OK ){
89483 if( NEVER(p->readOnly) ){
89484 sqlite3VdbeLeave(p);
89485 return SQLITE_ERROR;
89486 }
@@ -90341,19 +90371,19 @@
90341 /* pMem->flags = 0; // sqlite3VdbeSerialGet() will set this for us */
90342 pMem->szMalloc = 0;
90343 pMem->z = 0;
90344 sqlite3VdbeSerialGet(&aKey[d], serial_type, pMem);
90345 d += sqlite3VdbeSerialTypeLen(serial_type);
90346 pMem++;
90347 if( (++u)>=p->nField ) break;
 
90348 }
90349 if( d>(u32)nKey && u ){
90350 assert( CORRUPT_DB );
90351 /* In a corrupt record entry, the last pMem might have been set up using
90352 ** uninitialized memory. Overwrite its value with NULL, to prevent
90353 ** warnings from MSAN. */
90354 sqlite3VdbeMemSetNull(pMem-1);
90355 }
90356 testcase( u == pKeyInfo->nKeyField + 1 );
90357 testcase( u < pKeyInfo->nKeyField + 1 );
90358 assert( u<=pKeyInfo->nKeyField + 1 );
90359 p->nField = u;
@@ -90520,10 +90550,36 @@
90520 ** Both *pMem1 and *pMem2 contain string values. Compare the two values
90521 ** using the collation sequence pColl. As usual, return a negative , zero
90522 ** or positive value if *pMem1 is less than, equal to or greater than
90523 ** *pMem2, respectively. Similar in spirit to "rc = (*pMem1) - (*pMem2);".
90524 */
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
90525 static int vdbeCompareMemString(
90526 const Mem *pMem1,
90527 const Mem *pMem2,
90528 const CollSeq *pColl,
90529 u8 *prcErr /* If an OOM occurs, set to SQLITE_NOMEM */
@@ -90531,29 +90587,11 @@
90531 if( pMem1->enc==pColl->enc ){
90532 /* The strings are already in the correct encoding. Call the
90533 ** comparison function directly */
90534 return pColl->xCmp(pColl->pUser,pMem1->n,pMem1->z,pMem2->n,pMem2->z);
90535 }else{
90536 int rc;
90537 const void *v1, *v2;
90538 Mem c1;
90539 Mem c2;
90540 sqlite3VdbeMemInit(&c1, pMem1->db, MEM_Null);
90541 sqlite3VdbeMemInit(&c2, pMem1->db, MEM_Null);
90542 sqlite3VdbeMemShallowCopy(&c1, pMem1, MEM_Ephem);
90543 sqlite3VdbeMemShallowCopy(&c2, pMem2, MEM_Ephem);
90544 v1 = sqlite3ValueText((sqlite3_value*)&c1, pColl->enc);
90545 v2 = sqlite3ValueText((sqlite3_value*)&c2, pColl->enc);
90546 if( (v1==0 || v2==0) ){
90547 if( prcErr ) *prcErr = SQLITE_NOMEM_BKPT;
90548 rc = 0;
90549 }else{
90550 rc = pColl->xCmp(pColl->pUser, c1.n, v1, c2.n, v2);
90551 }
90552 sqlite3VdbeMemReleaseMalloc(&c1);
90553 sqlite3VdbeMemReleaseMalloc(&c2);
90554 return rc;
90555 }
90556 }
90557
90558 /*
90559 ** The input pBlob is guaranteed to be a Blob that is not marked
@@ -96256,11 +96294,11 @@
96256 ** exits. This opcode is used to raise foreign key constraint errors prior
96257 ** to returning results such as a row change count or the result of a
96258 ** RETURNING clause.
96259 */
96260 case OP_FkCheck: {
96261 if( (rc = sqlite3VdbeCheckFk(p,0))!=SQLITE_OK ){
96262 goto abort_due_to_error;
96263 }
96264 break;
96265 }
96266
@@ -98440,11 +98478,11 @@
98440 ** and this is a RELEASE command, then the current transaction
98441 ** is committed.
98442 */
98443 int isTransaction = pSavepoint->pNext==0 && db->isTransactionSavepoint;
98444 if( isTransaction && p1==SAVEPOINT_RELEASE ){
98445 if( (rc = sqlite3VdbeCheckFk(p, 1))!=SQLITE_OK ){
98446 goto vdbe_return;
98447 }
98448 db->autoCommit = 1;
98449 if( sqlite3VdbeHalt(p)==SQLITE_BUSY ){
98450 p->pc = (int)(pOp - aOp);
@@ -98558,11 +98596,11 @@
98558 */
98559 sqlite3VdbeError(p, "cannot commit transaction - "
98560 "SQL statements in progress");
98561 rc = SQLITE_BUSY;
98562 goto abort_due_to_error;
98563 }else if( (rc = sqlite3VdbeCheckFk(p, 1))!=SQLITE_OK ){
98564 goto vdbe_return;
98565 }else{
98566 db->autoCommit = (u8)desiredAutoCommit;
98567 }
98568 if( sqlite3VdbeHalt(p)==SQLITE_BUSY ){
@@ -109256,12 +109294,12 @@
109256 assert( ((X)&~(NC_IsCheck|NC_PartIdx|NC_IdxExpr|NC_GenCol))==0 ); \
109257 if( ((N)->ncFlags & (X))!=0 ) notValidImpl(P,N,M,E,R);
109258
109259 /*
109260 ** Expression p should encode a floating point value between 1.0 and 0.0.
109261 ** Return 1024 times this value. Or return -1 if p is not a floating point
109262 ** value between 1.0 and 0.0.
109263 */
109264 static int exprProbability(Expr *p){
109265 double r = -1.0;
109266 if( p->op!=TK_FLOAT ) return -1;
109267 assert( !ExprHasProperty(p, EP_IntValue) );
@@ -115633,10 +115671,16 @@
115633 case TK_STRING: {
115634 assert( !ExprHasProperty(pExpr, EP_IntValue) );
115635 sqlite3VdbeLoadString(v, target, pExpr->u.zToken);
115636 return target;
115637 }
 
 
 
 
 
 
115638 default: {
115639 /* Make NULL the default case so that if a bug causes an illegal
115640 ** Expr node to be passed into this function, it will be handled
115641 ** sanely and not crash. But keep the assert() to bring the problem
115642 ** to the attention of the developers. */
@@ -116341,10 +116385,29 @@
116341 }
116342 pParse->pConstExpr = p;
116343 }
116344 return regDest;
116345 }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
116346
116347 /*
116348 ** Generate code to evaluate an expression and store the results
116349 ** into a register. Return the register number where the results
116350 ** are stored.
@@ -152746,10 +152809,11 @@
152746 Select *pSub = pWhere->x.pSelect;
152747 Expr *pSubWhere = pSub->pWhere;
152748 if( pSub->pSrc->nSrc==1
152749 && (pSub->selFlags & SF_Aggregate)==0
152750 && !pSub->pSrc->a[0].fg.isSubquery
 
152751 ){
152752 memset(pWhere, 0, sizeof(*pWhere));
152753 pWhere->op = TK_INTEGER;
152754 pWhere->u.iValue = 1;
152755 ExprSetProperty(pWhere, EP_IntValue);
@@ -153766,10 +153830,11 @@
153766 iBMem = pParse->nMem + 1;
153767 pParse->nMem += pGroupBy->nExpr;
153768 sqlite3VdbeAddOp2(v, OP_Integer, 0, iAbortFlag);
153769 VdbeComment((v, "clear abort flag"));
153770 sqlite3VdbeAddOp3(v, OP_Null, 0, iAMem, iAMem+pGroupBy->nExpr-1);
 
153771
153772 /* Begin a loop that will extract all source rows in GROUP BY order.
153773 ** This might involve two separate loops with an OP_Sort in between, or
153774 ** it might be a single loop that uses an index to extract information
153775 ** in the right order to begin with.
@@ -169084,10 +169149,11 @@
169084 if( pProbe->bNoQuery ) continue;
169085 rSize = pProbe->aiRowLogEst[0];
169086 pNew->u.btree.nEq = 0;
169087 pNew->u.btree.nBtm = 0;
169088 pNew->u.btree.nTop = 0;
 
169089 pNew->nSkip = 0;
169090 pNew->nLTerm = 0;
169091 pNew->iSortIdx = 0;
169092 pNew->rSetup = 0;
169093 pNew->prereq = mPrereq;
@@ -170152,12 +170218,10 @@
170152 && ((wctrlFlags&(WHERE_DISTINCTBY|WHERE_SORTBYGROUP))!=WHERE_DISTINCTBY)
170153 ){
170154 obSat = obDone;
170155 }
170156 break;
170157 }else if( wctrlFlags & WHERE_DISTINCTBY ){
170158 pLoop->u.btree.nDistinctCol = 0;
170159 }
170160 iCur = pWInfo->pTabList->a[pLoop->iTab].iCursor;
170161
170162 /* Mark off any ORDER BY term X that is a column in the table of
170163 ** the current loop for which there is term in the WHERE
@@ -258113,11 +258177,11 @@
258113 int nArg, /* Number of args */
258114 sqlite3_value **apUnused /* Function arguments */
258115 ){
258116 assert( nArg==0 );
258117 UNUSED_PARAM2(nArg, apUnused);
258118 sqlite3_result_text(pCtx, "fts5: 2025-07-15 19:00:01 9f184f8dfa5ef6d57e10376adc30e0060ceda07d283c23dfdfe3dbdd6608f839", -1, SQLITE_TRANSIENT);
258119 }
258120
258121 /*
258122 ** Implementation of fts5_locale(LOCALE, TEXT) function.
258123 **
258124
--- extsrc/sqlite3.c
+++ extsrc/sqlite3.c
@@ -16,11 +16,11 @@
16 ** if you want a wrapper to interface SQLite with your choice of programming
17 ** language. The code for the "sqlite3" command-line shell is also in a
18 ** separate file. This file contains only code for the core SQLite library.
19 **
20 ** The content in this amalgamation comes from Fossil check-in
21 ** cf7163f82ca380958a79350473b2c5a2cebd with changes in files:
22 **
23 **
24 */
25 #ifndef SQLITE_AMALGAMATION
26 #define SQLITE_CORE 1
@@ -465,11 +465,11 @@
465 ** [sqlite3_libversion_number()], [sqlite3_sourceid()],
466 ** [sqlite_version()] and [sqlite_source_id()].
467 */
468 #define SQLITE_VERSION "3.51.0"
469 #define SQLITE_VERSION_NUMBER 3051000
470 #define SQLITE_SOURCE_ID "2025-07-30 16:17:14 cf7163f82ca380958a79350473b2c5a2cebda7496d6d575fa2835c362010fea1"
471
472 /*
473 ** CAPI3REF: Run-Time Library Version Numbers
474 ** KEYWORDS: sqlite3_version sqlite3_sourceid
475 **
@@ -814,10 +814,13 @@
814 ** [sqlite3_extended_errcode()].
815 */
816 #define SQLITE_ERROR_MISSING_COLLSEQ (SQLITE_ERROR | (1<<8))
817 #define SQLITE_ERROR_RETRY (SQLITE_ERROR | (2<<8))
818 #define SQLITE_ERROR_SNAPSHOT (SQLITE_ERROR | (3<<8))
819 #define SQLITE_ERROR_RESERVESIZE (SQLITE_ERROR | (4<<8))
820 #define SQLITE_ERROR_KEY (SQLITE_ERROR | (5<<8))
821 #define SQLITE_ERROR_UNABLE (SQLITE_ERROR | (6<<8))
822 #define SQLITE_IOERR_READ (SQLITE_IOERR | (1<<8))
823 #define SQLITE_IOERR_SHORT_READ (SQLITE_IOERR | (2<<8))
824 #define SQLITE_IOERR_WRITE (SQLITE_IOERR | (3<<8))
825 #define SQLITE_IOERR_FSYNC (SQLITE_IOERR | (4<<8))
826 #define SQLITE_IOERR_DIR_FSYNC (SQLITE_IOERR | (5<<8))
@@ -848,10 +851,12 @@
851 #define SQLITE_IOERR_COMMIT_ATOMIC (SQLITE_IOERR | (30<<8))
852 #define SQLITE_IOERR_ROLLBACK_ATOMIC (SQLITE_IOERR | (31<<8))
853 #define SQLITE_IOERR_DATA (SQLITE_IOERR | (32<<8))
854 #define SQLITE_IOERR_CORRUPTFS (SQLITE_IOERR | (33<<8))
855 #define SQLITE_IOERR_IN_PAGE (SQLITE_IOERR | (34<<8))
856 #define SQLITE_IOERR_BADKEY (SQLITE_IOERR | (35<<8))
857 #define SQLITE_IOERR_CODEC (SQLITE_IOERR | (36<<8))
858 #define SQLITE_LOCKED_SHAREDCACHE (SQLITE_LOCKED | (1<<8))
859 #define SQLITE_LOCKED_VTAB (SQLITE_LOCKED | (2<<8))
860 #define SQLITE_BUSY_RECOVERY (SQLITE_BUSY | (1<<8))
861 #define SQLITE_BUSY_SNAPSHOT (SQLITE_BUSY | (2<<8))
862 #define SQLITE_BUSY_TIMEOUT (SQLITE_BUSY | (3<<8))
@@ -19503,10 +19508,11 @@
19508 AggInfo *pAggInfo; /* Used by TK_AGG_COLUMN and TK_AGG_FUNCTION */
19509 union {
19510 Table *pTab; /* TK_COLUMN: Table containing column. Can be NULL
19511 ** for a column of an index on an expression */
19512 Window *pWin; /* EP_WinFunc: Window/Filter defn for a function */
19513 int nReg; /* TK_NULLS: Number of registers to NULL out */
19514 struct { /* TK_IN, TK_SELECT, and TK_EXISTS */
19515 int iAddr; /* Subroutine entry address */
19516 int regReturn; /* Register used to hold return address */
19517 } sub;
19518 } y;
@@ -21540,10 +21546,11 @@
21546 SQLITE_PRIVATE void sqlite3ExprCodeGeneratedColumn(Parse*, Table*, Column*, int);
21547 #endif
21548 SQLITE_PRIVATE void sqlite3ExprCodeCopy(Parse*, Expr*, int);
21549 SQLITE_PRIVATE void sqlite3ExprCodeFactorable(Parse*, Expr*, int);
21550 SQLITE_PRIVATE int sqlite3ExprCodeRunJustOnce(Parse*, Expr*, int);
21551 SQLITE_PRIVATE void sqlite3ExprNullRegisterRange(Parse*, int, int);
21552 SQLITE_PRIVATE int sqlite3ExprCodeTemp(Parse*, Expr*, int*);
21553 SQLITE_PRIVATE int sqlite3ExprCodeTarget(Parse*, Expr*, int);
21554 SQLITE_PRIVATE int sqlite3ExprCodeExprList(Parse*, ExprList*, int, int, u8);
21555 #define SQLITE_ECEL_DUP 0x01 /* Deep, not shallow copies */
21556 #define SQLITE_ECEL_FACTOR 0x02 /* Factor out constant terms */
@@ -24366,13 +24373,15 @@
24373 SQLITE_PRIVATE void sqlite3VdbeMemAboutToChange(Vdbe*,Mem*);
24374 SQLITE_PRIVATE int sqlite3VdbeCheckMemInvariants(Mem*);
24375 #endif
24376
24377 #ifndef SQLITE_OMIT_FOREIGN_KEY
24378 SQLITE_PRIVATE int sqlite3VdbeCheckFkImmediate(Vdbe*);
24379 SQLITE_PRIVATE int sqlite3VdbeCheckFkDeferred(Vdbe*);
24380 #else
24381 # define sqlite3VdbeCheckFkImmediate(p) 0
24382 # define sqlite3VdbeCheckFkDeferred(p) 0
24383 #endif
24384
24385 #ifdef SQLITE_DEBUG
24386 SQLITE_PRIVATE void sqlite3VdbePrintSql(Vdbe*);
24387 SQLITE_PRIVATE void sqlite3VdbeMemPrettyPrint(Mem *pMem, StrAccum *pStr);
@@ -32252,11 +32261,25 @@
32261 length = sqlite3Strlen30(bufpt);
32262 break;
32263 }
32264 }
32265 if( s.sign=='-' ){
32266 if( flag_alternateform
32267 && !flag_prefix
32268 && xtype==etFLOAT
32269 && s.iDP<=iRound
32270 ){
32271 /* Suppress the minus sign if all of the following are true:
32272 ** * The value displayed is zero
32273 ** * The '#' flag is used
32274 ** * The '+' flag is not used, and
32275 ** * The format is %f
32276 */
32277 prefix = 0;
32278 }else{
32279 prefix = '-';
32280 }
32281 }else{
32282 prefix = flag_prefix;
32283 }
32284
32285 exp = s.iDP-1;
@@ -51192,10 +51215,107 @@
51215 ** on allocation size granularity boundaries.
51216 ** During sqlite3_os_init() we do a GetSystemInfo()
51217 ** to get the granularity size.
51218 */
51219 static SYSTEM_INFO winSysInfo;
51220
51221 /*
51222 ** Convert a UTF-8 filename into whatever form the underlying
51223 ** operating system wants filenames in. Space to hold the result
51224 ** is obtained from malloc and must be freed by the calling
51225 ** function
51226 **
51227 ** On Cygwin, 3 possible input forms are accepted:
51228 ** - If the filename starts with "<drive>:/" or "<drive>:\",
51229 ** it is converted to UTF-16 as-is.
51230 ** - If the filename contains '/', it is assumed to be a
51231 ** Cygwin absolute path, it is converted to a win32
51232 ** absolute path in UTF-16.
51233 ** - Otherwise it must be a filename only, the win32 filename
51234 ** is returned in UTF-16.
51235 ** Note: If the function cygwin_conv_path() fails, only
51236 ** UTF-8 -> UTF-16 conversion will be done. This can only
51237 ** happen when the file path >32k, in which case winUtf8ToUnicode()
51238 ** will fail too.
51239 */
51240 static void *winConvertFromUtf8Filename(const char *zFilename){
51241 void *zConverted = 0;
51242 if( osIsNT() ){
51243 #ifdef __CYGWIN__
51244 int nChar;
51245 LPWSTR zWideFilename;
51246
51247 if( osCygwin_conv_path && !(winIsDriveLetterAndColon(zFilename)
51248 && winIsDirSep(zFilename[2])) ){
51249 i64 nByte;
51250 int convertflag = CCP_POSIX_TO_WIN_W;
51251 if( !strchr(zFilename, '/') ) convertflag |= CCP_RELATIVE;
51252 nByte = (i64)osCygwin_conv_path(convertflag,
51253 zFilename, 0, 0);
51254 if( nByte>0 ){
51255 zConverted = sqlite3MallocZero(12+(u64)nByte);
51256 if ( zConverted==0 ){
51257 return zConverted;
51258 }
51259 zWideFilename = zConverted;
51260 /* Filenames should be prefixed, except when converted
51261 * full path already starts with "\\?\". */
51262 if( osCygwin_conv_path(convertflag, zFilename,
51263 zWideFilename+4, nByte)==0 ){
51264 if( (convertflag&CCP_RELATIVE) ){
51265 memmove(zWideFilename, zWideFilename+4, nByte);
51266 }else if( memcmp(zWideFilename+4, L"\\\\", 4) ){
51267 memcpy(zWideFilename, L"\\\\?\\", 8);
51268 }else if( zWideFilename[6]!='?' ){
51269 memmove(zWideFilename+6, zWideFilename+4, nByte);
51270 memcpy(zWideFilename, L"\\\\?\\UNC", 14);
51271 }else{
51272 memmove(zWideFilename, zWideFilename+4, nByte);
51273 }
51274 return zConverted;
51275 }
51276 sqlite3_free(zConverted);
51277 }
51278 }
51279 nChar = osMultiByteToWideChar(CP_UTF8, 0, zFilename, -1, NULL, 0);
51280 if( nChar==0 ){
51281 return 0;
51282 }
51283 zWideFilename = sqlite3MallocZero( nChar*sizeof(WCHAR)+12 );
51284 if( zWideFilename==0 ){
51285 return 0;
51286 }
51287 nChar = osMultiByteToWideChar(CP_UTF8, 0, zFilename, -1,
51288 zWideFilename, nChar);
51289 if( nChar==0 ){
51290 sqlite3_free(zWideFilename);
51291 zWideFilename = 0;
51292 }else if( nChar>MAX_PATH
51293 && winIsDriveLetterAndColon(zFilename)
51294 && winIsDirSep(zFilename[2]) ){
51295 memmove(zWideFilename+4, zWideFilename, nChar*sizeof(WCHAR));
51296 zWideFilename[2] = '\\';
51297 memcpy(zWideFilename, L"\\\\?\\", 8);
51298 }else if( nChar>MAX_PATH
51299 && winIsDirSep(zFilename[0]) && winIsDirSep(zFilename[1])
51300 && zFilename[2] != '?' ){
51301 memmove(zWideFilename+6, zWideFilename, nChar*sizeof(WCHAR));
51302 memcpy(zWideFilename, L"\\\\?\\UNC", 14);
51303 }
51304 zConverted = zWideFilename;
51305 #else
51306 zConverted = winUtf8ToUnicode(zFilename);
51307 #endif /* __CYGWIN__ */
51308 }
51309 #if defined(SQLITE_WIN32_HAS_ANSI) && defined(_WIN32)
51310 else{
51311 zConverted = winUtf8ToMbcs(zFilename, osAreFileApisANSI());
51312 }
51313 #endif
51314 /* caller will handle out of memory */
51315 return zConverted;
51316 }
51317
51318 #ifndef SQLITE_OMIT_WAL
51319
51320 /*
51321 ** Helper functions to obtain and relinquish the global mutex. The
@@ -51387,107 +51507,10 @@
51507
51508 return rc;
51509 }
51510
51511
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
51512 /*
51513 ** This function is used to open a handle on a *-shm file.
51514 **
51515 ** If SQLITE_ENABLE_SETLK_TIMEOUT is defined at build time, then the file
51516 ** is opened with FILE_FLAG_OVERLAPPED specified. If not, it is not.
@@ -89078,14 +89101,16 @@
89101 ** simple case then too.
89102 */
89103 if( 0==sqlite3Strlen30(sqlite3BtreeGetFilename(db->aDb[0].pBt))
89104 || nTrans<=1
89105 ){
89106 if( needXcommit ){
89107 for(i=0; rc==SQLITE_OK && i<db->nDb; i++){
89108 Btree *pBt = db->aDb[i].pBt;
89109 if( sqlite3BtreeTxnState(pBt)>=SQLITE_TXN_WRITE ){
89110 rc = sqlite3BtreeCommitPhaseOne(pBt, 0);
89111 }
89112 }
89113 }
89114
89115 /* Do the commit only if all databases successfully complete phase 1.
89116 ** If one of the BtreeCommitPhaseOne() calls fails, this indicates an
@@ -89092,11 +89117,13 @@
89117 ** IO error while deleting or truncating a journal file. It is unlikely,
89118 ** but could happen. In this case abandon processing and return the error.
89119 */
89120 for(i=0; rc==SQLITE_OK && i<db->nDb; i++){
89121 Btree *pBt = db->aDb[i].pBt;
89122 int txn = sqlite3BtreeTxnState(pBt);
89123 if( txn!=SQLITE_TXN_NONE ){
89124 assert( needXcommit || txn==SQLITE_TXN_READ );
89125 rc = sqlite3BtreeCommitPhaseTwo(pBt, 0);
89126 }
89127 }
89128 if( rc==SQLITE_OK ){
89129 sqlite3VtabCommit(db);
@@ -89347,32 +89374,35 @@
89374 return SQLITE_OK;
89375 }
89376
89377
89378 /*
89379 ** These functions are called when a transaction opened by the database
89380 ** handle associated with the VM passed as an argument is about to be
89381 ** committed. If there are outstanding foreign key constraint violations
89382 ** return an error code. Otherwise, SQLITE_OK.
89383 **
89384 ** If there are outstanding FK violations and this function returns
89385 ** non-zero, set the result of the VM to SQLITE_CONSTRAINT_FOREIGNKEY
89386 ** and write an error message to it.
89387 */
89388 #ifndef SQLITE_OMIT_FOREIGN_KEY
89389 static SQLITE_NOINLINE int vdbeFkError(Vdbe *p){
89390 p->rc = SQLITE_CONSTRAINT_FOREIGNKEY;
89391 p->errorAction = OE_Abort;
89392 sqlite3VdbeError(p, "FOREIGN KEY constraint failed");
89393 if( (p->prepFlags & SQLITE_PREPARE_SAVESQL)==0 ) return SQLITE_ERROR;
89394 return SQLITE_CONSTRAINT_FOREIGNKEY;
89395 }
89396 SQLITE_PRIVATE int sqlite3VdbeCheckFkImmediate(Vdbe *p){
89397 if( p->nFkConstraint==0 ) return SQLITE_OK;
89398 return vdbeFkError(p);
89399 }
89400 SQLITE_PRIVATE int sqlite3VdbeCheckFkDeferred(Vdbe *p){
89401 sqlite3 *db = p->db;
89402 if( (db->nDeferredCons+db->nDeferredImmCons)==0 ) return SQLITE_OK;
89403 return vdbeFkError(p);
 
 
 
 
 
 
 
 
89404 }
89405 #endif
89406
89407 /*
89408 ** This routine is called the when a VDBE tries to halt. If the VDBE
@@ -89462,11 +89492,11 @@
89492 }
89493 }
89494
89495 /* Check for immediate foreign key violations. */
89496 if( p->rc==SQLITE_OK || (p->errorAction==OE_Fail && !isSpecialError) ){
89497 (void)sqlite3VdbeCheckFkImmediate(p);
89498 }
89499
89500 /* If the auto-commit flag is set and this is the only active writer
89501 ** VM, then we do either a commit or rollback of the current transaction.
89502 **
@@ -89476,11 +89506,11 @@
89506 if( !sqlite3VtabInSync(db)
89507 && db->autoCommit
89508 && db->nVdbeWrite==(p->readOnly==0)
89509 ){
89510 if( p->rc==SQLITE_OK || (p->errorAction==OE_Fail && !isSpecialError) ){
89511 rc = sqlite3VdbeCheckFkDeferred(p);
89512 if( rc!=SQLITE_OK ){
89513 if( NEVER(p->readOnly) ){
89514 sqlite3VdbeLeave(p);
89515 return SQLITE_ERROR;
89516 }
@@ -90341,19 +90371,19 @@
90371 /* pMem->flags = 0; // sqlite3VdbeSerialGet() will set this for us */
90372 pMem->szMalloc = 0;
90373 pMem->z = 0;
90374 sqlite3VdbeSerialGet(&aKey[d], serial_type, pMem);
90375 d += sqlite3VdbeSerialTypeLen(serial_type);
 
90376 if( (++u)>=p->nField ) break;
90377 pMem++;
90378 }
90379 if( d>(u32)nKey && u ){
90380 assert( CORRUPT_DB );
90381 /* In a corrupt record entry, the last pMem might have been set up using
90382 ** uninitialized memory. Overwrite its value with NULL, to prevent
90383 ** warnings from MSAN. */
90384 sqlite3VdbeMemSetNull(pMem-(u<p->nField));
90385 }
90386 testcase( u == pKeyInfo->nKeyField + 1 );
90387 testcase( u < pKeyInfo->nKeyField + 1 );
90388 assert( u<=pKeyInfo->nKeyField + 1 );
90389 p->nField = u;
@@ -90520,10 +90550,36 @@
90550 ** Both *pMem1 and *pMem2 contain string values. Compare the two values
90551 ** using the collation sequence pColl. As usual, return a negative , zero
90552 ** or positive value if *pMem1 is less than, equal to or greater than
90553 ** *pMem2, respectively. Similar in spirit to "rc = (*pMem1) - (*pMem2);".
90554 */
90555 static SQLITE_NOINLINE int vdbeCompareMemStringWithEncodingChange(
90556 const Mem *pMem1,
90557 const Mem *pMem2,
90558 const CollSeq *pColl,
90559 u8 *prcErr /* If an OOM occurs, set to SQLITE_NOMEM */
90560 ){
90561 int rc;
90562 const void *v1, *v2;
90563 Mem c1;
90564 Mem c2;
90565 sqlite3VdbeMemInit(&c1, pMem1->db, MEM_Null);
90566 sqlite3VdbeMemInit(&c2, pMem1->db, MEM_Null);
90567 sqlite3VdbeMemShallowCopy(&c1, pMem1, MEM_Ephem);
90568 sqlite3VdbeMemShallowCopy(&c2, pMem2, MEM_Ephem);
90569 v1 = sqlite3ValueText((sqlite3_value*)&c1, pColl->enc);
90570 v2 = sqlite3ValueText((sqlite3_value*)&c2, pColl->enc);
90571 if( (v1==0 || v2==0) ){
90572 if( prcErr ) *prcErr = SQLITE_NOMEM_BKPT;
90573 rc = 0;
90574 }else{
90575 rc = pColl->xCmp(pColl->pUser, c1.n, v1, c2.n, v2);
90576 }
90577 sqlite3VdbeMemReleaseMalloc(&c1);
90578 sqlite3VdbeMemReleaseMalloc(&c2);
90579 return rc;
90580 }
90581 static int vdbeCompareMemString(
90582 const Mem *pMem1,
90583 const Mem *pMem2,
90584 const CollSeq *pColl,
90585 u8 *prcErr /* If an OOM occurs, set to SQLITE_NOMEM */
@@ -90531,29 +90587,11 @@
90587 if( pMem1->enc==pColl->enc ){
90588 /* The strings are already in the correct encoding. Call the
90589 ** comparison function directly */
90590 return pColl->xCmp(pColl->pUser,pMem1->n,pMem1->z,pMem2->n,pMem2->z);
90591 }else{
90592 return vdbeCompareMemStringWithEncodingChange(pMem1,pMem2,pColl,prcErr);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
90593 }
90594 }
90595
90596 /*
90597 ** The input pBlob is guaranteed to be a Blob that is not marked
@@ -96256,11 +96294,11 @@
96294 ** exits. This opcode is used to raise foreign key constraint errors prior
96295 ** to returning results such as a row change count or the result of a
96296 ** RETURNING clause.
96297 */
96298 case OP_FkCheck: {
96299 if( (rc = sqlite3VdbeCheckFkImmediate(p))!=SQLITE_OK ){
96300 goto abort_due_to_error;
96301 }
96302 break;
96303 }
96304
@@ -98440,11 +98478,11 @@
98478 ** and this is a RELEASE command, then the current transaction
98479 ** is committed.
98480 */
98481 int isTransaction = pSavepoint->pNext==0 && db->isTransactionSavepoint;
98482 if( isTransaction && p1==SAVEPOINT_RELEASE ){
98483 if( (rc = sqlite3VdbeCheckFkDeferred(p))!=SQLITE_OK ){
98484 goto vdbe_return;
98485 }
98486 db->autoCommit = 1;
98487 if( sqlite3VdbeHalt(p)==SQLITE_BUSY ){
98488 p->pc = (int)(pOp - aOp);
@@ -98558,11 +98596,11 @@
98596 */
98597 sqlite3VdbeError(p, "cannot commit transaction - "
98598 "SQL statements in progress");
98599 rc = SQLITE_BUSY;
98600 goto abort_due_to_error;
98601 }else if( (rc = sqlite3VdbeCheckFkDeferred(p))!=SQLITE_OK ){
98602 goto vdbe_return;
98603 }else{
98604 db->autoCommit = (u8)desiredAutoCommit;
98605 }
98606 if( sqlite3VdbeHalt(p)==SQLITE_BUSY ){
@@ -109256,12 +109294,12 @@
109294 assert( ((X)&~(NC_IsCheck|NC_PartIdx|NC_IdxExpr|NC_GenCol))==0 ); \
109295 if( ((N)->ncFlags & (X))!=0 ) notValidImpl(P,N,M,E,R);
109296
109297 /*
109298 ** Expression p should encode a floating point value between 1.0 and 0.0.
109299 ** Return 134,217,728 (2^27) times this value. Or return -1 if p is not
109300 ** a floating point value between 1.0 and 0.0.
109301 */
109302 static int exprProbability(Expr *p){
109303 double r = -1.0;
109304 if( p->op!=TK_FLOAT ) return -1;
109305 assert( !ExprHasProperty(p, EP_IntValue) );
@@ -115633,10 +115671,16 @@
115671 case TK_STRING: {
115672 assert( !ExprHasProperty(pExpr, EP_IntValue) );
115673 sqlite3VdbeLoadString(v, target, pExpr->u.zToken);
115674 return target;
115675 }
115676 case TK_NULLS: {
115677 /* Set a range of registers to NULL. pExpr->y.nReg registers starting
115678 ** with target */
115679 sqlite3VdbeAddOp3(v, OP_Null, 0, target, target + pExpr->y.nReg - 1);
115680 return target;
115681 }
115682 default: {
115683 /* Make NULL the default case so that if a bug causes an illegal
115684 ** Expr node to be passed into this function, it will be handled
115685 ** sanely and not crash. But keep the assert() to bring the problem
115686 ** to the attention of the developers. */
@@ -116341,10 +116385,29 @@
116385 }
116386 pParse->pConstExpr = p;
116387 }
116388 return regDest;
116389 }
116390
116391 /*
116392 ** Make arrangements to invoke OP_Null on a range of registers
116393 ** during initialization.
116394 */
116395 SQLITE_PRIVATE SQLITE_NOINLINE void sqlite3ExprNullRegisterRange(
116396 Parse *pParse, /* Parsing context */
116397 int iReg, /* First register to set to NULL */
116398 int nReg /* Number of sequential registers to NULL out */
116399 ){
116400 u8 okConstFactor = pParse->okConstFactor;
116401 Expr t;
116402 memset(&t, 0, sizeof(t));
116403 t.op = TK_NULLS;
116404 t.y.nReg = nReg;
116405 pParse->okConstFactor = 1;
116406 sqlite3ExprCodeRunJustOnce(pParse, &t, iReg);
116407 pParse->okConstFactor = okConstFactor;
116408 }
116409
116410 /*
116411 ** Generate code to evaluate an expression and store the results
116412 ** into a register. Return the register number where the results
116413 ** are stored.
@@ -152746,10 +152809,11 @@
152809 Select *pSub = pWhere->x.pSelect;
152810 Expr *pSubWhere = pSub->pWhere;
152811 if( pSub->pSrc->nSrc==1
152812 && (pSub->selFlags & SF_Aggregate)==0
152813 && !pSub->pSrc->a[0].fg.isSubquery
152814 && pSub->pLimit==0
152815 ){
152816 memset(pWhere, 0, sizeof(*pWhere));
152817 pWhere->op = TK_INTEGER;
152818 pWhere->u.iValue = 1;
152819 ExprSetProperty(pWhere, EP_IntValue);
@@ -153766,10 +153830,11 @@
153830 iBMem = pParse->nMem + 1;
153831 pParse->nMem += pGroupBy->nExpr;
153832 sqlite3VdbeAddOp2(v, OP_Integer, 0, iAbortFlag);
153833 VdbeComment((v, "clear abort flag"));
153834 sqlite3VdbeAddOp3(v, OP_Null, 0, iAMem, iAMem+pGroupBy->nExpr-1);
153835 sqlite3ExprNullRegisterRange(pParse, iAMem, pGroupBy->nExpr);
153836
153837 /* Begin a loop that will extract all source rows in GROUP BY order.
153838 ** This might involve two separate loops with an OP_Sort in between, or
153839 ** it might be a single loop that uses an index to extract information
153840 ** in the right order to begin with.
@@ -169084,10 +169149,11 @@
169149 if( pProbe->bNoQuery ) continue;
169150 rSize = pProbe->aiRowLogEst[0];
169151 pNew->u.btree.nEq = 0;
169152 pNew->u.btree.nBtm = 0;
169153 pNew->u.btree.nTop = 0;
169154 pNew->u.btree.nDistinctCol = 0;
169155 pNew->nSkip = 0;
169156 pNew->nLTerm = 0;
169157 pNew->iSortIdx = 0;
169158 pNew->rSetup = 0;
169159 pNew->prereq = mPrereq;
@@ -170152,12 +170218,10 @@
170218 && ((wctrlFlags&(WHERE_DISTINCTBY|WHERE_SORTBYGROUP))!=WHERE_DISTINCTBY)
170219 ){
170220 obSat = obDone;
170221 }
170222 break;
 
 
170223 }
170224 iCur = pWInfo->pTabList->a[pLoop->iTab].iCursor;
170225
170226 /* Mark off any ORDER BY term X that is a column in the table of
170227 ** the current loop for which there is term in the WHERE
@@ -258113,11 +258177,11 @@
258177 int nArg, /* Number of args */
258178 sqlite3_value **apUnused /* Function arguments */
258179 ){
258180 assert( nArg==0 );
258181 UNUSED_PARAM2(nArg, apUnused);
258182 sqlite3_result_text(pCtx, "fts5: 2025-07-30 16:17:14 cf7163f82ca380958a79350473b2c5a2cebda7496d6d575fa2835c362010fea1", -1, SQLITE_TRANSIENT);
258183 }
258184
258185 /*
258186 ** Implementation of fts5_locale(LOCALE, TEXT) function.
258187 **
258188
--- extsrc/sqlite3.h
+++ extsrc/sqlite3.h
@@ -146,11 +146,11 @@
146146
** [sqlite3_libversion_number()], [sqlite3_sourceid()],
147147
** [sqlite_version()] and [sqlite_source_id()].
148148
*/
149149
#define SQLITE_VERSION "3.51.0"
150150
#define SQLITE_VERSION_NUMBER 3051000
151
-#define SQLITE_SOURCE_ID "2025-07-15 19:00:01 9f184f8dfa5ef6d57e10376adc30e0060ceda07d283c23dfdfe3dbdd6608f839"
151
+#define SQLITE_SOURCE_ID "2025-07-30 16:17:14 cf7163f82ca380958a79350473b2c5a2cebda7496d6d575fa2835c362010fea1"
152152
153153
/*
154154
** CAPI3REF: Run-Time Library Version Numbers
155155
** KEYWORDS: sqlite3_version sqlite3_sourceid
156156
**
@@ -495,10 +495,13 @@
495495
** [sqlite3_extended_errcode()].
496496
*/
497497
#define SQLITE_ERROR_MISSING_COLLSEQ (SQLITE_ERROR | (1<<8))
498498
#define SQLITE_ERROR_RETRY (SQLITE_ERROR | (2<<8))
499499
#define SQLITE_ERROR_SNAPSHOT (SQLITE_ERROR | (3<<8))
500
+#define SQLITE_ERROR_RESERVESIZE (SQLITE_ERROR | (4<<8))
501
+#define SQLITE_ERROR_KEY (SQLITE_ERROR | (5<<8))
502
+#define SQLITE_ERROR_UNABLE (SQLITE_ERROR | (6<<8))
500503
#define SQLITE_IOERR_READ (SQLITE_IOERR | (1<<8))
501504
#define SQLITE_IOERR_SHORT_READ (SQLITE_IOERR | (2<<8))
502505
#define SQLITE_IOERR_WRITE (SQLITE_IOERR | (3<<8))
503506
#define SQLITE_IOERR_FSYNC (SQLITE_IOERR | (4<<8))
504507
#define SQLITE_IOERR_DIR_FSYNC (SQLITE_IOERR | (5<<8))
@@ -529,10 +532,12 @@
529532
#define SQLITE_IOERR_COMMIT_ATOMIC (SQLITE_IOERR | (30<<8))
530533
#define SQLITE_IOERR_ROLLBACK_ATOMIC (SQLITE_IOERR | (31<<8))
531534
#define SQLITE_IOERR_DATA (SQLITE_IOERR | (32<<8))
532535
#define SQLITE_IOERR_CORRUPTFS (SQLITE_IOERR | (33<<8))
533536
#define SQLITE_IOERR_IN_PAGE (SQLITE_IOERR | (34<<8))
537
+#define SQLITE_IOERR_BADKEY (SQLITE_IOERR | (35<<8))
538
+#define SQLITE_IOERR_CODEC (SQLITE_IOERR | (36<<8))
534539
#define SQLITE_LOCKED_SHAREDCACHE (SQLITE_LOCKED | (1<<8))
535540
#define SQLITE_LOCKED_VTAB (SQLITE_LOCKED | (2<<8))
536541
#define SQLITE_BUSY_RECOVERY (SQLITE_BUSY | (1<<8))
537542
#define SQLITE_BUSY_SNAPSHOT (SQLITE_BUSY | (2<<8))
538543
#define SQLITE_BUSY_TIMEOUT (SQLITE_BUSY | (3<<8))
539544
--- extsrc/sqlite3.h
+++ extsrc/sqlite3.h
@@ -146,11 +146,11 @@
146 ** [sqlite3_libversion_number()], [sqlite3_sourceid()],
147 ** [sqlite_version()] and [sqlite_source_id()].
148 */
149 #define SQLITE_VERSION "3.51.0"
150 #define SQLITE_VERSION_NUMBER 3051000
151 #define SQLITE_SOURCE_ID "2025-07-15 19:00:01 9f184f8dfa5ef6d57e10376adc30e0060ceda07d283c23dfdfe3dbdd6608f839"
152
153 /*
154 ** CAPI3REF: Run-Time Library Version Numbers
155 ** KEYWORDS: sqlite3_version sqlite3_sourceid
156 **
@@ -495,10 +495,13 @@
495 ** [sqlite3_extended_errcode()].
496 */
497 #define SQLITE_ERROR_MISSING_COLLSEQ (SQLITE_ERROR | (1<<8))
498 #define SQLITE_ERROR_RETRY (SQLITE_ERROR | (2<<8))
499 #define SQLITE_ERROR_SNAPSHOT (SQLITE_ERROR | (3<<8))
 
 
 
500 #define SQLITE_IOERR_READ (SQLITE_IOERR | (1<<8))
501 #define SQLITE_IOERR_SHORT_READ (SQLITE_IOERR | (2<<8))
502 #define SQLITE_IOERR_WRITE (SQLITE_IOERR | (3<<8))
503 #define SQLITE_IOERR_FSYNC (SQLITE_IOERR | (4<<8))
504 #define SQLITE_IOERR_DIR_FSYNC (SQLITE_IOERR | (5<<8))
@@ -529,10 +532,12 @@
529 #define SQLITE_IOERR_COMMIT_ATOMIC (SQLITE_IOERR | (30<<8))
530 #define SQLITE_IOERR_ROLLBACK_ATOMIC (SQLITE_IOERR | (31<<8))
531 #define SQLITE_IOERR_DATA (SQLITE_IOERR | (32<<8))
532 #define SQLITE_IOERR_CORRUPTFS (SQLITE_IOERR | (33<<8))
533 #define SQLITE_IOERR_IN_PAGE (SQLITE_IOERR | (34<<8))
 
 
534 #define SQLITE_LOCKED_SHAREDCACHE (SQLITE_LOCKED | (1<<8))
535 #define SQLITE_LOCKED_VTAB (SQLITE_LOCKED | (2<<8))
536 #define SQLITE_BUSY_RECOVERY (SQLITE_BUSY | (1<<8))
537 #define SQLITE_BUSY_SNAPSHOT (SQLITE_BUSY | (2<<8))
538 #define SQLITE_BUSY_TIMEOUT (SQLITE_BUSY | (3<<8))
539
--- extsrc/sqlite3.h
+++ extsrc/sqlite3.h
@@ -146,11 +146,11 @@
146 ** [sqlite3_libversion_number()], [sqlite3_sourceid()],
147 ** [sqlite_version()] and [sqlite_source_id()].
148 */
149 #define SQLITE_VERSION "3.51.0"
150 #define SQLITE_VERSION_NUMBER 3051000
151 #define SQLITE_SOURCE_ID "2025-07-30 16:17:14 cf7163f82ca380958a79350473b2c5a2cebda7496d6d575fa2835c362010fea1"
152
153 /*
154 ** CAPI3REF: Run-Time Library Version Numbers
155 ** KEYWORDS: sqlite3_version sqlite3_sourceid
156 **
@@ -495,10 +495,13 @@
495 ** [sqlite3_extended_errcode()].
496 */
497 #define SQLITE_ERROR_MISSING_COLLSEQ (SQLITE_ERROR | (1<<8))
498 #define SQLITE_ERROR_RETRY (SQLITE_ERROR | (2<<8))
499 #define SQLITE_ERROR_SNAPSHOT (SQLITE_ERROR | (3<<8))
500 #define SQLITE_ERROR_RESERVESIZE (SQLITE_ERROR | (4<<8))
501 #define SQLITE_ERROR_KEY (SQLITE_ERROR | (5<<8))
502 #define SQLITE_ERROR_UNABLE (SQLITE_ERROR | (6<<8))
503 #define SQLITE_IOERR_READ (SQLITE_IOERR | (1<<8))
504 #define SQLITE_IOERR_SHORT_READ (SQLITE_IOERR | (2<<8))
505 #define SQLITE_IOERR_WRITE (SQLITE_IOERR | (3<<8))
506 #define SQLITE_IOERR_FSYNC (SQLITE_IOERR | (4<<8))
507 #define SQLITE_IOERR_DIR_FSYNC (SQLITE_IOERR | (5<<8))
@@ -529,10 +532,12 @@
532 #define SQLITE_IOERR_COMMIT_ATOMIC (SQLITE_IOERR | (30<<8))
533 #define SQLITE_IOERR_ROLLBACK_ATOMIC (SQLITE_IOERR | (31<<8))
534 #define SQLITE_IOERR_DATA (SQLITE_IOERR | (32<<8))
535 #define SQLITE_IOERR_CORRUPTFS (SQLITE_IOERR | (33<<8))
536 #define SQLITE_IOERR_IN_PAGE (SQLITE_IOERR | (34<<8))
537 #define SQLITE_IOERR_BADKEY (SQLITE_IOERR | (35<<8))
538 #define SQLITE_IOERR_CODEC (SQLITE_IOERR | (36<<8))
539 #define SQLITE_LOCKED_SHAREDCACHE (SQLITE_LOCKED | (1<<8))
540 #define SQLITE_LOCKED_VTAB (SQLITE_LOCKED | (2<<8))
541 #define SQLITE_BUSY_RECOVERY (SQLITE_BUSY | (1<<8))
542 #define SQLITE_BUSY_SNAPSHOT (SQLITE_BUSY | (2<<8))
543 #define SQLITE_BUSY_TIMEOUT (SQLITE_BUSY | (3<<8))
544
+1 -1
--- src/bisect.c
+++ src/bisect.c
@@ -336,11 +336,11 @@
336336
}
337337
zUuid = db_text(0,"SELECT lower(uuid) FROM blob WHERE rid=%d", rid);
338338
if( blob_size(&link)>0 ) blob_append(&link, "-", 1);
339339
blob_appendf(&link, "%c%.10s", cPrefix, zUuid);
340340
}
341
- zResult = mprintf("%s", blob_str(&link));
341
+ zResult = fossil_strdup(blob_str(&link));
342342
blob_reset(&link);
343343
blob_reset(&log);
344344
blob_reset(&id);
345345
return zResult;
346346
}
347347
--- src/bisect.c
+++ src/bisect.c
@@ -336,11 +336,11 @@
336 }
337 zUuid = db_text(0,"SELECT lower(uuid) FROM blob WHERE rid=%d", rid);
338 if( blob_size(&link)>0 ) blob_append(&link, "-", 1);
339 blob_appendf(&link, "%c%.10s", cPrefix, zUuid);
340 }
341 zResult = mprintf("%s", blob_str(&link));
342 blob_reset(&link);
343 blob_reset(&log);
344 blob_reset(&id);
345 return zResult;
346 }
347
--- src/bisect.c
+++ src/bisect.c
@@ -336,11 +336,11 @@
336 }
337 zUuid = db_text(0,"SELECT lower(uuid) FROM blob WHERE rid=%d", rid);
338 if( blob_size(&link)>0 ) blob_append(&link, "-", 1);
339 blob_appendf(&link, "%c%.10s", cPrefix, zUuid);
340 }
341 zResult = fossil_strdup(blob_str(&link));
342 blob_reset(&link);
343 blob_reset(&log);
344 blob_reset(&id);
345 return zResult;
346 }
347
--- src/browse.c
+++ src/browse.c
@@ -1162,11 +1162,10 @@
11621162
int showId = PB("showid");
11631163
Stmt q1, q2;
11641164
double baseTime;
11651165
login_check_credentials();
11661166
if( !g.perm.Read ){ login_needed(g.anon.Read); return; }
1167
- if( exclude_spiders(0) ) return;
11681167
zName = P("name");
11691168
if( zName==0 ) zName = "tip";
11701169
rid = symbolic_name_to_rid(zName, "ci");
11711170
if( rid==0 ){
11721171
fossil_fatal("not a valid check-in: %s", zName);
11731172
--- src/browse.c
+++ src/browse.c
@@ -1162,11 +1162,10 @@
1162 int showId = PB("showid");
1163 Stmt q1, q2;
1164 double baseTime;
1165 login_check_credentials();
1166 if( !g.perm.Read ){ login_needed(g.anon.Read); return; }
1167 if( exclude_spiders(0) ) return;
1168 zName = P("name");
1169 if( zName==0 ) zName = "tip";
1170 rid = symbolic_name_to_rid(zName, "ci");
1171 if( rid==0 ){
1172 fossil_fatal("not a valid check-in: %s", zName);
1173
--- src/browse.c
+++ src/browse.c
@@ -1162,11 +1162,10 @@
1162 int showId = PB("showid");
1163 Stmt q1, q2;
1164 double baseTime;
1165 login_check_credentials();
1166 if( !g.perm.Read ){ login_needed(g.anon.Read); return; }
 
1167 zName = P("name");
1168 if( zName==0 ) zName = "tip";
1169 rid = symbolic_name_to_rid(zName, "ci");
1170 if( rid==0 ){
1171 fossil_fatal("not a valid check-in: %s", zName);
1172
+12 -8
--- src/captcha.c
+++ src/captcha.c
@@ -744,11 +744,11 @@
744744
(void)exclude_spiders(1);
745745
@ <hr><p>The captcha is shown above. Add a name=HEX query parameter
746746
@ to see how HEX would be rendered in the current captcha font.
747747
@ <h2>Debug/Testing Values:</h2>
748748
@ <ul>
749
- @ <li> g.isHuman = %d(g.isHuman)
749
+ @ <li> g.isRobot = %d(g.isRobot)
750750
@ <li> g.zLogin = %h(g.zLogin)
751751
@ <li> login_cookie_welformed() = %d(login_cookie_wellformed())
752752
@ <li> captcha_is_correct(1) = %d(captcha_is_correct(1)).
753753
@ </ul>
754754
style_finish_page();
@@ -759,10 +759,18 @@
759759
@ %s(captcha_render(zPw))
760760
@ </pre>
761761
style_finish_page();
762762
}
763763
}
764
+
765
+/*
766
+** WEBPAGE: honeypot
767
+** This page is a honeypot for spiders and bots.
768
+*/
769
+void honeypot_page(void){
770
+ (void)exclude_spiders(0);
771
+}
764772
765773
/*
766774
** Check to see if the current request is coming from an agent that
767775
** self-identifies as a spider.
768776
**
@@ -776,27 +784,23 @@
776784
** If the bTest argument is non-zero, then show the captcha regardless of
777785
** how the agent identifies. This is used for testing only.
778786
*/
779787
int exclude_spiders(int bTest){
780788
if( !bTest ){
781
- if( g.isHuman ) return 0; /* This user has already proven human */
782789
if( g.zLogin!=0 ) return 0; /* Logged in. Consider them human */
783790
if( login_cookie_wellformed() ){
784791
/* Logged into another member of the login group */
785792
return 0;
786793
}
787794
}
788795
789796
/* This appears to be a spider. Offer the captcha */
790797
style_set_current_feature("captcha");
791
- style_header("I think you are a robot");
798
+ style_header("Captcha");
792799
style_submenu_enable(0);
793800
@ <form method='POST' action='%R/ityaar'>
794
- @ <p>You seem like a robot.
795
- @
796
- @ <p>If you are human, you can prove that by solving the captcha below,
797
- @ after which you will be allowed to proceed.
801
+ @ <h2>Prove that you are human:
798802
if( bTest ){
799803
@ <input type="hidden" name="istest" value="1">
800804
}
801805
captcha_generate(3);
802806
@ </form>
@@ -830,11 +834,11 @@
830834
}
831835
cgi_append_header("X-Robot: 0\r\n");
832836
}
833837
login_redirect_to_g();
834838
}else{
835
- g.isHuman = 0;
839
+ g.isRobot = 1;
836840
(void)exclude_spiders(bTest);
837841
if( bTest ){
838842
@ <hr><p>Wrong code. Try again
839843
style_finish_page();
840844
}
841845
--- src/captcha.c
+++ src/captcha.c
@@ -744,11 +744,11 @@
744 (void)exclude_spiders(1);
745 @ <hr><p>The captcha is shown above. Add a name=HEX query parameter
746 @ to see how HEX would be rendered in the current captcha font.
747 @ <h2>Debug/Testing Values:</h2>
748 @ <ul>
749 @ <li> g.isHuman = %d(g.isHuman)
750 @ <li> g.zLogin = %h(g.zLogin)
751 @ <li> login_cookie_welformed() = %d(login_cookie_wellformed())
752 @ <li> captcha_is_correct(1) = %d(captcha_is_correct(1)).
753 @ </ul>
754 style_finish_page();
@@ -759,10 +759,18 @@
759 @ %s(captcha_render(zPw))
760 @ </pre>
761 style_finish_page();
762 }
763 }
 
 
 
 
 
 
 
 
764
765 /*
766 ** Check to see if the current request is coming from an agent that
767 ** self-identifies as a spider.
768 **
@@ -776,27 +784,23 @@
776 ** If the bTest argument is non-zero, then show the captcha regardless of
777 ** how the agent identifies. This is used for testing only.
778 */
779 int exclude_spiders(int bTest){
780 if( !bTest ){
781 if( g.isHuman ) return 0; /* This user has already proven human */
782 if( g.zLogin!=0 ) return 0; /* Logged in. Consider them human */
783 if( login_cookie_wellformed() ){
784 /* Logged into another member of the login group */
785 return 0;
786 }
787 }
788
789 /* This appears to be a spider. Offer the captcha */
790 style_set_current_feature("captcha");
791 style_header("I think you are a robot");
792 style_submenu_enable(0);
793 @ <form method='POST' action='%R/ityaar'>
794 @ <p>You seem like a robot.
795 @
796 @ <p>If you are human, you can prove that by solving the captcha below,
797 @ after which you will be allowed to proceed.
798 if( bTest ){
799 @ <input type="hidden" name="istest" value="1">
800 }
801 captcha_generate(3);
802 @ </form>
@@ -830,11 +834,11 @@
830 }
831 cgi_append_header("X-Robot: 0\r\n");
832 }
833 login_redirect_to_g();
834 }else{
835 g.isHuman = 0;
836 (void)exclude_spiders(bTest);
837 if( bTest ){
838 @ <hr><p>Wrong code. Try again
839 style_finish_page();
840 }
841
--- src/captcha.c
+++ src/captcha.c
@@ -744,11 +744,11 @@
744 (void)exclude_spiders(1);
745 @ <hr><p>The captcha is shown above. Add a name=HEX query parameter
746 @ to see how HEX would be rendered in the current captcha font.
747 @ <h2>Debug/Testing Values:</h2>
748 @ <ul>
749 @ <li> g.isRobot = %d(g.isRobot)
750 @ <li> g.zLogin = %h(g.zLogin)
751 @ <li> login_cookie_welformed() = %d(login_cookie_wellformed())
752 @ <li> captcha_is_correct(1) = %d(captcha_is_correct(1)).
753 @ </ul>
754 style_finish_page();
@@ -759,10 +759,18 @@
759 @ %s(captcha_render(zPw))
760 @ </pre>
761 style_finish_page();
762 }
763 }
764
765 /*
766 ** WEBPAGE: honeypot
767 ** This page is a honeypot for spiders and bots.
768 */
769 void honeypot_page(void){
770 (void)exclude_spiders(0);
771 }
772
773 /*
774 ** Check to see if the current request is coming from an agent that
775 ** self-identifies as a spider.
776 **
@@ -776,27 +784,23 @@
784 ** If the bTest argument is non-zero, then show the captcha regardless of
785 ** how the agent identifies. This is used for testing only.
786 */
787 int exclude_spiders(int bTest){
788 if( !bTest ){
 
789 if( g.zLogin!=0 ) return 0; /* Logged in. Consider them human */
790 if( login_cookie_wellformed() ){
791 /* Logged into another member of the login group */
792 return 0;
793 }
794 }
795
796 /* This appears to be a spider. Offer the captcha */
797 style_set_current_feature("captcha");
798 style_header("Captcha");
799 style_submenu_enable(0);
800 @ <form method='POST' action='%R/ityaar'>
801 @ <h2>Prove that you are human:
 
 
 
802 if( bTest ){
803 @ <input type="hidden" name="istest" value="1">
804 }
805 captcha_generate(3);
806 @ </form>
@@ -830,11 +834,11 @@
834 }
835 cgi_append_header("X-Robot: 0\r\n");
836 }
837 login_redirect_to_g();
838 }else{
839 g.isRobot = 1;
840 (void)exclude_spiders(bTest);
841 if( bTest ){
842 @ <hr><p>Wrong code. Try again
843 style_finish_page();
844 }
845
+37 -9
--- src/cgi.c
+++ src/cgi.c
@@ -964,11 +964,11 @@
964964
** * it is impossible for a cookie or query parameter to
965965
** override the value of an environment variable since
966966
** environment variables always have uppercase names.
967967
**
968968
** 2018-03-29: Also ignore the entry if NAME that contains any characters
969
-** other than [a-zA-Z0-9_]. There are no known exploits involving unusual
969
+** other than [-a-zA-Z0-9_]. There are no known exploits involving unusual
970970
** names that contain characters outside that set, but it never hurts to
971971
** be extra cautious when sanitizing inputs.
972972
**
973973
** Parameters are separated by the "terminator" character. Whitespace
974974
** before the NAME is ignored.
@@ -1280,36 +1280,49 @@
12801280
12811281
/* Forward declaration */
12821282
static NORETURN void malformed_request(const char *zMsg, ...);
12831283
12841284
/*
1285
-** Checks the QUERY_STRING environment variable, sets it up
1286
-** via add_param_list() and, if found, applies its "skin"
1287
-** setting. Returns 0 if no QUERY_STRING is set, 1 if it is,
1288
-** and 2 if it sets the skin (in which case the cookie may
1289
-** still need flushing by the page, via cookie_render()).
1285
+** Checks the QUERY_STRING environment variable, sets it up via
1286
+** add_param_list() and, if found, applies its "skin" setting. Returns
1287
+** 0 if no QUERY_STRING is set, else it returns a bitmask of:
1288
+**
1289
+** 0x01 = QUERY_STRING was set up
1290
+** 0x02 = "skin" URL param arg was processed
1291
+** 0x04 = "x-f-l-c" cookie arg was processed.
1292
+**
1293
+* In the case of the skin, the cookie may still need flushing
1294
+** by the page, via cookie_render().
12901295
*/
12911296
int cgi_setup_query_string(void){
12921297
int rc = 0;
12931298
char * z = (char*)P("QUERY_STRING");
12941299
if( z ){
1295
- ++rc;
1300
+ rc = 0x01;
12961301
z = fossil_strdup(z);
12971302
add_param_list(z, '&');
12981303
z = (char*)P("skin");
12991304
if( z ){
13001305
char *zErr = skin_use_alternative(z, 2, SKIN_FROM_QPARAM);
1301
- ++rc;
1306
+ rc |= 0x02;
13021307
if( !zErr && P("once")==0 ){
13031308
cookie_write_parameter("skin","skin",z);
13041309
/* Per /chat discussion, passing ?skin=... without "once"
13051310
** implies the "udc" argument, so we force that into the
13061311
** environment here. */
13071312
cgi_set_parameter_nocopy("udc", "1", 1);
13081313
}
13091314
fossil_free(zErr);
13101315
}
1316
+ }
1317
+ if( !g.syncInfo.zLoginCard && 0!=(z=(char*)P("x-f-l-c")) ){
1318
+ /* x-f-l-c (X-Fossil-Login-Card card transmitted via cookie
1319
+ ** instead of in the sync payload. */
1320
+ rc |= 0x04;
1321
+ g.syncInfo.zLoginCard = fossil_strdup(z);
1322
+ g.syncInfo.fLoginCardMode |= 0x02;
1323
+ cgi_delete_parameter("x-f-l-c");
13111324
}
13121325
return rc;
13131326
}
13141327
13151328
/*
@@ -1599,10 +1612,25 @@
15991612
}
16001613
}
16011614
CGIDEBUG(("no-match [%s]\n", zName));
16021615
return zDefault;
16031616
}
1617
+
1618
+/*
1619
+** Return TRUE if the specific parameter exists and is a query parameter.
1620
+** Return FALSE if the parameter is a cookie or environment variable.
1621
+*/
1622
+int cgi_is_qp(const char *zName){
1623
+ int i;
1624
+ if( zName==0 || fossil_isupper(zName[0]) ) return 0;
1625
+ for(i=0; i<nUsedQP; i++){
1626
+ if( fossil_strcmp(aParamQP[i].zName,zName)==0 ){
1627
+ return aParamQP[i].isQP;
1628
+ }
1629
+ }
1630
+ return 0;
1631
+}
16041632
16051633
/*
16061634
** Renders the "begone, spider" page and exits.
16071635
*/
16081636
static void cgi_begone_spider(const char *zName){
@@ -2125,10 +2153,11 @@
21252153
int i;
21262154
const char *zScheme = "http";
21272155
char zLine[2000]; /* A single line of input. */
21282156
g.fullHttpReply = 1;
21292157
g.zReqType = "HTTP";
2158
+
21302159
if( cgi_fgets(zLine, sizeof(zLine))==0 ){
21312160
malformed_request("missing header");
21322161
}
21332162
blob_append(&g.httpHeader, zLine, -1);
21342163
cgi_trace(zLine);
@@ -2160,11 +2189,10 @@
21602189
}
21612190
if( zIpAddr ){
21622191
cgi_setenv("REMOTE_ADDR", zIpAddr);
21632192
g.zIpAddr = fossil_strdup(zIpAddr);
21642193
}
2165
-
21662194
21672195
/* Get all the optional fields that follow the first line.
21682196
*/
21692197
while( cgi_fgets(zLine,sizeof(zLine)) ){
21702198
char *zFieldName;
21712199
--- src/cgi.c
+++ src/cgi.c
@@ -964,11 +964,11 @@
964 ** * it is impossible for a cookie or query parameter to
965 ** override the value of an environment variable since
966 ** environment variables always have uppercase names.
967 **
968 ** 2018-03-29: Also ignore the entry if NAME that contains any characters
969 ** other than [a-zA-Z0-9_]. There are no known exploits involving unusual
970 ** names that contain characters outside that set, but it never hurts to
971 ** be extra cautious when sanitizing inputs.
972 **
973 ** Parameters are separated by the "terminator" character. Whitespace
974 ** before the NAME is ignored.
@@ -1280,36 +1280,49 @@
1280
1281 /* Forward declaration */
1282 static NORETURN void malformed_request(const char *zMsg, ...);
1283
1284 /*
1285 ** Checks the QUERY_STRING environment variable, sets it up
1286 ** via add_param_list() and, if found, applies its "skin"
1287 ** setting. Returns 0 if no QUERY_STRING is set, 1 if it is,
1288 ** and 2 if it sets the skin (in which case the cookie may
1289 ** still need flushing by the page, via cookie_render()).
 
 
 
 
 
1290 */
1291 int cgi_setup_query_string(void){
1292 int rc = 0;
1293 char * z = (char*)P("QUERY_STRING");
1294 if( z ){
1295 ++rc;
1296 z = fossil_strdup(z);
1297 add_param_list(z, '&');
1298 z = (char*)P("skin");
1299 if( z ){
1300 char *zErr = skin_use_alternative(z, 2, SKIN_FROM_QPARAM);
1301 ++rc;
1302 if( !zErr && P("once")==0 ){
1303 cookie_write_parameter("skin","skin",z);
1304 /* Per /chat discussion, passing ?skin=... without "once"
1305 ** implies the "udc" argument, so we force that into the
1306 ** environment here. */
1307 cgi_set_parameter_nocopy("udc", "1", 1);
1308 }
1309 fossil_free(zErr);
1310 }
 
 
 
 
 
 
 
 
1311 }
1312 return rc;
1313 }
1314
1315 /*
@@ -1599,10 +1612,25 @@
1599 }
1600 }
1601 CGIDEBUG(("no-match [%s]\n", zName));
1602 return zDefault;
1603 }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1604
1605 /*
1606 ** Renders the "begone, spider" page and exits.
1607 */
1608 static void cgi_begone_spider(const char *zName){
@@ -2125,10 +2153,11 @@
2125 int i;
2126 const char *zScheme = "http";
2127 char zLine[2000]; /* A single line of input. */
2128 g.fullHttpReply = 1;
2129 g.zReqType = "HTTP";
 
2130 if( cgi_fgets(zLine, sizeof(zLine))==0 ){
2131 malformed_request("missing header");
2132 }
2133 blob_append(&g.httpHeader, zLine, -1);
2134 cgi_trace(zLine);
@@ -2160,11 +2189,10 @@
2160 }
2161 if( zIpAddr ){
2162 cgi_setenv("REMOTE_ADDR", zIpAddr);
2163 g.zIpAddr = fossil_strdup(zIpAddr);
2164 }
2165
2166
2167 /* Get all the optional fields that follow the first line.
2168 */
2169 while( cgi_fgets(zLine,sizeof(zLine)) ){
2170 char *zFieldName;
2171
--- src/cgi.c
+++ src/cgi.c
@@ -964,11 +964,11 @@
964 ** * it is impossible for a cookie or query parameter to
965 ** override the value of an environment variable since
966 ** environment variables always have uppercase names.
967 **
968 ** 2018-03-29: Also ignore the entry if NAME that contains any characters
969 ** other than [-a-zA-Z0-9_]. There are no known exploits involving unusual
970 ** names that contain characters outside that set, but it never hurts to
971 ** be extra cautious when sanitizing inputs.
972 **
973 ** Parameters are separated by the "terminator" character. Whitespace
974 ** before the NAME is ignored.
@@ -1280,36 +1280,49 @@
1280
1281 /* Forward declaration */
1282 static NORETURN void malformed_request(const char *zMsg, ...);
1283
1284 /*
1285 ** Checks the QUERY_STRING environment variable, sets it up via
1286 ** add_param_list() and, if found, applies its "skin" setting. Returns
1287 ** 0 if no QUERY_STRING is set, else it returns a bitmask of:
1288 **
1289 ** 0x01 = QUERY_STRING was set up
1290 ** 0x02 = "skin" URL param arg was processed
1291 ** 0x04 = "x-f-l-c" cookie arg was processed.
1292 **
1293 * In the case of the skin, the cookie may still need flushing
1294 ** by the page, via cookie_render().
1295 */
1296 int cgi_setup_query_string(void){
1297 int rc = 0;
1298 char * z = (char*)P("QUERY_STRING");
1299 if( z ){
1300 rc = 0x01;
1301 z = fossil_strdup(z);
1302 add_param_list(z, '&');
1303 z = (char*)P("skin");
1304 if( z ){
1305 char *zErr = skin_use_alternative(z, 2, SKIN_FROM_QPARAM);
1306 rc |= 0x02;
1307 if( !zErr && P("once")==0 ){
1308 cookie_write_parameter("skin","skin",z);
1309 /* Per /chat discussion, passing ?skin=... without "once"
1310 ** implies the "udc" argument, so we force that into the
1311 ** environment here. */
1312 cgi_set_parameter_nocopy("udc", "1", 1);
1313 }
1314 fossil_free(zErr);
1315 }
1316 }
1317 if( !g.syncInfo.zLoginCard && 0!=(z=(char*)P("x-f-l-c")) ){
1318 /* x-f-l-c (X-Fossil-Login-Card card transmitted via cookie
1319 ** instead of in the sync payload. */
1320 rc |= 0x04;
1321 g.syncInfo.zLoginCard = fossil_strdup(z);
1322 g.syncInfo.fLoginCardMode |= 0x02;
1323 cgi_delete_parameter("x-f-l-c");
1324 }
1325 return rc;
1326 }
1327
1328 /*
@@ -1599,10 +1612,25 @@
1612 }
1613 }
1614 CGIDEBUG(("no-match [%s]\n", zName));
1615 return zDefault;
1616 }
1617
1618 /*
1619 ** Return TRUE if the specific parameter exists and is a query parameter.
1620 ** Return FALSE if the parameter is a cookie or environment variable.
1621 */
1622 int cgi_is_qp(const char *zName){
1623 int i;
1624 if( zName==0 || fossil_isupper(zName[0]) ) return 0;
1625 for(i=0; i<nUsedQP; i++){
1626 if( fossil_strcmp(aParamQP[i].zName,zName)==0 ){
1627 return aParamQP[i].isQP;
1628 }
1629 }
1630 return 0;
1631 }
1632
1633 /*
1634 ** Renders the "begone, spider" page and exits.
1635 */
1636 static void cgi_begone_spider(const char *zName){
@@ -2125,10 +2153,11 @@
2153 int i;
2154 const char *zScheme = "http";
2155 char zLine[2000]; /* A single line of input. */
2156 g.fullHttpReply = 1;
2157 g.zReqType = "HTTP";
2158
2159 if( cgi_fgets(zLine, sizeof(zLine))==0 ){
2160 malformed_request("missing header");
2161 }
2162 blob_append(&g.httpHeader, zLine, -1);
2163 cgi_trace(zLine);
@@ -2160,11 +2189,10 @@
2189 }
2190 if( zIpAddr ){
2191 cgi_setenv("REMOTE_ADDR", zIpAddr);
2192 g.zIpAddr = fossil_strdup(zIpAddr);
2193 }
 
2194
2195 /* Get all the optional fields that follow the first line.
2196 */
2197 while( cgi_fgets(zLine,sizeof(zLine)) ){
2198 char *zFieldName;
2199
+1 -1
--- src/chat.c
+++ src/chat.c
@@ -1321,11 +1321,11 @@
13211321
if( zMsg && zMsg[0] ){
13221322
blob_appendf(&up,"\r\nContent-Disposition: form-data; name=\"msg\"\r\n"
13231323
"\r\n%s\r\n%s", zMsg, zBoundary);
13241324
}
13251325
if( zFilename && blob_read_from_file(&fcontent, zFilename, ExtFILE)>0 ){
1326
- char *zFN = mprintf("%s", file_tail(zAs ? zAs : zFilename));
1326
+ char *zFN = fossil_strdup(file_tail(zAs ? zAs : zFilename));
13271327
int i;
13281328
const char *zMime = mimetype_from_name(zFN);
13291329
for(i=0; zFN[i]; i++){
13301330
char c = zFN[i];
13311331
if( fossil_isalnum(c) ) continue;
13321332
--- src/chat.c
+++ src/chat.c
@@ -1321,11 +1321,11 @@
1321 if( zMsg && zMsg[0] ){
1322 blob_appendf(&up,"\r\nContent-Disposition: form-data; name=\"msg\"\r\n"
1323 "\r\n%s\r\n%s", zMsg, zBoundary);
1324 }
1325 if( zFilename && blob_read_from_file(&fcontent, zFilename, ExtFILE)>0 ){
1326 char *zFN = mprintf("%s", file_tail(zAs ? zAs : zFilename));
1327 int i;
1328 const char *zMime = mimetype_from_name(zFN);
1329 for(i=0; zFN[i]; i++){
1330 char c = zFN[i];
1331 if( fossil_isalnum(c) ) continue;
1332
--- src/chat.c
+++ src/chat.c
@@ -1321,11 +1321,11 @@
1321 if( zMsg && zMsg[0] ){
1322 blob_appendf(&up,"\r\nContent-Disposition: form-data; name=\"msg\"\r\n"
1323 "\r\n%s\r\n%s", zMsg, zBoundary);
1324 }
1325 if( zFilename && blob_read_from_file(&fcontent, zFilename, ExtFILE)>0 ){
1326 char *zFN = fossil_strdup(file_tail(zAs ? zAs : zFilename));
1327 int i;
1328 const char *zMime = mimetype_from_name(zFN);
1329 for(i=0; zFN[i]; i++){
1330 char c = zFN[i];
1331 if( fossil_isalnum(c) ) continue;
1332
+2 -2
--- src/checkin.c
+++ src/checkin.c
@@ -1386,11 +1386,11 @@
13861386
file_relative_name(g.zLocalRoot, &fname, 1);
13871387
zFile = db_text(0, "SELECT '%qci-comment-'||hex(randomblob(6))||'.txt'",
13881388
blob_str(&fname));
13891389
}else{
13901390
file_tempname(&fname, "ci-comment",0);
1391
- zFile = mprintf("%s", blob_str(&fname));
1391
+ zFile = fossil_strdup(blob_str(&fname));
13921392
}
13931393
blob_reset(&fname);
13941394
}
13951395
#if defined(_WIN32)
13961396
blob_add_cr(pPrompt);
@@ -1502,11 +1502,11 @@
15021502
"# * All other text will be displayed as written\n", -1);
15031503
}else{
15041504
blob_append(&prompt,
15051505
"# * Hyperlinks: [target] or [target|display-text]\n"
15061506
"# * Blank lines cause a paragraph break\n"
1507
- "# * Other text rendered as if it where HTML\n", -1
1507
+ "# * Other text rendered as if it were HTML\n", -1
15081508
);
15091509
}
15101510
blob_append(&prompt, "#\n", 2);
15111511
15121512
if( dryRunFlag ){
15131513
--- src/checkin.c
+++ src/checkin.c
@@ -1386,11 +1386,11 @@
1386 file_relative_name(g.zLocalRoot, &fname, 1);
1387 zFile = db_text(0, "SELECT '%qci-comment-'||hex(randomblob(6))||'.txt'",
1388 blob_str(&fname));
1389 }else{
1390 file_tempname(&fname, "ci-comment",0);
1391 zFile = mprintf("%s", blob_str(&fname));
1392 }
1393 blob_reset(&fname);
1394 }
1395 #if defined(_WIN32)
1396 blob_add_cr(pPrompt);
@@ -1502,11 +1502,11 @@
1502 "# * All other text will be displayed as written\n", -1);
1503 }else{
1504 blob_append(&prompt,
1505 "# * Hyperlinks: [target] or [target|display-text]\n"
1506 "# * Blank lines cause a paragraph break\n"
1507 "# * Other text rendered as if it where HTML\n", -1
1508 );
1509 }
1510 blob_append(&prompt, "#\n", 2);
1511
1512 if( dryRunFlag ){
1513
--- src/checkin.c
+++ src/checkin.c
@@ -1386,11 +1386,11 @@
1386 file_relative_name(g.zLocalRoot, &fname, 1);
1387 zFile = db_text(0, "SELECT '%qci-comment-'||hex(randomblob(6))||'.txt'",
1388 blob_str(&fname));
1389 }else{
1390 file_tempname(&fname, "ci-comment",0);
1391 zFile = fossil_strdup(blob_str(&fname));
1392 }
1393 blob_reset(&fname);
1394 }
1395 #if defined(_WIN32)
1396 blob_add_cr(pPrompt);
@@ -1502,11 +1502,11 @@
1502 "# * All other text will be displayed as written\n", -1);
1503 }else{
1504 blob_append(&prompt,
1505 "# * Hyperlinks: [target] or [target|display-text]\n"
1506 "# * Blank lines cause a paragraph break\n"
1507 "# * Other text rendered as if it were HTML\n", -1
1508 );
1509 }
1510 blob_append(&prompt, "#\n", 2);
1511
1512 if( dryRunFlag ){
1513
+1 -1
--- src/clearsign.c
+++ src/clearsign.c
@@ -65,11 +65,11 @@
6565
blob_appendf(pOut, "%s", "-----BEGIN SSH SIGNED MESSAGE-----\n\n");
6666
blob_appendf(pOut, "%s", blob_str(&tmpBlob));
6767
blob_zero(&tmpBlob);
6868
blob_read_from_file(&tmpBlob, zIn, ExtFILE);
6969
/* Add signature - already armored by SSH */
70
- blob_appendf(pOut, "%s", blob_str(&tmpBlob));
70
+ blob_appendb(pOut, &tmpBlob);
7171
}else{
7272
/* Assume that the external command creates non-detached signatures */
7373
blob_read_from_file(pOut, zIn, ExtFILE);
7474
}
7575
}else{
7676
--- src/clearsign.c
+++ src/clearsign.c
@@ -65,11 +65,11 @@
65 blob_appendf(pOut, "%s", "-----BEGIN SSH SIGNED MESSAGE-----\n\n");
66 blob_appendf(pOut, "%s", blob_str(&tmpBlob));
67 blob_zero(&tmpBlob);
68 blob_read_from_file(&tmpBlob, zIn, ExtFILE);
69 /* Add signature - already armored by SSH */
70 blob_appendf(pOut, "%s", blob_str(&tmpBlob));
71 }else{
72 /* Assume that the external command creates non-detached signatures */
73 blob_read_from_file(pOut, zIn, ExtFILE);
74 }
75 }else{
76
--- src/clearsign.c
+++ src/clearsign.c
@@ -65,11 +65,11 @@
65 blob_appendf(pOut, "%s", "-----BEGIN SSH SIGNED MESSAGE-----\n\n");
66 blob_appendf(pOut, "%s", blob_str(&tmpBlob));
67 blob_zero(&tmpBlob);
68 blob_read_from_file(&tmpBlob, zIn, ExtFILE);
69 /* Add signature - already armored by SSH */
70 blob_appendb(pOut, &tmpBlob);
71 }else{
72 /* Assume that the external command creates non-detached signatures */
73 blob_read_from_file(pOut, zIn, ExtFILE);
74 }
75 }else{
76
+2 -2
--- src/clone.c
+++ src/clone.c
@@ -348,11 +348,11 @@
348348
const char *zHttpAuth, /* Credentials in the form "user:password" */
349349
int fRemember, /* True to remember credentials for later reuse */
350350
const char *zUrl /* URL for which these credentials apply */
351351
){
352352
if( zHttpAuth && zHttpAuth[0] ){
353
- g.zHttpAuth = mprintf("%s", zHttpAuth);
353
+ g.zHttpAuth = fossil_strdup(zHttpAuth);
354354
}
355355
if( fRemember ){
356356
if( g.zHttpAuth && g.zHttpAuth[0] ){
357357
set_httpauth(g.zHttpAuth);
358358
}else if( zUrl && zUrl[0] ){
@@ -388,11 +388,11 @@
388388
void clone_ssh_find_options(void){
389389
const char *zSshCmd; /* SSH command string */
390390
391391
zSshCmd = find_option("ssh-command","c",1);
392392
if( zSshCmd && zSshCmd[0] ){
393
- g.zSshCmd = mprintf("%s", zSshCmd);
393
+ g.zSshCmd = fossil_strdup(zSshCmd);
394394
}
395395
}
396396
397397
/*
398398
** Set SSH options discovered in global variables (set from command line
399399
--- src/clone.c
+++ src/clone.c
@@ -348,11 +348,11 @@
348 const char *zHttpAuth, /* Credentials in the form "user:password" */
349 int fRemember, /* True to remember credentials for later reuse */
350 const char *zUrl /* URL for which these credentials apply */
351 ){
352 if( zHttpAuth && zHttpAuth[0] ){
353 g.zHttpAuth = mprintf("%s", zHttpAuth);
354 }
355 if( fRemember ){
356 if( g.zHttpAuth && g.zHttpAuth[0] ){
357 set_httpauth(g.zHttpAuth);
358 }else if( zUrl && zUrl[0] ){
@@ -388,11 +388,11 @@
388 void clone_ssh_find_options(void){
389 const char *zSshCmd; /* SSH command string */
390
391 zSshCmd = find_option("ssh-command","c",1);
392 if( zSshCmd && zSshCmd[0] ){
393 g.zSshCmd = mprintf("%s", zSshCmd);
394 }
395 }
396
397 /*
398 ** Set SSH options discovered in global variables (set from command line
399
--- src/clone.c
+++ src/clone.c
@@ -348,11 +348,11 @@
348 const char *zHttpAuth, /* Credentials in the form "user:password" */
349 int fRemember, /* True to remember credentials for later reuse */
350 const char *zUrl /* URL for which these credentials apply */
351 ){
352 if( zHttpAuth && zHttpAuth[0] ){
353 g.zHttpAuth = fossil_strdup(zHttpAuth);
354 }
355 if( fRemember ){
356 if( g.zHttpAuth && g.zHttpAuth[0] ){
357 set_httpauth(g.zHttpAuth);
358 }else if( zUrl && zUrl[0] ){
@@ -388,11 +388,11 @@
388 void clone_ssh_find_options(void){
389 const char *zSshCmd; /* SSH command string */
390
391 zSshCmd = find_option("ssh-command","c",1);
392 if( zSshCmd && zSshCmd[0] ){
393 g.zSshCmd = fossil_strdup(zSshCmd);
394 }
395 }
396
397 /*
398 ** Set SSH options discovered in global variables (set from command line
399
+2 -2
--- src/comformat.c
+++ src/comformat.c
@@ -807,17 +807,17 @@
807807
verify_all_options();
808808
zPrefix = zText = zOrigText = 0;
809809
if( fromFile ){
810810
Blob fileData;
811811
blob_read_from_file(&fileData, fromFile, ExtFILE);
812
- zText = mprintf("%s", blob_str(&fileData));
812
+ zText = fossil_strdup(blob_str(&fileData));
813813
blob_reset(&fileData);
814814
}
815815
if( fromOrig ){
816816
Blob fileData;
817817
blob_read_from_file(&fileData, fromOrig, ExtFILE);
818
- zOrigText = mprintf("%s", blob_str(&fileData));
818
+ zOrigText = fossil_strdup(blob_str(&fileData));
819819
blob_reset(&fileData);
820820
}
821821
for(i=2; i<g.argc; i++){
822822
if( zText==0 ){
823823
zText = g.argv[i];
824824
--- src/comformat.c
+++ src/comformat.c
@@ -807,17 +807,17 @@
807 verify_all_options();
808 zPrefix = zText = zOrigText = 0;
809 if( fromFile ){
810 Blob fileData;
811 blob_read_from_file(&fileData, fromFile, ExtFILE);
812 zText = mprintf("%s", blob_str(&fileData));
813 blob_reset(&fileData);
814 }
815 if( fromOrig ){
816 Blob fileData;
817 blob_read_from_file(&fileData, fromOrig, ExtFILE);
818 zOrigText = mprintf("%s", blob_str(&fileData));
819 blob_reset(&fileData);
820 }
821 for(i=2; i<g.argc; i++){
822 if( zText==0 ){
823 zText = g.argv[i];
824
--- src/comformat.c
+++ src/comformat.c
@@ -807,17 +807,17 @@
807 verify_all_options();
808 zPrefix = zText = zOrigText = 0;
809 if( fromFile ){
810 Blob fileData;
811 blob_read_from_file(&fileData, fromFile, ExtFILE);
812 zText = fossil_strdup(blob_str(&fileData));
813 blob_reset(&fileData);
814 }
815 if( fromOrig ){
816 Blob fileData;
817 blob_read_from_file(&fileData, fromOrig, ExtFILE);
818 zOrigText = fossil_strdup(blob_str(&fileData));
819 blob_reset(&fileData);
820 }
821 for(i=2; i<g.argc; i++){
822 if( zText==0 ){
823 zText = g.argv[i];
824
--- src/content.c
+++ src/content.c
@@ -600,10 +600,11 @@
600600
);
601601
db_bind_blob(&s1, ":data", &cmpr);
602602
db_exec(&s1);
603603
rid = db_last_insert_rowid();
604604
if( !pBlob ){
605
+ assert(!"cannot happen: pBlob is always non-NULL");
605606
db_multi_exec("INSERT OR IGNORE INTO phantom VALUES(%d)", rid);
606607
}
607608
}
608609
if( g.markPrivate || isPrivate ){
609610
db_multi_exec("INSERT OR IGNORE INTO private VALUES(%d)", rid);
610611
--- src/content.c
+++ src/content.c
@@ -600,10 +600,11 @@
600 );
601 db_bind_blob(&s1, ":data", &cmpr);
602 db_exec(&s1);
603 rid = db_last_insert_rowid();
604 if( !pBlob ){
 
605 db_multi_exec("INSERT OR IGNORE INTO phantom VALUES(%d)", rid);
606 }
607 }
608 if( g.markPrivate || isPrivate ){
609 db_multi_exec("INSERT OR IGNORE INTO private VALUES(%d)", rid);
610
--- src/content.c
+++ src/content.c
@@ -600,10 +600,11 @@
600 );
601 db_bind_blob(&s1, ":data", &cmpr);
602 db_exec(&s1);
603 rid = db_last_insert_rowid();
604 if( !pBlob ){
605 assert(!"cannot happen: pBlob is always non-NULL");
606 db_multi_exec("INSERT OR IGNORE INTO phantom VALUES(%d)", rid);
607 }
608 }
609 if( g.markPrivate || isPrivate ){
610 db_multi_exec("INSERT OR IGNORE INTO private VALUES(%d)", rid);
611
+5 -1
--- src/cookies.c
+++ src/cookies.c
@@ -86,11 +86,11 @@
8686
void cookie_parse(void){
8787
char *z;
8888
if( cookies.bIsInit ) return;
8989
z = (char*)P(DISPLAY_SETTINGS_COOKIE);
9090
if( z==0 ) z = "";
91
- cookies.zCookieValue = z = mprintf("%s", z);
91
+ cookies.zCookieValue = z = fossil_strdup(z);
9292
cookies.bIsInit = 1;
9393
while( cookies.nParam<COOKIE_NPARAM ){
9494
while( fossil_isspace(z[0]) ) z++;
9595
if( z[0]==0 ) break;
9696
cookies.aParam[cookies.nParam].zPName = z;
@@ -282,10 +282,14 @@
282282
&& hex_prefix_length(&zName[7])==16
283283
&& hex_prefix_length(zValue)>24
284284
){
285285
@ <p>This appears to be a login cookie for another Fossil repository
286286
@ in the same website.
287
+ }else
288
+ if( fossil_strcmp(zName, ROBOT_COOKIE)==0 ){
289
+ @ <p>This cookie shows that your web-browser has been tested is
290
+ @ believed to be operated by a human, not a robot.
287291
}
288292
else {
289293
@ <p>This cookie was not generated by Fossil. It might be something
290294
@ from another program on the same website.
291295
}
292296
--- src/cookies.c
+++ src/cookies.c
@@ -86,11 +86,11 @@
86 void cookie_parse(void){
87 char *z;
88 if( cookies.bIsInit ) return;
89 z = (char*)P(DISPLAY_SETTINGS_COOKIE);
90 if( z==0 ) z = "";
91 cookies.zCookieValue = z = mprintf("%s", z);
92 cookies.bIsInit = 1;
93 while( cookies.nParam<COOKIE_NPARAM ){
94 while( fossil_isspace(z[0]) ) z++;
95 if( z[0]==0 ) break;
96 cookies.aParam[cookies.nParam].zPName = z;
@@ -282,10 +282,14 @@
282 && hex_prefix_length(&zName[7])==16
283 && hex_prefix_length(zValue)>24
284 ){
285 @ <p>This appears to be a login cookie for another Fossil repository
286 @ in the same website.
 
 
 
 
287 }
288 else {
289 @ <p>This cookie was not generated by Fossil. It might be something
290 @ from another program on the same website.
291 }
292
--- src/cookies.c
+++ src/cookies.c
@@ -86,11 +86,11 @@
86 void cookie_parse(void){
87 char *z;
88 if( cookies.bIsInit ) return;
89 z = (char*)P(DISPLAY_SETTINGS_COOKIE);
90 if( z==0 ) z = "";
91 cookies.zCookieValue = z = fossil_strdup(z);
92 cookies.bIsInit = 1;
93 while( cookies.nParam<COOKIE_NPARAM ){
94 while( fossil_isspace(z[0]) ) z++;
95 if( z[0]==0 ) break;
96 cookies.aParam[cookies.nParam].zPName = z;
@@ -282,10 +282,14 @@
282 && hex_prefix_length(&zName[7])==16
283 && hex_prefix_length(zValue)>24
284 ){
285 @ <p>This appears to be a login cookie for another Fossil repository
286 @ in the same website.
287 }else
288 if( fossil_strcmp(zName, ROBOT_COOKIE)==0 ){
289 @ <p>This cookie shows that your web-browser has been tested is
290 @ believed to be operated by a human, not a robot.
291 }
292 else {
293 @ <p>This cookie was not generated by Fossil. It might be something
294 @ from another program on the same website.
295 }
296
+19 -22
--- src/copybtn.js
+++ src/copybtn.js
@@ -1,32 +1,34 @@
11
/* Manage "Copy Buttons" linked to target elements, to copy the text (or, parts
22
** thereof) of the target elements to the clipboard.
33
**
4
-** Newly created buttons are <span> elements with an SVG background icon,
5
-** defined by the "copy-button" class in the default CSS style sheet, and are
6
-** assigned the element ID "copy-<idTarget>".
7
-**
8
-** To simplify customization, the only properties modified for HTML-defined
9
-** buttons are the "onclick" handler, and the "transition" and "opacity" styles
10
-** (used for animation).
4
+** Newly created buttons are <button> elements plus a nested <span> element with
5
+** an SVG background icon, defined by the "copy-button" class in the default CSS
6
+** style sheet, and are assigned the element ID "copy-<idTarget>".
117
**
128
** For HTML-defined buttons, either initCopyButtonById(), or initCopyButton(),
139
** needs to be called to attach the "onclick" handler (done automatically from
14
-** a handler attached to the "DOMContentLoaded" event).
10
+** a handler attached to the "DOMContentLoaded" event). These functions create
11
+** the nested <span> element if the <button> element has no child nodes. Using
12
+** static HTML for the <span> element ensures the buttons are visible if there
13
+** are script errors, which may be useful for Fossil JS hackers (as good parts
14
+** of the Fossil web UI come down on JS errors, anyway).
1515
**
1616
** The initialization functions do not overwrite the "data-copytarget" and
1717
** "data-copylength" attributes with empty or null values for <idTarget> and
1818
** <cchLength>, respectively. Set <cchLength> to "-1" to explicitly remove the
1919
** previous copy length limit.
2020
**
2121
** HTML snippet for statically created buttons:
2222
**
23
-** <span class="copy-button" id="copy-<idTarget>"
24
-** data-copytarget="<idTarget>" data-copylength="<cchLength>"></span>
23
+** <button class="copy-button" id="copy-<idTarget>"
24
+** data-copytarget="<idTarget>" data-copylength="<cchLength>">
25
+** <span></span>
26
+** </button>
2527
*/
2628
function makeCopyButton(idTarget,bFlipped,cchLength){
27
- var elButton = document.createElement("span");
29
+ var elButton = document.createElement("button");
2830
elButton.className = "copy-button";
2931
if( bFlipped ) elButton.className += " copy-button-flipped";
3032
elButton.id = "copy-" + idTarget;
3133
initCopyButton(elButton,idTarget,cchLength);
3234
return elButton;
@@ -36,15 +38,18 @@
3638
var elButton = document.getElementById(idButton);
3739
if( elButton ) initCopyButton(elButton,idTarget,cchLength);
3840
return elButton;
3941
}
4042
function initCopyButton(elButton,idTarget,cchLength){
41
- elButton.style.transition = "";
42
- elButton.style.opacity = 1;
4343
if( idTarget ) elButton.setAttribute("data-copytarget",idTarget);
4444
if( cchLength ) elButton.setAttribute("data-copylength",cchLength);
4545
elButton.onclick = clickCopyButton;
46
+ /* Make sure the <button> contains a single nested <span>. */
47
+ if( elButton.childElementCount!=1 || elButton.firstChild.tagName!="SPAN" ){
48
+ while( elButton.firstChild ) elButton.removeChild(elButton.lastChild);
49
+ elButton.appendChild(document.createElement("span"));
50
+ }
4651
return elButton;
4752
}
4853
setTimeout(function(){
4954
var elButtons = document.getElementsByClassName("copy-button");
5055
for ( var i=0; i<elButtons.length; i++ ){
@@ -53,14 +58,11 @@
5358
},1);
5459
/* The onclick handler for the "Copy Button". */
5560
function clickCopyButton(e){
5661
e.preventDefault(); /* Mandatory for <a> and <button>. */
5762
e.stopPropagation();
58
- if( this.getAttribute("data-copylocked") ) return;
59
- this.setAttribute("data-copylocked","1");
60
- this.style.transition = "opacity 400ms ease-in-out";
61
- this.style.opacity = 0;
63
+ if( this.disabled ) return; /* This check is probably redundant. */
6264
var idTarget = this.getAttribute("data-copytarget");
6365
var elTarget = document.getElementById(idTarget);
6466
if( elTarget ){
6567
var text = elTarget.innerText.replace(/^\s+|\s+$/g,"");
6668
var cchLength = parseInt(this.getAttribute("data-copylength"));
@@ -67,15 +69,10 @@
6769
if( !isNaN(cchLength) && cchLength>0 ){
6870
text = text.slice(0,cchLength); /* Assume single-byte chars. */
6971
}
7072
copyTextToClipboard(text);
7173
}
72
- setTimeout(function(){
73
- this.style.transition = "";
74
- this.style.opacity = 1;
75
- this.removeAttribute("data-copylocked");
76
- }.bind(this),400);
7774
}
7875
/* Create a temporary <textarea> element and copy the contents to clipboard. */
7976
function copyTextToClipboard(text){
8077
if( window.clipboardData && window.clipboardData.setData ){
8178
window.clipboardData.setData("Text",text);
8279
--- src/copybtn.js
+++ src/copybtn.js
@@ -1,32 +1,34 @@
1 /* Manage "Copy Buttons" linked to target elements, to copy the text (or, parts
2 ** thereof) of the target elements to the clipboard.
3 **
4 ** Newly created buttons are <span> elements with an SVG background icon,
5 ** defined by the "copy-button" class in the default CSS style sheet, and are
6 ** assigned the element ID "copy-<idTarget>".
7 **
8 ** To simplify customization, the only properties modified for HTML-defined
9 ** buttons are the "onclick" handler, and the "transition" and "opacity" styles
10 ** (used for animation).
11 **
12 ** For HTML-defined buttons, either initCopyButtonById(), or initCopyButton(),
13 ** needs to be called to attach the "onclick" handler (done automatically from
14 ** a handler attached to the "DOMContentLoaded" event).
 
 
 
 
15 **
16 ** The initialization functions do not overwrite the "data-copytarget" and
17 ** "data-copylength" attributes with empty or null values for <idTarget> and
18 ** <cchLength>, respectively. Set <cchLength> to "-1" to explicitly remove the
19 ** previous copy length limit.
20 **
21 ** HTML snippet for statically created buttons:
22 **
23 ** <span class="copy-button" id="copy-<idTarget>"
24 ** data-copytarget="<idTarget>" data-copylength="<cchLength>"></span>
 
 
25 */
26 function makeCopyButton(idTarget,bFlipped,cchLength){
27 var elButton = document.createElement("span");
28 elButton.className = "copy-button";
29 if( bFlipped ) elButton.className += " copy-button-flipped";
30 elButton.id = "copy-" + idTarget;
31 initCopyButton(elButton,idTarget,cchLength);
32 return elButton;
@@ -36,15 +38,18 @@
36 var elButton = document.getElementById(idButton);
37 if( elButton ) initCopyButton(elButton,idTarget,cchLength);
38 return elButton;
39 }
40 function initCopyButton(elButton,idTarget,cchLength){
41 elButton.style.transition = "";
42 elButton.style.opacity = 1;
43 if( idTarget ) elButton.setAttribute("data-copytarget",idTarget);
44 if( cchLength ) elButton.setAttribute("data-copylength",cchLength);
45 elButton.onclick = clickCopyButton;
 
 
 
 
 
46 return elButton;
47 }
48 setTimeout(function(){
49 var elButtons = document.getElementsByClassName("copy-button");
50 for ( var i=0; i<elButtons.length; i++ ){
@@ -53,14 +58,11 @@
53 },1);
54 /* The onclick handler for the "Copy Button". */
55 function clickCopyButton(e){
56 e.preventDefault(); /* Mandatory for <a> and <button>. */
57 e.stopPropagation();
58 if( this.getAttribute("data-copylocked") ) return;
59 this.setAttribute("data-copylocked","1");
60 this.style.transition = "opacity 400ms ease-in-out";
61 this.style.opacity = 0;
62 var idTarget = this.getAttribute("data-copytarget");
63 var elTarget = document.getElementById(idTarget);
64 if( elTarget ){
65 var text = elTarget.innerText.replace(/^\s+|\s+$/g,"");
66 var cchLength = parseInt(this.getAttribute("data-copylength"));
@@ -67,15 +69,10 @@
67 if( !isNaN(cchLength) && cchLength>0 ){
68 text = text.slice(0,cchLength); /* Assume single-byte chars. */
69 }
70 copyTextToClipboard(text);
71 }
72 setTimeout(function(){
73 this.style.transition = "";
74 this.style.opacity = 1;
75 this.removeAttribute("data-copylocked");
76 }.bind(this),400);
77 }
78 /* Create a temporary <textarea> element and copy the contents to clipboard. */
79 function copyTextToClipboard(text){
80 if( window.clipboardData && window.clipboardData.setData ){
81 window.clipboardData.setData("Text",text);
82
--- src/copybtn.js
+++ src/copybtn.js
@@ -1,32 +1,34 @@
1 /* Manage "Copy Buttons" linked to target elements, to copy the text (or, parts
2 ** thereof) of the target elements to the clipboard.
3 **
4 ** Newly created buttons are <button> elements plus a nested <span> element with
5 ** an SVG background icon, defined by the "copy-button" class in the default CSS
6 ** style sheet, and are assigned the element ID "copy-<idTarget>".
 
 
 
 
7 **
8 ** For HTML-defined buttons, either initCopyButtonById(), or initCopyButton(),
9 ** needs to be called to attach the "onclick" handler (done automatically from
10 ** a handler attached to the "DOMContentLoaded" event). These functions create
11 ** the nested <span> element if the <button> element has no child nodes. Using
12 ** static HTML for the <span> element ensures the buttons are visible if there
13 ** are script errors, which may be useful for Fossil JS hackers (as good parts
14 ** of the Fossil web UI come down on JS errors, anyway).
15 **
16 ** The initialization functions do not overwrite the "data-copytarget" and
17 ** "data-copylength" attributes with empty or null values for <idTarget> and
18 ** <cchLength>, respectively. Set <cchLength> to "-1" to explicitly remove the
19 ** previous copy length limit.
20 **
21 ** HTML snippet for statically created buttons:
22 **
23 ** <button class="copy-button" id="copy-<idTarget>"
24 ** data-copytarget="<idTarget>" data-copylength="<cchLength>">
25 ** <span></span>
26 ** </button>
27 */
28 function makeCopyButton(idTarget,bFlipped,cchLength){
29 var elButton = document.createElement("button");
30 elButton.className = "copy-button";
31 if( bFlipped ) elButton.className += " copy-button-flipped";
32 elButton.id = "copy-" + idTarget;
33 initCopyButton(elButton,idTarget,cchLength);
34 return elButton;
@@ -36,15 +38,18 @@
38 var elButton = document.getElementById(idButton);
39 if( elButton ) initCopyButton(elButton,idTarget,cchLength);
40 return elButton;
41 }
42 function initCopyButton(elButton,idTarget,cchLength){
 
 
43 if( idTarget ) elButton.setAttribute("data-copytarget",idTarget);
44 if( cchLength ) elButton.setAttribute("data-copylength",cchLength);
45 elButton.onclick = clickCopyButton;
46 /* Make sure the <button> contains a single nested <span>. */
47 if( elButton.childElementCount!=1 || elButton.firstChild.tagName!="SPAN" ){
48 while( elButton.firstChild ) elButton.removeChild(elButton.lastChild);
49 elButton.appendChild(document.createElement("span"));
50 }
51 return elButton;
52 }
53 setTimeout(function(){
54 var elButtons = document.getElementsByClassName("copy-button");
55 for ( var i=0; i<elButtons.length; i++ ){
@@ -53,14 +58,11 @@
58 },1);
59 /* The onclick handler for the "Copy Button". */
60 function clickCopyButton(e){
61 e.preventDefault(); /* Mandatory for <a> and <button>. */
62 e.stopPropagation();
63 if( this.disabled ) return; /* This check is probably redundant. */
 
 
 
64 var idTarget = this.getAttribute("data-copytarget");
65 var elTarget = document.getElementById(idTarget);
66 if( elTarget ){
67 var text = elTarget.innerText.replace(/^\s+|\s+$/g,"");
68 var cchLength = parseInt(this.getAttribute("data-copylength"));
@@ -67,15 +69,10 @@
69 if( !isNaN(cchLength) && cchLength>0 ){
70 text = text.slice(0,cchLength); /* Assume single-byte chars. */
71 }
72 copyTextToClipboard(text);
73 }
 
 
 
 
 
74 }
75 /* Create a temporary <textarea> element and copy the contents to clipboard. */
76 function copyTextToClipboard(text){
77 if( window.clipboardData && window.clipboardData.setData ){
78 window.clipboardData.setData("Text",text);
79
+10
--- src/db.c
+++ src/db.c
@@ -125,10 +125,20 @@
125125
#endif /* FOSSIL_ENABLE_JSON */
126126
if( g.xferPanic && g.cgiOutput==1 ){
127127
cgi_reset_content();
128128
@ error Database\serror:\s%F(z)
129129
cgi_reply();
130
+ }
131
+ if( strstr(z,"attempt to write a readonly database") ){
132
+ static const char *azDbNames[] = { "repository", "localdb", "configdb" };
133
+ int i;
134
+ for(i=0; i<3; i++){
135
+ if( sqlite3_db_readonly(g.db, azDbNames[i])==1 ){
136
+ z = mprintf("\"%s\" is readonly.\n%s",
137
+ sqlite3_db_filename(g.db,azDbNames[i]), z);
138
+ }
139
+ }
130140
}
131141
fossil_fatal("Database error: %s", z);
132142
}
133143
134144
/*
135145
--- src/db.c
+++ src/db.c
@@ -125,10 +125,20 @@
125 #endif /* FOSSIL_ENABLE_JSON */
126 if( g.xferPanic && g.cgiOutput==1 ){
127 cgi_reset_content();
128 @ error Database\serror:\s%F(z)
129 cgi_reply();
 
 
 
 
 
 
 
 
 
 
130 }
131 fossil_fatal("Database error: %s", z);
132 }
133
134 /*
135
--- src/db.c
+++ src/db.c
@@ -125,10 +125,20 @@
125 #endif /* FOSSIL_ENABLE_JSON */
126 if( g.xferPanic && g.cgiOutput==1 ){
127 cgi_reset_content();
128 @ error Database\serror:\s%F(z)
129 cgi_reply();
130 }
131 if( strstr(z,"attempt to write a readonly database") ){
132 static const char *azDbNames[] = { "repository", "localdb", "configdb" };
133 int i;
134 for(i=0; i<3; i++){
135 if( sqlite3_db_readonly(g.db, azDbNames[i])==1 ){
136 z = mprintf("\"%s\" is readonly.\n%s",
137 sqlite3_db_filename(g.db,azDbNames[i]), z);
138 }
139 }
140 }
141 fossil_fatal("Database error: %s", z);
142 }
143
144 /*
145
+30 -8
--- src/default.css
+++ src/default.css
@@ -1,10 +1,13 @@
11
/* This CSS file holds the default implementations for all of fossil's
22
CSS classes. When /style.css is requested, the rules in this file
33
are emitted first, followed by (1) page-specific CSS (if any) and
44
(2) skin-specific CSS.
55
*/
6
+body {
7
+ z-index: 0 /* Used by robot.c:robot_proofofwork() and href.js */;
8
+}
69
div.sidebox {
710
float: right;
811
background-color: white;
912
border-width: medium;
1013
border-style: double;
@@ -1098,19 +1101,40 @@
10981101
white-space: nowrap;
10991102
}
11001103
label[for] {
11011104
cursor: pointer;
11021105
}
1103
-.copy-button {
1104
- display: inline-block;
1106
+button.copy-button,
1107
+button.copy-button:hover,
1108
+button.copy-button:focus,
1109
+button.copy-button:active {
11051110
width: 14px;
11061111
height: 14px;
11071112
/*Note: .24em is slightly smaller than the average width of a normal space.*/
11081113
margin: -2px .24em 0 0;
11091114
padding: 0;
11101115
border: 0;
1116
+ outline: 0;
1117
+ background: none;
1118
+ font-size: inherit; /* Required for horizontal spacing. */
11111119
vertical-align: middle;
1120
+ user-select: none;
1121
+ cursor: pointer;
1122
+}
1123
+button.copy-button-flipped,
1124
+button.copy-button-flipped:hover,
1125
+button.copy-button-flipped:focus,
1126
+button.copy-button-flipped:active {
1127
+ margin: -2px 0 0 .24em;
1128
+}
1129
+button.copy-button span {
1130
+ display: block;
1131
+ width: 100%;
1132
+ height: 100%;
1133
+ margin: 0;
1134
+ padding: 0;
1135
+ border: 0;
11121136
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' \
11131137
viewBox='0,0,14,14'%3E%3Cpath style='fill:black;opacity:0' \
11141138
d='M14,14H0V0h14v14z'/%3E%3Cpath style='fill:rgb(240,240,240)' \
11151139
d='M1,0h6.6l2,2h1l3.4,3.4v8.6h-10v-2h-3z'/%3E%3Cpath style='fill:rgb(64,64,64)' \
11161140
d='M2,1h5l3,3v7h-8z'/%3E%3Cpath style='fill:rgb(248,248,248)' \
@@ -1121,19 +1145,17 @@
11211145
d='M7,8h4v1h-4zm0,2h4v1h-4z'/%3E%3C/svg%3E");
11221146
background-repeat: no-repeat;
11231147
background-position: center;
11241148
cursor: pointer;
11251149
}
1126
-.copy-button.disabled {
1150
+button.copy-button:enabled:active span {
1151
+ background-size: 90%;
1152
+}
1153
+button.copy-button:disabled span {
11271154
filter: grayscale(1);
11281155
opacity: 0.4;
11291156
}
1130
-.copy-button-flipped {
1131
-/*Note: .16em is suitable for element grouping.*/
1132
- margin-left: .16em;
1133
- margin-right: 0;
1134
-}
11351157
.nobr {
11361158
white-space: nowrap;
11371159
}
11381160
.accordion {
11391161
cursor: pointer;
11401162
--- src/default.css
+++ src/default.css
@@ -1,10 +1,13 @@
1 /* This CSS file holds the default implementations for all of fossil's
2 CSS classes. When /style.css is requested, the rules in this file
3 are emitted first, followed by (1) page-specific CSS (if any) and
4 (2) skin-specific CSS.
5 */
 
 
 
6 div.sidebox {
7 float: right;
8 background-color: white;
9 border-width: medium;
10 border-style: double;
@@ -1098,19 +1101,40 @@
1098 white-space: nowrap;
1099 }
1100 label[for] {
1101 cursor: pointer;
1102 }
1103 .copy-button {
1104 display: inline-block;
 
 
1105 width: 14px;
1106 height: 14px;
1107 /*Note: .24em is slightly smaller than the average width of a normal space.*/
1108 margin: -2px .24em 0 0;
1109 padding: 0;
1110 border: 0;
 
 
 
1111 vertical-align: middle;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1112 background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' \
1113 viewBox='0,0,14,14'%3E%3Cpath style='fill:black;opacity:0' \
1114 d='M14,14H0V0h14v14z'/%3E%3Cpath style='fill:rgb(240,240,240)' \
1115 d='M1,0h6.6l2,2h1l3.4,3.4v8.6h-10v-2h-3z'/%3E%3Cpath style='fill:rgb(64,64,64)' \
1116 d='M2,1h5l3,3v7h-8z'/%3E%3Cpath style='fill:rgb(248,248,248)' \
@@ -1121,19 +1145,17 @@
1121 d='M7,8h4v1h-4zm0,2h4v1h-4z'/%3E%3C/svg%3E");
1122 background-repeat: no-repeat;
1123 background-position: center;
1124 cursor: pointer;
1125 }
1126 .copy-button.disabled {
 
 
 
1127 filter: grayscale(1);
1128 opacity: 0.4;
1129 }
1130 .copy-button-flipped {
1131 /*Note: .16em is suitable for element grouping.*/
1132 margin-left: .16em;
1133 margin-right: 0;
1134 }
1135 .nobr {
1136 white-space: nowrap;
1137 }
1138 .accordion {
1139 cursor: pointer;
1140
--- src/default.css
+++ src/default.css
@@ -1,10 +1,13 @@
1 /* This CSS file holds the default implementations for all of fossil's
2 CSS classes. When /style.css is requested, the rules in this file
3 are emitted first, followed by (1) page-specific CSS (if any) and
4 (2) skin-specific CSS.
5 */
6 body {
7 z-index: 0 /* Used by robot.c:robot_proofofwork() and href.js */;
8 }
9 div.sidebox {
10 float: right;
11 background-color: white;
12 border-width: medium;
13 border-style: double;
@@ -1098,19 +1101,40 @@
1101 white-space: nowrap;
1102 }
1103 label[for] {
1104 cursor: pointer;
1105 }
1106 button.copy-button,
1107 button.copy-button:hover,
1108 button.copy-button:focus,
1109 button.copy-button:active {
1110 width: 14px;
1111 height: 14px;
1112 /*Note: .24em is slightly smaller than the average width of a normal space.*/
1113 margin: -2px .24em 0 0;
1114 padding: 0;
1115 border: 0;
1116 outline: 0;
1117 background: none;
1118 font-size: inherit; /* Required for horizontal spacing. */
1119 vertical-align: middle;
1120 user-select: none;
1121 cursor: pointer;
1122 }
1123 button.copy-button-flipped,
1124 button.copy-button-flipped:hover,
1125 button.copy-button-flipped:focus,
1126 button.copy-button-flipped:active {
1127 margin: -2px 0 0 .24em;
1128 }
1129 button.copy-button span {
1130 display: block;
1131 width: 100%;
1132 height: 100%;
1133 margin: 0;
1134 padding: 0;
1135 border: 0;
1136 background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' \
1137 viewBox='0,0,14,14'%3E%3Cpath style='fill:black;opacity:0' \
1138 d='M14,14H0V0h14v14z'/%3E%3Cpath style='fill:rgb(240,240,240)' \
1139 d='M1,0h6.6l2,2h1l3.4,3.4v8.6h-10v-2h-3z'/%3E%3Cpath style='fill:rgb(64,64,64)' \
1140 d='M2,1h5l3,3v7h-8z'/%3E%3Cpath style='fill:rgb(248,248,248)' \
@@ -1121,19 +1145,17 @@
1145 d='M7,8h4v1h-4zm0,2h4v1h-4z'/%3E%3C/svg%3E");
1146 background-repeat: no-repeat;
1147 background-position: center;
1148 cursor: pointer;
1149 }
1150 button.copy-button:enabled:active span {
1151 background-size: 90%;
1152 }
1153 button.copy-button:disabled span {
1154 filter: grayscale(1);
1155 opacity: 0.4;
1156 }
 
 
 
 
 
1157 .nobr {
1158 white-space: nowrap;
1159 }
1160 .accordion {
1161 cursor: pointer;
1162
+30 -8
--- src/default.css
+++ src/default.css
@@ -1,10 +1,13 @@
11
/* This CSS file holds the default implementations for all of fossil's
22
CSS classes. When /style.css is requested, the rules in this file
33
are emitted first, followed by (1) page-specific CSS (if any) and
44
(2) skin-specific CSS.
55
*/
6
+body {
7
+ z-index: 0 /* Used by robot.c:robot_proofofwork() and href.js */;
8
+}
69
div.sidebox {
710
float: right;
811
background-color: white;
912
border-width: medium;
1013
border-style: double;
@@ -1098,19 +1101,40 @@
10981101
white-space: nowrap;
10991102
}
11001103
label[for] {
11011104
cursor: pointer;
11021105
}
1103
-.copy-button {
1104
- display: inline-block;
1106
+button.copy-button,
1107
+button.copy-button:hover,
1108
+button.copy-button:focus,
1109
+button.copy-button:active {
11051110
width: 14px;
11061111
height: 14px;
11071112
/*Note: .24em is slightly smaller than the average width of a normal space.*/
11081113
margin: -2px .24em 0 0;
11091114
padding: 0;
11101115
border: 0;
1116
+ outline: 0;
1117
+ background: none;
1118
+ font-size: inherit; /* Required for horizontal spacing. */
11111119
vertical-align: middle;
1120
+ user-select: none;
1121
+ cursor: pointer;
1122
+}
1123
+button.copy-button-flipped,
1124
+button.copy-button-flipped:hover,
1125
+button.copy-button-flipped:focus,
1126
+button.copy-button-flipped:active {
1127
+ margin: -2px 0 0 .24em;
1128
+}
1129
+button.copy-button span {
1130
+ display: block;
1131
+ width: 100%;
1132
+ height: 100%;
1133
+ margin: 0;
1134
+ padding: 0;
1135
+ border: 0;
11121136
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' \
11131137
viewBox='0,0,14,14'%3E%3Cpath style='fill:black;opacity:0' \
11141138
d='M14,14H0V0h14v14z'/%3E%3Cpath style='fill:rgb(240,240,240)' \
11151139
d='M1,0h6.6l2,2h1l3.4,3.4v8.6h-10v-2h-3z'/%3E%3Cpath style='fill:rgb(64,64,64)' \
11161140
d='M2,1h5l3,3v7h-8z'/%3E%3Cpath style='fill:rgb(248,248,248)' \
@@ -1121,19 +1145,17 @@
11211145
d='M7,8h4v1h-4zm0,2h4v1h-4z'/%3E%3C/svg%3E");
11221146
background-repeat: no-repeat;
11231147
background-position: center;
11241148
cursor: pointer;
11251149
}
1126
-.copy-button.disabled {
1150
+button.copy-button:enabled:active span {
1151
+ background-size: 90%;
1152
+}
1153
+button.copy-button:disabled span {
11271154
filter: grayscale(1);
11281155
opacity: 0.4;
11291156
}
1130
-.copy-button-flipped {
1131
-/*Note: .16em is suitable for element grouping.*/
1132
- margin-left: .16em;
1133
- margin-right: 0;
1134
-}
11351157
.nobr {
11361158
white-space: nowrap;
11371159
}
11381160
.accordion {
11391161
cursor: pointer;
11401162
--- src/default.css
+++ src/default.css
@@ -1,10 +1,13 @@
1 /* This CSS file holds the default implementations for all of fossil's
2 CSS classes. When /style.css is requested, the rules in this file
3 are emitted first, followed by (1) page-specific CSS (if any) and
4 (2) skin-specific CSS.
5 */
 
 
 
6 div.sidebox {
7 float: right;
8 background-color: white;
9 border-width: medium;
10 border-style: double;
@@ -1098,19 +1101,40 @@
1098 white-space: nowrap;
1099 }
1100 label[for] {
1101 cursor: pointer;
1102 }
1103 .copy-button {
1104 display: inline-block;
 
 
1105 width: 14px;
1106 height: 14px;
1107 /*Note: .24em is slightly smaller than the average width of a normal space.*/
1108 margin: -2px .24em 0 0;
1109 padding: 0;
1110 border: 0;
 
 
 
1111 vertical-align: middle;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1112 background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' \
1113 viewBox='0,0,14,14'%3E%3Cpath style='fill:black;opacity:0' \
1114 d='M14,14H0V0h14v14z'/%3E%3Cpath style='fill:rgb(240,240,240)' \
1115 d='M1,0h6.6l2,2h1l3.4,3.4v8.6h-10v-2h-3z'/%3E%3Cpath style='fill:rgb(64,64,64)' \
1116 d='M2,1h5l3,3v7h-8z'/%3E%3Cpath style='fill:rgb(248,248,248)' \
@@ -1121,19 +1145,17 @@
1121 d='M7,8h4v1h-4zm0,2h4v1h-4z'/%3E%3C/svg%3E");
1122 background-repeat: no-repeat;
1123 background-position: center;
1124 cursor: pointer;
1125 }
1126 .copy-button.disabled {
 
 
 
1127 filter: grayscale(1);
1128 opacity: 0.4;
1129 }
1130 .copy-button-flipped {
1131 /*Note: .16em is suitable for element grouping.*/
1132 margin-left: .16em;
1133 margin-right: 0;
1134 }
1135 .nobr {
1136 white-space: nowrap;
1137 }
1138 .accordion {
1139 cursor: pointer;
1140
--- src/default.css
+++ src/default.css
@@ -1,10 +1,13 @@
1 /* This CSS file holds the default implementations for all of fossil's
2 CSS classes. When /style.css is requested, the rules in this file
3 are emitted first, followed by (1) page-specific CSS (if any) and
4 (2) skin-specific CSS.
5 */
6 body {
7 z-index: 0 /* Used by robot.c:robot_proofofwork() and href.js */;
8 }
9 div.sidebox {
10 float: right;
11 background-color: white;
12 border-width: medium;
13 border-style: double;
@@ -1098,19 +1101,40 @@
1101 white-space: nowrap;
1102 }
1103 label[for] {
1104 cursor: pointer;
1105 }
1106 button.copy-button,
1107 button.copy-button:hover,
1108 button.copy-button:focus,
1109 button.copy-button:active {
1110 width: 14px;
1111 height: 14px;
1112 /*Note: .24em is slightly smaller than the average width of a normal space.*/
1113 margin: -2px .24em 0 0;
1114 padding: 0;
1115 border: 0;
1116 outline: 0;
1117 background: none;
1118 font-size: inherit; /* Required for horizontal spacing. */
1119 vertical-align: middle;
1120 user-select: none;
1121 cursor: pointer;
1122 }
1123 button.copy-button-flipped,
1124 button.copy-button-flipped:hover,
1125 button.copy-button-flipped:focus,
1126 button.copy-button-flipped:active {
1127 margin: -2px 0 0 .24em;
1128 }
1129 button.copy-button span {
1130 display: block;
1131 width: 100%;
1132 height: 100%;
1133 margin: 0;
1134 padding: 0;
1135 border: 0;
1136 background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' \
1137 viewBox='0,0,14,14'%3E%3Cpath style='fill:black;opacity:0' \
1138 d='M14,14H0V0h14v14z'/%3E%3Cpath style='fill:rgb(240,240,240)' \
1139 d='M1,0h6.6l2,2h1l3.4,3.4v8.6h-10v-2h-3z'/%3E%3Cpath style='fill:rgb(64,64,64)' \
1140 d='M2,1h5l3,3v7h-8z'/%3E%3Cpath style='fill:rgb(248,248,248)' \
@@ -1121,19 +1145,17 @@
1145 d='M7,8h4v1h-4zm0,2h4v1h-4z'/%3E%3C/svg%3E");
1146 background-repeat: no-repeat;
1147 background-position: center;
1148 cursor: pointer;
1149 }
1150 button.copy-button:enabled:active span {
1151 background-size: 90%;
1152 }
1153 button.copy-button:disabled span {
1154 filter: grayscale(1);
1155 opacity: 0.4;
1156 }
 
 
 
 
 
1157 .nobr {
1158 white-space: nowrap;
1159 }
1160 .accordion {
1161 cursor: pointer;
1162
+17 -11
--- src/diff.c
+++ src/diff.c
@@ -2970,10 +2970,11 @@
29702970
Blob *pOut, /* Write diff here if not NULL */
29712971
DiffConfig *pCfg /* Configuration options */
29722972
){
29732973
int ignoreWs; /* Ignore whitespace */
29742974
DContext c;
2975
+ int nDel = 0, nIns = 0;
29752976
29762977
if( pCfg->diffFlags & DIFF_INVERT ){
29772978
Blob *pTemp = pA_Blob;
29782979
pA_Blob = pB_Blob;
29792980
pB_Blob = pTemp;
@@ -3048,22 +3049,27 @@
30483049
c.aEdit[i+1] = sum;
30493050
for(k=0, sum=0; k<c.aEdit[i+2]; k++) sum += c.aTo[iB++].n;
30503051
c.aEdit[i+2] = sum;
30513052
}
30523053
}
3054
+
3055
+ if( pCfg->diffFlags & DIFF_NUMSTAT ){
3056
+ int i;
3057
+ for(i=0; c.aEdit[i] || c.aEdit[i+1] || c.aEdit[i+2]; i+=3){
3058
+ nDel += c.aEdit[i+1];
3059
+ nIns += c.aEdit[i+2];
3060
+ }
3061
+ g.diffCnt[1] += nIns;
3062
+ g.diffCnt[2] += nDel;
3063
+ if( nIns+nDel ){
3064
+ g.diffCnt[0]++;
3065
+ }
3066
+ }
30533067
30543068
if( pOut ){
3055
- if( pCfg->diffFlags & DIFF_NUMSTAT ){
3056
- int nDel = 0, nIns = 0, i;
3057
- for(i=0; c.aEdit[i] || c.aEdit[i+1] || c.aEdit[i+2]; i+=3){
3058
- nDel += c.aEdit[i+1];
3059
- nIns += c.aEdit[i+2];
3060
- }
3061
- g.diffCnt[1] += nIns;
3062
- g.diffCnt[2] += nDel;
3069
+ if( pCfg->diffFlags & DIFF_NUMSTAT && !(pCfg->diffFlags & DIFF_HTML)){
30633070
if( nIns+nDel ){
3064
- g.diffCnt[0]++;
30653071
if( !(pCfg->diffFlags & DIFF_BRIEF) ){
30663072
blob_appendf(pOut, "%10d %10d", nIns, nDel);
30673073
}
30683074
}
30693075
}else if( pCfg->diffFlags & (DIFF_RAW|DIFF_BY_TOKEN) ){
@@ -3666,12 +3672,12 @@
36663672
unsigned clr1, clr2, clr;
36673673
int bBlame = g.zPath[0]!='a';/* True for BLAME output. False for ANNOTATE. */
36683674
36693675
/* Gather query parameters */
36703676
login_check_credentials();
3671
- if( !g.perm.Read || g.zLogin==0 ){ login_needed(g.anon.Read); return; }
3672
- if( exclude_spiders(0) ) return;
3677
+ if( !g.perm.Read ){ login_needed(g.anon.Read); return; }
3678
+ if( robot_restrict("annotate") ) return;
36733679
fossil_nice_default();
36743680
zFilename = P("filename");
36753681
zRevision = PD("checkin",0);
36763682
zOrigin = P("origin");
36773683
zLimit = P("limit");
36783684
--- src/diff.c
+++ src/diff.c
@@ -2970,10 +2970,11 @@
2970 Blob *pOut, /* Write diff here if not NULL */
2971 DiffConfig *pCfg /* Configuration options */
2972 ){
2973 int ignoreWs; /* Ignore whitespace */
2974 DContext c;
 
2975
2976 if( pCfg->diffFlags & DIFF_INVERT ){
2977 Blob *pTemp = pA_Blob;
2978 pA_Blob = pB_Blob;
2979 pB_Blob = pTemp;
@@ -3048,22 +3049,27 @@
3048 c.aEdit[i+1] = sum;
3049 for(k=0, sum=0; k<c.aEdit[i+2]; k++) sum += c.aTo[iB++].n;
3050 c.aEdit[i+2] = sum;
3051 }
3052 }
 
 
 
 
 
 
 
 
 
 
 
 
 
3053
3054 if( pOut ){
3055 if( pCfg->diffFlags & DIFF_NUMSTAT ){
3056 int nDel = 0, nIns = 0, i;
3057 for(i=0; c.aEdit[i] || c.aEdit[i+1] || c.aEdit[i+2]; i+=3){
3058 nDel += c.aEdit[i+1];
3059 nIns += c.aEdit[i+2];
3060 }
3061 g.diffCnt[1] += nIns;
3062 g.diffCnt[2] += nDel;
3063 if( nIns+nDel ){
3064 g.diffCnt[0]++;
3065 if( !(pCfg->diffFlags & DIFF_BRIEF) ){
3066 blob_appendf(pOut, "%10d %10d", nIns, nDel);
3067 }
3068 }
3069 }else if( pCfg->diffFlags & (DIFF_RAW|DIFF_BY_TOKEN) ){
@@ -3666,12 +3672,12 @@
3666 unsigned clr1, clr2, clr;
3667 int bBlame = g.zPath[0]!='a';/* True for BLAME output. False for ANNOTATE. */
3668
3669 /* Gather query parameters */
3670 login_check_credentials();
3671 if( !g.perm.Read || g.zLogin==0 ){ login_needed(g.anon.Read); return; }
3672 if( exclude_spiders(0) ) return;
3673 fossil_nice_default();
3674 zFilename = P("filename");
3675 zRevision = PD("checkin",0);
3676 zOrigin = P("origin");
3677 zLimit = P("limit");
3678
--- src/diff.c
+++ src/diff.c
@@ -2970,10 +2970,11 @@
2970 Blob *pOut, /* Write diff here if not NULL */
2971 DiffConfig *pCfg /* Configuration options */
2972 ){
2973 int ignoreWs; /* Ignore whitespace */
2974 DContext c;
2975 int nDel = 0, nIns = 0;
2976
2977 if( pCfg->diffFlags & DIFF_INVERT ){
2978 Blob *pTemp = pA_Blob;
2979 pA_Blob = pB_Blob;
2980 pB_Blob = pTemp;
@@ -3048,22 +3049,27 @@
3049 c.aEdit[i+1] = sum;
3050 for(k=0, sum=0; k<c.aEdit[i+2]; k++) sum += c.aTo[iB++].n;
3051 c.aEdit[i+2] = sum;
3052 }
3053 }
3054
3055 if( pCfg->diffFlags & DIFF_NUMSTAT ){
3056 int i;
3057 for(i=0; c.aEdit[i] || c.aEdit[i+1] || c.aEdit[i+2]; i+=3){
3058 nDel += c.aEdit[i+1];
3059 nIns += c.aEdit[i+2];
3060 }
3061 g.diffCnt[1] += nIns;
3062 g.diffCnt[2] += nDel;
3063 if( nIns+nDel ){
3064 g.diffCnt[0]++;
3065 }
3066 }
3067
3068 if( pOut ){
3069 if( pCfg->diffFlags & DIFF_NUMSTAT && !(pCfg->diffFlags & DIFF_HTML)){
 
 
 
 
 
 
 
3070 if( nIns+nDel ){
 
3071 if( !(pCfg->diffFlags & DIFF_BRIEF) ){
3072 blob_appendf(pOut, "%10d %10d", nIns, nDel);
3073 }
3074 }
3075 }else if( pCfg->diffFlags & (DIFF_RAW|DIFF_BY_TOKEN) ){
@@ -3666,12 +3672,12 @@
3672 unsigned clr1, clr2, clr;
3673 int bBlame = g.zPath[0]!='a';/* True for BLAME output. False for ANNOTATE. */
3674
3675 /* Gather query parameters */
3676 login_check_credentials();
3677 if( !g.perm.Read ){ login_needed(g.anon.Read); return; }
3678 if( robot_restrict("annotate") ) return;
3679 fossil_nice_default();
3680 zFilename = P("filename");
3681 zRevision = PD("checkin",0);
3682 zOrigin = P("origin");
3683 zLimit = P("limit");
3684
+17 -11
--- src/diff.c
+++ src/diff.c
@@ -2970,10 +2970,11 @@
29702970
Blob *pOut, /* Write diff here if not NULL */
29712971
DiffConfig *pCfg /* Configuration options */
29722972
){
29732973
int ignoreWs; /* Ignore whitespace */
29742974
DContext c;
2975
+ int nDel = 0, nIns = 0;
29752976
29762977
if( pCfg->diffFlags & DIFF_INVERT ){
29772978
Blob *pTemp = pA_Blob;
29782979
pA_Blob = pB_Blob;
29792980
pB_Blob = pTemp;
@@ -3048,22 +3049,27 @@
30483049
c.aEdit[i+1] = sum;
30493050
for(k=0, sum=0; k<c.aEdit[i+2]; k++) sum += c.aTo[iB++].n;
30503051
c.aEdit[i+2] = sum;
30513052
}
30523053
}
3054
+
3055
+ if( pCfg->diffFlags & DIFF_NUMSTAT ){
3056
+ int i;
3057
+ for(i=0; c.aEdit[i] || c.aEdit[i+1] || c.aEdit[i+2]; i+=3){
3058
+ nDel += c.aEdit[i+1];
3059
+ nIns += c.aEdit[i+2];
3060
+ }
3061
+ g.diffCnt[1] += nIns;
3062
+ g.diffCnt[2] += nDel;
3063
+ if( nIns+nDel ){
3064
+ g.diffCnt[0]++;
3065
+ }
3066
+ }
30533067
30543068
if( pOut ){
3055
- if( pCfg->diffFlags & DIFF_NUMSTAT ){
3056
- int nDel = 0, nIns = 0, i;
3057
- for(i=0; c.aEdit[i] || c.aEdit[i+1] || c.aEdit[i+2]; i+=3){
3058
- nDel += c.aEdit[i+1];
3059
- nIns += c.aEdit[i+2];
3060
- }
3061
- g.diffCnt[1] += nIns;
3062
- g.diffCnt[2] += nDel;
3069
+ if( pCfg->diffFlags & DIFF_NUMSTAT && !(pCfg->diffFlags & DIFF_HTML)){
30633070
if( nIns+nDel ){
3064
- g.diffCnt[0]++;
30653071
if( !(pCfg->diffFlags & DIFF_BRIEF) ){
30663072
blob_appendf(pOut, "%10d %10d", nIns, nDel);
30673073
}
30683074
}
30693075
}else if( pCfg->diffFlags & (DIFF_RAW|DIFF_BY_TOKEN) ){
@@ -3666,12 +3672,12 @@
36663672
unsigned clr1, clr2, clr;
36673673
int bBlame = g.zPath[0]!='a';/* True for BLAME output. False for ANNOTATE. */
36683674
36693675
/* Gather query parameters */
36703676
login_check_credentials();
3671
- if( !g.perm.Read || g.zLogin==0 ){ login_needed(g.anon.Read); return; }
3672
- if( exclude_spiders(0) ) return;
3677
+ if( !g.perm.Read ){ login_needed(g.anon.Read); return; }
3678
+ if( robot_restrict("annotate") ) return;
36733679
fossil_nice_default();
36743680
zFilename = P("filename");
36753681
zRevision = PD("checkin",0);
36763682
zOrigin = P("origin");
36773683
zLimit = P("limit");
36783684
--- src/diff.c
+++ src/diff.c
@@ -2970,10 +2970,11 @@
2970 Blob *pOut, /* Write diff here if not NULL */
2971 DiffConfig *pCfg /* Configuration options */
2972 ){
2973 int ignoreWs; /* Ignore whitespace */
2974 DContext c;
 
2975
2976 if( pCfg->diffFlags & DIFF_INVERT ){
2977 Blob *pTemp = pA_Blob;
2978 pA_Blob = pB_Blob;
2979 pB_Blob = pTemp;
@@ -3048,22 +3049,27 @@
3048 c.aEdit[i+1] = sum;
3049 for(k=0, sum=0; k<c.aEdit[i+2]; k++) sum += c.aTo[iB++].n;
3050 c.aEdit[i+2] = sum;
3051 }
3052 }
 
 
 
 
 
 
 
 
 
 
 
 
 
3053
3054 if( pOut ){
3055 if( pCfg->diffFlags & DIFF_NUMSTAT ){
3056 int nDel = 0, nIns = 0, i;
3057 for(i=0; c.aEdit[i] || c.aEdit[i+1] || c.aEdit[i+2]; i+=3){
3058 nDel += c.aEdit[i+1];
3059 nIns += c.aEdit[i+2];
3060 }
3061 g.diffCnt[1] += nIns;
3062 g.diffCnt[2] += nDel;
3063 if( nIns+nDel ){
3064 g.diffCnt[0]++;
3065 if( !(pCfg->diffFlags & DIFF_BRIEF) ){
3066 blob_appendf(pOut, "%10d %10d", nIns, nDel);
3067 }
3068 }
3069 }else if( pCfg->diffFlags & (DIFF_RAW|DIFF_BY_TOKEN) ){
@@ -3666,12 +3672,12 @@
3666 unsigned clr1, clr2, clr;
3667 int bBlame = g.zPath[0]!='a';/* True for BLAME output. False for ANNOTATE. */
3668
3669 /* Gather query parameters */
3670 login_check_credentials();
3671 if( !g.perm.Read || g.zLogin==0 ){ login_needed(g.anon.Read); return; }
3672 if( exclude_spiders(0) ) return;
3673 fossil_nice_default();
3674 zFilename = P("filename");
3675 zRevision = PD("checkin",0);
3676 zOrigin = P("origin");
3677 zLimit = P("limit");
3678
--- src/diff.c
+++ src/diff.c
@@ -2970,10 +2970,11 @@
2970 Blob *pOut, /* Write diff here if not NULL */
2971 DiffConfig *pCfg /* Configuration options */
2972 ){
2973 int ignoreWs; /* Ignore whitespace */
2974 DContext c;
2975 int nDel = 0, nIns = 0;
2976
2977 if( pCfg->diffFlags & DIFF_INVERT ){
2978 Blob *pTemp = pA_Blob;
2979 pA_Blob = pB_Blob;
2980 pB_Blob = pTemp;
@@ -3048,22 +3049,27 @@
3049 c.aEdit[i+1] = sum;
3050 for(k=0, sum=0; k<c.aEdit[i+2]; k++) sum += c.aTo[iB++].n;
3051 c.aEdit[i+2] = sum;
3052 }
3053 }
3054
3055 if( pCfg->diffFlags & DIFF_NUMSTAT ){
3056 int i;
3057 for(i=0; c.aEdit[i] || c.aEdit[i+1] || c.aEdit[i+2]; i+=3){
3058 nDel += c.aEdit[i+1];
3059 nIns += c.aEdit[i+2];
3060 }
3061 g.diffCnt[1] += nIns;
3062 g.diffCnt[2] += nDel;
3063 if( nIns+nDel ){
3064 g.diffCnt[0]++;
3065 }
3066 }
3067
3068 if( pOut ){
3069 if( pCfg->diffFlags & DIFF_NUMSTAT && !(pCfg->diffFlags & DIFF_HTML)){
 
 
 
 
 
 
 
3070 if( nIns+nDel ){
 
3071 if( !(pCfg->diffFlags & DIFF_BRIEF) ){
3072 blob_appendf(pOut, "%10d %10d", nIns, nDel);
3073 }
3074 }
3075 }else if( pCfg->diffFlags & (DIFF_RAW|DIFF_BY_TOKEN) ){
@@ -3666,12 +3672,12 @@
3672 unsigned clr1, clr2, clr;
3673 int bBlame = g.zPath[0]!='a';/* True for BLAME output. False for ANNOTATE. */
3674
3675 /* Gather query parameters */
3676 login_check_credentials();
3677 if( !g.perm.Read ){ login_needed(g.anon.Read); return; }
3678 if( robot_restrict("annotate") ) return;
3679 fossil_nice_default();
3680 zFilename = P("filename");
3681 zRevision = PD("checkin",0);
3682 zOrigin = P("origin");
3683 zLimit = P("limit");
3684
+1 -1
--- src/diff.tcl
+++ src/diff.tcl
@@ -281,12 +281,12 @@
281281
if {$type ne "txt"} {
282282
$c config -width $widths($type)
283283
}
284284
$c config -state disabled
285285
}
286
+ .wfiles.lb config -height $nDiffs
286287
if {$nDiffs <= [.wfiles.lb cget -height]} {
287
- .wfiles.lb config -height $nDiffs
288288
grid remove .wfiles.sb
289289
}
290290
291291
return $nDiffs
292292
}
293293
--- src/diff.tcl
+++ src/diff.tcl
@@ -281,12 +281,12 @@
281 if {$type ne "txt"} {
282 $c config -width $widths($type)
283 }
284 $c config -state disabled
285 }
 
286 if {$nDiffs <= [.wfiles.lb cget -height]} {
287 .wfiles.lb config -height $nDiffs
288 grid remove .wfiles.sb
289 }
290
291 return $nDiffs
292 }
293
--- src/diff.tcl
+++ src/diff.tcl
@@ -281,12 +281,12 @@
281 if {$type ne "txt"} {
282 $c config -width $widths($type)
283 }
284 $c config -state disabled
285 }
286 .wfiles.lb config -height $nDiffs
287 if {$nDiffs <= [.wfiles.lb cget -height]} {
 
288 grid remove .wfiles.sb
289 }
290
291 return $nDiffs
292 }
293
--- src/diffcmd.c
+++ src/diffcmd.c
@@ -1502,10 +1502,11 @@
15021502
DiffConfig DCfg;
15031503
cgi_check_for_malice();
15041504
login_check_credentials();
15051505
if( !g.perm.Read ){ login_needed(g.anon.Read); return; }
15061506
if( zFrom==0 || zTo==0 ) fossil_redirect_home();
1507
+ if( robot_restrict("diff") ) return;
15071508
15081509
fossil_nice_default();
15091510
cgi_set_content_type("text/plain");
15101511
diff_config_init(&DCfg, DIFF_VERBOSE);
15111512
diff_two_versions(zFrom, zTo, &DCfg, 0);
15121513
--- src/diffcmd.c
+++ src/diffcmd.c
@@ -1502,10 +1502,11 @@
1502 DiffConfig DCfg;
1503 cgi_check_for_malice();
1504 login_check_credentials();
1505 if( !g.perm.Read ){ login_needed(g.anon.Read); return; }
1506 if( zFrom==0 || zTo==0 ) fossil_redirect_home();
 
1507
1508 fossil_nice_default();
1509 cgi_set_content_type("text/plain");
1510 diff_config_init(&DCfg, DIFF_VERBOSE);
1511 diff_two_versions(zFrom, zTo, &DCfg, 0);
1512
--- src/diffcmd.c
+++ src/diffcmd.c
@@ -1502,10 +1502,11 @@
1502 DiffConfig DCfg;
1503 cgi_check_for_malice();
1504 login_check_credentials();
1505 if( !g.perm.Read ){ login_needed(g.anon.Read); return; }
1506 if( zFrom==0 || zTo==0 ) fossil_redirect_home();
1507 if( robot_restrict("diff") ) return;
1508
1509 fossil_nice_default();
1510 cgi_set_content_type("text/plain");
1511 diff_config_init(&DCfg, DIFF_VERBOSE);
1512 diff_two_versions(zFrom, zTo, &DCfg, 0);
1513
--- src/diffcmd.c
+++ src/diffcmd.c
@@ -1502,10 +1502,11 @@
15021502
DiffConfig DCfg;
15031503
cgi_check_for_malice();
15041504
login_check_credentials();
15051505
if( !g.perm.Read ){ login_needed(g.anon.Read); return; }
15061506
if( zFrom==0 || zTo==0 ) fossil_redirect_home();
1507
+ if( robot_restrict("diff") ) return;
15071508
15081509
fossil_nice_default();
15091510
cgi_set_content_type("text/plain");
15101511
diff_config_init(&DCfg, DIFF_VERBOSE);
15111512
diff_two_versions(zFrom, zTo, &DCfg, 0);
15121513
--- src/diffcmd.c
+++ src/diffcmd.c
@@ -1502,10 +1502,11 @@
1502 DiffConfig DCfg;
1503 cgi_check_for_malice();
1504 login_check_credentials();
1505 if( !g.perm.Read ){ login_needed(g.anon.Read); return; }
1506 if( zFrom==0 || zTo==0 ) fossil_redirect_home();
 
1507
1508 fossil_nice_default();
1509 cgi_set_content_type("text/plain");
1510 diff_config_init(&DCfg, DIFF_VERBOSE);
1511 diff_two_versions(zFrom, zTo, &DCfg, 0);
1512
--- src/diffcmd.c
+++ src/diffcmd.c
@@ -1502,10 +1502,11 @@
1502 DiffConfig DCfg;
1503 cgi_check_for_malice();
1504 login_check_credentials();
1505 if( !g.perm.Read ){ login_needed(g.anon.Read); return; }
1506 if( zFrom==0 || zTo==0 ) fossil_redirect_home();
1507 if( robot_restrict("diff") ) return;
1508
1509 fossil_nice_default();
1510 cgi_set_content_type("text/plain");
1511 diff_config_init(&DCfg, DIFF_VERBOSE);
1512 diff_two_versions(zFrom, zTo, &DCfg, 0);
1513
+1 -1
--- src/event.c
+++ src/event.c
@@ -382,11 +382,11 @@
382382
const char *zClr; /* Name of the background color */
383383
const char *zMimetype = P("mimetype"); /* Mimetype of zBody */
384384
int isNew = 0;
385385
386386
if( zBody ){
387
- zBody = mprintf("%s", zBody);
387
+ zBody = fossil_strdup(zBody);
388388
}
389389
login_check_credentials();
390390
zId = P("name");
391391
if( zId==0 ){
392392
zId = db_text(0, "SELECT lower(hex(randomblob(20)))");
393393
--- src/event.c
+++ src/event.c
@@ -382,11 +382,11 @@
382 const char *zClr; /* Name of the background color */
383 const char *zMimetype = P("mimetype"); /* Mimetype of zBody */
384 int isNew = 0;
385
386 if( zBody ){
387 zBody = mprintf("%s", zBody);
388 }
389 login_check_credentials();
390 zId = P("name");
391 if( zId==0 ){
392 zId = db_text(0, "SELECT lower(hex(randomblob(20)))");
393
--- src/event.c
+++ src/event.c
@@ -382,11 +382,11 @@
382 const char *zClr; /* Name of the background color */
383 const char *zMimetype = P("mimetype"); /* Mimetype of zBody */
384 int isNew = 0;
385
386 if( zBody ){
387 zBody = fossil_strdup(zBody);
388 }
389 login_check_credentials();
390 zId = P("name");
391 if( zId==0 ){
392 zId = db_text(0, "SELECT lower(hex(randomblob(20)))");
393
+5 -5
--- src/export.c
+++ src/export.c
@@ -58,11 +58,11 @@
5858
}
5959
db_static_prepare(&q, "SELECT info FROM user WHERE login=:user");
6060
db_bind_text(&q, ":user", zUser);
6161
if( db_step(&q)!=SQLITE_ROW ){
6262
db_reset(&q);
63
- zName = mprintf("%s", zUser);
63
+ zName = fossil_strdup(zUser);
6464
for(i=j=0; zName[i]; i++){
6565
if( zName[i]!='<' && zName[i]!='>' && zName[i]!='"' ){
6666
zName[j++] = zName[i];
6767
}
6868
}
@@ -102,11 +102,11 @@
102102
atEmailFirst = i+1;
103103
}
104104
}
105105
if( zContact[i]==0 ){
106106
/* No email address found. Take as user info if not empty */
107
- zName = mprintf("%s", zContact[0] ? zContact : zUser);
107
+ zName = fossil_strdup(zContact[0] ? zContact : zUser);
108108
for(i=j=0; zName[i]; i++){
109109
if( zName[i]!='<' && zName[i]!='>' && zName[i]!='"' ){
110110
zName[j++] = zName[i];
111111
}
112112
}
@@ -149,11 +149,11 @@
149149
for(j=0; j<i && zContact[j] && zContact[j]==' '; j++){}
150150
zName = mprintf("%.*s", i-j+1, &zContact[j]);
151151
}
152152
}
153153
154
- if( zName==NULL ) zName = mprintf("%s", zUser);
154
+ if( zName==NULL ) zName = fossil_strdup(zUser);
155155
for(i=j=0; zName[i]; i++){
156156
if( zName[i]!='<' && zName[i]!='>' && zName[i]!='"' ){
157157
zName[j++] = zName[i];
158158
}
159159
}
@@ -172,11 +172,11 @@
172172
** https://git-scm.com/docs/git-check-ref-format
173173
** This implementation assumes we are only printing
174174
** the branch or tag part of the reference.
175175
*/
176176
static void print_ref(const char *zRef){
177
- char *zEncoded = mprintf("%s", zRef);
177
+ char *zEncoded = fossil_strdup(zRef);
178178
int i, w;
179179
if (zEncoded[0]=='@' && zEncoded[1]=='\0'){
180180
putchar(REFREPLACEMENT);
181181
return;
182182
}
@@ -1152,11 +1152,11 @@
11521152
TAG_BRANCH, rid
11531153
);
11541154
if( fossil_strcmp(zBranch,"trunk")==0 ){
11551155
assert( gitmirror_mainbranch!=0 );
11561156
fossil_free(zBranch);
1157
- zBranch = mprintf("%s",gitmirror_mainbranch);
1157
+ zBranch = fossil_strdup(gitmirror_mainbranch);
11581158
}else if( zBranch==0 ){
11591159
zBranch = mprintf("unknown");
11601160
}else{
11611161
gitmirror_sanitize_name(zBranch);
11621162
}
11631163
--- src/export.c
+++ src/export.c
@@ -58,11 +58,11 @@
58 }
59 db_static_prepare(&q, "SELECT info FROM user WHERE login=:user");
60 db_bind_text(&q, ":user", zUser);
61 if( db_step(&q)!=SQLITE_ROW ){
62 db_reset(&q);
63 zName = mprintf("%s", zUser);
64 for(i=j=0; zName[i]; i++){
65 if( zName[i]!='<' && zName[i]!='>' && zName[i]!='"' ){
66 zName[j++] = zName[i];
67 }
68 }
@@ -102,11 +102,11 @@
102 atEmailFirst = i+1;
103 }
104 }
105 if( zContact[i]==0 ){
106 /* No email address found. Take as user info if not empty */
107 zName = mprintf("%s", zContact[0] ? zContact : zUser);
108 for(i=j=0; zName[i]; i++){
109 if( zName[i]!='<' && zName[i]!='>' && zName[i]!='"' ){
110 zName[j++] = zName[i];
111 }
112 }
@@ -149,11 +149,11 @@
149 for(j=0; j<i && zContact[j] && zContact[j]==' '; j++){}
150 zName = mprintf("%.*s", i-j+1, &zContact[j]);
151 }
152 }
153
154 if( zName==NULL ) zName = mprintf("%s", zUser);
155 for(i=j=0; zName[i]; i++){
156 if( zName[i]!='<' && zName[i]!='>' && zName[i]!='"' ){
157 zName[j++] = zName[i];
158 }
159 }
@@ -172,11 +172,11 @@
172 ** https://git-scm.com/docs/git-check-ref-format
173 ** This implementation assumes we are only printing
174 ** the branch or tag part of the reference.
175 */
176 static void print_ref(const char *zRef){
177 char *zEncoded = mprintf("%s", zRef);
178 int i, w;
179 if (zEncoded[0]=='@' && zEncoded[1]=='\0'){
180 putchar(REFREPLACEMENT);
181 return;
182 }
@@ -1152,11 +1152,11 @@
1152 TAG_BRANCH, rid
1153 );
1154 if( fossil_strcmp(zBranch,"trunk")==0 ){
1155 assert( gitmirror_mainbranch!=0 );
1156 fossil_free(zBranch);
1157 zBranch = mprintf("%s",gitmirror_mainbranch);
1158 }else if( zBranch==0 ){
1159 zBranch = mprintf("unknown");
1160 }else{
1161 gitmirror_sanitize_name(zBranch);
1162 }
1163
--- src/export.c
+++ src/export.c
@@ -58,11 +58,11 @@
58 }
59 db_static_prepare(&q, "SELECT info FROM user WHERE login=:user");
60 db_bind_text(&q, ":user", zUser);
61 if( db_step(&q)!=SQLITE_ROW ){
62 db_reset(&q);
63 zName = fossil_strdup(zUser);
64 for(i=j=0; zName[i]; i++){
65 if( zName[i]!='<' && zName[i]!='>' && zName[i]!='"' ){
66 zName[j++] = zName[i];
67 }
68 }
@@ -102,11 +102,11 @@
102 atEmailFirst = i+1;
103 }
104 }
105 if( zContact[i]==0 ){
106 /* No email address found. Take as user info if not empty */
107 zName = fossil_strdup(zContact[0] ? zContact : zUser);
108 for(i=j=0; zName[i]; i++){
109 if( zName[i]!='<' && zName[i]!='>' && zName[i]!='"' ){
110 zName[j++] = zName[i];
111 }
112 }
@@ -149,11 +149,11 @@
149 for(j=0; j<i && zContact[j] && zContact[j]==' '; j++){}
150 zName = mprintf("%.*s", i-j+1, &zContact[j]);
151 }
152 }
153
154 if( zName==NULL ) zName = fossil_strdup(zUser);
155 for(i=j=0; zName[i]; i++){
156 if( zName[i]!='<' && zName[i]!='>' && zName[i]!='"' ){
157 zName[j++] = zName[i];
158 }
159 }
@@ -172,11 +172,11 @@
172 ** https://git-scm.com/docs/git-check-ref-format
173 ** This implementation assumes we are only printing
174 ** the branch or tag part of the reference.
175 */
176 static void print_ref(const char *zRef){
177 char *zEncoded = fossil_strdup(zRef);
178 int i, w;
179 if (zEncoded[0]=='@' && zEncoded[1]=='\0'){
180 putchar(REFREPLACEMENT);
181 return;
182 }
@@ -1152,11 +1152,11 @@
1152 TAG_BRANCH, rid
1153 );
1154 if( fossil_strcmp(zBranch,"trunk")==0 ){
1155 assert( gitmirror_mainbranch!=0 );
1156 fossil_free(zBranch);
1157 zBranch = fossil_strdup(gitmirror_mainbranch);
1158 }else if( zBranch==0 ){
1159 zBranch = mprintf("unknown");
1160 }else{
1161 gitmirror_sanitize_name(zBranch);
1162 }
1163
+2 -1
--- src/extcgi.c
+++ src/extcgi.c
@@ -229,11 +229,12 @@
229229
zFailReason = "path does not match any file or script";
230230
goto ext_not_found;
231231
}
232232
assert( nScript>=nRoot+1 );
233233
style_set_current_page("ext/%s", &zScript[nRoot+1]);
234
- zMime = mimetype_from_name(zScript);
234
+ zMime = P("mimetype");
235
+ if( zMime==0 ) zMime = mimetype_from_name(zScript);
235236
if( zMime==0 ) zMime = "application/octet-stream";
236237
if( !file_isexe(zScript, ExtFILE) ){
237238
/* File is not executable. Must be a regular file. In that case,
238239
** disallow extra path elements */
239240
if( zPath[nScript]!=0 ){
240241
--- src/extcgi.c
+++ src/extcgi.c
@@ -229,11 +229,12 @@
229 zFailReason = "path does not match any file or script";
230 goto ext_not_found;
231 }
232 assert( nScript>=nRoot+1 );
233 style_set_current_page("ext/%s", &zScript[nRoot+1]);
234 zMime = mimetype_from_name(zScript);
 
235 if( zMime==0 ) zMime = "application/octet-stream";
236 if( !file_isexe(zScript, ExtFILE) ){
237 /* File is not executable. Must be a regular file. In that case,
238 ** disallow extra path elements */
239 if( zPath[nScript]!=0 ){
240
--- src/extcgi.c
+++ src/extcgi.c
@@ -229,11 +229,12 @@
229 zFailReason = "path does not match any file or script";
230 goto ext_not_found;
231 }
232 assert( nScript>=nRoot+1 );
233 style_set_current_page("ext/%s", &zScript[nRoot+1]);
234 zMime = P("mimetype");
235 if( zMime==0 ) zMime = mimetype_from_name(zScript);
236 if( zMime==0 ) zMime = "application/octet-stream";
237 if( !file_isexe(zScript, ExtFILE) ){
238 /* File is not executable. Must be a regular file. In that case,
239 ** disallow extra path elements */
240 if( zPath[nScript]!=0 ){
241
+5 -5
--- src/file.c
+++ src/file.c
@@ -260,11 +260,11 @@
260260
int i, nName;
261261
char *zName, zBuf[1000];
262262
263263
nName = strlen(zLinkFile);
264264
if( nName>=(int)sizeof(zBuf) ){
265
- zName = mprintf("%s", zLinkFile);
265
+ zName = fossil_strdup(zLinkFile);
266266
}else{
267267
zName = zBuf;
268268
memcpy(zName, zLinkFile, nName+1);
269269
}
270270
nName = file_simplify_name(zName, nName, 0);
@@ -425,11 +425,11 @@
425425
*/
426426
int file_isdir(const char *zFilename, int eFType){
427427
int rc;
428428
char *zFN;
429429
430
- zFN = mprintf("%s", zFilename);
430
+ zFN = fossil_strdup(zFilename);
431431
file_simplify_name(zFN, -1, 0);
432432
rc = getStat(zFN, eFType);
433433
if( rc ){
434434
rc = 0; /* It does not exist at all. */
435435
}else if( S_ISDIR(fx.fileStat.st_mode) ){
@@ -904,11 +904,11 @@
904904
){
905905
int nName, rc = 0;
906906
char *zName;
907907
908908
nName = strlen(zFilename);
909
- zName = mprintf("%s", zFilename);
909
+ zName = fossil_strdup(zFilename);
910910
nName = file_simplify_name(zName, nName, 0);
911911
while( nName>0 && zName[nName-1]!='/' ){ nName--; }
912912
if( nName>1 ){
913913
zName[nName-1] = 0;
914914
if( file_isdir(zName, eFType)!=1 ){
@@ -1277,13 +1277,13 @@
12771277
const char *zTail;
12781278
for(i=2; i<g.argc; i++){
12791279
zTail = file_skip_userhost(g.argv[i]);
12801280
if( zTail ){
12811281
fossil_print("... ON REMOTE: %.*s\n", (int)(zTail-g.argv[i]), g.argv[i]);
1282
- z = mprintf("%s", zTail);
1282
+ z = fossil_strdup(zTail);
12831283
}else{
1284
- z = mprintf("%s", g.argv[i]);
1284
+ z = fossil_strdup(g.argv[i]);
12851285
}
12861286
fossil_print("[%s] -> ", z);
12871287
file_simplify_name(z, -1, 0);
12881288
fossil_print("[%s]\n", z);
12891289
fossil_free(z);
12901290
--- src/file.c
+++ src/file.c
@@ -260,11 +260,11 @@
260 int i, nName;
261 char *zName, zBuf[1000];
262
263 nName = strlen(zLinkFile);
264 if( nName>=(int)sizeof(zBuf) ){
265 zName = mprintf("%s", zLinkFile);
266 }else{
267 zName = zBuf;
268 memcpy(zName, zLinkFile, nName+1);
269 }
270 nName = file_simplify_name(zName, nName, 0);
@@ -425,11 +425,11 @@
425 */
426 int file_isdir(const char *zFilename, int eFType){
427 int rc;
428 char *zFN;
429
430 zFN = mprintf("%s", zFilename);
431 file_simplify_name(zFN, -1, 0);
432 rc = getStat(zFN, eFType);
433 if( rc ){
434 rc = 0; /* It does not exist at all. */
435 }else if( S_ISDIR(fx.fileStat.st_mode) ){
@@ -904,11 +904,11 @@
904 ){
905 int nName, rc = 0;
906 char *zName;
907
908 nName = strlen(zFilename);
909 zName = mprintf("%s", zFilename);
910 nName = file_simplify_name(zName, nName, 0);
911 while( nName>0 && zName[nName-1]!='/' ){ nName--; }
912 if( nName>1 ){
913 zName[nName-1] = 0;
914 if( file_isdir(zName, eFType)!=1 ){
@@ -1277,13 +1277,13 @@
1277 const char *zTail;
1278 for(i=2; i<g.argc; i++){
1279 zTail = file_skip_userhost(g.argv[i]);
1280 if( zTail ){
1281 fossil_print("... ON REMOTE: %.*s\n", (int)(zTail-g.argv[i]), g.argv[i]);
1282 z = mprintf("%s", zTail);
1283 }else{
1284 z = mprintf("%s", g.argv[i]);
1285 }
1286 fossil_print("[%s] -> ", z);
1287 file_simplify_name(z, -1, 0);
1288 fossil_print("[%s]\n", z);
1289 fossil_free(z);
1290
--- src/file.c
+++ src/file.c
@@ -260,11 +260,11 @@
260 int i, nName;
261 char *zName, zBuf[1000];
262
263 nName = strlen(zLinkFile);
264 if( nName>=(int)sizeof(zBuf) ){
265 zName = fossil_strdup(zLinkFile);
266 }else{
267 zName = zBuf;
268 memcpy(zName, zLinkFile, nName+1);
269 }
270 nName = file_simplify_name(zName, nName, 0);
@@ -425,11 +425,11 @@
425 */
426 int file_isdir(const char *zFilename, int eFType){
427 int rc;
428 char *zFN;
429
430 zFN = fossil_strdup(zFilename);
431 file_simplify_name(zFN, -1, 0);
432 rc = getStat(zFN, eFType);
433 if( rc ){
434 rc = 0; /* It does not exist at all. */
435 }else if( S_ISDIR(fx.fileStat.st_mode) ){
@@ -904,11 +904,11 @@
904 ){
905 int nName, rc = 0;
906 char *zName;
907
908 nName = strlen(zFilename);
909 zName = fossil_strdup(zFilename);
910 nName = file_simplify_name(zName, nName, 0);
911 while( nName>0 && zName[nName-1]!='/' ){ nName--; }
912 if( nName>1 ){
913 zName[nName-1] = 0;
914 if( file_isdir(zName, eFType)!=1 ){
@@ -1277,13 +1277,13 @@
1277 const char *zTail;
1278 for(i=2; i<g.argc; i++){
1279 zTail = file_skip_userhost(g.argv[i]);
1280 if( zTail ){
1281 fossil_print("... ON REMOTE: %.*s\n", (int)(zTail-g.argv[i]), g.argv[i]);
1282 z = fossil_strdup(zTail);
1283 }else{
1284 z = fossil_strdup(g.argv[i]);
1285 }
1286 fossil_print("[%s] -> ", z);
1287 file_simplify_name(z, -1, 0);
1288 fossil_print("[%s]\n", z);
1289 fossil_free(z);
1290
+6 -6
--- src/fileedit.c
+++ src/fileedit.c
@@ -825,13 +825,13 @@
825825
}
826826
db_begin_transaction();
827827
zFilename = g.argv[2];
828828
cimi.zFilename = mprintf("%/", zAsFilename ? zAsFilename : zFilename);
829829
cimi.filePerm = file_perm(zFilename, ExtFILE);
830
- cimi.zUser = mprintf("%s", zUser ? zUser : login_name());
830
+ cimi.zUser = fossil_strdup(zUser ? zUser : login_name());
831831
if(zDate){
832
- cimi.zDate = mprintf("%s", zDate);
832
+ cimi.zDate = fossil_strdup(zDate);
833833
}
834834
if(zRevision==0 || zRevision[0]==0){
835835
if(g.localOpen/*check-out*/){
836836
zRevision = db_lget("checkout-hash", 0)/*leak*/;
837837
}else{
@@ -928,11 +928,11 @@
928928
char * zFileUuid = 0;
929929
db_prepare(&stmt, "SELECT uuid, perm FROM files_of_checkin "
930930
"WHERE filename=%Q %s AND checkinID=%d",
931931
zFilename, filename_collation(), vid);
932932
if(SQLITE_ROW==db_step(&stmt)){
933
- zFileUuid = mprintf("%s",db_column_text(&stmt, 0));
933
+ zFileUuid = fossil_strdup(db_column_text(&stmt, 0));
934934
if(pFilePerm){
935935
*pFilePerm = mfile_permstr_int(db_column_text(&stmt, 1));
936936
}
937937
}
938938
db_finalize(&stmt);
@@ -1187,11 +1187,11 @@
11871187
if(bIsMissingArg){
11881188
*bIsMissingArg = 1;
11891189
}
11901190
fail((pErr,"Missing required 'filename' parameter."));
11911191
}
1192
- p->zFilename = mprintf("%s",zFlag);
1192
+ p->zFilename = fossil_strdup(zFlag);
11931193
11941194
if(0==fileedit_is_editable(p->zFilename)){
11951195
rc = 403;
11961196
fail((pErr,"Filename [%h] is disallowed "
11971197
"by the [fileedit-glob] repository "
@@ -1248,11 +1248,11 @@
12481248
if(zFlag!=0 && *zFlag!=0){
12491249
blob_append(&p->comment, zFlag, -1);
12501250
}
12511251
zFlag = P("comment_mimetype");
12521252
if(zFlag){
1253
- p->zCommentMimetype = mprintf("%s",zFlag);
1253
+ p->zCommentMimetype = fossil_strdup(zFlag);
12541254
zFlag = 0;
12551255
}
12561256
#define p_int(K) atoi(PD(K,"0"))
12571257
if(p_int("dry_run")!=0){
12581258
p->flags |= CIMINI_DRY_RUN;
@@ -1284,11 +1284,11 @@
12841284
#undef p_int
12851285
/*
12861286
** TODO?: date-override date selection field. Maybe use
12871287
** an input[type=datetime-local].
12881288
*/
1289
- p->zUser = mprintf("%s",g.zLogin);
1289
+ p->zUser = fossil_strdup(g.zLogin);
12901290
return 0;
12911291
end_fail:
12921292
#undef fail
12931293
fossil_free(zFileUuid);
12941294
return rc ? rc : 500;
12951295
--- src/fileedit.c
+++ src/fileedit.c
@@ -825,13 +825,13 @@
825 }
826 db_begin_transaction();
827 zFilename = g.argv[2];
828 cimi.zFilename = mprintf("%/", zAsFilename ? zAsFilename : zFilename);
829 cimi.filePerm = file_perm(zFilename, ExtFILE);
830 cimi.zUser = mprintf("%s", zUser ? zUser : login_name());
831 if(zDate){
832 cimi.zDate = mprintf("%s", zDate);
833 }
834 if(zRevision==0 || zRevision[0]==0){
835 if(g.localOpen/*check-out*/){
836 zRevision = db_lget("checkout-hash", 0)/*leak*/;
837 }else{
@@ -928,11 +928,11 @@
928 char * zFileUuid = 0;
929 db_prepare(&stmt, "SELECT uuid, perm FROM files_of_checkin "
930 "WHERE filename=%Q %s AND checkinID=%d",
931 zFilename, filename_collation(), vid);
932 if(SQLITE_ROW==db_step(&stmt)){
933 zFileUuid = mprintf("%s",db_column_text(&stmt, 0));
934 if(pFilePerm){
935 *pFilePerm = mfile_permstr_int(db_column_text(&stmt, 1));
936 }
937 }
938 db_finalize(&stmt);
@@ -1187,11 +1187,11 @@
1187 if(bIsMissingArg){
1188 *bIsMissingArg = 1;
1189 }
1190 fail((pErr,"Missing required 'filename' parameter."));
1191 }
1192 p->zFilename = mprintf("%s",zFlag);
1193
1194 if(0==fileedit_is_editable(p->zFilename)){
1195 rc = 403;
1196 fail((pErr,"Filename [%h] is disallowed "
1197 "by the [fileedit-glob] repository "
@@ -1248,11 +1248,11 @@
1248 if(zFlag!=0 && *zFlag!=0){
1249 blob_append(&p->comment, zFlag, -1);
1250 }
1251 zFlag = P("comment_mimetype");
1252 if(zFlag){
1253 p->zCommentMimetype = mprintf("%s",zFlag);
1254 zFlag = 0;
1255 }
1256 #define p_int(K) atoi(PD(K,"0"))
1257 if(p_int("dry_run")!=0){
1258 p->flags |= CIMINI_DRY_RUN;
@@ -1284,11 +1284,11 @@
1284 #undef p_int
1285 /*
1286 ** TODO?: date-override date selection field. Maybe use
1287 ** an input[type=datetime-local].
1288 */
1289 p->zUser = mprintf("%s",g.zLogin);
1290 return 0;
1291 end_fail:
1292 #undef fail
1293 fossil_free(zFileUuid);
1294 return rc ? rc : 500;
1295
--- src/fileedit.c
+++ src/fileedit.c
@@ -825,13 +825,13 @@
825 }
826 db_begin_transaction();
827 zFilename = g.argv[2];
828 cimi.zFilename = mprintf("%/", zAsFilename ? zAsFilename : zFilename);
829 cimi.filePerm = file_perm(zFilename, ExtFILE);
830 cimi.zUser = fossil_strdup(zUser ? zUser : login_name());
831 if(zDate){
832 cimi.zDate = fossil_strdup(zDate);
833 }
834 if(zRevision==0 || zRevision[0]==0){
835 if(g.localOpen/*check-out*/){
836 zRevision = db_lget("checkout-hash", 0)/*leak*/;
837 }else{
@@ -928,11 +928,11 @@
928 char * zFileUuid = 0;
929 db_prepare(&stmt, "SELECT uuid, perm FROM files_of_checkin "
930 "WHERE filename=%Q %s AND checkinID=%d",
931 zFilename, filename_collation(), vid);
932 if(SQLITE_ROW==db_step(&stmt)){
933 zFileUuid = fossil_strdup(db_column_text(&stmt, 0));
934 if(pFilePerm){
935 *pFilePerm = mfile_permstr_int(db_column_text(&stmt, 1));
936 }
937 }
938 db_finalize(&stmt);
@@ -1187,11 +1187,11 @@
1187 if(bIsMissingArg){
1188 *bIsMissingArg = 1;
1189 }
1190 fail((pErr,"Missing required 'filename' parameter."));
1191 }
1192 p->zFilename = fossil_strdup(zFlag);
1193
1194 if(0==fileedit_is_editable(p->zFilename)){
1195 rc = 403;
1196 fail((pErr,"Filename [%h] is disallowed "
1197 "by the [fileedit-glob] repository "
@@ -1248,11 +1248,11 @@
1248 if(zFlag!=0 && *zFlag!=0){
1249 blob_append(&p->comment, zFlag, -1);
1250 }
1251 zFlag = P("comment_mimetype");
1252 if(zFlag){
1253 p->zCommentMimetype = fossil_strdup(zFlag);
1254 zFlag = 0;
1255 }
1256 #define p_int(K) atoi(PD(K,"0"))
1257 if(p_int("dry_run")!=0){
1258 p->flags |= CIMINI_DRY_RUN;
@@ -1284,11 +1284,11 @@
1284 #undef p_int
1285 /*
1286 ** TODO?: date-override date selection field. Maybe use
1287 ** an input[type=datetime-local].
1288 */
1289 p->zUser = fossil_strdup(g.zLogin);
1290 return 0;
1291 end_fail:
1292 #undef fail
1293 fossil_free(zFileUuid);
1294 return rc ? rc : 500;
1295
--- src/fossil.copybutton.js
+++ src/fossil.copybutton.js
@@ -42,13 +42,11 @@
4242
4343
.oncopy: an optional callback function which is added as an event
4444
listener for the 'text-copied' event (see below). There is
4545
functionally no difference from setting this option or adding a
4646
'text-copied' event listener to the element, and this option is
47
- considered to be a convenience form of that. For the sake of
48
- framework-level consistency, the default value is a callback
49
- which passes the copy button to fossil.dom.flashOnce().
47
+ considered to be a convenience form of that.
5048
5149
Note that this function's own defaultOptions object holds default
5250
values for some options. Any changes made to that object affect
5351
any future calls to this function.
5452
@@ -62,25 +60,21 @@
6260
member is an object with a "text" property holding the copied
6361
text. Other properties may be added in the future. The event is
6462
not fired if copying to the clipboard fails (e.g. is not
6563
available in the current environment).
6664
67
- As a special case, the copy button's click handler is suppressed
68
- (becomes a no-op) for as long as the element has the CSS class
69
- "disabled". This allows elements which cannot be disabled via
70
- HTML attributes, e.g. a SPAN, to act as a copy button while still
71
- providing a way to disable them.
65
+ The copy button's click handler is suppressed (becomes a no-op)
66
+ for as long as the element has the "disabled" attribute.
7267
7368
Returns the copy-initialized element.
7469
7570
Example:
7671
7772
const button = fossil.copyButton('#my-copy-button', {
7873
copyFromId: 'some-other-element-id'
7974
});
8075
button.addEventListener('text-copied',function(ev){
81
- fossil.dom.flashOnce(ev.target);
8276
console.debug("Copied text:",ev.detail.text);
8377
});
8478
*/
8579
F.copyButton = function f(e, opt){
8680
if('string'===typeof e){
@@ -103,11 +97,11 @@
10397
e.addEventListener(
10498
'click',
10599
function(ev){
106100
ev.preventDefault();
107101
ev.stopPropagation();
108
- if(e.classList.contains('disabled')) return;
102
+ if(e.disabled) return; /* This check is probably redundant. */
109103
const txt = extract.call(opt);
110104
if(txt && D.copyTextToClipboard(txt)){
111105
e.dispatchEvent(new CustomEvent('text-copied',{
112106
detail: {text: txt}
113107
}));
@@ -116,15 +110,19 @@
116110
false
117111
);
118112
if('function' === typeof opt.oncopy){
119113
e.addEventListener('text-copied', opt.oncopy, false);
120114
}
115
+ /* Make sure the <button> contains a single nested <span>. */
116
+ if(e.childElementCount!=1 || e.firstChild.tagName!='SPAN'){
117
+ D.append(D.clearElement(e), D.span());
118
+ }
121119
return e;
122120
};
123121
124122
F.copyButton.defaultOptions = {
125123
cssClass: 'copy-button',
126
- oncopy: D.flashOnce.eventHandler,
124
+ oncopy: undefined,
127125
style: {/*properties copied as-is into element.style*/}
128126
};
129127
130128
})(window.fossil);
131129
--- src/fossil.copybutton.js
+++ src/fossil.copybutton.js
@@ -42,13 +42,11 @@
42
43 .oncopy: an optional callback function which is added as an event
44 listener for the 'text-copied' event (see below). There is
45 functionally no difference from setting this option or adding a
46 'text-copied' event listener to the element, and this option is
47 considered to be a convenience form of that. For the sake of
48 framework-level consistency, the default value is a callback
49 which passes the copy button to fossil.dom.flashOnce().
50
51 Note that this function's own defaultOptions object holds default
52 values for some options. Any changes made to that object affect
53 any future calls to this function.
54
@@ -62,25 +60,21 @@
62 member is an object with a "text" property holding the copied
63 text. Other properties may be added in the future. The event is
64 not fired if copying to the clipboard fails (e.g. is not
65 available in the current environment).
66
67 As a special case, the copy button's click handler is suppressed
68 (becomes a no-op) for as long as the element has the CSS class
69 "disabled". This allows elements which cannot be disabled via
70 HTML attributes, e.g. a SPAN, to act as a copy button while still
71 providing a way to disable them.
72
73 Returns the copy-initialized element.
74
75 Example:
76
77 const button = fossil.copyButton('#my-copy-button', {
78 copyFromId: 'some-other-element-id'
79 });
80 button.addEventListener('text-copied',function(ev){
81 fossil.dom.flashOnce(ev.target);
82 console.debug("Copied text:",ev.detail.text);
83 });
84 */
85 F.copyButton = function f(e, opt){
86 if('string'===typeof e){
@@ -103,11 +97,11 @@
103 e.addEventListener(
104 'click',
105 function(ev){
106 ev.preventDefault();
107 ev.stopPropagation();
108 if(e.classList.contains('disabled')) return;
109 const txt = extract.call(opt);
110 if(txt && D.copyTextToClipboard(txt)){
111 e.dispatchEvent(new CustomEvent('text-copied',{
112 detail: {text: txt}
113 }));
@@ -116,15 +110,19 @@
116 false
117 );
118 if('function' === typeof opt.oncopy){
119 e.addEventListener('text-copied', opt.oncopy, false);
120 }
 
 
 
 
121 return e;
122 };
123
124 F.copyButton.defaultOptions = {
125 cssClass: 'copy-button',
126 oncopy: D.flashOnce.eventHandler,
127 style: {/*properties copied as-is into element.style*/}
128 };
129
130 })(window.fossil);
131
--- src/fossil.copybutton.js
+++ src/fossil.copybutton.js
@@ -42,13 +42,11 @@
42
43 .oncopy: an optional callback function which is added as an event
44 listener for the 'text-copied' event (see below). There is
45 functionally no difference from setting this option or adding a
46 'text-copied' event listener to the element, and this option is
47 considered to be a convenience form of that.
 
 
48
49 Note that this function's own defaultOptions object holds default
50 values for some options. Any changes made to that object affect
51 any future calls to this function.
52
@@ -62,25 +60,21 @@
60 member is an object with a "text" property holding the copied
61 text. Other properties may be added in the future. The event is
62 not fired if copying to the clipboard fails (e.g. is not
63 available in the current environment).
64
65 The copy button's click handler is suppressed (becomes a no-op)
66 for as long as the element has the "disabled" attribute.
 
 
 
67
68 Returns the copy-initialized element.
69
70 Example:
71
72 const button = fossil.copyButton('#my-copy-button', {
73 copyFromId: 'some-other-element-id'
74 });
75 button.addEventListener('text-copied',function(ev){
 
76 console.debug("Copied text:",ev.detail.text);
77 });
78 */
79 F.copyButton = function f(e, opt){
80 if('string'===typeof e){
@@ -103,11 +97,11 @@
97 e.addEventListener(
98 'click',
99 function(ev){
100 ev.preventDefault();
101 ev.stopPropagation();
102 if(e.disabled) return; /* This check is probably redundant. */
103 const txt = extract.call(opt);
104 if(txt && D.copyTextToClipboard(txt)){
105 e.dispatchEvent(new CustomEvent('text-copied',{
106 detail: {text: txt}
107 }));
@@ -116,15 +110,19 @@
110 false
111 );
112 if('function' === typeof opt.oncopy){
113 e.addEventListener('text-copied', opt.oncopy, false);
114 }
115 /* Make sure the <button> contains a single nested <span>. */
116 if(e.childElementCount!=1 || e.firstChild.tagName!='SPAN'){
117 D.append(D.clearElement(e), D.span());
118 }
119 return e;
120 };
121
122 F.copyButton.defaultOptions = {
123 cssClass: 'copy-button',
124 oncopy: undefined,
125 style: {/*properties copied as-is into element.style*/}
126 };
127
128 })(window.fossil);
129
--- src/fossil.numbered-lines.js
+++ src/fossil.numbered-lines.js
@@ -23,13 +23,10 @@
2323
.replace(/&?\budc=[^&]*/,'') /* "update display prefs cookie" */
2424
.replace(/&?\bln=[^&]*/,'') /* inbound line number/range */
2525
.replace('?&','?');
2626
const lineState = { urlArgs: urlArgsRaw, start: 0, end: 0 };
2727
const lineTip = new F.PopupWidget({
28
- style: {
29
- cursor: 'pointer'
30
- },
3128
refresh: function(){
3229
const link = this.state.link;
3330
D.clearElement(link);
3431
if(lineState.start){
3532
const ls = [lineState.start];
@@ -48,23 +45,22 @@
4845
D.append(link, "No lines selected.");
4946
}
5047
},
5148
init: function(){
5249
const e = this.e;
53
- const btnCopy = D.span(),
54
- link = D.span();
50
+ const btnCopy = D.attr(D.button(), 'id', 'linenum-copy-button');
51
+ link = D.label('linenum-copy-button');
5552
this.state = {link};
5653
F.copyButton(btnCopy,{
5754
copyFromElement: link,
5855
extractText: ()=>link.dataset.url,
5956
oncopy: (ev)=>{
60
- D.flashOnce(ev.target, undefined, ()=>lineTip.hide());
57
+ setTimeout(()=>lineTip.hide(), 400);
6158
// arguably too snazzy: F.toast.message("Copied link to clipboard.");
6259
}
6360
});
64
- this.e.addEventListener('click', ()=>btnCopy.click(), false);
65
- D.append(this.e, btnCopy, link)
61
+ D.append(this.e, btnCopy, link);
6662
}
6763
});
6864
6965
tbl.addEventListener('click', ()=>lineTip.hide(), true);
7066
7167
--- src/fossil.numbered-lines.js
+++ src/fossil.numbered-lines.js
@@ -23,13 +23,10 @@
23 .replace(/&?\budc=[^&]*/,'') /* "update display prefs cookie" */
24 .replace(/&?\bln=[^&]*/,'') /* inbound line number/range */
25 .replace('?&','?');
26 const lineState = { urlArgs: urlArgsRaw, start: 0, end: 0 };
27 const lineTip = new F.PopupWidget({
28 style: {
29 cursor: 'pointer'
30 },
31 refresh: function(){
32 const link = this.state.link;
33 D.clearElement(link);
34 if(lineState.start){
35 const ls = [lineState.start];
@@ -48,23 +45,22 @@
48 D.append(link, "No lines selected.");
49 }
50 },
51 init: function(){
52 const e = this.e;
53 const btnCopy = D.span(),
54 link = D.span();
55 this.state = {link};
56 F.copyButton(btnCopy,{
57 copyFromElement: link,
58 extractText: ()=>link.dataset.url,
59 oncopy: (ev)=>{
60 D.flashOnce(ev.target, undefined, ()=>lineTip.hide());
61 // arguably too snazzy: F.toast.message("Copied link to clipboard.");
62 }
63 });
64 this.e.addEventListener('click', ()=>btnCopy.click(), false);
65 D.append(this.e, btnCopy, link)
66 }
67 });
68
69 tbl.addEventListener('click', ()=>lineTip.hide(), true);
70
71
--- src/fossil.numbered-lines.js
+++ src/fossil.numbered-lines.js
@@ -23,13 +23,10 @@
23 .replace(/&?\budc=[^&]*/,'') /* "update display prefs cookie" */
24 .replace(/&?\bln=[^&]*/,'') /* inbound line number/range */
25 .replace('?&','?');
26 const lineState = { urlArgs: urlArgsRaw, start: 0, end: 0 };
27 const lineTip = new F.PopupWidget({
 
 
 
28 refresh: function(){
29 const link = this.state.link;
30 D.clearElement(link);
31 if(lineState.start){
32 const ls = [lineState.start];
@@ -48,23 +45,22 @@
45 D.append(link, "No lines selected.");
46 }
47 },
48 init: function(){
49 const e = this.e;
50 const btnCopy = D.attr(D.button(), 'id', 'linenum-copy-button');
51 link = D.label('linenum-copy-button');
52 this.state = {link};
53 F.copyButton(btnCopy,{
54 copyFromElement: link,
55 extractText: ()=>link.dataset.url,
56 oncopy: (ev)=>{
57 setTimeout(()=>lineTip.hide(), 400);
58 // arguably too snazzy: F.toast.message("Copied link to clipboard.");
59 }
60 });
61 D.append(this.e, btnCopy, link);
 
62 }
63 });
64
65 tbl.addEventListener('click', ()=>lineTip.hide(), true);
66
67
--- src/fossil.page.chat.js
+++ src/fossil.page.chat.js
@@ -901,14 +901,13 @@
901901
const cpId = 'copy-to-clipboard-'+id;
902902
/* ^^^ copy button element ID, needed for LABEL element
903903
pairing. Recall that we destroy all child elements of
904904
`content` each time we hit this block, so we can reuse
905905
that element ID on subsequent toggles. */
906
- const btnCp = D.attr(D.addClass(D.span(),'copy-button'), 'id', cpId);
906
+ const btnCp = D.attr(D.addClass(D.button(),'copy-button'), 'id', cpId);
907907
F.copyButton(btnCp, {extractText: ()=>child._xmsgRaw});
908908
const lblCp = D.label(cpId, "Copy unformatted text");
909
- lblCp.addEventListener('click',()=>btnCp.click(), false);
910909
D.append(content, D.append(D.addClass(D.span(), 'nobr'), btnCp, lblCp));
911910
}
912911
delete e.$isToggling;
913912
D.append(content, child);
914913
return;
@@ -1161,11 +1160,11 @@
11611160
if(body && !body.style.fontSize){
11621161
/** _Attempt_ to force the iframe to inherit the message's text size
11631162
if the body has no explicit size set. On desktop systems
11641163
the size is apparently being inherited in that case, but on mobile
11651164
not. */
1166
- body.style.fontSize = window.getComputedStyle(msgObj.e.content);
1165
+ body.style.fontSize = window.getComputedStyle(msgObj.e.content).fontSize;
11671166
}
11681167
if('' === iframe.style.maxHeight){
11691168
/* Resize iframe height to fit the content. Workaround: if we
11701169
adjust the iframe height while it's hidden then its height
11711170
is 0, so we must briefly unhide it. */
@@ -1736,14 +1735,10 @@
17361735
(k)=>Chat.e.inputX.addEventListener(k, noDragDropEvents, false)
17371736
);
17381737
return bxs;
17391738
})()/*drag/drop/paste*/;
17401739
1741
- const tzOffsetToString = function(off){
1742
- const hours = Math.round(off/60), min = Math.round(off % 30);
1743
- return ''+(hours + (min ? '.5' : ''));
1744
- };
17451740
const localTime8601 = function(d){
17461741
return [
17471742
d.getYear()+1900, '-', pad2(d.getMonth()+1), '-', pad2(d.getDate()),
17481743
'T', pad2(d.getHours()),':', pad2(d.getMinutes()),':',pad2(d.getSeconds())
17491744
].join('');
17501745
--- src/fossil.page.chat.js
+++ src/fossil.page.chat.js
@@ -901,14 +901,13 @@
901 const cpId = 'copy-to-clipboard-'+id;
902 /* ^^^ copy button element ID, needed for LABEL element
903 pairing. Recall that we destroy all child elements of
904 `content` each time we hit this block, so we can reuse
905 that element ID on subsequent toggles. */
906 const btnCp = D.attr(D.addClass(D.span(),'copy-button'), 'id', cpId);
907 F.copyButton(btnCp, {extractText: ()=>child._xmsgRaw});
908 const lblCp = D.label(cpId, "Copy unformatted text");
909 lblCp.addEventListener('click',()=>btnCp.click(), false);
910 D.append(content, D.append(D.addClass(D.span(), 'nobr'), btnCp, lblCp));
911 }
912 delete e.$isToggling;
913 D.append(content, child);
914 return;
@@ -1161,11 +1160,11 @@
1161 if(body && !body.style.fontSize){
1162 /** _Attempt_ to force the iframe to inherit the message's text size
1163 if the body has no explicit size set. On desktop systems
1164 the size is apparently being inherited in that case, but on mobile
1165 not. */
1166 body.style.fontSize = window.getComputedStyle(msgObj.e.content);
1167 }
1168 if('' === iframe.style.maxHeight){
1169 /* Resize iframe height to fit the content. Workaround: if we
1170 adjust the iframe height while it's hidden then its height
1171 is 0, so we must briefly unhide it. */
@@ -1736,14 +1735,10 @@
1736 (k)=>Chat.e.inputX.addEventListener(k, noDragDropEvents, false)
1737 );
1738 return bxs;
1739 })()/*drag/drop/paste*/;
1740
1741 const tzOffsetToString = function(off){
1742 const hours = Math.round(off/60), min = Math.round(off % 30);
1743 return ''+(hours + (min ? '.5' : ''));
1744 };
1745 const localTime8601 = function(d){
1746 return [
1747 d.getYear()+1900, '-', pad2(d.getMonth()+1), '-', pad2(d.getDate()),
1748 'T', pad2(d.getHours()),':', pad2(d.getMinutes()),':',pad2(d.getSeconds())
1749 ].join('');
1750
--- src/fossil.page.chat.js
+++ src/fossil.page.chat.js
@@ -901,14 +901,13 @@
901 const cpId = 'copy-to-clipboard-'+id;
902 /* ^^^ copy button element ID, needed for LABEL element
903 pairing. Recall that we destroy all child elements of
904 `content` each time we hit this block, so we can reuse
905 that element ID on subsequent toggles. */
906 const btnCp = D.attr(D.addClass(D.button(),'copy-button'), 'id', cpId);
907 F.copyButton(btnCp, {extractText: ()=>child._xmsgRaw});
908 const lblCp = D.label(cpId, "Copy unformatted text");
 
909 D.append(content, D.append(D.addClass(D.span(), 'nobr'), btnCp, lblCp));
910 }
911 delete e.$isToggling;
912 D.append(content, child);
913 return;
@@ -1161,11 +1160,11 @@
1160 if(body && !body.style.fontSize){
1161 /** _Attempt_ to force the iframe to inherit the message's text size
1162 if the body has no explicit size set. On desktop systems
1163 the size is apparently being inherited in that case, but on mobile
1164 not. */
1165 body.style.fontSize = window.getComputedStyle(msgObj.e.content).fontSize;
1166 }
1167 if('' === iframe.style.maxHeight){
1168 /* Resize iframe height to fit the content. Workaround: if we
1169 adjust the iframe height while it's hidden then its height
1170 is 0, so we must briefly unhide it. */
@@ -1736,14 +1735,10 @@
1735 (k)=>Chat.e.inputX.addEventListener(k, noDragDropEvents, false)
1736 );
1737 return bxs;
1738 })()/*drag/drop/paste*/;
1739
 
 
 
 
1740 const localTime8601 = function(d){
1741 return [
1742 d.getYear()+1900, '-', pad2(d.getMonth()+1), '-', pad2(d.getDate()),
1743 'T', pad2(d.getHours()),':', pad2(d.getMinutes()),':',pad2(d.getSeconds())
1744 ].join('');
1745
--- src/fossil.page.pikchrshow.js
+++ src/fossil.page.pikchrshow.js
@@ -47,11 +47,11 @@
4747
document.body.classList.add('pikchrshow');
4848
P.e = { /* various DOM elements we work with... */
4949
previewTarget: E('#pikchrshow-output'),
5050
previewLegend: E('#pikchrshow-output-wrapper > legend'),
5151
previewCopyButton: D.attr(
52
- D.addClass(D.span(),'copy-button'),
52
+ D.addClass(D.button(),'copy-button'),
5353
'id','preview-copy-button'
5454
),
5555
previewModeLabel: D.label('preview-copy-button'),
5656
btnSubmit: E('#pikchr-submit-preview'),
5757
btnStash: E('#pikchr-stash'),
@@ -119,11 +119,10 @@
119119
}, false);
120120
121121
////////////////////////////////////////////////////////////
122122
// Setup clipboard-copy of markup/SVG...
123123
F.copyButton(P.e.previewCopyButton, {copyFromElement: P.e.taPreviewText});
124
- P.e.previewModeLabel.addEventListener('click', ()=>P.e.previewCopyButton.click(), false);
125124
126125
////////////////////////////////////////////////////////////
127126
// Set up dark mode simulator...
128127
P.e.cbDarkMode.addEventListener('change', function(ev){
129128
if(ev.target.checked) D.addClass(P.e.previewTarget, 'dark-mode');
@@ -348,11 +347,11 @@
348347
D.addClass(preTgt, 'error');
349348
this.e.previewModeLabel.innerText = "Error";
350349
return;
351350
}
352351
D.removeClass(preTgt, 'error');
353
- D.removeClass(this.e.previewCopyButton, 'disabled');
352
+ this.e.previewCopyButton.disabled = false;
354353
D.removeClass(this.e.markupAlignWrapper, 'hidden');
355354
D.enable(this.e.previewModeToggle, this.e.markupAlignRadios);
356355
let label, svg;
357356
switch(this.previewMode){
358357
case 0:
@@ -427,11 +426,11 @@
427426
P.renderPreview();
428427
};
429428
}
430429
D.disable(fp.toDisable, this.e.previewModeToggle, this.e.markupAlignRadios);
431430
D.addClass(this.e.markupAlignWrapper, 'hidden');
432
- D.addClass(this.e.previewCopyButton, 'disabled');
431
+ this.e.previewCopyButton.disabled = true;
433432
const content = this.e.taContent.value.trim();
434433
this.response.raw = this.response.rawSvg = undefined;
435434
this.response.inputText = content;
436435
const sampleScript = fp.$_sampleScript;
437436
delete fp.$_sampleScript;
438437
--- src/fossil.page.pikchrshow.js
+++ src/fossil.page.pikchrshow.js
@@ -47,11 +47,11 @@
47 document.body.classList.add('pikchrshow');
48 P.e = { /* various DOM elements we work with... */
49 previewTarget: E('#pikchrshow-output'),
50 previewLegend: E('#pikchrshow-output-wrapper > legend'),
51 previewCopyButton: D.attr(
52 D.addClass(D.span(),'copy-button'),
53 'id','preview-copy-button'
54 ),
55 previewModeLabel: D.label('preview-copy-button'),
56 btnSubmit: E('#pikchr-submit-preview'),
57 btnStash: E('#pikchr-stash'),
@@ -119,11 +119,10 @@
119 }, false);
120
121 ////////////////////////////////////////////////////////////
122 // Setup clipboard-copy of markup/SVG...
123 F.copyButton(P.e.previewCopyButton, {copyFromElement: P.e.taPreviewText});
124 P.e.previewModeLabel.addEventListener('click', ()=>P.e.previewCopyButton.click(), false);
125
126 ////////////////////////////////////////////////////////////
127 // Set up dark mode simulator...
128 P.e.cbDarkMode.addEventListener('change', function(ev){
129 if(ev.target.checked) D.addClass(P.e.previewTarget, 'dark-mode');
@@ -348,11 +347,11 @@
348 D.addClass(preTgt, 'error');
349 this.e.previewModeLabel.innerText = "Error";
350 return;
351 }
352 D.removeClass(preTgt, 'error');
353 D.removeClass(this.e.previewCopyButton, 'disabled');
354 D.removeClass(this.e.markupAlignWrapper, 'hidden');
355 D.enable(this.e.previewModeToggle, this.e.markupAlignRadios);
356 let label, svg;
357 switch(this.previewMode){
358 case 0:
@@ -427,11 +426,11 @@
427 P.renderPreview();
428 };
429 }
430 D.disable(fp.toDisable, this.e.previewModeToggle, this.e.markupAlignRadios);
431 D.addClass(this.e.markupAlignWrapper, 'hidden');
432 D.addClass(this.e.previewCopyButton, 'disabled');
433 const content = this.e.taContent.value.trim();
434 this.response.raw = this.response.rawSvg = undefined;
435 this.response.inputText = content;
436 const sampleScript = fp.$_sampleScript;
437 delete fp.$_sampleScript;
438
--- src/fossil.page.pikchrshow.js
+++ src/fossil.page.pikchrshow.js
@@ -47,11 +47,11 @@
47 document.body.classList.add('pikchrshow');
48 P.e = { /* various DOM elements we work with... */
49 previewTarget: E('#pikchrshow-output'),
50 previewLegend: E('#pikchrshow-output-wrapper > legend'),
51 previewCopyButton: D.attr(
52 D.addClass(D.button(),'copy-button'),
53 'id','preview-copy-button'
54 ),
55 previewModeLabel: D.label('preview-copy-button'),
56 btnSubmit: E('#pikchr-submit-preview'),
57 btnStash: E('#pikchr-stash'),
@@ -119,11 +119,10 @@
119 }, false);
120
121 ////////////////////////////////////////////////////////////
122 // Setup clipboard-copy of markup/SVG...
123 F.copyButton(P.e.previewCopyButton, {copyFromElement: P.e.taPreviewText});
 
124
125 ////////////////////////////////////////////////////////////
126 // Set up dark mode simulator...
127 P.e.cbDarkMode.addEventListener('change', function(ev){
128 if(ev.target.checked) D.addClass(P.e.previewTarget, 'dark-mode');
@@ -348,11 +347,11 @@
347 D.addClass(preTgt, 'error');
348 this.e.previewModeLabel.innerText = "Error";
349 return;
350 }
351 D.removeClass(preTgt, 'error');
352 this.e.previewCopyButton.disabled = false;
353 D.removeClass(this.e.markupAlignWrapper, 'hidden');
354 D.enable(this.e.previewModeToggle, this.e.markupAlignRadios);
355 let label, svg;
356 switch(this.previewMode){
357 case 0:
@@ -427,11 +426,11 @@
426 P.renderPreview();
427 };
428 }
429 D.disable(fp.toDisable, this.e.previewModeToggle, this.e.markupAlignRadios);
430 D.addClass(this.e.markupAlignWrapper, 'hidden');
431 this.e.previewCopyButton.disabled = true;
432 const content = this.e.taContent.value.trim();
433 this.response.raw = this.response.rawSvg = undefined;
434 this.response.inputText = content;
435 const sampleScript = fp.$_sampleScript;
436 delete fp.$_sampleScript;
437
--- src/fossil.page.pikchrshowasm.js
+++ src/fossil.page.pikchrshowasm.js
@@ -312,11 +312,10 @@
312312
if(this.e.pikOut.dataset.pikchr){
313313
this.render(this.e.pikOut.dataset.pikchr);
314314
}
315315
}.bind(PS));
316316
F.copyButton(PS.e.previewCopyButton, {copyFromElement: PS.e.outText});
317
- PS.e.previewModeLabel.addEventListener('click', ()=>PS.e.previewCopyButton.click(), false);
318317
319318
PS.addMsgHandler('working',function f(ev){
320319
switch(ev.data){
321320
case 'start': /* See notes in preStartWork(). */; return;
322321
case 'end':
323322
--- src/fossil.page.pikchrshowasm.js
+++ src/fossil.page.pikchrshowasm.js
@@ -312,11 +312,10 @@
312 if(this.e.pikOut.dataset.pikchr){
313 this.render(this.e.pikOut.dataset.pikchr);
314 }
315 }.bind(PS));
316 F.copyButton(PS.e.previewCopyButton, {copyFromElement: PS.e.outText});
317 PS.e.previewModeLabel.addEventListener('click', ()=>PS.e.previewCopyButton.click(), false);
318
319 PS.addMsgHandler('working',function f(ev){
320 switch(ev.data){
321 case 'start': /* See notes in preStartWork(). */; return;
322 case 'end':
323
--- src/fossil.page.pikchrshowasm.js
+++ src/fossil.page.pikchrshowasm.js
@@ -312,11 +312,10 @@
312 if(this.e.pikOut.dataset.pikchr){
313 this.render(this.e.pikOut.dataset.pikchr);
314 }
315 }.bind(PS));
316 F.copyButton(PS.e.previewCopyButton, {copyFromElement: PS.e.outText});
 
317
318 PS.addMsgHandler('working',function f(ev){
319 switch(ev.data){
320 case 'start': /* See notes in preStartWork(). */; return;
321 case 'end':
322
--- src/fossil.page.wikiedit.js
+++ src/fossil.page.wikiedit.js
@@ -1234,13 +1234,13 @@
12341234
encodeURIComponent(a.filename)
12351235
].join(''),
12361236
"raw/"+a.src
12371237
].forEach(function(url){
12381238
const imgUrl = D.append(D.addClass(D.span(), 'monospace'), url);
1239
- const urlCopy = D.span();
1239
+ const urlCopy = D.button();
12401240
const li = D.li(ul);
1241
- D.append(li, urlCopy, " ", imgUrl);
1241
+ D.append(li, urlCopy, imgUrl);
12421242
F.copyButton(urlCopy, {copyFromElement: imgUrl});
12431243
});
12441244
});
12451245
return this;
12461246
};
12471247
--- src/fossil.page.wikiedit.js
+++ src/fossil.page.wikiedit.js
@@ -1234,13 +1234,13 @@
1234 encodeURIComponent(a.filename)
1235 ].join(''),
1236 "raw/"+a.src
1237 ].forEach(function(url){
1238 const imgUrl = D.append(D.addClass(D.span(), 'monospace'), url);
1239 const urlCopy = D.span();
1240 const li = D.li(ul);
1241 D.append(li, urlCopy, " ", imgUrl);
1242 F.copyButton(urlCopy, {copyFromElement: imgUrl});
1243 });
1244 });
1245 return this;
1246 };
1247
--- src/fossil.page.wikiedit.js
+++ src/fossil.page.wikiedit.js
@@ -1234,13 +1234,13 @@
1234 encodeURIComponent(a.filename)
1235 ].join(''),
1236 "raw/"+a.src
1237 ].forEach(function(url){
1238 const imgUrl = D.append(D.addClass(D.span(), 'monospace'), url);
1239 const urlCopy = D.button();
1240 const li = D.li(ul);
1241 D.append(li, urlCopy, imgUrl);
1242 F.copyButton(urlCopy, {copyFromElement: imgUrl});
1243 });
1244 });
1245 return this;
1246 };
1247
+1 -1
--- src/graph.c
+++ src/graph.c
@@ -226,11 +226,11 @@
226226
for(i=0; i<p->nBranch; i++){
227227
if( fossil_strcmp(zBranch, p->azBranch[i])==0 ) return p->azBranch[i];
228228
}
229229
p->nBranch++;
230230
p->azBranch = fossil_realloc(p->azBranch, sizeof(char*)*p->nBranch);
231
- p->azBranch[p->nBranch-1] = mprintf("%s", zBranch);
231
+ p->azBranch[p->nBranch-1] = fossil_strdup(zBranch);
232232
return p->azBranch[p->nBranch-1];
233233
}
234234
235235
/*
236236
** Add a new row to the graph context. Rows are added from top to bottom.
237237
--- src/graph.c
+++ src/graph.c
@@ -226,11 +226,11 @@
226 for(i=0; i<p->nBranch; i++){
227 if( fossil_strcmp(zBranch, p->azBranch[i])==0 ) return p->azBranch[i];
228 }
229 p->nBranch++;
230 p->azBranch = fossil_realloc(p->azBranch, sizeof(char*)*p->nBranch);
231 p->azBranch[p->nBranch-1] = mprintf("%s", zBranch);
232 return p->azBranch[p->nBranch-1];
233 }
234
235 /*
236 ** Add a new row to the graph context. Rows are added from top to bottom.
237
--- src/graph.c
+++ src/graph.c
@@ -226,11 +226,11 @@
226 for(i=0; i<p->nBranch; i++){
227 if( fossil_strcmp(zBranch, p->azBranch[i])==0 ) return p->azBranch[i];
228 }
229 p->nBranch++;
230 p->azBranch = fossil_realloc(p->azBranch, sizeof(char*)*p->nBranch);
231 p->azBranch[p->nBranch-1] = fossil_strdup(zBranch);
232 return p->azBranch[p->nBranch-1];
233 }
234
235 /*
236 ** Add a new row to the graph context. Rows are added from top to bottom.
237
+16 -12
--- src/href.js
+++ src/href.js
@@ -1,12 +1,12 @@
11
/* As an anti-robot defense, <a> elements are initially coded with the
22
** href= set to the honeypot, and <form> elements are initialized with
33
** action= set to the login page. The real values for href= and action=
44
** are held in data-href= and data-action=. The following code moves
55
** data-href= into href= and data-action= into action= for all
6
-** <a> and <form> elements, after delay and maybe also after mouse
7
-** movement is seen.
6
+** <a> and <form> elements, after CSS has been loaded, and after a delay,
7
+** and maybe also after mouse movement is seen.
88
**
99
** Before sourcing this script, create a separate <script> element
1010
** (with type='application/json' to avoid Content Security Policy issues)
1111
** containing:
1212
**
@@ -18,20 +18,23 @@
1818
** until the first mousedown event that occurs after the timer expires.
1919
*/
2020
var antiRobot = 0;
2121
function antiRobotGo(){
2222
if( antiRobot!=3 ) return;
23
- antiRobot = 7;
24
- var anchors = document.getElementsByTagName("a");
25
- for(var i=0; i<anchors.length; i++){
26
- var j = anchors[i];
27
- if(j.hasAttribute("data-href")) j.href=j.getAttribute("data-href");
28
- }
29
- var forms = document.getElementsByTagName("form");
30
- for(var i=0; i<forms.length; i++){
31
- var j = forms[i];
32
- if(j.hasAttribute("data-action")) j.action=j.getAttribute("data-action");
23
+ var z = window.getComputedStyle(document.body).zIndex;
24
+ if( z==='0' || z===0 ){
25
+ antiRobot = 7;
26
+ var anchors = document.getElementsByTagName("a");
27
+ for(var i=0; i<anchors.length; i++){
28
+ var j = anchors[i];
29
+ if(j.hasAttribute("data-href")) j.href=j.getAttribute("data-href");
30
+ }
31
+ var forms = document.getElementsByTagName("form");
32
+ for(var i=0; i<forms.length; i++){
33
+ var j = forms[i];
34
+ if(j.hasAttribute("data-action")) j.action=j.getAttribute("data-action");
35
+ }
3336
}
3437
}
3538
function antiRobotDefense(){
3639
var x = document.getElementById("href-data");
3740
var jx = x.textContent || x.innerText;
@@ -56,8 +59,9 @@
5659
antiRobotGo();
5760
}, g.delay)
5861
}else{
5962
antiRobot |= 1;
6063
}
64
+ window.addEventListener('load',antiRobotGo);
6165
antiRobotGo();
6266
}
6367
antiRobotDefense();
6468
--- src/href.js
+++ src/href.js
@@ -1,12 +1,12 @@
1 /* As an anti-robot defense, <a> elements are initially coded with the
2 ** href= set to the honeypot, and <form> elements are initialized with
3 ** action= set to the login page. The real values for href= and action=
4 ** are held in data-href= and data-action=. The following code moves
5 ** data-href= into href= and data-action= into action= for all
6 ** <a> and <form> elements, after delay and maybe also after mouse
7 ** movement is seen.
8 **
9 ** Before sourcing this script, create a separate <script> element
10 ** (with type='application/json' to avoid Content Security Policy issues)
11 ** containing:
12 **
@@ -18,20 +18,23 @@
18 ** until the first mousedown event that occurs after the timer expires.
19 */
20 var antiRobot = 0;
21 function antiRobotGo(){
22 if( antiRobot!=3 ) return;
23 antiRobot = 7;
24 var anchors = document.getElementsByTagName("a");
25 for(var i=0; i<anchors.length; i++){
26 var j = anchors[i];
27 if(j.hasAttribute("data-href")) j.href=j.getAttribute("data-href");
28 }
29 var forms = document.getElementsByTagName("form");
30 for(var i=0; i<forms.length; i++){
31 var j = forms[i];
32 if(j.hasAttribute("data-action")) j.action=j.getAttribute("data-action");
 
 
 
33 }
34 }
35 function antiRobotDefense(){
36 var x = document.getElementById("href-data");
37 var jx = x.textContent || x.innerText;
@@ -56,8 +59,9 @@
56 antiRobotGo();
57 }, g.delay)
58 }else{
59 antiRobot |= 1;
60 }
 
61 antiRobotGo();
62 }
63 antiRobotDefense();
64
--- src/href.js
+++ src/href.js
@@ -1,12 +1,12 @@
1 /* As an anti-robot defense, <a> elements are initially coded with the
2 ** href= set to the honeypot, and <form> elements are initialized with
3 ** action= set to the login page. The real values for href= and action=
4 ** are held in data-href= and data-action=. The following code moves
5 ** data-href= into href= and data-action= into action= for all
6 ** <a> and <form> elements, after CSS has been loaded, and after a delay,
7 ** and maybe also after mouse movement is seen.
8 **
9 ** Before sourcing this script, create a separate <script> element
10 ** (with type='application/json' to avoid Content Security Policy issues)
11 ** containing:
12 **
@@ -18,20 +18,23 @@
18 ** until the first mousedown event that occurs after the timer expires.
19 */
20 var antiRobot = 0;
21 function antiRobotGo(){
22 if( antiRobot!=3 ) return;
23 var z = window.getComputedStyle(document.body).zIndex;
24 if( z==='0' || z===0 ){
25 antiRobot = 7;
26 var anchors = document.getElementsByTagName("a");
27 for(var i=0; i<anchors.length; i++){
28 var j = anchors[i];
29 if(j.hasAttribute("data-href")) j.href=j.getAttribute("data-href");
30 }
31 var forms = document.getElementsByTagName("form");
32 for(var i=0; i<forms.length; i++){
33 var j = forms[i];
34 if(j.hasAttribute("data-action")) j.action=j.getAttribute("data-action");
35 }
36 }
37 }
38 function antiRobotDefense(){
39 var x = document.getElementById("href-data");
40 var jx = x.textContent || x.innerText;
@@ -56,8 +59,9 @@
59 antiRobotGo();
60 }, g.delay)
61 }else{
62 antiRobot |= 1;
63 }
64 window.addEventListener('load',antiRobotGo);
65 antiRobotGo();
66 }
67 antiRobotDefense();
68
+51 -22
--- src/http.c
+++ src/http.c
@@ -52,30 +52,33 @@
5252
** Construct the "login" card with the client credentials.
5353
**
5454
** login LOGIN NONCE SIGNATURE
5555
**
5656
** The LOGIN is the user id of the client. NONCE is the sha1 checksum
57
-** of all payload that follows the login card. Randomness for the NONCE
58
-** must be provided in the payload (in xfer.c). SIGNATURE is the sha1
59
-** checksum of the nonce followed by the user password.
57
+** of all payload that follows the login card. Randomness for the
58
+** NONCE must be provided in the payload (in xfer.c) (e.g. by
59
+** appending a timestamp or random bytes as a comment line to the
60
+** payload). SIGNATURE is the sha1 checksum of the nonce followed by
61
+** the fossil-hashed version of the user's password.
6062
**
61
-** Write the constructed login card into pLogin. pLogin is initialized
62
-** by this routine.
63
+** Write the constructed login card into pLogin. The result does not
64
+** have an EOL added to it because which type of EOL it needs has to
65
+** be determined later. pLogin is initialized by this routine.
6366
*/
64
-static void http_build_login_card(Blob *pPayload, Blob *pLogin){
67
+static void http_build_login_card(Blob * const pPayload, Blob * const pLogin){
6568
Blob nonce; /* The nonce */
6669
const char *zLogin; /* The user login name */
6770
const char *zPw; /* The user password */
6871
Blob pw; /* The nonce with user password appended */
6972
Blob sig; /* The signature field */
7073
7174
blob_zero(pLogin);
7275
if( g.url.user==0 || fossil_strcmp(g.url.user, "anonymous")==0 ){
73
- return; /* If no login card for users "nobody" and "anonymous" */
76
+ return; /* No login card for users "nobody" and "anonymous" */
7477
}
7578
if( g.url.isSsh ){
76
- return; /* If no login card for SSH: */
79
+ return; /* No login card for SSH: */
7780
}
7881
blob_zero(&nonce);
7982
blob_zero(&pw);
8083
sha1sum_blob(pPayload, &nonce);
8184
blob_copy(&pw, &nonce);
@@ -119,32 +122,34 @@
119122
g.url.passwd = fossil_strdup(zPw);
120123
}
121124
122125
blob_append(&pw, zPw, -1);
123126
sha1sum_blob(&pw, &sig);
124
- blob_appendf(pLogin, "login %F %b %b\n", zLogin, &nonce, &sig);
127
+ blob_appendf(pLogin, "login %F %b %b", zLogin, &nonce, &sig);
125128
blob_reset(&pw);
126129
blob_reset(&sig);
127130
blob_reset(&nonce);
128131
}
129132
130133
/*
131134
** Construct an appropriate HTTP request header. Write the header
132135
** into pHdr. This routine initializes the pHdr blob. pPayload is
133
-** the complete payload (including the login card) already compressed.
136
+** the complete payload (including the login card if pLogin is NULL or
137
+** empty) already compressed.
134138
*/
135139
static void http_build_header(
136140
Blob *pPayload, /* the payload that will be sent */
137141
Blob *pHdr, /* construct the header here */
142
+ Blob *pLogin, /* Login card header value or NULL */
138143
const char *zAltMimetype /* Alternative mimetype */
139144
){
140145
int nPayload = pPayload ? blob_size(pPayload) : 0;
141146
142147
blob_zero(pHdr);
143
- blob_appendf(pHdr, "%s %s%s HTTP/1.0\r\n",
144
- nPayload>0 ? "POST" : "GET", g.url.path,
145
- g.url.path[0]==0 ? "/" : "");
148
+ blob_appendf(pHdr, "%s %s HTTP/1.0\r\n",
149
+ nPayload>0 ? "POST" : "GET",
150
+ (g.url.path && g.url.path[0]) ? g.url.path : "/");
146151
if( g.url.proxyAuth ){
147152
blob_appendf(pHdr, "Proxy-Authorization: %s\r\n", g.url.proxyAuth);
148153
}
149154
if( g.zHttpAuth && g.zHttpAuth[0] ){
150155
const char *zCredentials = g.zHttpAuth;
@@ -153,10 +158,16 @@
153158
fossil_free(zEncoded);
154159
}
155160
blob_appendf(pHdr, "Host: %s\r\n", g.url.hostname);
156161
blob_appendf(pHdr, "User-Agent: %s\r\n", get_user_agent());
157162
if( g.url.isSsh ) blob_appendf(pHdr, "X-Fossil-Transport: SSH\r\n");
163
+ if( g.syncInfo.fLoginCardMode>0
164
+ && nPayload>0 && pLogin && blob_size(pLogin) ){
165
+ /* Add sync login card via a transient cookie. We can only do this
166
+ if we know the remote supports it. */
167
+ blob_appendf(pHdr, "Cookie: x-f-l-c=%T\r\n", blob_str(pLogin));
168
+ }
158169
if( nPayload ){
159170
if( zAltMimetype ){
160171
blob_appendf(pHdr, "Content-Type: %s\r\n", zAltMimetype);
161172
}else if( g.fHttpTrace ){
162173
blob_appendf(pHdr, "Content-Type: application/x-fossil-debug\r\n");
@@ -386,11 +397,11 @@
386397
** * The test-ssh-needs-path command that shows the settings
387398
** that cache whether or not a PATH= is needed for a particular
388399
** HOSTNAME.
389400
*/
390401
void ssh_add_path_argument(Blob *pCmd){
391
- blob_append_escaped_arg(pCmd,
402
+ blob_append_escaped_arg(pCmd,
392403
"PATH=$HOME/bin:/usr/local/bin:/opt/homebrew/bin:$PATH", 1);
393404
}
394405
395406
/*
396407
** Return the complete text of the last HTTP reply as saved in the
@@ -457,28 +468,46 @@
457468
458469
if( transport_open(&g.url) ){
459470
fossil_warning("%s", transport_errmsg(&g.url));
460471
return 1;
461472
}
462
-
463473
/* Construct the login card and prepare the complete payload */
474
+ blob_zero(&login);
464475
if( blob_size(pSend)==0 ){
465476
blob_zero(&payload);
466477
}else{
467
- blob_zero(&login);
468478
if( mHttpFlags & HTTP_USE_LOGIN ) http_build_login_card(pSend, &login);
469
- if( g.fHttpTrace || (mHttpFlags & HTTP_NOCOMPRESS)!=0 ){
470
- payload = login;
471
- blob_append(&payload, blob_buffer(pSend), blob_size(pSend));
479
+ if( g.syncInfo.fLoginCardMode ){
480
+ /* The login card will be sent via an HTTP header and/or URL flag. */
481
+ if( g.fHttpTrace || (mHttpFlags & HTTP_NOCOMPRESS)!=0 ){
482
+ /* Maintenance note: we cannot blob_swap(pSend,&payload) here
483
+ ** because the HTTP 401 and redirect response handling below
484
+ ** needs pSend unmodified. payload won't be modified after
485
+ ** this point, so we can make it a proxy for pSend for
486
+ ** zero heap memory. */
487
+ blob_init(&payload, blob_buffer(pSend), blob_size(pSend));
488
+ }else{
489
+ blob_compress(pSend, &payload);
490
+ }
472491
}else{
473
- blob_compress2(&login, pSend, &payload);
474
- blob_reset(&login);
492
+ /* Prepend the login card (if set) to the payload */
493
+ if( blob_size(&login) ){
494
+ blob_append_char(&login, '\n');
495
+ }
496
+ if( g.fHttpTrace || (mHttpFlags & HTTP_NOCOMPRESS)!=0 ){
497
+ payload = login;
498
+ login = empty_blob/*transfer ownership*/;
499
+ blob_append(&payload, blob_buffer(pSend), blob_size(pSend));
500
+ }else{
501
+ blob_compress2(&login, pSend, &payload);
502
+ blob_reset(&login);
503
+ }
475504
}
476505
}
477506
478507
/* Construct the HTTP request header */
479
- http_build_header(&payload, &hdr, zAltMimetype);
508
+ http_build_header(&payload, &hdr, &login, zAltMimetype);
480509
481510
/* When tracing, write the transmitted HTTP message both to standard
482511
** output and into a file. The file can then be used to drive the
483512
** server-side like this:
484513
**
485514
--- src/http.c
+++ src/http.c
@@ -52,30 +52,33 @@
52 ** Construct the "login" card with the client credentials.
53 **
54 ** login LOGIN NONCE SIGNATURE
55 **
56 ** The LOGIN is the user id of the client. NONCE is the sha1 checksum
57 ** of all payload that follows the login card. Randomness for the NONCE
58 ** must be provided in the payload (in xfer.c). SIGNATURE is the sha1
59 ** checksum of the nonce followed by the user password.
 
 
60 **
61 ** Write the constructed login card into pLogin. pLogin is initialized
62 ** by this routine.
 
63 */
64 static void http_build_login_card(Blob *pPayload, Blob *pLogin){
65 Blob nonce; /* The nonce */
66 const char *zLogin; /* The user login name */
67 const char *zPw; /* The user password */
68 Blob pw; /* The nonce with user password appended */
69 Blob sig; /* The signature field */
70
71 blob_zero(pLogin);
72 if( g.url.user==0 || fossil_strcmp(g.url.user, "anonymous")==0 ){
73 return; /* If no login card for users "nobody" and "anonymous" */
74 }
75 if( g.url.isSsh ){
76 return; /* If no login card for SSH: */
77 }
78 blob_zero(&nonce);
79 blob_zero(&pw);
80 sha1sum_blob(pPayload, &nonce);
81 blob_copy(&pw, &nonce);
@@ -119,32 +122,34 @@
119 g.url.passwd = fossil_strdup(zPw);
120 }
121
122 blob_append(&pw, zPw, -1);
123 sha1sum_blob(&pw, &sig);
124 blob_appendf(pLogin, "login %F %b %b\n", zLogin, &nonce, &sig);
125 blob_reset(&pw);
126 blob_reset(&sig);
127 blob_reset(&nonce);
128 }
129
130 /*
131 ** Construct an appropriate HTTP request header. Write the header
132 ** into pHdr. This routine initializes the pHdr blob. pPayload is
133 ** the complete payload (including the login card) already compressed.
 
134 */
135 static void http_build_header(
136 Blob *pPayload, /* the payload that will be sent */
137 Blob *pHdr, /* construct the header here */
 
138 const char *zAltMimetype /* Alternative mimetype */
139 ){
140 int nPayload = pPayload ? blob_size(pPayload) : 0;
141
142 blob_zero(pHdr);
143 blob_appendf(pHdr, "%s %s%s HTTP/1.0\r\n",
144 nPayload>0 ? "POST" : "GET", g.url.path,
145 g.url.path[0]==0 ? "/" : "");
146 if( g.url.proxyAuth ){
147 blob_appendf(pHdr, "Proxy-Authorization: %s\r\n", g.url.proxyAuth);
148 }
149 if( g.zHttpAuth && g.zHttpAuth[0] ){
150 const char *zCredentials = g.zHttpAuth;
@@ -153,10 +158,16 @@
153 fossil_free(zEncoded);
154 }
155 blob_appendf(pHdr, "Host: %s\r\n", g.url.hostname);
156 blob_appendf(pHdr, "User-Agent: %s\r\n", get_user_agent());
157 if( g.url.isSsh ) blob_appendf(pHdr, "X-Fossil-Transport: SSH\r\n");
 
 
 
 
 
 
158 if( nPayload ){
159 if( zAltMimetype ){
160 blob_appendf(pHdr, "Content-Type: %s\r\n", zAltMimetype);
161 }else if( g.fHttpTrace ){
162 blob_appendf(pHdr, "Content-Type: application/x-fossil-debug\r\n");
@@ -386,11 +397,11 @@
386 ** * The test-ssh-needs-path command that shows the settings
387 ** that cache whether or not a PATH= is needed for a particular
388 ** HOSTNAME.
389 */
390 void ssh_add_path_argument(Blob *pCmd){
391 blob_append_escaped_arg(pCmd,
392 "PATH=$HOME/bin:/usr/local/bin:/opt/homebrew/bin:$PATH", 1);
393 }
394
395 /*
396 ** Return the complete text of the last HTTP reply as saved in the
@@ -457,28 +468,46 @@
457
458 if( transport_open(&g.url) ){
459 fossil_warning("%s", transport_errmsg(&g.url));
460 return 1;
461 }
462
463 /* Construct the login card and prepare the complete payload */
 
464 if( blob_size(pSend)==0 ){
465 blob_zero(&payload);
466 }else{
467 blob_zero(&login);
468 if( mHttpFlags & HTTP_USE_LOGIN ) http_build_login_card(pSend, &login);
469 if( g.fHttpTrace || (mHttpFlags & HTTP_NOCOMPRESS)!=0 ){
470 payload = login;
471 blob_append(&payload, blob_buffer(pSend), blob_size(pSend));
 
 
 
 
 
 
 
 
 
472 }else{
473 blob_compress2(&login, pSend, &payload);
474 blob_reset(&login);
 
 
 
 
 
 
 
 
 
 
475 }
476 }
477
478 /* Construct the HTTP request header */
479 http_build_header(&payload, &hdr, zAltMimetype);
480
481 /* When tracing, write the transmitted HTTP message both to standard
482 ** output and into a file. The file can then be used to drive the
483 ** server-side like this:
484 **
485
--- src/http.c
+++ src/http.c
@@ -52,30 +52,33 @@
52 ** Construct the "login" card with the client credentials.
53 **
54 ** login LOGIN NONCE SIGNATURE
55 **
56 ** The LOGIN is the user id of the client. NONCE is the sha1 checksum
57 ** of all payload that follows the login card. Randomness for the
58 ** NONCE must be provided in the payload (in xfer.c) (e.g. by
59 ** appending a timestamp or random bytes as a comment line to the
60 ** payload). SIGNATURE is the sha1 checksum of the nonce followed by
61 ** the fossil-hashed version of the user's password.
62 **
63 ** Write the constructed login card into pLogin. The result does not
64 ** have an EOL added to it because which type of EOL it needs has to
65 ** be determined later. pLogin is initialized by this routine.
66 */
67 static void http_build_login_card(Blob * const pPayload, Blob * const pLogin){
68 Blob nonce; /* The nonce */
69 const char *zLogin; /* The user login name */
70 const char *zPw; /* The user password */
71 Blob pw; /* The nonce with user password appended */
72 Blob sig; /* The signature field */
73
74 blob_zero(pLogin);
75 if( g.url.user==0 || fossil_strcmp(g.url.user, "anonymous")==0 ){
76 return; /* No login card for users "nobody" and "anonymous" */
77 }
78 if( g.url.isSsh ){
79 return; /* No login card for SSH: */
80 }
81 blob_zero(&nonce);
82 blob_zero(&pw);
83 sha1sum_blob(pPayload, &nonce);
84 blob_copy(&pw, &nonce);
@@ -119,32 +122,34 @@
122 g.url.passwd = fossil_strdup(zPw);
123 }
124
125 blob_append(&pw, zPw, -1);
126 sha1sum_blob(&pw, &sig);
127 blob_appendf(pLogin, "login %F %b %b", zLogin, &nonce, &sig);
128 blob_reset(&pw);
129 blob_reset(&sig);
130 blob_reset(&nonce);
131 }
132
133 /*
134 ** Construct an appropriate HTTP request header. Write the header
135 ** into pHdr. This routine initializes the pHdr blob. pPayload is
136 ** the complete payload (including the login card if pLogin is NULL or
137 ** empty) already compressed.
138 */
139 static void http_build_header(
140 Blob *pPayload, /* the payload that will be sent */
141 Blob *pHdr, /* construct the header here */
142 Blob *pLogin, /* Login card header value or NULL */
143 const char *zAltMimetype /* Alternative mimetype */
144 ){
145 int nPayload = pPayload ? blob_size(pPayload) : 0;
146
147 blob_zero(pHdr);
148 blob_appendf(pHdr, "%s %s HTTP/1.0\r\n",
149 nPayload>0 ? "POST" : "GET",
150 (g.url.path && g.url.path[0]) ? g.url.path : "/");
151 if( g.url.proxyAuth ){
152 blob_appendf(pHdr, "Proxy-Authorization: %s\r\n", g.url.proxyAuth);
153 }
154 if( g.zHttpAuth && g.zHttpAuth[0] ){
155 const char *zCredentials = g.zHttpAuth;
@@ -153,10 +158,16 @@
158 fossil_free(zEncoded);
159 }
160 blob_appendf(pHdr, "Host: %s\r\n", g.url.hostname);
161 blob_appendf(pHdr, "User-Agent: %s\r\n", get_user_agent());
162 if( g.url.isSsh ) blob_appendf(pHdr, "X-Fossil-Transport: SSH\r\n");
163 if( g.syncInfo.fLoginCardMode>0
164 && nPayload>0 && pLogin && blob_size(pLogin) ){
165 /* Add sync login card via a transient cookie. We can only do this
166 if we know the remote supports it. */
167 blob_appendf(pHdr, "Cookie: x-f-l-c=%T\r\n", blob_str(pLogin));
168 }
169 if( nPayload ){
170 if( zAltMimetype ){
171 blob_appendf(pHdr, "Content-Type: %s\r\n", zAltMimetype);
172 }else if( g.fHttpTrace ){
173 blob_appendf(pHdr, "Content-Type: application/x-fossil-debug\r\n");
@@ -386,11 +397,11 @@
397 ** * The test-ssh-needs-path command that shows the settings
398 ** that cache whether or not a PATH= is needed for a particular
399 ** HOSTNAME.
400 */
401 void ssh_add_path_argument(Blob *pCmd){
402 blob_append_escaped_arg(pCmd,
403 "PATH=$HOME/bin:/usr/local/bin:/opt/homebrew/bin:$PATH", 1);
404 }
405
406 /*
407 ** Return the complete text of the last HTTP reply as saved in the
@@ -457,28 +468,46 @@
468
469 if( transport_open(&g.url) ){
470 fossil_warning("%s", transport_errmsg(&g.url));
471 return 1;
472 }
 
473 /* Construct the login card and prepare the complete payload */
474 blob_zero(&login);
475 if( blob_size(pSend)==0 ){
476 blob_zero(&payload);
477 }else{
 
478 if( mHttpFlags & HTTP_USE_LOGIN ) http_build_login_card(pSend, &login);
479 if( g.syncInfo.fLoginCardMode ){
480 /* The login card will be sent via an HTTP header and/or URL flag. */
481 if( g.fHttpTrace || (mHttpFlags & HTTP_NOCOMPRESS)!=0 ){
482 /* Maintenance note: we cannot blob_swap(pSend,&payload) here
483 ** because the HTTP 401 and redirect response handling below
484 ** needs pSend unmodified. payload won't be modified after
485 ** this point, so we can make it a proxy for pSend for
486 ** zero heap memory. */
487 blob_init(&payload, blob_buffer(pSend), blob_size(pSend));
488 }else{
489 blob_compress(pSend, &payload);
490 }
491 }else{
492 /* Prepend the login card (if set) to the payload */
493 if( blob_size(&login) ){
494 blob_append_char(&login, '\n');
495 }
496 if( g.fHttpTrace || (mHttpFlags & HTTP_NOCOMPRESS)!=0 ){
497 payload = login;
498 login = empty_blob/*transfer ownership*/;
499 blob_append(&payload, blob_buffer(pSend), blob_size(pSend));
500 }else{
501 blob_compress2(&login, pSend, &payload);
502 blob_reset(&login);
503 }
504 }
505 }
506
507 /* Construct the HTTP request header */
508 http_build_header(&payload, &hdr, &login, zAltMimetype);
509
510 /* When tracing, write the transmitted HTTP message both to standard
511 ** output and into a file. The file can then be used to drive the
512 ** server-side like this:
513 **
514
--- src/http_socket.c
+++ src/http_socket.c
@@ -175,11 +175,11 @@
175175
0, 0, NI_NUMERICHOST);
176176
if( rc ){
177177
socket_set_errmsg("getnameinfo() failed: %s", gai_strerror(rc));
178178
goto end_socket_open;
179179
}
180
- g.zIpAddr = mprintf("%s", zRemote);
180
+ g.zIpAddr = fossil_strdup(zRemote);
181181
break;
182182
}
183183
if( p==0 ){
184184
socket_set_errmsg("cannot connect to host %s:%d", pUrlData->name,
185185
pUrlData->port);
186186
--- src/http_socket.c
+++ src/http_socket.c
@@ -175,11 +175,11 @@
175 0, 0, NI_NUMERICHOST);
176 if( rc ){
177 socket_set_errmsg("getnameinfo() failed: %s", gai_strerror(rc));
178 goto end_socket_open;
179 }
180 g.zIpAddr = mprintf("%s", zRemote);
181 break;
182 }
183 if( p==0 ){
184 socket_set_errmsg("cannot connect to host %s:%d", pUrlData->name,
185 pUrlData->port);
186
--- src/http_socket.c
+++ src/http_socket.c
@@ -175,11 +175,11 @@
175 0, 0, NI_NUMERICHOST);
176 if( rc ){
177 socket_set_errmsg("getnameinfo() failed: %s", gai_strerror(rc));
178 goto end_socket_open;
179 }
180 g.zIpAddr = fossil_strdup(zRemote);
181 break;
182 }
183 if( p==0 ){
184 socket_set_errmsg("cannot connect to host %s:%d", pUrlData->name,
185 pUrlData->port);
186
+1 -1
--- src/http_ssl.c
+++ src/http_ssl.c
@@ -638,11 +638,11 @@
638638
** check here for the correct LIBRESSL_VERSION_NUMBER too. For now: disable
639639
*/
640640
#if defined(OPENSSL_VERSION_NUMBER) && OPENSSL_VERSION_NUMBER >= 0x10100000L \
641641
&& !defined(LIBRESSL_VERSION_NUMBER)
642642
char *ip = BIO_ADDR_hostname_string(BIO_get_conn_address(iBio),1);
643
- g.zIpAddr = mprintf("%s", ip);
643
+ g.zIpAddr = fossil_strdup(ip);
644644
OPENSSL_free(ip);
645645
#else
646646
/* IPv4 only code */
647647
const unsigned char *ip;
648648
ip = (const unsigned char*)BIO_ptr_ctrl(iBio,BIO_C_GET_CONNECT,2);
649649
--- src/http_ssl.c
+++ src/http_ssl.c
@@ -638,11 +638,11 @@
638 ** check here for the correct LIBRESSL_VERSION_NUMBER too. For now: disable
639 */
640 #if defined(OPENSSL_VERSION_NUMBER) && OPENSSL_VERSION_NUMBER >= 0x10100000L \
641 && !defined(LIBRESSL_VERSION_NUMBER)
642 char *ip = BIO_ADDR_hostname_string(BIO_get_conn_address(iBio),1);
643 g.zIpAddr = mprintf("%s", ip);
644 OPENSSL_free(ip);
645 #else
646 /* IPv4 only code */
647 const unsigned char *ip;
648 ip = (const unsigned char*)BIO_ptr_ctrl(iBio,BIO_C_GET_CONNECT,2);
649
--- src/http_ssl.c
+++ src/http_ssl.c
@@ -638,11 +638,11 @@
638 ** check here for the correct LIBRESSL_VERSION_NUMBER too. For now: disable
639 */
640 #if defined(OPENSSL_VERSION_NUMBER) && OPENSSL_VERSION_NUMBER >= 0x10100000L \
641 && !defined(LIBRESSL_VERSION_NUMBER)
642 char *ip = BIO_ADDR_hostname_string(BIO_get_conn_address(iBio),1);
643 g.zIpAddr = fossil_strdup(ip);
644 OPENSSL_free(ip);
645 #else
646 /* IPv4 only code */
647 const unsigned char *ip;
648 ip = (const unsigned char*)BIO_ptr_ctrl(iBio,BIO_C_GET_CONNECT,2);
649
--- src/http_transport.c
+++ src/http_transport.c
@@ -121,11 +121,11 @@
121121
*/
122122
Blob zCmd; /* The SSH command */
123123
char *zHost; /* The host name to contact */
124124
125125
fossil_free(g.zIpAddr);
126
- g.zIpAddr = mprintf("%s", pUrlData->name);
126
+ g.zIpAddr = fossil_strdup(pUrlData->name);
127127
transport_ssh_command(&zCmd);
128128
if( pUrlData->port!=pUrlData->dfltPort && pUrlData->port ){
129129
blob_appendf(&zCmd, " -p %d", pUrlData->port);
130130
}
131131
blob_appendf(&zCmd, " -T --"); /* End of switches */
@@ -141,11 +141,11 @@
141141
){
142142
fossil_fatal("the ssh:// URL is asking to run an unsafe command [%s] on "
143143
"the server.", pUrlData->fossil);
144144
}
145145
if( (pUrlData->flags & URL_SSH_EXE)==0
146
- && (pUrlData->flags & URL_SSH_PATH)!=0
146
+ && (pUrlData->flags & URL_SSH_PATH)!=0
147147
){
148148
ssh_add_path_argument(&zCmd);
149149
}
150150
blob_append_escaped_arg(&zCmd, pUrlData->fossil, 1);
151151
blob_append(&zCmd, " test-http", 10);
@@ -245,11 +245,11 @@
245245
}
246246
247247
/*
248248
** Send content over the wire.
249249
*/
250
-void transport_send(UrlData *pUrlData, Blob *toSend){
250
+void transport_send(UrlData const *pUrlData, const Blob *toSend){
251251
char *z = blob_buffer(toSend);
252252
int n = blob_size(toSend);
253253
transport.nSent += n;
254254
if( pUrlData->isSsh ){
255255
fwrite(z, 1, n, sshOut);
256256
--- src/http_transport.c
+++ src/http_transport.c
@@ -121,11 +121,11 @@
121 */
122 Blob zCmd; /* The SSH command */
123 char *zHost; /* The host name to contact */
124
125 fossil_free(g.zIpAddr);
126 g.zIpAddr = mprintf("%s", pUrlData->name);
127 transport_ssh_command(&zCmd);
128 if( pUrlData->port!=pUrlData->dfltPort && pUrlData->port ){
129 blob_appendf(&zCmd, " -p %d", pUrlData->port);
130 }
131 blob_appendf(&zCmd, " -T --"); /* End of switches */
@@ -141,11 +141,11 @@
141 ){
142 fossil_fatal("the ssh:// URL is asking to run an unsafe command [%s] on "
143 "the server.", pUrlData->fossil);
144 }
145 if( (pUrlData->flags & URL_SSH_EXE)==0
146 && (pUrlData->flags & URL_SSH_PATH)!=0
147 ){
148 ssh_add_path_argument(&zCmd);
149 }
150 blob_append_escaped_arg(&zCmd, pUrlData->fossil, 1);
151 blob_append(&zCmd, " test-http", 10);
@@ -245,11 +245,11 @@
245 }
246
247 /*
248 ** Send content over the wire.
249 */
250 void transport_send(UrlData *pUrlData, Blob *toSend){
251 char *z = blob_buffer(toSend);
252 int n = blob_size(toSend);
253 transport.nSent += n;
254 if( pUrlData->isSsh ){
255 fwrite(z, 1, n, sshOut);
256
--- src/http_transport.c
+++ src/http_transport.c
@@ -121,11 +121,11 @@
121 */
122 Blob zCmd; /* The SSH command */
123 char *zHost; /* The host name to contact */
124
125 fossil_free(g.zIpAddr);
126 g.zIpAddr = fossil_strdup(pUrlData->name);
127 transport_ssh_command(&zCmd);
128 if( pUrlData->port!=pUrlData->dfltPort && pUrlData->port ){
129 blob_appendf(&zCmd, " -p %d", pUrlData->port);
130 }
131 blob_appendf(&zCmd, " -T --"); /* End of switches */
@@ -141,11 +141,11 @@
141 ){
142 fossil_fatal("the ssh:// URL is asking to run an unsafe command [%s] on "
143 "the server.", pUrlData->fossil);
144 }
145 if( (pUrlData->flags & URL_SSH_EXE)==0
146 && (pUrlData->flags & URL_SSH_PATH)!=0
147 ){
148 ssh_add_path_argument(&zCmd);
149 }
150 blob_append_escaped_arg(&zCmd, pUrlData->fossil, 1);
151 blob_append(&zCmd, " test-http", 10);
@@ -245,11 +245,11 @@
245 }
246
247 /*
248 ** Send content over the wire.
249 */
250 void transport_send(UrlData const *pUrlData, const Blob *toSend){
251 char *z = blob_buffer(toSend);
252 int n = blob_size(toSend);
253 transport.nSent += n;
254 if( pUrlData->isSsh ){
255 fwrite(z, 1, n, sshOut);
256
+3 -3
--- src/import.c
+++ src/import.c
@@ -895,11 +895,11 @@
895895
char *sep;
896896
if( zLine[0]=='\n' ) break;
897897
rec->nHeaders += 1;
898898
rec->aHeaders = fossil_realloc(rec->aHeaders,
899899
sizeof(rec->aHeaders[0])*rec->nHeaders);
900
- rec->aHeaders[rec->nHeaders-1].zKey = mprintf("%s", zLine);
900
+ rec->aHeaders[rec->nHeaders-1].zKey = fossil_strdup(zLine);
901901
sep = strchr(rec->aHeaders[rec->nHeaders-1].zKey, ':');
902902
if( !sep ){
903903
trim_newline(zLine);
904904
fossil_fatal("bad header line: [%s]", zLine);
905905
}
@@ -1426,12 +1426,12 @@
14261426
fossil_free(gsvn.zDate);
14271427
bag_clear(&gsvn.newBranches);
14281428
}
14291429
/* start new revision */
14301430
gsvn.rev = atoi(zTemp);
1431
- gsvn.zUser = mprintf("%s", svn_find_prop(rec, "svn:author"));
1432
- gsvn.zComment = mprintf("%s", svn_find_prop(rec, "svn:log"));
1431
+ gsvn.zUser = fossil_strdup(svn_find_prop(rec, "svn:author"));
1432
+ gsvn.zComment = fossil_strdup(svn_find_prop(rec, "svn:log"));
14331433
zDate = svn_find_prop(rec, "svn:date");
14341434
if( zDate ){
14351435
gsvn.zDate = date_in_standard_format(zDate);
14361436
}else{
14371437
gsvn.zDate = date_in_standard_format("now");
14381438
--- src/import.c
+++ src/import.c
@@ -895,11 +895,11 @@
895 char *sep;
896 if( zLine[0]=='\n' ) break;
897 rec->nHeaders += 1;
898 rec->aHeaders = fossil_realloc(rec->aHeaders,
899 sizeof(rec->aHeaders[0])*rec->nHeaders);
900 rec->aHeaders[rec->nHeaders-1].zKey = mprintf("%s", zLine);
901 sep = strchr(rec->aHeaders[rec->nHeaders-1].zKey, ':');
902 if( !sep ){
903 trim_newline(zLine);
904 fossil_fatal("bad header line: [%s]", zLine);
905 }
@@ -1426,12 +1426,12 @@
1426 fossil_free(gsvn.zDate);
1427 bag_clear(&gsvn.newBranches);
1428 }
1429 /* start new revision */
1430 gsvn.rev = atoi(zTemp);
1431 gsvn.zUser = mprintf("%s", svn_find_prop(rec, "svn:author"));
1432 gsvn.zComment = mprintf("%s", svn_find_prop(rec, "svn:log"));
1433 zDate = svn_find_prop(rec, "svn:date");
1434 if( zDate ){
1435 gsvn.zDate = date_in_standard_format(zDate);
1436 }else{
1437 gsvn.zDate = date_in_standard_format("now");
1438
--- src/import.c
+++ src/import.c
@@ -895,11 +895,11 @@
895 char *sep;
896 if( zLine[0]=='\n' ) break;
897 rec->nHeaders += 1;
898 rec->aHeaders = fossil_realloc(rec->aHeaders,
899 sizeof(rec->aHeaders[0])*rec->nHeaders);
900 rec->aHeaders[rec->nHeaders-1].zKey = fossil_strdup(zLine);
901 sep = strchr(rec->aHeaders[rec->nHeaders-1].zKey, ':');
902 if( !sep ){
903 trim_newline(zLine);
904 fossil_fatal("bad header line: [%s]", zLine);
905 }
@@ -1426,12 +1426,12 @@
1426 fossil_free(gsvn.zDate);
1427 bag_clear(&gsvn.newBranches);
1428 }
1429 /* start new revision */
1430 gsvn.rev = atoi(zTemp);
1431 gsvn.zUser = fossil_strdup(svn_find_prop(rec, "svn:author"));
1432 gsvn.zComment = fossil_strdup(svn_find_prop(rec, "svn:log"));
1433 zDate = svn_find_prop(rec, "svn:date");
1434 if( zDate ){
1435 gsvn.zDate = date_in_standard_format(zDate);
1436 }else{
1437 gsvn.zDate = date_in_standard_format("now");
1438
+33 -9
--- src/info.c
+++ src/info.c
@@ -1167,15 +1167,16 @@
11671167
}
11681168
render_backlink_graph(zUuid,
11691169
"<div class=\"section accordion\">References</div>\n");
11701170
@ <div class="section accordion">Context</div><div class="accordion_panel">
11711171
render_checkin_context(rid, 0, 0, 0);
1172
- @ </div><div class="section accordion">Changes</div>
1172
+ @ </div><div class="section accordion" id="changes_section">Changes</div>
11731173
@ <div class="accordion_panel">
11741174
@ <div class="sectionmenu info-changes-menu">
11751175
/* ^^^ .info-changes-menu is used by diff scroll sync */
11761176
pCfg = construct_diff_flags(diffType, &DCfg);
1177
+ DCfg.diffFlags |= DIFF_NUMSTAT; /* Show stats in the 'Changes' section */
11771178
DCfg.pRe = pRe;
11781179
zW = (DCfg.diffFlags&DIFF_IGNORE_ALLWS)?"&w":"";
11791180
if( diffType!=1 ){
11801181
@ %z(chref("button","%R/%s/%T?diff=1%s",zPage,zName,zW))\
11811182
@ Unified&nbsp;Diff</a>
@@ -1227,10 +1228,18 @@
12271228
append_file_change_line(zUuid, zName, zOld, zNew, zOldName,
12281229
pCfg,mperm);
12291230
}
12301231
db_finalize(&q3);
12311232
@ </div>
1233
+ if( diffType!=0 ){
1234
+ @ <script nonce='%h(style_nonce())'>;/* info.c:%d(__LINE__) */
1235
+ @ document.getElementById('changes_section').textContent = 'Changes ' +
1236
+ @ '(%d(g.diffCnt[0]) file' + (%d(g.diffCnt[0])===1 ? '' : 's') + ': ' +
1237
+ @ '+%d(g.diffCnt[1]) ' +
1238
+ @ '−%d(g.diffCnt[2]))'
1239
+ @ </script>
1240
+ }
12321241
append_diff_javascript(diffType);
12331242
style_finish_page();
12341243
}
12351244
12361245
/*
@@ -1414,10 +1423,11 @@
14141423
Blob qpGlob; /* glob= query parameter for generated links */
14151424
int bInvert = PB("inv");
14161425
14171426
login_check_credentials();
14181427
if( !g.perm.Read ){ login_needed(g.anon.Read); return; }
1428
+ if( robot_restrict("diff") ) return;
14191429
login_anonymous_available();
14201430
fossil_nice_default();
14211431
blob_init(&qp, 0, 0);
14221432
blob_init(&qpGlob, 0, 0);
14231433
diffType = preferred_diff_type();
@@ -1918,18 +1928,31 @@
19181928
** * The "preferred-diff-type" setting
19191929
** * 1 for mobile and 2 for desktop, based on the UserAgent
19201930
*/
19211931
int preferred_diff_type(void){
19221932
int dflt;
1933
+ int res;
1934
+ int isBot;
19231935
static char zDflt[2]
19241936
/*static b/c cookie_link_parameter() does not copy it!*/;
1925
- dflt = db_get_int("preferred-diff-type",-99);
1926
- if( dflt<=0 ) dflt = user_agent_is_likely_mobile() ? 1 : 2;
1937
+ if( client_might_be_a_robot() ){
1938
+ dflt = 0;
1939
+ isBot = 1;
1940
+ }else{
1941
+ dflt = db_get_int("preferred-diff-type",-99);
1942
+ if( dflt<=0 ) dflt = user_agent_is_likely_mobile() ? 1 : 2;
1943
+ isBot = 0;
1944
+ }
19271945
zDflt[0] = dflt + '0';
19281946
zDflt[1] = 0;
19291947
cookie_link_parameter("diff","diff", zDflt);
1930
- return atoi(PD_NoBot("diff",zDflt));
1948
+ res = atoi(PD_NoBot("diff",zDflt));
1949
+ if( isBot && res>0 && robot_restrict("diff") ){
1950
+ cgi_reply();
1951
+ fossil_exit(0);
1952
+ }
1953
+ return res;
19311954
}
19321955
19331956
19341957
/*
19351958
** WEBPAGE: fdiff
@@ -1967,10 +1990,11 @@
19671990
int verbose = PB("verbose");
19681991
DiffConfig DCfg;
19691992
19701993
login_check_credentials();
19711994
if( !g.perm.Read ){ login_needed(g.anon.Read); return; }
1995
+ if( robot_restrict("diff") ) return;
19721996
diff_config_init(&DCfg, 0);
19731997
diffType = preferred_diff_type();
19741998
if( P("from") && P("to") ){
19751999
v1 = artifact_from_ci_and_filename("from");
19762000
v2 = artifact_from_ci_and_filename("to");
@@ -2407,15 +2431,15 @@
24072431
object_description(rid, objdescFlags, 0, &downloadName);
24082432
style_submenu_element("Download", "%R/raw/%s?at=%T",
24092433
zUuid, file_tail(blob_str(&downloadName)));
24102434
@ <hr>
24112435
content_get(rid, &content);
2412
- if( !g.isHuman ){
2436
+ if( blob_size(&content)>100000 ){
24132437
/* Prevent robots from running hexdump on megabyte-sized source files
24142438
** and there by eating up lots of CPU time and bandwidth. There is
24152439
** no good reason for a robot to need a hexdump. */
2416
- @ <p>A hex dump of this file is not available.
2440
+ @ <p>A hex dump of this file is not available because it is too large.
24172441
@ Please download the raw binary file and generate a hex dump yourself.</p>
24182442
}else{
24192443
@ <blockquote><pre>
24202444
hexdump(&content);
24212445
@ </pre></blockquote>
@@ -2886,11 +2910,11 @@
28862910
style_set_current_page("doc/%t/%T", zCI, zName);
28872911
}else if( zCIUuid && zCIUuid[0] ){
28882912
zHeader = mprintf("%s at [%S]", file_tail(zName), zCIUuid);
28892913
style_set_current_page("doc/%S/%T", zCIUuid, zName);
28902914
}else{
2891
- zHeader = mprintf("%s", file_tail(zName));
2915
+ zHeader = fossil_strdup(file_tail(zName));
28922916
style_set_current_page("doc/tip/%T", zName);
28932917
}
28942918
}else if( descOnly ){
28952919
zHeader = mprintf("Artifact Description [%S]", zUuid);
28962920
}else{
@@ -4240,11 +4264,11 @@
42404264
descr->isDirty = -1;
42414265
return (rid-1);
42424266
}
42434267
42444268
zUuid = rid_to_uuid(rid);
4245
- descr->zCommitHash = mprintf("%s", zUuid);
4269
+ descr->zCommitHash = fossil_strdup(zUuid);
42464270
descr->isDirty = unsaved_changes(0);
42474271
42484272
db_multi_exec(
42494273
"DROP TABLE IF EXISTS temp.singletonTag;"
42504274
"CREATE TEMP TABLE singletonTag("
@@ -4289,11 +4313,11 @@
42894313
rid, rid
42904314
);
42914315
42924316
if( db_step(&q)==SQLITE_ROW ){
42934317
const char *lastTag = db_column_text(&q, 0);
4294
- descr->zRelTagname = mprintf("%s", lastTag);
4318
+ descr->zRelTagname = fossil_strdup(lastTag);
42954319
descr->nCommitsSince = db_column_int(&q, 1);
42964320
nRet = 0;
42974321
}else{
42984322
/* no ancestor commit with a fitting singleton tag found */
42994323
descr->zRelTagname = mprintf("");
43004324
--- src/info.c
+++ src/info.c
@@ -1167,15 +1167,16 @@
1167 }
1168 render_backlink_graph(zUuid,
1169 "<div class=\"section accordion\">References</div>\n");
1170 @ <div class="section accordion">Context</div><div class="accordion_panel">
1171 render_checkin_context(rid, 0, 0, 0);
1172 @ </div><div class="section accordion">Changes</div>
1173 @ <div class="accordion_panel">
1174 @ <div class="sectionmenu info-changes-menu">
1175 /* ^^^ .info-changes-menu is used by diff scroll sync */
1176 pCfg = construct_diff_flags(diffType, &DCfg);
 
1177 DCfg.pRe = pRe;
1178 zW = (DCfg.diffFlags&DIFF_IGNORE_ALLWS)?"&w":"";
1179 if( diffType!=1 ){
1180 @ %z(chref("button","%R/%s/%T?diff=1%s",zPage,zName,zW))\
1181 @ Unified&nbsp;Diff</a>
@@ -1227,10 +1228,18 @@
1227 append_file_change_line(zUuid, zName, zOld, zNew, zOldName,
1228 pCfg,mperm);
1229 }
1230 db_finalize(&q3);
1231 @ </div>
 
 
 
 
 
 
 
 
1232 append_diff_javascript(diffType);
1233 style_finish_page();
1234 }
1235
1236 /*
@@ -1414,10 +1423,11 @@
1414 Blob qpGlob; /* glob= query parameter for generated links */
1415 int bInvert = PB("inv");
1416
1417 login_check_credentials();
1418 if( !g.perm.Read ){ login_needed(g.anon.Read); return; }
 
1419 login_anonymous_available();
1420 fossil_nice_default();
1421 blob_init(&qp, 0, 0);
1422 blob_init(&qpGlob, 0, 0);
1423 diffType = preferred_diff_type();
@@ -1918,18 +1928,31 @@
1918 ** * The "preferred-diff-type" setting
1919 ** * 1 for mobile and 2 for desktop, based on the UserAgent
1920 */
1921 int preferred_diff_type(void){
1922 int dflt;
 
 
1923 static char zDflt[2]
1924 /*static b/c cookie_link_parameter() does not copy it!*/;
1925 dflt = db_get_int("preferred-diff-type",-99);
1926 if( dflt<=0 ) dflt = user_agent_is_likely_mobile() ? 1 : 2;
 
 
 
 
 
 
1927 zDflt[0] = dflt + '0';
1928 zDflt[1] = 0;
1929 cookie_link_parameter("diff","diff", zDflt);
1930 return atoi(PD_NoBot("diff",zDflt));
 
 
 
 
 
1931 }
1932
1933
1934 /*
1935 ** WEBPAGE: fdiff
@@ -1967,10 +1990,11 @@
1967 int verbose = PB("verbose");
1968 DiffConfig DCfg;
1969
1970 login_check_credentials();
1971 if( !g.perm.Read ){ login_needed(g.anon.Read); return; }
 
1972 diff_config_init(&DCfg, 0);
1973 diffType = preferred_diff_type();
1974 if( P("from") && P("to") ){
1975 v1 = artifact_from_ci_and_filename("from");
1976 v2 = artifact_from_ci_and_filename("to");
@@ -2407,15 +2431,15 @@
2407 object_description(rid, objdescFlags, 0, &downloadName);
2408 style_submenu_element("Download", "%R/raw/%s?at=%T",
2409 zUuid, file_tail(blob_str(&downloadName)));
2410 @ <hr>
2411 content_get(rid, &content);
2412 if( !g.isHuman ){
2413 /* Prevent robots from running hexdump on megabyte-sized source files
2414 ** and there by eating up lots of CPU time and bandwidth. There is
2415 ** no good reason for a robot to need a hexdump. */
2416 @ <p>A hex dump of this file is not available.
2417 @ Please download the raw binary file and generate a hex dump yourself.</p>
2418 }else{
2419 @ <blockquote><pre>
2420 hexdump(&content);
2421 @ </pre></blockquote>
@@ -2886,11 +2910,11 @@
2886 style_set_current_page("doc/%t/%T", zCI, zName);
2887 }else if( zCIUuid && zCIUuid[0] ){
2888 zHeader = mprintf("%s at [%S]", file_tail(zName), zCIUuid);
2889 style_set_current_page("doc/%S/%T", zCIUuid, zName);
2890 }else{
2891 zHeader = mprintf("%s", file_tail(zName));
2892 style_set_current_page("doc/tip/%T", zName);
2893 }
2894 }else if( descOnly ){
2895 zHeader = mprintf("Artifact Description [%S]", zUuid);
2896 }else{
@@ -4240,11 +4264,11 @@
4240 descr->isDirty = -1;
4241 return (rid-1);
4242 }
4243
4244 zUuid = rid_to_uuid(rid);
4245 descr->zCommitHash = mprintf("%s", zUuid);
4246 descr->isDirty = unsaved_changes(0);
4247
4248 db_multi_exec(
4249 "DROP TABLE IF EXISTS temp.singletonTag;"
4250 "CREATE TEMP TABLE singletonTag("
@@ -4289,11 +4313,11 @@
4289 rid, rid
4290 );
4291
4292 if( db_step(&q)==SQLITE_ROW ){
4293 const char *lastTag = db_column_text(&q, 0);
4294 descr->zRelTagname = mprintf("%s", lastTag);
4295 descr->nCommitsSince = db_column_int(&q, 1);
4296 nRet = 0;
4297 }else{
4298 /* no ancestor commit with a fitting singleton tag found */
4299 descr->zRelTagname = mprintf("");
4300
--- src/info.c
+++ src/info.c
@@ -1167,15 +1167,16 @@
1167 }
1168 render_backlink_graph(zUuid,
1169 "<div class=\"section accordion\">References</div>\n");
1170 @ <div class="section accordion">Context</div><div class="accordion_panel">
1171 render_checkin_context(rid, 0, 0, 0);
1172 @ </div><div class="section accordion" id="changes_section">Changes</div>
1173 @ <div class="accordion_panel">
1174 @ <div class="sectionmenu info-changes-menu">
1175 /* ^^^ .info-changes-menu is used by diff scroll sync */
1176 pCfg = construct_diff_flags(diffType, &DCfg);
1177 DCfg.diffFlags |= DIFF_NUMSTAT; /* Show stats in the 'Changes' section */
1178 DCfg.pRe = pRe;
1179 zW = (DCfg.diffFlags&DIFF_IGNORE_ALLWS)?"&w":"";
1180 if( diffType!=1 ){
1181 @ %z(chref("button","%R/%s/%T?diff=1%s",zPage,zName,zW))\
1182 @ Unified&nbsp;Diff</a>
@@ -1227,10 +1228,18 @@
1228 append_file_change_line(zUuid, zName, zOld, zNew, zOldName,
1229 pCfg,mperm);
1230 }
1231 db_finalize(&q3);
1232 @ </div>
1233 if( diffType!=0 ){
1234 @ <script nonce='%h(style_nonce())'>;/* info.c:%d(__LINE__) */
1235 @ document.getElementById('changes_section').textContent = 'Changes ' +
1236 @ '(%d(g.diffCnt[0]) file' + (%d(g.diffCnt[0])===1 ? '' : 's') + ': ' +
1237 @ '+%d(g.diffCnt[1]) ' +
1238 @ '−%d(g.diffCnt[2]))'
1239 @ </script>
1240 }
1241 append_diff_javascript(diffType);
1242 style_finish_page();
1243 }
1244
1245 /*
@@ -1414,10 +1423,11 @@
1423 Blob qpGlob; /* glob= query parameter for generated links */
1424 int bInvert = PB("inv");
1425
1426 login_check_credentials();
1427 if( !g.perm.Read ){ login_needed(g.anon.Read); return; }
1428 if( robot_restrict("diff") ) return;
1429 login_anonymous_available();
1430 fossil_nice_default();
1431 blob_init(&qp, 0, 0);
1432 blob_init(&qpGlob, 0, 0);
1433 diffType = preferred_diff_type();
@@ -1918,18 +1928,31 @@
1928 ** * The "preferred-diff-type" setting
1929 ** * 1 for mobile and 2 for desktop, based on the UserAgent
1930 */
1931 int preferred_diff_type(void){
1932 int dflt;
1933 int res;
1934 int isBot;
1935 static char zDflt[2]
1936 /*static b/c cookie_link_parameter() does not copy it!*/;
1937 if( client_might_be_a_robot() ){
1938 dflt = 0;
1939 isBot = 1;
1940 }else{
1941 dflt = db_get_int("preferred-diff-type",-99);
1942 if( dflt<=0 ) dflt = user_agent_is_likely_mobile() ? 1 : 2;
1943 isBot = 0;
1944 }
1945 zDflt[0] = dflt + '0';
1946 zDflt[1] = 0;
1947 cookie_link_parameter("diff","diff", zDflt);
1948 res = atoi(PD_NoBot("diff",zDflt));
1949 if( isBot && res>0 && robot_restrict("diff") ){
1950 cgi_reply();
1951 fossil_exit(0);
1952 }
1953 return res;
1954 }
1955
1956
1957 /*
1958 ** WEBPAGE: fdiff
@@ -1967,10 +1990,11 @@
1990 int verbose = PB("verbose");
1991 DiffConfig DCfg;
1992
1993 login_check_credentials();
1994 if( !g.perm.Read ){ login_needed(g.anon.Read); return; }
1995 if( robot_restrict("diff") ) return;
1996 diff_config_init(&DCfg, 0);
1997 diffType = preferred_diff_type();
1998 if( P("from") && P("to") ){
1999 v1 = artifact_from_ci_and_filename("from");
2000 v2 = artifact_from_ci_and_filename("to");
@@ -2407,15 +2431,15 @@
2431 object_description(rid, objdescFlags, 0, &downloadName);
2432 style_submenu_element("Download", "%R/raw/%s?at=%T",
2433 zUuid, file_tail(blob_str(&downloadName)));
2434 @ <hr>
2435 content_get(rid, &content);
2436 if( blob_size(&content)>100000 ){
2437 /* Prevent robots from running hexdump on megabyte-sized source files
2438 ** and there by eating up lots of CPU time and bandwidth. There is
2439 ** no good reason for a robot to need a hexdump. */
2440 @ <p>A hex dump of this file is not available because it is too large.
2441 @ Please download the raw binary file and generate a hex dump yourself.</p>
2442 }else{
2443 @ <blockquote><pre>
2444 hexdump(&content);
2445 @ </pre></blockquote>
@@ -2886,11 +2910,11 @@
2910 style_set_current_page("doc/%t/%T", zCI, zName);
2911 }else if( zCIUuid && zCIUuid[0] ){
2912 zHeader = mprintf("%s at [%S]", file_tail(zName), zCIUuid);
2913 style_set_current_page("doc/%S/%T", zCIUuid, zName);
2914 }else{
2915 zHeader = fossil_strdup(file_tail(zName));
2916 style_set_current_page("doc/tip/%T", zName);
2917 }
2918 }else if( descOnly ){
2919 zHeader = mprintf("Artifact Description [%S]", zUuid);
2920 }else{
@@ -4240,11 +4264,11 @@
4264 descr->isDirty = -1;
4265 return (rid-1);
4266 }
4267
4268 zUuid = rid_to_uuid(rid);
4269 descr->zCommitHash = fossil_strdup(zUuid);
4270 descr->isDirty = unsaved_changes(0);
4271
4272 db_multi_exec(
4273 "DROP TABLE IF EXISTS temp.singletonTag;"
4274 "CREATE TEMP TABLE singletonTag("
@@ -4289,11 +4313,11 @@
4313 rid, rid
4314 );
4315
4316 if( db_step(&q)==SQLITE_ROW ){
4317 const char *lastTag = db_column_text(&q, 0);
4318 descr->zRelTagname = fossil_strdup(lastTag);
4319 descr->nCommitsSince = db_column_int(&q, 1);
4320 nRet = 0;
4321 }else{
4322 /* no ancestor commit with a fitting singleton tag found */
4323 descr->zRelTagname = mprintf("");
4324
+1 -2
--- src/json.c
+++ src/json.c
@@ -1643,11 +1643,11 @@
16431643
int json_set_err( int code, char const * fmt, ... ){
16441644
assert( (code>=1000) && (code<=9999) );
16451645
fossil_free(g.zErrMsg);
16461646
g.json.resultCode = code;
16471647
if(!fmt || !*fmt){
1648
- g.zErrMsg = mprintf("%s", json_err_cstr(code));
1648
+ g.zErrMsg = fossil_strdup(json_err_cstr(code));
16491649
}else{
16501650
va_list vargs;
16511651
char * msg;
16521652
va_start(vargs,fmt);
16531653
msg = vmprintf(fmt, vargs);
@@ -1961,11 +1961,10 @@
19611961
19621962
#define ADD(X,K) cson_object_set(obj, K, cson_value_new_bool(g.perm.X))
19631963
ADD(Setup,"setup");
19641964
ADD(Admin,"admin");
19651965
ADD(Password,"password");
1966
- ADD(Query,"query"); /* don't think this one is actually used */
19671966
ADD(Write,"checkin");
19681967
ADD(Read,"checkout");
19691968
ADD(Hyperlink,"history");
19701969
ADD(Clone,"clone");
19711970
ADD(RdWiki,"readWiki");
19721971
--- src/json.c
+++ src/json.c
@@ -1643,11 +1643,11 @@
1643 int json_set_err( int code, char const * fmt, ... ){
1644 assert( (code>=1000) && (code<=9999) );
1645 fossil_free(g.zErrMsg);
1646 g.json.resultCode = code;
1647 if(!fmt || !*fmt){
1648 g.zErrMsg = mprintf("%s", json_err_cstr(code));
1649 }else{
1650 va_list vargs;
1651 char * msg;
1652 va_start(vargs,fmt);
1653 msg = vmprintf(fmt, vargs);
@@ -1961,11 +1961,10 @@
1961
1962 #define ADD(X,K) cson_object_set(obj, K, cson_value_new_bool(g.perm.X))
1963 ADD(Setup,"setup");
1964 ADD(Admin,"admin");
1965 ADD(Password,"password");
1966 ADD(Query,"query"); /* don't think this one is actually used */
1967 ADD(Write,"checkin");
1968 ADD(Read,"checkout");
1969 ADD(Hyperlink,"history");
1970 ADD(Clone,"clone");
1971 ADD(RdWiki,"readWiki");
1972
--- src/json.c
+++ src/json.c
@@ -1643,11 +1643,11 @@
1643 int json_set_err( int code, char const * fmt, ... ){
1644 assert( (code>=1000) && (code<=9999) );
1645 fossil_free(g.zErrMsg);
1646 g.json.resultCode = code;
1647 if(!fmt || !*fmt){
1648 g.zErrMsg = fossil_strdup(json_err_cstr(code));
1649 }else{
1650 va_list vargs;
1651 char * msg;
1652 va_start(vargs,fmt);
1653 msg = vmprintf(fmt, vargs);
@@ -1961,11 +1961,10 @@
1961
1962 #define ADD(X,K) cson_object_set(obj, K, cson_value_new_bool(g.perm.X))
1963 ADD(Setup,"setup");
1964 ADD(Admin,"admin");
1965 ADD(Password,"password");
 
1966 ADD(Write,"checkin");
1967 ADD(Read,"checkout");
1968 ADD(Hyperlink,"history");
1969 ADD(Clone,"clone");
1970 ADD(RdWiki,"readWiki");
1971
--- src/json_report.c
+++ src/json_report.c
@@ -202,11 +202,11 @@
202202
limit = json_find_option_int("limit",NULL,"n",-1);
203203
204204
205205
/* Copy over report's SQL...*/
206206
blob_append(&sql, db_column_text(&q,0), -1);
207
- zTitle = mprintf("%s", db_column_text(&q,1));
207
+ zTitle = fossil_strdup(db_column_text(&q,1));
208208
db_finalize(&q);
209209
db_prepare(&q, "%s", blob_sql_text(&sql));
210210
211211
/** Build the response... */
212212
pay = cson_new_object();
213213
--- src/json_report.c
+++ src/json_report.c
@@ -202,11 +202,11 @@
202 limit = json_find_option_int("limit",NULL,"n",-1);
203
204
205 /* Copy over report's SQL...*/
206 blob_append(&sql, db_column_text(&q,0), -1);
207 zTitle = mprintf("%s", db_column_text(&q,1));
208 db_finalize(&q);
209 db_prepare(&q, "%s", blob_sql_text(&sql));
210
211 /** Build the response... */
212 pay = cson_new_object();
213
--- src/json_report.c
+++ src/json_report.c
@@ -202,11 +202,11 @@
202 limit = json_find_option_int("limit",NULL,"n",-1);
203
204
205 /* Copy over report's SQL...*/
206 blob_append(&sql, db_column_text(&q,0), -1);
207 zTitle = fossil_strdup(db_column_text(&q,1));
208 db_finalize(&q);
209 db_prepare(&q, "%s", blob_sql_text(&sql));
210
211 /** Build the response... */
212 pay = cson_new_object();
213
+131 -142
--- src/login.c
+++ src/login.c
@@ -160,10 +160,11 @@
160160
161161
if( zUsername==0 ) return 0;
162162
else if( zPassword==0 ) return 0;
163163
else if( zCS==0 ) return 0;
164164
else if( fossil_strcmp(zUsername,"anonymous")!=0 ) return 0;
165
+ else if( anon_cookie_lifespan()==0 ) return 0;
165166
while( 1/*exit-by-break*/ ){
166167
zPw = captcha_decode((unsigned int)atoi(zCS), n);
167168
if( zPw==0 ) return 0;
168169
if( fossil_stricmp(zPw, zPassword)==0 ) break;
169170
n++;
@@ -338,33 +339,62 @@
338339
*zDest = zCookie;
339340
}else{
340341
free(zCookie);
341342
}
342343
}
344
+
345
+/*
346
+** SETTING: anon-cookie-lifespan width=10 default=480
347
+** The number of minutes for which an anonymous login cookie is
348
+** valid. Anonymous logins are prohibited if this value is zero.
349
+*/
350
+
351
+
352
+/*
353
+** The default lifetime of an anoymous cookie, in minutes.
354
+*/
355
+#define ANONYMOUS_COOKIE_LIFESPAN (8*60)
356
+
357
+/*
358
+** Return the lifetime of an anonymous cookie, in minutes.
359
+*/
360
+int anon_cookie_lifespan(void){
361
+ static int lifespan = -1;
362
+ if( lifespan<0 ){
363
+ lifespan = db_get_int("anon-cookie-lifespan", ANONYMOUS_COOKIE_LIFESPAN);
364
+ if( lifespan<0 ) lifespan = 0;
365
+ }
366
+ return lifespan;
367
+}
343368
344369
/* Sets a cookie for an anonymous user login, which looks like this:
345370
**
346371
** HASH/TIME/anonymous
347372
**
348
-** Where HASH is the sha1sum of TIME/SECRET, in which SECRET is captcha-secret.
373
+** Where HASH is the sha1sum of TIME/USERAGENT/SECRET, in which SECRET
374
+** is captcha-secret and USERAGENT is the HTTP_USER_AGENT value.
349375
**
350376
** If zCookieDest is not NULL then the generated cookie is assigned to
351377
** *zCookieDest and the caller must eventually free() it.
352378
**
353379
** If bSessionCookie is true, the cookie will be a session cookie.
380
+**
381
+** Search for tag-20250817a to find the code that recognizes this cookie.
354382
*/
355383
void login_set_anon_cookie(char **zCookieDest, int bSessionCookie){
356384
char *zNow; /* Current time (julian day number) */
357385
char *zCookie; /* The login cookie */
386
+ const char *zUserAgent; /* The user agent */
358387
const char *zCookieName; /* Name of the login cookie */
359388
Blob b; /* Blob used during cookie construction */
360
- int expires = bSessionCookie ? 0 : 6*3600;
389
+ int expires = bSessionCookie ? 0 : anon_cookie_lifespan();
361390
zCookieName = login_cookie_name();
362391
zNow = db_text("0", "SELECT julianday('now')");
363392
assert( zCookieName && zNow );
364393
blob_init(&b, zNow, -1);
365
- blob_appendf(&b, "/%z", captcha_secret(0));
394
+ zUserAgent = PD("HTTP_USER_AGENT","nil");
395
+ blob_appendf(&b, "/%s/%z", zUserAgent, captcha_secret(0));
366396
sha1sum_blob(&b, &b);
367397
zCookie = mprintf("%s/%s/anonymous", blob_buffer(&b), zNow);
368398
blob_reset(&b);
369399
cgi_set_cookie(zCookieName, zCookie, login_cookie_path(), expires);
370400
if( zCookieDest ){
@@ -581,17 +611,35 @@
581611
/* If the "Reset Password" button in the form was pressed, render
582612
** the Request Password Reset page in place of this one. */
583613
login_reqpwreset_page();
584614
return;
585615
}
586
- login_check_credentials();
616
+
617
+ /* If the "anon" query parameter is 1 or 2, that means rework the web-page
618
+ ** to make it a more user-friendly captcha. Extraneous text and boxes
619
+ ** are omitted. The user has just the captcha image and an entry box
620
+ ** and a "Verify" button. Underneath is the same login page for user
621
+ ** "anonymous", just displayed in an easier to digest format for one-time
622
+ ** visitors.
623
+ **
624
+ ** anon=1 is advisory and only has effect if there is not some other login
625
+ ** cookie. anon=2 means always show the captcha.
626
+ */
627
+ anonFlag = anon_cookie_lifespan()>0 ? atoi(PD("anon","0")) : 0;
628
+ if( anonFlag==2 ){
629
+ g.zLogin = 0;
630
+ }else{
631
+ login_check_credentials();
632
+ if( g.zLogin!=0 ) anonFlag = 0;
633
+ }
634
+
587635
fossil_redirect_to_https_if_needed(1);
588636
sqlite3_create_function(g.db, "constant_time_cmp", 2, SQLITE_UTF8, 0,
589637
constant_time_cmp_function, 0, 0);
590638
zUsername = P("u");
591639
zPasswd = P("p");
592
- anonFlag = g.zLogin==0 && PB("anon");
640
+
593641
/* Handle log-out requests */
594642
if( P("out") && cgi_csrf_safe(2) ){
595643
login_clear_login_data();
596644
login_redirect_to_g();
597645
return;
@@ -717,10 +765,11 @@
717765
login_redirect_to_g();
718766
}
719767
}
720768
style_set_current_feature("login");
721769
style_header("Login/Logout");
770
+ if( anonFlag==2 ) g.zLogin = 0;
722771
style_adunit_config(ADUNIT_OFF);
723772
@ %s(zErrMsg)
724773
if( zGoto && !noAnon ){
725774
char *zAbbrev = fossil_strdup(zGoto);
726775
int i;
@@ -728,12 +777,12 @@
728777
zAbbrev[i] = 0;
729778
if( g.zLogin ){
730779
@ <p>Use a different login with greater privilege than <b>%h(g.zLogin)</b>
731780
@ to access <b>%h(zAbbrev)</b>.
732781
}else if( anonFlag ){
733
- @ <p>Login as <b>anonymous</b> or any named user
734
- @ to access page <b>%h(zAbbrev)</b>.
782
+ @ <p><b>Verify that you are human by typing in the 8-character text
783
+ @ password shown below.</b></p>
735784
}else{
736785
@ <p>Login as a named user to access page <b>%h(zAbbrev)</b>.
737786
}
738787
fossil_free(zAbbrev);
739788
}
@@ -748,26 +797,27 @@
748797
if( zGoto ){
749798
@ <input type="hidden" name="g" value="%h(zGoto)">
750799
}
751800
if( anonFlag ){
752801
@ <input type="hidden" name="anon" value="1">
802
+ @ <input type="hidden" name="u" value="anonymous">
753803
}
754804
if( g.zLogin ){
755805
@ <p>Currently logged in as <b>%h(g.zLogin)</b>.
756806
@ <input type="submit" name="out" value="Logout" autofocus></p>
757807
@ </form>
758808
}else{
759809
unsigned int uSeed = captcha_seed();
760
- if( g.zLogin==0 && (anonFlag || zGoto==0) ){
810
+ if( g.zLogin==0 && (anonFlag || zGoto==0) && anon_cookie_lifespan()>0 ){
761811
zAnonPw = db_text(0, "SELECT pw FROM user"
762812
" WHERE login='anonymous'"
763813
" AND cap!=''");
764814
}else{
765815
zAnonPw = 0;
766816
}
767817
@ <table class="login_out">
768
- if( P("HTTPS")==0 ){
818
+ if( P("HTTPS")==0 && !anonFlag ){
769819
@ <tr><td class="form_label">Warning:</td>
770820
@ <td><span class='securityWarning'>
771821
@ Login information, including the password,
772822
@ will be sent in the clear over an unencrypted connection.
773823
if( !g.sslNotAvailable ){
@@ -774,41 +824,55 @@
774824
@ Consider logging in at
775825
@ <a href='%s(g.zHttpsURL)'>%h(g.zHttpsURL)</a> instead.
776826
}
777827
@ </span></td></tr>
778828
}
779
- @ <tr>
780
- @ <td class="form_label" id="userlabel1">User ID:</td>
781
- @ <td><input type="text" id="u" aria-labelledby="userlabel1" name="u" \
782
- @ size="30" value="%s(anonFlag?"anonymous":"")" autofocus></td>
783
- @ </tr>
829
+ if( !anonFlag ){
830
+ @ <tr>
831
+ @ <td class="form_label" id="userlabel1">User ID:</td>
832
+ @ <td><input type="text" id="u" aria-labelledby="userlabel1" name="u" \
833
+ @ size="30" value="" autofocus></td>
834
+ @ </tr>
835
+ }
784836
@ <tr>
785837
@ <td class="form_label" id="pswdlabel">Password:</td>
786838
@ <td><input aria-labelledby="pswdlabel" type="password" id="p" \
787
- @ name="p" value="" size="30">\
788
- if( zAnonPw && !noAnon ){
839
+ @ name="p" value="" size="30"%s(anonFlag ? " autofocus" : "")>
840
+ if( anonFlag ){
841
+ @ </td></tr>
842
+ @ <tr>
843
+ @ <td></td><td>\
844
+ captcha_speakit_button(uSeed, "Read the password out loud");
845
+ }else if( zAnonPw && !noAnon ){
789846
captcha_speakit_button(uSeed, "Speak password for \"anonymous\"");
790847
}
791848
@ </td>
792849
@ </tr>
793
- @ <tr>
794
- @ <td></td>
795
- @ <td><input type="checkbox" name="remember" value="1" \
796
- @ id="remember-me" %s(rememberMe ? "checked=\"checked\"" : "")>
797
- @ <label for="remember-me">Remember me?</label></td>
798
- @ </tr>
799
- @ <tr>
800
- @ <td></td>
801
- @ <td><input type="submit" name="in" value="Login">
802
- @ </tr>
803
- if( !noAnon && login_self_register_available(0) ){
850
+ if( !anonFlag ){
851
+ @ <tr>
852
+ @ <td></td>
853
+ @ <td><input type="checkbox" name="remember" value="1" \
854
+ @ id="remember-me" %s(rememberMe ? "checked=\"checked\"" : "")>
855
+ @ <label for="remember-me">Remember me?</label></td>
856
+ @ </tr>
857
+ @ <tr>
858
+ @ <td></td>
859
+ @ <td><input type="submit" name="in" value="Login">
860
+ @ </tr>
861
+ }else{
862
+ @ <tr>
863
+ @ <td></td>
864
+ @ <td><input type="submit" name="in" value="Verify that I am human">
865
+ @ </tr>
866
+ }
867
+ if( !anonFlag && !noAnon && login_self_register_available(0) ){
804868
@ <tr>
805869
@ <td></td>
806870
@ <td><input type="submit" name="self" value="Create A New Account">
807871
@ </tr>
808872
}
809
- if( login_self_password_reset_available() ){
873
+ if( !anonFlag && login_self_password_reset_available() ){
810874
@ <tr>
811875
@ <td></td>
812876
@ <td><input type="submit" name="pwreset" value="Reset My Password">
813877
@ </tr>
814878
}
@@ -817,27 +881,29 @@
817881
const char *zDecoded = captcha_decode(uSeed, 0);
818882
int bAutoCaptcha = db_get_boolean("auto-captcha", 0);
819883
char *zCaptcha = captcha_render(zDecoded);
820884
821885
@ <p><input type="hidden" name="cs" value="%u(uSeed)">
822
- @ Visitors may enter <b>anonymous</b> as the user-ID with
823
- @ the 8-character hexadecimal password shown below:</p>
886
+ if( !anonFlag ){
887
+ @ Visitors may enter <b>anonymous</b> as the user-ID with
888
+ @ the 8-character hexadecimal password shown below:</p>
889
+ }
824890
@ <div class="captcha"><table class="captcha"><tr><td>\
825891
@ <pre class="captcha">
826892
@ %h(zCaptcha)
827893
@ </pre></td></tr></table>
828
- if( bAutoCaptcha ) {
894
+ if( bAutoCaptcha && !anonFlag ) {
829895
@ <input type="button" value="Fill out captcha" id='autofillButton' \
830896
@ data-af='%s(zDecoded)'>
831897
builtin_request_js("login.js");
832898
}
833899
@ </div>
834900
free(zCaptcha);
835901
}
836902
@ </form>
837903
}
838
- if( login_is_individual() ){
904
+ if( login_is_individual() && !anonFlag ){
839905
if( g.perm.EmailAlert && alert_enabled() ){
840906
@ <hr>
841907
@ <p>Configure <a href="%R/alerts">Email Alerts</a>
842908
@ for user <b>%h(g.zLogin)</b></p>
843909
}
@@ -845,15 +911,18 @@
845911
@ <hr><p>
846912
@ <a href="%R/timeline?ss=v&y=f&vfx&u=%t(g.zLogin)">Forum
847913
@ post timeline</a> for user <b>%h(g.zLogin)</b></p>
848914
}
849915
}
850
- @ <hr><p>
851
- @ Select your preferred <a href="%R/skins">site skin</a>.
852
- @ </p>
853
- @ <hr><p>
854
- @ Manage your <a href="%R/cookies">cookies</a>.</p>
916
+ if( !anonFlag ){
917
+ @ <hr><p>
918
+ @ Select your preferred <a href="%R/skins">site skin</a>.
919
+ @ </p>
920
+ @ <hr><p>
921
+ @ Manage your <a href="%R/cookies">cookies</a> or your
922
+ @ <a href="%R/tokens">access tokens</a>.</p>
923
+ }
855924
if( login_is_individual() ){
856925
if( g.perm.Password ){
857926
char *zRPW = fossil_random_password(12);
858927
@ <hr>
859928
@ <p>Change Password for user <b>%h(g.zLogin)</b>:</p>
@@ -1262,98 +1331,10 @@
12621331
}
12631332
fossil_free(zDecode);
12641333
return uid;
12651334
}
12661335
1267
-/*
1268
-** SETTING: robot-restrict width=40 block-text
1269
-** The VALUE of this setting is a list of GLOB patterns that match
1270
-** pages for which complex HTTP requests from robots should be disallowed.
1271
-** The recommended value for this setting is:
1272
-**
1273
-** timeline,vdiff,fdiff,annotate,blame
1274
-**
1275
-*/
1276
-
1277
-/*
1278
-** Check to see if the current HTTP request is a complex request that
1279
-** is coming from a robot and if access should restricted for such robots.
1280
-** For the purposes of this module, a "complex request" is an HTTP
1281
-** request with one or more query parameters other than "name".
1282
-**
1283
-** If this routine determines that robots should be restricted, then
1284
-** this routine publishes a redirect to the honeypot and exits without
1285
-** returning to the caller.
1286
-**
1287
-** This routine believes that this is a complex request is coming from
1288
-** a robot if all of the following are true:
1289
-**
1290
-** * The user is "nobody".
1291
-** * Either the REFERER field of the HTTP header is missing or empty,
1292
-** or the USERAGENT field of the HTTP header suggests that
1293
-** the request as coming from a robot.
1294
-** * There are one or more query parameters other than "name".
1295
-**
1296
-** Robot restrictions are governed by settings.
1297
-**
1298
-** robot-restrict The value is a list of GLOB patterns for pages
1299
-** that should restrict robot access. No restrictions
1300
-** are applied if this setting is undefined or is
1301
-** an empty string.
1302
-*/
1303
-void login_restrict_robot_access(void){
1304
- const char *zGlob;
1305
- int isMatch = 1;
1306
- int nQP; /* Number of query parameters other than name= */
1307
- if( g.zLogin!=0 ) return;
1308
- zGlob = db_get("robot-restrict",0);
1309
- if( zGlob==0 || zGlob[0]==0 ) return;
1310
- if( g.isHuman ){
1311
- const char *zReferer;
1312
- const char *zAccept;
1313
- const char *zBr;
1314
- zReferer = P("HTTP_REFERER");
1315
- if( zReferer && zReferer[0]!=0 ) return;
1316
-
1317
- /* Robots typically do not accept the brotli encoding, at least not
1318
- ** at the time of this writing (2025-04-01), but standard web-browser
1319
- ** all generally do accept brotli. So if brotli is accepted,
1320
- ** assume we are not talking to a robot. We might want to revisit this
1321
- ** heuristic in the future...
1322
- */
1323
- if( (zAccept = P("HTTP_ACCEPT_ENCODING"))!=0
1324
- && (zBr = strstr(zAccept,"br"))!=0
1325
- && !fossil_isalnum(zBr[2])
1326
- && (zBr==zAccept || !fossil_isalnum(zBr[-1]))
1327
- ){
1328
- return;
1329
- }
1330
- }
1331
- nQP = cgi_qp_count();
1332
- if( nQP<1 ) return;
1333
- isMatch = glob_multi_match(zGlob, g.zPath);
1334
- if( !isMatch ) return;
1335
-
1336
- /* Check for exceptions to the restriction on the number of query
1337
- ** parameters. */
1338
- zGlob = db_get("robot-restrict-qp",0);
1339
- if( zGlob && zGlob[0] ){
1340
- char *zPath = mprintf("%s/%d", g.zPath, nQP);
1341
- isMatch = glob_multi_match(zGlob, zPath);
1342
- fossil_free(zPath);
1343
- if( isMatch ) return;
1344
- }
1345
-
1346
- /* If we reach this point, it means we have a situation where we
1347
- ** want to restrict the activity of a robot.
1348
- */
1349
- g.isHuman = 0;
1350
- (void)exclude_spiders(0);
1351
- cgi_reply();
1352
- fossil_exit(0);
1353
-}
1354
-
13551336
/*
13561337
** When this routine is called, we know that the request does not
13571338
** have a login on the present repository. This routine checks to
13581339
** see if their login cookie might be for another member of the
13591340
** login-group.
@@ -1388,11 +1369,11 @@
13881369
**
13891370
** g.userUid Database USER.UID value. Might be -1 for "nobody"
13901371
** g.zLogin Database USER.LOGIN value. NULL for user "nobody"
13911372
** g.perm Permissions granted to this user
13921373
** g.anon Permissions that would be available to anonymous
1393
-** g.isHuman True if the user is human, not a spider or robot
1374
+** g.isRobot True if the client is known to be a spider or robot
13941375
** g.perm Populated based on user account's capabilities
13951376
**
13961377
*/
13971378
void login_check_credentials(void){
13981379
int uid = 0; /* User id */
@@ -1429,11 +1410,11 @@
14291410
uid = db_int(0, "SELECT uid FROM user WHERE cap LIKE '%%s%%'");
14301411
}
14311412
g.zLogin = db_text("?", "SELECT login FROM user WHERE uid=%d", uid);
14321413
zCap = "sxy";
14331414
g.noPswd = 1;
1434
- g.isHuman = 1;
1415
+ g.isRobot = 0;
14351416
zSeed = db_text("??", "SELECT uid||quote(login)||quote(pw)||quote(cookie)"
14361417
" FROM user WHERE uid=%d", uid);
14371418
login_create_csrf_secret(zSeed);
14381419
fossil_free(zSeed);
14391420
}
@@ -1457,33 +1438,38 @@
14571438
}
14581439
}
14591440
}
14601441
if( zUser==0 ){
14611442
/* Invalid cookie */
1462
- }else if( fossil_strcmp(zUser, "anonymous")==0 ){
1463
- /* Cookies of the form "HASH/TIME/anonymous". The TIME must not be
1464
- ** too old and the sha1 hash of TIME/SECRET must match HASH.
1465
- ** SECRET is the "captcha-secret" value in the repository.
1443
+ }else if( fossil_strcmp(zUser, "anonymous")==0
1444
+ && anon_cookie_lifespan()>0 ){
1445
+ /* Cookies of the form "HASH/TIME/anonymous". The TIME must
1446
+ ** not be more than ANONYMOUS_COOKIE_LIFESPAN seconds ago and
1447
+ ** the sha1 hash of TIME/USERAGENT/SECRET must match HASH. USERAGENT
1448
+ ** is the HTTP_USER_AGENT of the client and SECRET is the
1449
+ ** "captcha-secret" value in the repository. See tag-20250817a
1450
+ ** for the code the creates this cookie.
14661451
*/
14671452
double rTime = atof(zArg);
1453
+ const char *zUserAgent = PD("HTTP_USER_AGENT","nil");
14681454
Blob b;
14691455
char *zSecret;
14701456
int n = 0;
14711457
14721458
do{
14731459
blob_zero(&b);
14741460
zSecret = captcha_secret(n++);
14751461
if( zSecret==0 ) break;
1476
- blob_appendf(&b, "%s/%s", zArg, zSecret);
1462
+ blob_appendf(&b, "%s/%s/%s", zArg, zUserAgent, zSecret);
14771463
sha1sum_blob(&b, &b);
14781464
if( fossil_strcmp(zHash, blob_str(&b))==0 ){
14791465
uid = db_int(0,
14801466
"SELECT uid FROM user WHERE login='anonymous'"
14811467
" AND octet_length(cap)>0"
14821468
" AND octet_length(pw)>0"
1483
- " AND %.17g+0.25>julianday('now')",
1484
- rTime
1469
+ " AND %.17g>julianday('now')",
1470
+ rTime+anon_cookie_lifespan()/1440.0
14851471
);
14861472
}
14871473
}while( uid==0 );
14881474
blob_reset(&b);
14891475
}else{
@@ -1559,12 +1545,15 @@
15591545
login_create_csrf_secret("none");
15601546
}
15611547
15621548
login_set_uid(uid, zCap);
15631549
1564
- /* Maybe restrict access to robots */
1565
- login_restrict_robot_access();
1550
+ /* Maybe restrict access by robots */
1551
+ if( g.zLogin==0 && robot_restrict(g.zPath) ){
1552
+ cgi_reply();
1553
+ fossil_exit(0);
1554
+ }
15661555
}
15671556
15681557
/*
15691558
** Set the current logged in user to be uid. zCap is precomputed
15701559
** (override) capabilities. If zCap==0, then look up the capabilities
@@ -1599,15 +1588,15 @@
15991588
g.userUid = uid;
16001589
if( fossil_strcmp(g.zLogin,"nobody")==0 ){
16011590
g.zLogin = 0;
16021591
}
16031592
if( PB("isrobot") ){
1604
- g.isHuman = 0;
1593
+ g.isRobot = 1;
16051594
}else if( g.zLogin==0 ){
1606
- g.isHuman = isHuman(P("HTTP_USER_AGENT"));
1595
+ g.isRobot = !isHuman(P("HTTP_USER_AGENT"));
16071596
}else{
1608
- g.isHuman = 1;
1597
+ g.isRobot = 0;
16091598
}
16101599
16111600
/* Set the capabilities */
16121601
login_replace_capabilities(zCap, 0);
16131602
@@ -1617,11 +1606,11 @@
16171606
** enabled for this repository and make appropriate adjustments to the
16181607
** permission flags if it is. This should be done before the permissions
16191608
** are (potentially) copied to the anonymous permission set; otherwise,
16201609
** those will be out-of-sync.
16211610
*/
1622
- if( zCap[0] && !g.perm.Hyperlink && g.isHuman ){
1611
+ if( zCap[0] && !g.perm.Hyperlink && !g.isRobot ){
16231612
int autoLink = db_get_int("auto-hyperlink",1);
16241613
if( autoLink==1 ){
16251614
g.jsHref = 1;
16261615
g.perm.Hyperlink = 1;
16271616
}else if( autoLink==2 ){
@@ -1927,11 +1916,11 @@
19271916
blob_appendf(&redir, "%R/login?g=%T", zPathInfo);
19281917
}
19291918
if( zQS && zQS[0] ){
19301919
blob_appendf(&redir, "%%3f%T", zQS);
19311920
}
1932
- if( anonOk ) blob_append(&redir, "&anon", 5);
1921
+ if( anonOk ) blob_append(&redir, "&anon=1", 7);
19331922
cgi_redirect(blob_str(&redir));
19341923
/* NOTREACHED */
19351924
assert(0);
19361925
}
19371926
}
@@ -1941,11 +1930,11 @@
19411930
** the anonymous user has Hyperlink permission, then paint a mesage
19421931
** to inform the user that much more information is available by
19431932
** logging in as anonymous.
19441933
*/
19451934
void login_anonymous_available(void){
1946
- if( !g.perm.Hyperlink && g.anon.Hyperlink ){
1935
+ if( !g.perm.Hyperlink && g.anon.Hyperlink && anon_cookie_lifespan()>0 ){
19471936
const char *zUrl = PD("PATH_INFO", "");
19481937
@ <p>Many <span class="disabled">hyperlinks are disabled.</span><br>
19491938
@ Use <a href="%R/login?anon=1&amp;g=%T(zUrl)">anonymous login</a>
19501939
@ to enable hyperlinks.</p>
19511940
}
19521941
--- src/login.c
+++ src/login.c
@@ -160,10 +160,11 @@
160
161 if( zUsername==0 ) return 0;
162 else if( zPassword==0 ) return 0;
163 else if( zCS==0 ) return 0;
164 else if( fossil_strcmp(zUsername,"anonymous")!=0 ) return 0;
 
165 while( 1/*exit-by-break*/ ){
166 zPw = captcha_decode((unsigned int)atoi(zCS), n);
167 if( zPw==0 ) return 0;
168 if( fossil_stricmp(zPw, zPassword)==0 ) break;
169 n++;
@@ -338,33 +339,62 @@
338 *zDest = zCookie;
339 }else{
340 free(zCookie);
341 }
342 }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
343
344 /* Sets a cookie for an anonymous user login, which looks like this:
345 **
346 ** HASH/TIME/anonymous
347 **
348 ** Where HASH is the sha1sum of TIME/SECRET, in which SECRET is captcha-secret.
 
349 **
350 ** If zCookieDest is not NULL then the generated cookie is assigned to
351 ** *zCookieDest and the caller must eventually free() it.
352 **
353 ** If bSessionCookie is true, the cookie will be a session cookie.
 
 
354 */
355 void login_set_anon_cookie(char **zCookieDest, int bSessionCookie){
356 char *zNow; /* Current time (julian day number) */
357 char *zCookie; /* The login cookie */
 
358 const char *zCookieName; /* Name of the login cookie */
359 Blob b; /* Blob used during cookie construction */
360 int expires = bSessionCookie ? 0 : 6*3600;
361 zCookieName = login_cookie_name();
362 zNow = db_text("0", "SELECT julianday('now')");
363 assert( zCookieName && zNow );
364 blob_init(&b, zNow, -1);
365 blob_appendf(&b, "/%z", captcha_secret(0));
 
366 sha1sum_blob(&b, &b);
367 zCookie = mprintf("%s/%s/anonymous", blob_buffer(&b), zNow);
368 blob_reset(&b);
369 cgi_set_cookie(zCookieName, zCookie, login_cookie_path(), expires);
370 if( zCookieDest ){
@@ -581,17 +611,35 @@
581 /* If the "Reset Password" button in the form was pressed, render
582 ** the Request Password Reset page in place of this one. */
583 login_reqpwreset_page();
584 return;
585 }
586 login_check_credentials();
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
587 fossil_redirect_to_https_if_needed(1);
588 sqlite3_create_function(g.db, "constant_time_cmp", 2, SQLITE_UTF8, 0,
589 constant_time_cmp_function, 0, 0);
590 zUsername = P("u");
591 zPasswd = P("p");
592 anonFlag = g.zLogin==0 && PB("anon");
593 /* Handle log-out requests */
594 if( P("out") && cgi_csrf_safe(2) ){
595 login_clear_login_data();
596 login_redirect_to_g();
597 return;
@@ -717,10 +765,11 @@
717 login_redirect_to_g();
718 }
719 }
720 style_set_current_feature("login");
721 style_header("Login/Logout");
 
722 style_adunit_config(ADUNIT_OFF);
723 @ %s(zErrMsg)
724 if( zGoto && !noAnon ){
725 char *zAbbrev = fossil_strdup(zGoto);
726 int i;
@@ -728,12 +777,12 @@
728 zAbbrev[i] = 0;
729 if( g.zLogin ){
730 @ <p>Use a different login with greater privilege than <b>%h(g.zLogin)</b>
731 @ to access <b>%h(zAbbrev)</b>.
732 }else if( anonFlag ){
733 @ <p>Login as <b>anonymous</b> or any named user
734 @ to access page <b>%h(zAbbrev)</b>.
735 }else{
736 @ <p>Login as a named user to access page <b>%h(zAbbrev)</b>.
737 }
738 fossil_free(zAbbrev);
739 }
@@ -748,26 +797,27 @@
748 if( zGoto ){
749 @ <input type="hidden" name="g" value="%h(zGoto)">
750 }
751 if( anonFlag ){
752 @ <input type="hidden" name="anon" value="1">
 
753 }
754 if( g.zLogin ){
755 @ <p>Currently logged in as <b>%h(g.zLogin)</b>.
756 @ <input type="submit" name="out" value="Logout" autofocus></p>
757 @ </form>
758 }else{
759 unsigned int uSeed = captcha_seed();
760 if( g.zLogin==0 && (anonFlag || zGoto==0) ){
761 zAnonPw = db_text(0, "SELECT pw FROM user"
762 " WHERE login='anonymous'"
763 " AND cap!=''");
764 }else{
765 zAnonPw = 0;
766 }
767 @ <table class="login_out">
768 if( P("HTTPS")==0 ){
769 @ <tr><td class="form_label">Warning:</td>
770 @ <td><span class='securityWarning'>
771 @ Login information, including the password,
772 @ will be sent in the clear over an unencrypted connection.
773 if( !g.sslNotAvailable ){
@@ -774,41 +824,55 @@
774 @ Consider logging in at
775 @ <a href='%s(g.zHttpsURL)'>%h(g.zHttpsURL)</a> instead.
776 }
777 @ </span></td></tr>
778 }
779 @ <tr>
780 @ <td class="form_label" id="userlabel1">User ID:</td>
781 @ <td><input type="text" id="u" aria-labelledby="userlabel1" name="u" \
782 @ size="30" value="%s(anonFlag?"anonymous":"")" autofocus></td>
783 @ </tr>
 
 
784 @ <tr>
785 @ <td class="form_label" id="pswdlabel">Password:</td>
786 @ <td><input aria-labelledby="pswdlabel" type="password" id="p" \
787 @ name="p" value="" size="30">\
788 if( zAnonPw && !noAnon ){
 
 
 
 
 
789 captcha_speakit_button(uSeed, "Speak password for \"anonymous\"");
790 }
791 @ </td>
792 @ </tr>
793 @ <tr>
794 @ <td></td>
795 @ <td><input type="checkbox" name="remember" value="1" \
796 @ id="remember-me" %s(rememberMe ? "checked=\"checked\"" : "")>
797 @ <label for="remember-me">Remember me?</label></td>
798 @ </tr>
799 @ <tr>
800 @ <td></td>
801 @ <td><input type="submit" name="in" value="Login">
802 @ </tr>
803 if( !noAnon && login_self_register_available(0) ){
 
 
 
 
 
 
 
804 @ <tr>
805 @ <td></td>
806 @ <td><input type="submit" name="self" value="Create A New Account">
807 @ </tr>
808 }
809 if( login_self_password_reset_available() ){
810 @ <tr>
811 @ <td></td>
812 @ <td><input type="submit" name="pwreset" value="Reset My Password">
813 @ </tr>
814 }
@@ -817,27 +881,29 @@
817 const char *zDecoded = captcha_decode(uSeed, 0);
818 int bAutoCaptcha = db_get_boolean("auto-captcha", 0);
819 char *zCaptcha = captcha_render(zDecoded);
820
821 @ <p><input type="hidden" name="cs" value="%u(uSeed)">
822 @ Visitors may enter <b>anonymous</b> as the user-ID with
823 @ the 8-character hexadecimal password shown below:</p>
 
 
824 @ <div class="captcha"><table class="captcha"><tr><td>\
825 @ <pre class="captcha">
826 @ %h(zCaptcha)
827 @ </pre></td></tr></table>
828 if( bAutoCaptcha ) {
829 @ <input type="button" value="Fill out captcha" id='autofillButton' \
830 @ data-af='%s(zDecoded)'>
831 builtin_request_js("login.js");
832 }
833 @ </div>
834 free(zCaptcha);
835 }
836 @ </form>
837 }
838 if( login_is_individual() ){
839 if( g.perm.EmailAlert && alert_enabled() ){
840 @ <hr>
841 @ <p>Configure <a href="%R/alerts">Email Alerts</a>
842 @ for user <b>%h(g.zLogin)</b></p>
843 }
@@ -845,15 +911,18 @@
845 @ <hr><p>
846 @ <a href="%R/timeline?ss=v&y=f&vfx&u=%t(g.zLogin)">Forum
847 @ post timeline</a> for user <b>%h(g.zLogin)</b></p>
848 }
849 }
850 @ <hr><p>
851 @ Select your preferred <a href="%R/skins">site skin</a>.
852 @ </p>
853 @ <hr><p>
854 @ Manage your <a href="%R/cookies">cookies</a>.</p>
 
 
 
855 if( login_is_individual() ){
856 if( g.perm.Password ){
857 char *zRPW = fossil_random_password(12);
858 @ <hr>
859 @ <p>Change Password for user <b>%h(g.zLogin)</b>:</p>
@@ -1262,98 +1331,10 @@
1262 }
1263 fossil_free(zDecode);
1264 return uid;
1265 }
1266
1267 /*
1268 ** SETTING: robot-restrict width=40 block-text
1269 ** The VALUE of this setting is a list of GLOB patterns that match
1270 ** pages for which complex HTTP requests from robots should be disallowed.
1271 ** The recommended value for this setting is:
1272 **
1273 ** timeline,vdiff,fdiff,annotate,blame
1274 **
1275 */
1276
1277 /*
1278 ** Check to see if the current HTTP request is a complex request that
1279 ** is coming from a robot and if access should restricted for such robots.
1280 ** For the purposes of this module, a "complex request" is an HTTP
1281 ** request with one or more query parameters other than "name".
1282 **
1283 ** If this routine determines that robots should be restricted, then
1284 ** this routine publishes a redirect to the honeypot and exits without
1285 ** returning to the caller.
1286 **
1287 ** This routine believes that this is a complex request is coming from
1288 ** a robot if all of the following are true:
1289 **
1290 ** * The user is "nobody".
1291 ** * Either the REFERER field of the HTTP header is missing or empty,
1292 ** or the USERAGENT field of the HTTP header suggests that
1293 ** the request as coming from a robot.
1294 ** * There are one or more query parameters other than "name".
1295 **
1296 ** Robot restrictions are governed by settings.
1297 **
1298 ** robot-restrict The value is a list of GLOB patterns for pages
1299 ** that should restrict robot access. No restrictions
1300 ** are applied if this setting is undefined or is
1301 ** an empty string.
1302 */
1303 void login_restrict_robot_access(void){
1304 const char *zGlob;
1305 int isMatch = 1;
1306 int nQP; /* Number of query parameters other than name= */
1307 if( g.zLogin!=0 ) return;
1308 zGlob = db_get("robot-restrict",0);
1309 if( zGlob==0 || zGlob[0]==0 ) return;
1310 if( g.isHuman ){
1311 const char *zReferer;
1312 const char *zAccept;
1313 const char *zBr;
1314 zReferer = P("HTTP_REFERER");
1315 if( zReferer && zReferer[0]!=0 ) return;
1316
1317 /* Robots typically do not accept the brotli encoding, at least not
1318 ** at the time of this writing (2025-04-01), but standard web-browser
1319 ** all generally do accept brotli. So if brotli is accepted,
1320 ** assume we are not talking to a robot. We might want to revisit this
1321 ** heuristic in the future...
1322 */
1323 if( (zAccept = P("HTTP_ACCEPT_ENCODING"))!=0
1324 && (zBr = strstr(zAccept,"br"))!=0
1325 && !fossil_isalnum(zBr[2])
1326 && (zBr==zAccept || !fossil_isalnum(zBr[-1]))
1327 ){
1328 return;
1329 }
1330 }
1331 nQP = cgi_qp_count();
1332 if( nQP<1 ) return;
1333 isMatch = glob_multi_match(zGlob, g.zPath);
1334 if( !isMatch ) return;
1335
1336 /* Check for exceptions to the restriction on the number of query
1337 ** parameters. */
1338 zGlob = db_get("robot-restrict-qp",0);
1339 if( zGlob && zGlob[0] ){
1340 char *zPath = mprintf("%s/%d", g.zPath, nQP);
1341 isMatch = glob_multi_match(zGlob, zPath);
1342 fossil_free(zPath);
1343 if( isMatch ) return;
1344 }
1345
1346 /* If we reach this point, it means we have a situation where we
1347 ** want to restrict the activity of a robot.
1348 */
1349 g.isHuman = 0;
1350 (void)exclude_spiders(0);
1351 cgi_reply();
1352 fossil_exit(0);
1353 }
1354
1355 /*
1356 ** When this routine is called, we know that the request does not
1357 ** have a login on the present repository. This routine checks to
1358 ** see if their login cookie might be for another member of the
1359 ** login-group.
@@ -1388,11 +1369,11 @@
1388 **
1389 ** g.userUid Database USER.UID value. Might be -1 for "nobody"
1390 ** g.zLogin Database USER.LOGIN value. NULL for user "nobody"
1391 ** g.perm Permissions granted to this user
1392 ** g.anon Permissions that would be available to anonymous
1393 ** g.isHuman True if the user is human, not a spider or robot
1394 ** g.perm Populated based on user account's capabilities
1395 **
1396 */
1397 void login_check_credentials(void){
1398 int uid = 0; /* User id */
@@ -1429,11 +1410,11 @@
1429 uid = db_int(0, "SELECT uid FROM user WHERE cap LIKE '%%s%%'");
1430 }
1431 g.zLogin = db_text("?", "SELECT login FROM user WHERE uid=%d", uid);
1432 zCap = "sxy";
1433 g.noPswd = 1;
1434 g.isHuman = 1;
1435 zSeed = db_text("??", "SELECT uid||quote(login)||quote(pw)||quote(cookie)"
1436 " FROM user WHERE uid=%d", uid);
1437 login_create_csrf_secret(zSeed);
1438 fossil_free(zSeed);
1439 }
@@ -1457,33 +1438,38 @@
1457 }
1458 }
1459 }
1460 if( zUser==0 ){
1461 /* Invalid cookie */
1462 }else if( fossil_strcmp(zUser, "anonymous")==0 ){
1463 /* Cookies of the form "HASH/TIME/anonymous". The TIME must not be
1464 ** too old and the sha1 hash of TIME/SECRET must match HASH.
1465 ** SECRET is the "captcha-secret" value in the repository.
 
 
 
 
1466 */
1467 double rTime = atof(zArg);
 
1468 Blob b;
1469 char *zSecret;
1470 int n = 0;
1471
1472 do{
1473 blob_zero(&b);
1474 zSecret = captcha_secret(n++);
1475 if( zSecret==0 ) break;
1476 blob_appendf(&b, "%s/%s", zArg, zSecret);
1477 sha1sum_blob(&b, &b);
1478 if( fossil_strcmp(zHash, blob_str(&b))==0 ){
1479 uid = db_int(0,
1480 "SELECT uid FROM user WHERE login='anonymous'"
1481 " AND octet_length(cap)>0"
1482 " AND octet_length(pw)>0"
1483 " AND %.17g+0.25>julianday('now')",
1484 rTime
1485 );
1486 }
1487 }while( uid==0 );
1488 blob_reset(&b);
1489 }else{
@@ -1559,12 +1545,15 @@
1559 login_create_csrf_secret("none");
1560 }
1561
1562 login_set_uid(uid, zCap);
1563
1564 /* Maybe restrict access to robots */
1565 login_restrict_robot_access();
 
 
 
1566 }
1567
1568 /*
1569 ** Set the current logged in user to be uid. zCap is precomputed
1570 ** (override) capabilities. If zCap==0, then look up the capabilities
@@ -1599,15 +1588,15 @@
1599 g.userUid = uid;
1600 if( fossil_strcmp(g.zLogin,"nobody")==0 ){
1601 g.zLogin = 0;
1602 }
1603 if( PB("isrobot") ){
1604 g.isHuman = 0;
1605 }else if( g.zLogin==0 ){
1606 g.isHuman = isHuman(P("HTTP_USER_AGENT"));
1607 }else{
1608 g.isHuman = 1;
1609 }
1610
1611 /* Set the capabilities */
1612 login_replace_capabilities(zCap, 0);
1613
@@ -1617,11 +1606,11 @@
1617 ** enabled for this repository and make appropriate adjustments to the
1618 ** permission flags if it is. This should be done before the permissions
1619 ** are (potentially) copied to the anonymous permission set; otherwise,
1620 ** those will be out-of-sync.
1621 */
1622 if( zCap[0] && !g.perm.Hyperlink && g.isHuman ){
1623 int autoLink = db_get_int("auto-hyperlink",1);
1624 if( autoLink==1 ){
1625 g.jsHref = 1;
1626 g.perm.Hyperlink = 1;
1627 }else if( autoLink==2 ){
@@ -1927,11 +1916,11 @@
1927 blob_appendf(&redir, "%R/login?g=%T", zPathInfo);
1928 }
1929 if( zQS && zQS[0] ){
1930 blob_appendf(&redir, "%%3f%T", zQS);
1931 }
1932 if( anonOk ) blob_append(&redir, "&anon", 5);
1933 cgi_redirect(blob_str(&redir));
1934 /* NOTREACHED */
1935 assert(0);
1936 }
1937 }
@@ -1941,11 +1930,11 @@
1941 ** the anonymous user has Hyperlink permission, then paint a mesage
1942 ** to inform the user that much more information is available by
1943 ** logging in as anonymous.
1944 */
1945 void login_anonymous_available(void){
1946 if( !g.perm.Hyperlink && g.anon.Hyperlink ){
1947 const char *zUrl = PD("PATH_INFO", "");
1948 @ <p>Many <span class="disabled">hyperlinks are disabled.</span><br>
1949 @ Use <a href="%R/login?anon=1&amp;g=%T(zUrl)">anonymous login</a>
1950 @ to enable hyperlinks.</p>
1951 }
1952
--- src/login.c
+++ src/login.c
@@ -160,10 +160,11 @@
160
161 if( zUsername==0 ) return 0;
162 else if( zPassword==0 ) return 0;
163 else if( zCS==0 ) return 0;
164 else if( fossil_strcmp(zUsername,"anonymous")!=0 ) return 0;
165 else if( anon_cookie_lifespan()==0 ) return 0;
166 while( 1/*exit-by-break*/ ){
167 zPw = captcha_decode((unsigned int)atoi(zCS), n);
168 if( zPw==0 ) return 0;
169 if( fossil_stricmp(zPw, zPassword)==0 ) break;
170 n++;
@@ -338,33 +339,62 @@
339 *zDest = zCookie;
340 }else{
341 free(zCookie);
342 }
343 }
344
345 /*
346 ** SETTING: anon-cookie-lifespan width=10 default=480
347 ** The number of minutes for which an anonymous login cookie is
348 ** valid. Anonymous logins are prohibited if this value is zero.
349 */
350
351
352 /*
353 ** The default lifetime of an anoymous cookie, in minutes.
354 */
355 #define ANONYMOUS_COOKIE_LIFESPAN (8*60)
356
357 /*
358 ** Return the lifetime of an anonymous cookie, in minutes.
359 */
360 int anon_cookie_lifespan(void){
361 static int lifespan = -1;
362 if( lifespan<0 ){
363 lifespan = db_get_int("anon-cookie-lifespan", ANONYMOUS_COOKIE_LIFESPAN);
364 if( lifespan<0 ) lifespan = 0;
365 }
366 return lifespan;
367 }
368
369 /* Sets a cookie for an anonymous user login, which looks like this:
370 **
371 ** HASH/TIME/anonymous
372 **
373 ** Where HASH is the sha1sum of TIME/USERAGENT/SECRET, in which SECRET
374 ** is captcha-secret and USERAGENT is the HTTP_USER_AGENT value.
375 **
376 ** If zCookieDest is not NULL then the generated cookie is assigned to
377 ** *zCookieDest and the caller must eventually free() it.
378 **
379 ** If bSessionCookie is true, the cookie will be a session cookie.
380 **
381 ** Search for tag-20250817a to find the code that recognizes this cookie.
382 */
383 void login_set_anon_cookie(char **zCookieDest, int bSessionCookie){
384 char *zNow; /* Current time (julian day number) */
385 char *zCookie; /* The login cookie */
386 const char *zUserAgent; /* The user agent */
387 const char *zCookieName; /* Name of the login cookie */
388 Blob b; /* Blob used during cookie construction */
389 int expires = bSessionCookie ? 0 : anon_cookie_lifespan();
390 zCookieName = login_cookie_name();
391 zNow = db_text("0", "SELECT julianday('now')");
392 assert( zCookieName && zNow );
393 blob_init(&b, zNow, -1);
394 zUserAgent = PD("HTTP_USER_AGENT","nil");
395 blob_appendf(&b, "/%s/%z", zUserAgent, captcha_secret(0));
396 sha1sum_blob(&b, &b);
397 zCookie = mprintf("%s/%s/anonymous", blob_buffer(&b), zNow);
398 blob_reset(&b);
399 cgi_set_cookie(zCookieName, zCookie, login_cookie_path(), expires);
400 if( zCookieDest ){
@@ -581,17 +611,35 @@
611 /* If the "Reset Password" button in the form was pressed, render
612 ** the Request Password Reset page in place of this one. */
613 login_reqpwreset_page();
614 return;
615 }
616
617 /* If the "anon" query parameter is 1 or 2, that means rework the web-page
618 ** to make it a more user-friendly captcha. Extraneous text and boxes
619 ** are omitted. The user has just the captcha image and an entry box
620 ** and a "Verify" button. Underneath is the same login page for user
621 ** "anonymous", just displayed in an easier to digest format for one-time
622 ** visitors.
623 **
624 ** anon=1 is advisory and only has effect if there is not some other login
625 ** cookie. anon=2 means always show the captcha.
626 */
627 anonFlag = anon_cookie_lifespan()>0 ? atoi(PD("anon","0")) : 0;
628 if( anonFlag==2 ){
629 g.zLogin = 0;
630 }else{
631 login_check_credentials();
632 if( g.zLogin!=0 ) anonFlag = 0;
633 }
634
635 fossil_redirect_to_https_if_needed(1);
636 sqlite3_create_function(g.db, "constant_time_cmp", 2, SQLITE_UTF8, 0,
637 constant_time_cmp_function, 0, 0);
638 zUsername = P("u");
639 zPasswd = P("p");
640
641 /* Handle log-out requests */
642 if( P("out") && cgi_csrf_safe(2) ){
643 login_clear_login_data();
644 login_redirect_to_g();
645 return;
@@ -717,10 +765,11 @@
765 login_redirect_to_g();
766 }
767 }
768 style_set_current_feature("login");
769 style_header("Login/Logout");
770 if( anonFlag==2 ) g.zLogin = 0;
771 style_adunit_config(ADUNIT_OFF);
772 @ %s(zErrMsg)
773 if( zGoto && !noAnon ){
774 char *zAbbrev = fossil_strdup(zGoto);
775 int i;
@@ -728,12 +777,12 @@
777 zAbbrev[i] = 0;
778 if( g.zLogin ){
779 @ <p>Use a different login with greater privilege than <b>%h(g.zLogin)</b>
780 @ to access <b>%h(zAbbrev)</b>.
781 }else if( anonFlag ){
782 @ <p><b>Verify that you are human by typing in the 8-character text
783 @ password shown below.</b></p>
784 }else{
785 @ <p>Login as a named user to access page <b>%h(zAbbrev)</b>.
786 }
787 fossil_free(zAbbrev);
788 }
@@ -748,26 +797,27 @@
797 if( zGoto ){
798 @ <input type="hidden" name="g" value="%h(zGoto)">
799 }
800 if( anonFlag ){
801 @ <input type="hidden" name="anon" value="1">
802 @ <input type="hidden" name="u" value="anonymous">
803 }
804 if( g.zLogin ){
805 @ <p>Currently logged in as <b>%h(g.zLogin)</b>.
806 @ <input type="submit" name="out" value="Logout" autofocus></p>
807 @ </form>
808 }else{
809 unsigned int uSeed = captcha_seed();
810 if( g.zLogin==0 && (anonFlag || zGoto==0) && anon_cookie_lifespan()>0 ){
811 zAnonPw = db_text(0, "SELECT pw FROM user"
812 " WHERE login='anonymous'"
813 " AND cap!=''");
814 }else{
815 zAnonPw = 0;
816 }
817 @ <table class="login_out">
818 if( P("HTTPS")==0 && !anonFlag ){
819 @ <tr><td class="form_label">Warning:</td>
820 @ <td><span class='securityWarning'>
821 @ Login information, including the password,
822 @ will be sent in the clear over an unencrypted connection.
823 if( !g.sslNotAvailable ){
@@ -774,41 +824,55 @@
824 @ Consider logging in at
825 @ <a href='%s(g.zHttpsURL)'>%h(g.zHttpsURL)</a> instead.
826 }
827 @ </span></td></tr>
828 }
829 if( !anonFlag ){
830 @ <tr>
831 @ <td class="form_label" id="userlabel1">User ID:</td>
832 @ <td><input type="text" id="u" aria-labelledby="userlabel1" name="u" \
833 @ size="30" value="" autofocus></td>
834 @ </tr>
835 }
836 @ <tr>
837 @ <td class="form_label" id="pswdlabel">Password:</td>
838 @ <td><input aria-labelledby="pswdlabel" type="password" id="p" \
839 @ name="p" value="" size="30"%s(anonFlag ? " autofocus" : "")>
840 if( anonFlag ){
841 @ </td></tr>
842 @ <tr>
843 @ <td></td><td>\
844 captcha_speakit_button(uSeed, "Read the password out loud");
845 }else if( zAnonPw && !noAnon ){
846 captcha_speakit_button(uSeed, "Speak password for \"anonymous\"");
847 }
848 @ </td>
849 @ </tr>
850 if( !anonFlag ){
851 @ <tr>
852 @ <td></td>
853 @ <td><input type="checkbox" name="remember" value="1" \
854 @ id="remember-me" %s(rememberMe ? "checked=\"checked\"" : "")>
855 @ <label for="remember-me">Remember me?</label></td>
856 @ </tr>
857 @ <tr>
858 @ <td></td>
859 @ <td><input type="submit" name="in" value="Login">
860 @ </tr>
861 }else{
862 @ <tr>
863 @ <td></td>
864 @ <td><input type="submit" name="in" value="Verify that I am human">
865 @ </tr>
866 }
867 if( !anonFlag && !noAnon && login_self_register_available(0) ){
868 @ <tr>
869 @ <td></td>
870 @ <td><input type="submit" name="self" value="Create A New Account">
871 @ </tr>
872 }
873 if( !anonFlag && login_self_password_reset_available() ){
874 @ <tr>
875 @ <td></td>
876 @ <td><input type="submit" name="pwreset" value="Reset My Password">
877 @ </tr>
878 }
@@ -817,27 +881,29 @@
881 const char *zDecoded = captcha_decode(uSeed, 0);
882 int bAutoCaptcha = db_get_boolean("auto-captcha", 0);
883 char *zCaptcha = captcha_render(zDecoded);
884
885 @ <p><input type="hidden" name="cs" value="%u(uSeed)">
886 if( !anonFlag ){
887 @ Visitors may enter <b>anonymous</b> as the user-ID with
888 @ the 8-character hexadecimal password shown below:</p>
889 }
890 @ <div class="captcha"><table class="captcha"><tr><td>\
891 @ <pre class="captcha">
892 @ %h(zCaptcha)
893 @ </pre></td></tr></table>
894 if( bAutoCaptcha && !anonFlag ) {
895 @ <input type="button" value="Fill out captcha" id='autofillButton' \
896 @ data-af='%s(zDecoded)'>
897 builtin_request_js("login.js");
898 }
899 @ </div>
900 free(zCaptcha);
901 }
902 @ </form>
903 }
904 if( login_is_individual() && !anonFlag ){
905 if( g.perm.EmailAlert && alert_enabled() ){
906 @ <hr>
907 @ <p>Configure <a href="%R/alerts">Email Alerts</a>
908 @ for user <b>%h(g.zLogin)</b></p>
909 }
@@ -845,15 +911,18 @@
911 @ <hr><p>
912 @ <a href="%R/timeline?ss=v&y=f&vfx&u=%t(g.zLogin)">Forum
913 @ post timeline</a> for user <b>%h(g.zLogin)</b></p>
914 }
915 }
916 if( !anonFlag ){
917 @ <hr><p>
918 @ Select your preferred <a href="%R/skins">site skin</a>.
919 @ </p>
920 @ <hr><p>
921 @ Manage your <a href="%R/cookies">cookies</a> or your
922 @ <a href="%R/tokens">access tokens</a>.</p>
923 }
924 if( login_is_individual() ){
925 if( g.perm.Password ){
926 char *zRPW = fossil_random_password(12);
927 @ <hr>
928 @ <p>Change Password for user <b>%h(g.zLogin)</b>:</p>
@@ -1262,98 +1331,10 @@
1331 }
1332 fossil_free(zDecode);
1333 return uid;
1334 }
1335
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1336 /*
1337 ** When this routine is called, we know that the request does not
1338 ** have a login on the present repository. This routine checks to
1339 ** see if their login cookie might be for another member of the
1340 ** login-group.
@@ -1388,11 +1369,11 @@
1369 **
1370 ** g.userUid Database USER.UID value. Might be -1 for "nobody"
1371 ** g.zLogin Database USER.LOGIN value. NULL for user "nobody"
1372 ** g.perm Permissions granted to this user
1373 ** g.anon Permissions that would be available to anonymous
1374 ** g.isRobot True if the client is known to be a spider or robot
1375 ** g.perm Populated based on user account's capabilities
1376 **
1377 */
1378 void login_check_credentials(void){
1379 int uid = 0; /* User id */
@@ -1429,11 +1410,11 @@
1410 uid = db_int(0, "SELECT uid FROM user WHERE cap LIKE '%%s%%'");
1411 }
1412 g.zLogin = db_text("?", "SELECT login FROM user WHERE uid=%d", uid);
1413 zCap = "sxy";
1414 g.noPswd = 1;
1415 g.isRobot = 0;
1416 zSeed = db_text("??", "SELECT uid||quote(login)||quote(pw)||quote(cookie)"
1417 " FROM user WHERE uid=%d", uid);
1418 login_create_csrf_secret(zSeed);
1419 fossil_free(zSeed);
1420 }
@@ -1457,33 +1438,38 @@
1438 }
1439 }
1440 }
1441 if( zUser==0 ){
1442 /* Invalid cookie */
1443 }else if( fossil_strcmp(zUser, "anonymous")==0
1444 && anon_cookie_lifespan()>0 ){
1445 /* Cookies of the form "HASH/TIME/anonymous". The TIME must
1446 ** not be more than ANONYMOUS_COOKIE_LIFESPAN seconds ago and
1447 ** the sha1 hash of TIME/USERAGENT/SECRET must match HASH. USERAGENT
1448 ** is the HTTP_USER_AGENT of the client and SECRET is the
1449 ** "captcha-secret" value in the repository. See tag-20250817a
1450 ** for the code the creates this cookie.
1451 */
1452 double rTime = atof(zArg);
1453 const char *zUserAgent = PD("HTTP_USER_AGENT","nil");
1454 Blob b;
1455 char *zSecret;
1456 int n = 0;
1457
1458 do{
1459 blob_zero(&b);
1460 zSecret = captcha_secret(n++);
1461 if( zSecret==0 ) break;
1462 blob_appendf(&b, "%s/%s/%s", zArg, zUserAgent, zSecret);
1463 sha1sum_blob(&b, &b);
1464 if( fossil_strcmp(zHash, blob_str(&b))==0 ){
1465 uid = db_int(0,
1466 "SELECT uid FROM user WHERE login='anonymous'"
1467 " AND octet_length(cap)>0"
1468 " AND octet_length(pw)>0"
1469 " AND %.17g>julianday('now')",
1470 rTime+anon_cookie_lifespan()/1440.0
1471 );
1472 }
1473 }while( uid==0 );
1474 blob_reset(&b);
1475 }else{
@@ -1559,12 +1545,15 @@
1545 login_create_csrf_secret("none");
1546 }
1547
1548 login_set_uid(uid, zCap);
1549
1550 /* Maybe restrict access by robots */
1551 if( g.zLogin==0 && robot_restrict(g.zPath) ){
1552 cgi_reply();
1553 fossil_exit(0);
1554 }
1555 }
1556
1557 /*
1558 ** Set the current logged in user to be uid. zCap is precomputed
1559 ** (override) capabilities. If zCap==0, then look up the capabilities
@@ -1599,15 +1588,15 @@
1588 g.userUid = uid;
1589 if( fossil_strcmp(g.zLogin,"nobody")==0 ){
1590 g.zLogin = 0;
1591 }
1592 if( PB("isrobot") ){
1593 g.isRobot = 1;
1594 }else if( g.zLogin==0 ){
1595 g.isRobot = !isHuman(P("HTTP_USER_AGENT"));
1596 }else{
1597 g.isRobot = 0;
1598 }
1599
1600 /* Set the capabilities */
1601 login_replace_capabilities(zCap, 0);
1602
@@ -1617,11 +1606,11 @@
1606 ** enabled for this repository and make appropriate adjustments to the
1607 ** permission flags if it is. This should be done before the permissions
1608 ** are (potentially) copied to the anonymous permission set; otherwise,
1609 ** those will be out-of-sync.
1610 */
1611 if( zCap[0] && !g.perm.Hyperlink && !g.isRobot ){
1612 int autoLink = db_get_int("auto-hyperlink",1);
1613 if( autoLink==1 ){
1614 g.jsHref = 1;
1615 g.perm.Hyperlink = 1;
1616 }else if( autoLink==2 ){
@@ -1927,11 +1916,11 @@
1916 blob_appendf(&redir, "%R/login?g=%T", zPathInfo);
1917 }
1918 if( zQS && zQS[0] ){
1919 blob_appendf(&redir, "%%3f%T", zQS);
1920 }
1921 if( anonOk ) blob_append(&redir, "&anon=1", 7);
1922 cgi_redirect(blob_str(&redir));
1923 /* NOTREACHED */
1924 assert(0);
1925 }
1926 }
@@ -1941,11 +1930,11 @@
1930 ** the anonymous user has Hyperlink permission, then paint a mesage
1931 ** to inform the user that much more information is available by
1932 ** logging in as anonymous.
1933 */
1934 void login_anonymous_available(void){
1935 if( !g.perm.Hyperlink && g.anon.Hyperlink && anon_cookie_lifespan()>0 ){
1936 const char *zUrl = PD("PATH_INFO", "");
1937 @ <p>Many <span class="disabled">hyperlinks are disabled.</span><br>
1938 @ Use <a href="%R/login?anon=1&amp;g=%T(zUrl)">anonymous login</a>
1939 @ to enable hyperlinks.</p>
1940 }
1941
+46 -22
--- src/main.c
+++ src/main.c
@@ -83,11 +83,10 @@
8383
*/
8484
struct FossilUserPerms {
8585
char Setup; /* s: use Setup screens on web interface */
8686
char Admin; /* a: administrative permission */
8787
char Password; /* p: change password */
88
- char Query; /* q: create new reports */
8988
char Write; /* i: xfer inbound. check-in */
9089
char Read; /* o: xfer outbound. check-out */
9190
char Hyperlink; /* h: enable the display of hyperlinks */
9291
char Clone; /* g: clone */
9392
char RdWiki; /* j: view wiki via web */
@@ -234,11 +233,12 @@
234233
* applicable when using SEE on Windows or Linux. */
235234
#endif
236235
int useLocalauth; /* No login required if from 127.0.0.1 */
237236
int noPswd; /* Logged in without password (on 127.0.0.1) */
238237
int userUid; /* Integer user id */
239
- int isHuman; /* True if access by a human, not a spider or bot */
238
+ int isRobot; /* True if the client is definitely a robot. False
239
+ ** negatives are common for this flag */
240240
int comFmtFlags; /* Zero or more "COMMENT_PRINT_*" bit flags, should be
241241
** accessed through get_comment_format(). */
242242
const char *zSockName; /* Name of the unix-domain socket file */
243243
const char *zSockMode; /* File permissions for unix-domain socket */
244244
const char *zSockOwner; /* Owner, or owner:group for unix-domain socket */
@@ -289,10 +289,27 @@
289289
int allowSymlinks; /* Cached "allow-symlinks" option */
290290
int mainTimerId; /* Set to fossil_timer_start() */
291291
int nPendingRequest; /* # of HTTP requests in "fossil server" */
292292
int nRequest; /* Total # of HTTP request */
293293
int bAvoidDeltaManifests; /* Avoid using delta manifests if true */
294
+
295
+ /* State for communicating specific details between the inbound HTTP
296
+ ** header parser (cgi.c), xfer.c, and http.c. */
297
+ struct {
298
+ char *zLoginCard; /* Inbound "x-f-l-c" Cookie header. */
299
+ int fLoginCardMode; /* If non-0, emit login cards in outbound
300
+ ** requests as a HTTP cookie instead of as
301
+ ** part of the payload. Gets activated
302
+ ** on-demand based on xfer traffic
303
+ ** contents. Values, for
304
+ ** diagnostic/debugging purposes: 0x01=CLI
305
+ ** --flag, 0x02=cgi_setup_query_string(),
306
+ ** 0x04=page_xfer(),
307
+ ** 0x08=client_sync(). */
308
+ int remoteVersion; /* Remote fossil version. Used for negotiating
309
+ ** how to handle the login card. */
310
+ } syncInfo;
294311
#ifdef FOSSIL_ENABLE_JSON
295312
struct FossilJsonBits {
296313
int isJsonMode; /* True if running in JSON mode, else
297314
false. This changes how errors are
298315
reported. In JSON mode we try to
@@ -759,10 +776,17 @@
759776
g.tcl.argc = g.argc;
760777
g.tcl.argv = copy_args(g.argc, g.argv); /* save full arguments */
761778
#endif
762779
g.mainTimerId = fossil_timer_start();
763780
capture_case_sensitive_option();
781
+ g.syncInfo.fLoginCardMode =
782
+ /* The undocumented/unsupported --login-card-header provides a way
783
+ ** to force use of the feature added by the xfer-login-card branch
784
+ ** in 2025-07, intended for assisting in debugging any related
785
+ ** issues. It can be removed once we reach the level of "implicit
786
+ ** trust" in that feature. */
787
+ find_option("login-card-header",0,0) ? 0x01 : 0;
764788
g.zVfsName = find_option("vfs",0,1);
765789
if( g.zVfsName==0 ){
766790
g.zVfsName = fossil_getenv("FOSSIL_VFS");
767791
}
768792
if( g.zVfsName ){
@@ -1140,11 +1164,11 @@
11401164
*/
11411165
const char *find_repository_option(){
11421166
const char *zRepository = find_option("repository", "R", 1);
11431167
if( zRepository ){
11441168
if( g.zRepositoryOption ) fossil_free(g.zRepositoryOption);
1145
- g.zRepositoryOption = mprintf("%s", zRepository);
1169
+ g.zRepositoryOption = fossil_strdup(zRepository);
11461170
}
11471171
return g.zRepositoryOption;
11481172
}
11491173
11501174
/*
@@ -1386,20 +1410,20 @@
13861410
const char *zCur;
13871411
13881412
if( g.zBaseURL!=0 ) return;
13891413
if( zAltBase ){
13901414
int i, n, c;
1391
- g.zTop = g.zBaseURL = mprintf("%s", zAltBase);
1415
+ g.zTop = g.zBaseURL = fossil_strdup(zAltBase);
13921416
i = (int)strlen(g.zBaseURL);
13931417
while( i>3 && g.zBaseURL[i-1]=='/' ){ i--; }
13941418
g.zBaseURL[i] = 0;
13951419
if( strncmp(g.zTop, "http://", 7)==0 ){
13961420
/* it is HTTP, replace prefix with HTTPS. */
13971421
g.zHttpsURL = mprintf("https://%s", &g.zTop[7]);
13981422
}else if( strncmp(g.zTop, "https://", 8)==0 ){
13991423
/* it is already HTTPS, use it. */
1400
- g.zHttpsURL = mprintf("%s", g.zTop);
1424
+ g.zHttpsURL = fossil_strdup(g.zTop);
14011425
}else{
14021426
fossil_fatal("argument to --baseurl should be 'http://host/path'"
14031427
" or 'https://host/path'");
14041428
}
14051429
for(i=n=0; (c = g.zTop[i])!=0; i++){
@@ -1491,11 +1515,11 @@
14911515
/* In order for ?skin=... to work when visiting the site from
14921516
** a typical external link, we have to process it here, as
14931517
** that parameter gets lost during the redirect. We "could"
14941518
** pass the whole query string along instead, but that seems
14951519
** unnecessary. */
1496
- if(cgi_setup_query_string()>1){
1520
+ if(cgi_setup_query_string() & 0x02){
14971521
cookie_render();
14981522
}
14991523
cgi_redirectf("%R%s", db_get("index-page", "/index"));
15001524
}
15011525
@@ -1795,22 +1819,22 @@
17951819
}
17961820
17971821
17981822
/* Restrictions on the URI for security:
17991823
**
1800
- ** 1. Reject characters that are not ASCII alphanumerics,
1824
+ ** 1. Reject characters that are not ASCII alphanumerics,
18011825
** "-", "_", ".", "/", or unicode (above ASCII).
18021826
** In other words: No ASCII punctuation or control characters
18031827
** other than "-", "_", "." and "/".
1804
- ** 2. Exception to rule 1: Allow /X:/ where X is any ASCII
1828
+ ** 2. Exception to rule 1: Allow /X:/ where X is any ASCII
18051829
** alphabetic character at the beginning of the name on windows.
18061830
** 3. "-" may not occur immediately after "/"
18071831
** 4. "." may not be adjacent to another "." or to "/"
18081832
**
18091833
** Any character does not satisfy these constraints a Not Found
18101834
** error is returned.
1811
- */
1835
+ */
18121836
szFile = 0;
18131837
for(j=nBase+1, k=0; zRepo[j] && k<i-1; j++, k++){
18141838
char c = zRepo[j];
18151839
if( c>='a' && c<='z' ) continue;
18161840
if( c>='A' && c<='Z' ) continue;
@@ -2072,11 +2096,11 @@
20722096
cgi_redirectf("%R/ckout");
20732097
}else{
20742098
fossil_redirect_home() /*does not return*/;
20752099
}
20762100
}else{
2077
- zPath = mprintf("%s", zPathInfo);
2101
+ zPath = fossil_strdup(zPathInfo);
20782102
}
20792103
20802104
/* Make g.zPath point to the first element of the path. Make
20812105
** g.zExtra point to everything past that point.
20822106
*/
@@ -2483,21 +2507,21 @@
24832507
** If repository: is omitted, then terms of the PATH_INFO cgi parameter
24842508
** are appended to DIRECTORY looking for a repository (whose name ends
24852509
** in ".fossil") or a file in "files:".
24862510
*/
24872511
db_close(1);
2488
- g.zRepositoryName = mprintf("%s", blob_str(&value));
2512
+ g.zRepositoryName = fossil_strdup(blob_str(&value));
24892513
blob_reset(&value);
24902514
continue;
24912515
}
24922516
if( blob_eq(&key, "notfound:") && blob_token(&line, &value) ){
24932517
/* notfound: URL
24942518
**
24952519
** If using directory: and no suitable repository or file is found,
24962520
** then redirect to URL.
24972521
*/
2498
- zNotFound = mprintf("%s", blob_str(&value));
2522
+ zNotFound = fossil_strdup(blob_str(&value));
24992523
blob_reset(&value);
25002524
continue;
25012525
}
25022526
if( blob_eq(&key, "localauth") ){
25032527
/* localauth
@@ -2537,12 +2561,12 @@
25372561
&& blob_token(&line, &value2) ){
25382562
/* See the header comment on the redirect_web_page() function
25392563
** above for details. */
25402564
nRedirect++;
25412565
azRedirect = fossil_realloc(azRedirect, 2*nRedirect*sizeof(char*));
2542
- azRedirect[nRedirect*2-2] = mprintf("%s", blob_str(&value));
2543
- azRedirect[nRedirect*2-1] = mprintf("%s", blob_str(&value2));
2566
+ azRedirect[nRedirect*2-2] = fossil_strdup(blob_str(&value));
2567
+ azRedirect[nRedirect*2-1] = fossil_strdup(blob_str(&value2));
25442568
blob_reset(&value);
25452569
blob_reset(&value2);
25462570
continue;
25472571
}
25482572
if( blob_eq(&key, "files:") && blob_token(&line, &value) ){
@@ -2581,20 +2605,20 @@
25812605
/* errorlog: FILENAME
25822606
**
25832607
** Causes messages from warnings, errors, and panics to be appended
25842608
** to FILENAME.
25852609
*/
2586
- g.zErrlog = mprintf("%s", blob_str(&value));
2610
+ g.zErrlog = fossil_strdup(blob_str(&value));
25872611
blob_reset(&value);
25882612
continue;
25892613
}
25902614
if( blob_eq(&key, "extroot:") && blob_token(&line, &value) ){
25912615
/* extroot: DIRECTORY
25922616
**
25932617
** Enables the /ext webpage to use sub-cgi rooted at DIRECTORY
25942618
*/
2595
- g.zExtRoot = mprintf("%s", blob_str(&value));
2619
+ g.zExtRoot = fossil_strdup(blob_str(&value));
25962620
blob_reset(&value);
25972621
continue;
25982622
}
25992623
if( blob_eq(&key, "timeout:") && blob_token(&line, &value) ){
26002624
/* timeout: SECONDS
@@ -2651,11 +2675,11 @@
26512675
**
26522676
** Use the contents of FILENAME as the value of the site's
26532677
** "mainmenu" setting, overriding the contents (for this
26542678
** request) of the db-side setting or the hard-coded default.
26552679
*/
2656
- g.zMainMenuFile = mprintf("%s", blob_str(&value));
2680
+ g.zMainMenuFile = fossil_strdup(blob_str(&value));
26572681
blob_reset(&value);
26582682
continue;
26592683
}
26602684
if( blob_eq(&key, "cgi-debug:") && blob_token(&line, &value) ){
26612685
/* cgi-debug: FILENAME
@@ -2708,11 +2732,11 @@
27082732
db_must_be_within_tree();
27092733
}else{
27102734
const char *zRepo = g.argv[arg];
27112735
int isDir = file_isdir(zRepo, ExtFILE);
27122736
if( isDir==1 ){
2713
- g.zRepositoryName = mprintf("%s", zRepo);
2737
+ g.zRepositoryName = fossil_strdup(zRepo);
27142738
file_simplify_name(g.zRepositoryName, -1, 0);
27152739
}else{
27162740
if( isDir==0 && fCreate ){
27172741
const char *zPassword;
27182742
db_create_repository(zRepo);
@@ -2920,11 +2944,11 @@
29202944
** the argument being URL encoded, to avoid wildcard expansion in the
29212945
** shell. This option is for internal use and is undocumented.
29222946
*/
29232947
zFileGlob = find_option("files-urlenc",0,1);
29242948
if( zFileGlob ){
2925
- char *z = mprintf("%s", zFileGlob);
2949
+ char *z = fossil_strdup(zFileGlob);
29262950
dehttpize(z);
29272951
zFileGlob = z;
29282952
}else{
29292953
zFileGlob = find_option("files",0,1);
29302954
}
@@ -3330,11 +3354,11 @@
33303354
g.zExtRoot = find_option("extroot",0,1);
33313355
zJsMode = find_option("jsmode",0,1);
33323356
builtin_set_js_delivery_mode(zJsMode,0);
33333357
zFileGlob = find_option("files-urlenc",0,1);
33343358
if( zFileGlob ){
3335
- char *z = mprintf("%s", zFileGlob);
3359
+ char *z = fossil_strdup(zFileGlob);
33363360
dehttpize(z);
33373361
zFileGlob = z;
33383362
}else{
33393363
zFileGlob = find_option("files",0,1);
33403364
}
@@ -3511,11 +3535,11 @@
35113535
** "fossil ui --nobrowser" on the remote system and to set up a
35123536
** tunnel from the local machine to the remote. */
35133537
FILE *sshIn;
35143538
Blob ssh;
35153539
int bRunning = 0; /* True when fossil starts up on the remote */
3516
- int isRetry; /* True if on the second attempt */
3540
+ int isRetry; /* True if on the second attempt */
35173541
char zLine[1000];
35183542
35193543
blob_init(&ssh, 0, 0);
35203544
for(isRetry=0; isRetry<2 && !bRunning; isRetry++){
35213545
blob_reset(&ssh);
@@ -3550,11 +3574,11 @@
35503574
if( fCreate ) blob_appendf(&ssh, " --create");
35513575
blob_appendf(&ssh, " %$", g.argv[2]);
35523576
if( isRetry ){
35533577
fossil_print("First attempt to run \"fossil\" on %s failed\n"
35543578
"Retry: ", zRemote);
3555
- }
3579
+ }
35563580
fossil_print("%s\n", blob_str(&ssh));
35573581
sshIn = popen(blob_str(&ssh), "r");
35583582
if( sshIn==0 ){
35593583
fossil_fatal("unable to %s", blob_str(&ssh));
35603584
}
35613585
--- src/main.c
+++ src/main.c
@@ -83,11 +83,10 @@
83 */
84 struct FossilUserPerms {
85 char Setup; /* s: use Setup screens on web interface */
86 char Admin; /* a: administrative permission */
87 char Password; /* p: change password */
88 char Query; /* q: create new reports */
89 char Write; /* i: xfer inbound. check-in */
90 char Read; /* o: xfer outbound. check-out */
91 char Hyperlink; /* h: enable the display of hyperlinks */
92 char Clone; /* g: clone */
93 char RdWiki; /* j: view wiki via web */
@@ -234,11 +233,12 @@
234 * applicable when using SEE on Windows or Linux. */
235 #endif
236 int useLocalauth; /* No login required if from 127.0.0.1 */
237 int noPswd; /* Logged in without password (on 127.0.0.1) */
238 int userUid; /* Integer user id */
239 int isHuman; /* True if access by a human, not a spider or bot */
 
240 int comFmtFlags; /* Zero or more "COMMENT_PRINT_*" bit flags, should be
241 ** accessed through get_comment_format(). */
242 const char *zSockName; /* Name of the unix-domain socket file */
243 const char *zSockMode; /* File permissions for unix-domain socket */
244 const char *zSockOwner; /* Owner, or owner:group for unix-domain socket */
@@ -289,10 +289,27 @@
289 int allowSymlinks; /* Cached "allow-symlinks" option */
290 int mainTimerId; /* Set to fossil_timer_start() */
291 int nPendingRequest; /* # of HTTP requests in "fossil server" */
292 int nRequest; /* Total # of HTTP request */
293 int bAvoidDeltaManifests; /* Avoid using delta manifests if true */
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
294 #ifdef FOSSIL_ENABLE_JSON
295 struct FossilJsonBits {
296 int isJsonMode; /* True if running in JSON mode, else
297 false. This changes how errors are
298 reported. In JSON mode we try to
@@ -759,10 +776,17 @@
759 g.tcl.argc = g.argc;
760 g.tcl.argv = copy_args(g.argc, g.argv); /* save full arguments */
761 #endif
762 g.mainTimerId = fossil_timer_start();
763 capture_case_sensitive_option();
 
 
 
 
 
 
 
764 g.zVfsName = find_option("vfs",0,1);
765 if( g.zVfsName==0 ){
766 g.zVfsName = fossil_getenv("FOSSIL_VFS");
767 }
768 if( g.zVfsName ){
@@ -1140,11 +1164,11 @@
1140 */
1141 const char *find_repository_option(){
1142 const char *zRepository = find_option("repository", "R", 1);
1143 if( zRepository ){
1144 if( g.zRepositoryOption ) fossil_free(g.zRepositoryOption);
1145 g.zRepositoryOption = mprintf("%s", zRepository);
1146 }
1147 return g.zRepositoryOption;
1148 }
1149
1150 /*
@@ -1386,20 +1410,20 @@
1386 const char *zCur;
1387
1388 if( g.zBaseURL!=0 ) return;
1389 if( zAltBase ){
1390 int i, n, c;
1391 g.zTop = g.zBaseURL = mprintf("%s", zAltBase);
1392 i = (int)strlen(g.zBaseURL);
1393 while( i>3 && g.zBaseURL[i-1]=='/' ){ i--; }
1394 g.zBaseURL[i] = 0;
1395 if( strncmp(g.zTop, "http://", 7)==0 ){
1396 /* it is HTTP, replace prefix with HTTPS. */
1397 g.zHttpsURL = mprintf("https://%s", &g.zTop[7]);
1398 }else if( strncmp(g.zTop, "https://", 8)==0 ){
1399 /* it is already HTTPS, use it. */
1400 g.zHttpsURL = mprintf("%s", g.zTop);
1401 }else{
1402 fossil_fatal("argument to --baseurl should be 'http://host/path'"
1403 " or 'https://host/path'");
1404 }
1405 for(i=n=0; (c = g.zTop[i])!=0; i++){
@@ -1491,11 +1515,11 @@
1491 /* In order for ?skin=... to work when visiting the site from
1492 ** a typical external link, we have to process it here, as
1493 ** that parameter gets lost during the redirect. We "could"
1494 ** pass the whole query string along instead, but that seems
1495 ** unnecessary. */
1496 if(cgi_setup_query_string()>1){
1497 cookie_render();
1498 }
1499 cgi_redirectf("%R%s", db_get("index-page", "/index"));
1500 }
1501
@@ -1795,22 +1819,22 @@
1795 }
1796
1797
1798 /* Restrictions on the URI for security:
1799 **
1800 ** 1. Reject characters that are not ASCII alphanumerics,
1801 ** "-", "_", ".", "/", or unicode (above ASCII).
1802 ** In other words: No ASCII punctuation or control characters
1803 ** other than "-", "_", "." and "/".
1804 ** 2. Exception to rule 1: Allow /X:/ where X is any ASCII
1805 ** alphabetic character at the beginning of the name on windows.
1806 ** 3. "-" may not occur immediately after "/"
1807 ** 4. "." may not be adjacent to another "." or to "/"
1808 **
1809 ** Any character does not satisfy these constraints a Not Found
1810 ** error is returned.
1811 */
1812 szFile = 0;
1813 for(j=nBase+1, k=0; zRepo[j] && k<i-1; j++, k++){
1814 char c = zRepo[j];
1815 if( c>='a' && c<='z' ) continue;
1816 if( c>='A' && c<='Z' ) continue;
@@ -2072,11 +2096,11 @@
2072 cgi_redirectf("%R/ckout");
2073 }else{
2074 fossil_redirect_home() /*does not return*/;
2075 }
2076 }else{
2077 zPath = mprintf("%s", zPathInfo);
2078 }
2079
2080 /* Make g.zPath point to the first element of the path. Make
2081 ** g.zExtra point to everything past that point.
2082 */
@@ -2483,21 +2507,21 @@
2483 ** If repository: is omitted, then terms of the PATH_INFO cgi parameter
2484 ** are appended to DIRECTORY looking for a repository (whose name ends
2485 ** in ".fossil") or a file in "files:".
2486 */
2487 db_close(1);
2488 g.zRepositoryName = mprintf("%s", blob_str(&value));
2489 blob_reset(&value);
2490 continue;
2491 }
2492 if( blob_eq(&key, "notfound:") && blob_token(&line, &value) ){
2493 /* notfound: URL
2494 **
2495 ** If using directory: and no suitable repository or file is found,
2496 ** then redirect to URL.
2497 */
2498 zNotFound = mprintf("%s", blob_str(&value));
2499 blob_reset(&value);
2500 continue;
2501 }
2502 if( blob_eq(&key, "localauth") ){
2503 /* localauth
@@ -2537,12 +2561,12 @@
2537 && blob_token(&line, &value2) ){
2538 /* See the header comment on the redirect_web_page() function
2539 ** above for details. */
2540 nRedirect++;
2541 azRedirect = fossil_realloc(azRedirect, 2*nRedirect*sizeof(char*));
2542 azRedirect[nRedirect*2-2] = mprintf("%s", blob_str(&value));
2543 azRedirect[nRedirect*2-1] = mprintf("%s", blob_str(&value2));
2544 blob_reset(&value);
2545 blob_reset(&value2);
2546 continue;
2547 }
2548 if( blob_eq(&key, "files:") && blob_token(&line, &value) ){
@@ -2581,20 +2605,20 @@
2581 /* errorlog: FILENAME
2582 **
2583 ** Causes messages from warnings, errors, and panics to be appended
2584 ** to FILENAME.
2585 */
2586 g.zErrlog = mprintf("%s", blob_str(&value));
2587 blob_reset(&value);
2588 continue;
2589 }
2590 if( blob_eq(&key, "extroot:") && blob_token(&line, &value) ){
2591 /* extroot: DIRECTORY
2592 **
2593 ** Enables the /ext webpage to use sub-cgi rooted at DIRECTORY
2594 */
2595 g.zExtRoot = mprintf("%s", blob_str(&value));
2596 blob_reset(&value);
2597 continue;
2598 }
2599 if( blob_eq(&key, "timeout:") && blob_token(&line, &value) ){
2600 /* timeout: SECONDS
@@ -2651,11 +2675,11 @@
2651 **
2652 ** Use the contents of FILENAME as the value of the site's
2653 ** "mainmenu" setting, overriding the contents (for this
2654 ** request) of the db-side setting or the hard-coded default.
2655 */
2656 g.zMainMenuFile = mprintf("%s", blob_str(&value));
2657 blob_reset(&value);
2658 continue;
2659 }
2660 if( blob_eq(&key, "cgi-debug:") && blob_token(&line, &value) ){
2661 /* cgi-debug: FILENAME
@@ -2708,11 +2732,11 @@
2708 db_must_be_within_tree();
2709 }else{
2710 const char *zRepo = g.argv[arg];
2711 int isDir = file_isdir(zRepo, ExtFILE);
2712 if( isDir==1 ){
2713 g.zRepositoryName = mprintf("%s", zRepo);
2714 file_simplify_name(g.zRepositoryName, -1, 0);
2715 }else{
2716 if( isDir==0 && fCreate ){
2717 const char *zPassword;
2718 db_create_repository(zRepo);
@@ -2920,11 +2944,11 @@
2920 ** the argument being URL encoded, to avoid wildcard expansion in the
2921 ** shell. This option is for internal use and is undocumented.
2922 */
2923 zFileGlob = find_option("files-urlenc",0,1);
2924 if( zFileGlob ){
2925 char *z = mprintf("%s", zFileGlob);
2926 dehttpize(z);
2927 zFileGlob = z;
2928 }else{
2929 zFileGlob = find_option("files",0,1);
2930 }
@@ -3330,11 +3354,11 @@
3330 g.zExtRoot = find_option("extroot",0,1);
3331 zJsMode = find_option("jsmode",0,1);
3332 builtin_set_js_delivery_mode(zJsMode,0);
3333 zFileGlob = find_option("files-urlenc",0,1);
3334 if( zFileGlob ){
3335 char *z = mprintf("%s", zFileGlob);
3336 dehttpize(z);
3337 zFileGlob = z;
3338 }else{
3339 zFileGlob = find_option("files",0,1);
3340 }
@@ -3511,11 +3535,11 @@
3511 ** "fossil ui --nobrowser" on the remote system and to set up a
3512 ** tunnel from the local machine to the remote. */
3513 FILE *sshIn;
3514 Blob ssh;
3515 int bRunning = 0; /* True when fossil starts up on the remote */
3516 int isRetry; /* True if on the second attempt */
3517 char zLine[1000];
3518
3519 blob_init(&ssh, 0, 0);
3520 for(isRetry=0; isRetry<2 && !bRunning; isRetry++){
3521 blob_reset(&ssh);
@@ -3550,11 +3574,11 @@
3550 if( fCreate ) blob_appendf(&ssh, " --create");
3551 blob_appendf(&ssh, " %$", g.argv[2]);
3552 if( isRetry ){
3553 fossil_print("First attempt to run \"fossil\" on %s failed\n"
3554 "Retry: ", zRemote);
3555 }
3556 fossil_print("%s\n", blob_str(&ssh));
3557 sshIn = popen(blob_str(&ssh), "r");
3558 if( sshIn==0 ){
3559 fossil_fatal("unable to %s", blob_str(&ssh));
3560 }
3561
--- src/main.c
+++ src/main.c
@@ -83,11 +83,10 @@
83 */
84 struct FossilUserPerms {
85 char Setup; /* s: use Setup screens on web interface */
86 char Admin; /* a: administrative permission */
87 char Password; /* p: change password */
 
88 char Write; /* i: xfer inbound. check-in */
89 char Read; /* o: xfer outbound. check-out */
90 char Hyperlink; /* h: enable the display of hyperlinks */
91 char Clone; /* g: clone */
92 char RdWiki; /* j: view wiki via web */
@@ -234,11 +233,12 @@
233 * applicable when using SEE on Windows or Linux. */
234 #endif
235 int useLocalauth; /* No login required if from 127.0.0.1 */
236 int noPswd; /* Logged in without password (on 127.0.0.1) */
237 int userUid; /* Integer user id */
238 int isRobot; /* True if the client is definitely a robot. False
239 ** negatives are common for this flag */
240 int comFmtFlags; /* Zero or more "COMMENT_PRINT_*" bit flags, should be
241 ** accessed through get_comment_format(). */
242 const char *zSockName; /* Name of the unix-domain socket file */
243 const char *zSockMode; /* File permissions for unix-domain socket */
244 const char *zSockOwner; /* Owner, or owner:group for unix-domain socket */
@@ -289,10 +289,27 @@
289 int allowSymlinks; /* Cached "allow-symlinks" option */
290 int mainTimerId; /* Set to fossil_timer_start() */
291 int nPendingRequest; /* # of HTTP requests in "fossil server" */
292 int nRequest; /* Total # of HTTP request */
293 int bAvoidDeltaManifests; /* Avoid using delta manifests if true */
294
295 /* State for communicating specific details between the inbound HTTP
296 ** header parser (cgi.c), xfer.c, and http.c. */
297 struct {
298 char *zLoginCard; /* Inbound "x-f-l-c" Cookie header. */
299 int fLoginCardMode; /* If non-0, emit login cards in outbound
300 ** requests as a HTTP cookie instead of as
301 ** part of the payload. Gets activated
302 ** on-demand based on xfer traffic
303 ** contents. Values, for
304 ** diagnostic/debugging purposes: 0x01=CLI
305 ** --flag, 0x02=cgi_setup_query_string(),
306 ** 0x04=page_xfer(),
307 ** 0x08=client_sync(). */
308 int remoteVersion; /* Remote fossil version. Used for negotiating
309 ** how to handle the login card. */
310 } syncInfo;
311 #ifdef FOSSIL_ENABLE_JSON
312 struct FossilJsonBits {
313 int isJsonMode; /* True if running in JSON mode, else
314 false. This changes how errors are
315 reported. In JSON mode we try to
@@ -759,10 +776,17 @@
776 g.tcl.argc = g.argc;
777 g.tcl.argv = copy_args(g.argc, g.argv); /* save full arguments */
778 #endif
779 g.mainTimerId = fossil_timer_start();
780 capture_case_sensitive_option();
781 g.syncInfo.fLoginCardMode =
782 /* The undocumented/unsupported --login-card-header provides a way
783 ** to force use of the feature added by the xfer-login-card branch
784 ** in 2025-07, intended for assisting in debugging any related
785 ** issues. It can be removed once we reach the level of "implicit
786 ** trust" in that feature. */
787 find_option("login-card-header",0,0) ? 0x01 : 0;
788 g.zVfsName = find_option("vfs",0,1);
789 if( g.zVfsName==0 ){
790 g.zVfsName = fossil_getenv("FOSSIL_VFS");
791 }
792 if( g.zVfsName ){
@@ -1140,11 +1164,11 @@
1164 */
1165 const char *find_repository_option(){
1166 const char *zRepository = find_option("repository", "R", 1);
1167 if( zRepository ){
1168 if( g.zRepositoryOption ) fossil_free(g.zRepositoryOption);
1169 g.zRepositoryOption = fossil_strdup(zRepository);
1170 }
1171 return g.zRepositoryOption;
1172 }
1173
1174 /*
@@ -1386,20 +1410,20 @@
1410 const char *zCur;
1411
1412 if( g.zBaseURL!=0 ) return;
1413 if( zAltBase ){
1414 int i, n, c;
1415 g.zTop = g.zBaseURL = fossil_strdup(zAltBase);
1416 i = (int)strlen(g.zBaseURL);
1417 while( i>3 && g.zBaseURL[i-1]=='/' ){ i--; }
1418 g.zBaseURL[i] = 0;
1419 if( strncmp(g.zTop, "http://", 7)==0 ){
1420 /* it is HTTP, replace prefix with HTTPS. */
1421 g.zHttpsURL = mprintf("https://%s", &g.zTop[7]);
1422 }else if( strncmp(g.zTop, "https://", 8)==0 ){
1423 /* it is already HTTPS, use it. */
1424 g.zHttpsURL = fossil_strdup(g.zTop);
1425 }else{
1426 fossil_fatal("argument to --baseurl should be 'http://host/path'"
1427 " or 'https://host/path'");
1428 }
1429 for(i=n=0; (c = g.zTop[i])!=0; i++){
@@ -1491,11 +1515,11 @@
1515 /* In order for ?skin=... to work when visiting the site from
1516 ** a typical external link, we have to process it here, as
1517 ** that parameter gets lost during the redirect. We "could"
1518 ** pass the whole query string along instead, but that seems
1519 ** unnecessary. */
1520 if(cgi_setup_query_string() & 0x02){
1521 cookie_render();
1522 }
1523 cgi_redirectf("%R%s", db_get("index-page", "/index"));
1524 }
1525
@@ -1795,22 +1819,22 @@
1819 }
1820
1821
1822 /* Restrictions on the URI for security:
1823 **
1824 ** 1. Reject characters that are not ASCII alphanumerics,
1825 ** "-", "_", ".", "/", or unicode (above ASCII).
1826 ** In other words: No ASCII punctuation or control characters
1827 ** other than "-", "_", "." and "/".
1828 ** 2. Exception to rule 1: Allow /X:/ where X is any ASCII
1829 ** alphabetic character at the beginning of the name on windows.
1830 ** 3. "-" may not occur immediately after "/"
1831 ** 4. "." may not be adjacent to another "." or to "/"
1832 **
1833 ** Any character does not satisfy these constraints a Not Found
1834 ** error is returned.
1835 */
1836 szFile = 0;
1837 for(j=nBase+1, k=0; zRepo[j] && k<i-1; j++, k++){
1838 char c = zRepo[j];
1839 if( c>='a' && c<='z' ) continue;
1840 if( c>='A' && c<='Z' ) continue;
@@ -2072,11 +2096,11 @@
2096 cgi_redirectf("%R/ckout");
2097 }else{
2098 fossil_redirect_home() /*does not return*/;
2099 }
2100 }else{
2101 zPath = fossil_strdup(zPathInfo);
2102 }
2103
2104 /* Make g.zPath point to the first element of the path. Make
2105 ** g.zExtra point to everything past that point.
2106 */
@@ -2483,21 +2507,21 @@
2507 ** If repository: is omitted, then terms of the PATH_INFO cgi parameter
2508 ** are appended to DIRECTORY looking for a repository (whose name ends
2509 ** in ".fossil") or a file in "files:".
2510 */
2511 db_close(1);
2512 g.zRepositoryName = fossil_strdup(blob_str(&value));
2513 blob_reset(&value);
2514 continue;
2515 }
2516 if( blob_eq(&key, "notfound:") && blob_token(&line, &value) ){
2517 /* notfound: URL
2518 **
2519 ** If using directory: and no suitable repository or file is found,
2520 ** then redirect to URL.
2521 */
2522 zNotFound = fossil_strdup(blob_str(&value));
2523 blob_reset(&value);
2524 continue;
2525 }
2526 if( blob_eq(&key, "localauth") ){
2527 /* localauth
@@ -2537,12 +2561,12 @@
2561 && blob_token(&line, &value2) ){
2562 /* See the header comment on the redirect_web_page() function
2563 ** above for details. */
2564 nRedirect++;
2565 azRedirect = fossil_realloc(azRedirect, 2*nRedirect*sizeof(char*));
2566 azRedirect[nRedirect*2-2] = fossil_strdup(blob_str(&value));
2567 azRedirect[nRedirect*2-1] = fossil_strdup(blob_str(&value2));
2568 blob_reset(&value);
2569 blob_reset(&value2);
2570 continue;
2571 }
2572 if( blob_eq(&key, "files:") && blob_token(&line, &value) ){
@@ -2581,20 +2605,20 @@
2605 /* errorlog: FILENAME
2606 **
2607 ** Causes messages from warnings, errors, and panics to be appended
2608 ** to FILENAME.
2609 */
2610 g.zErrlog = fossil_strdup(blob_str(&value));
2611 blob_reset(&value);
2612 continue;
2613 }
2614 if( blob_eq(&key, "extroot:") && blob_token(&line, &value) ){
2615 /* extroot: DIRECTORY
2616 **
2617 ** Enables the /ext webpage to use sub-cgi rooted at DIRECTORY
2618 */
2619 g.zExtRoot = fossil_strdup(blob_str(&value));
2620 blob_reset(&value);
2621 continue;
2622 }
2623 if( blob_eq(&key, "timeout:") && blob_token(&line, &value) ){
2624 /* timeout: SECONDS
@@ -2651,11 +2675,11 @@
2675 **
2676 ** Use the contents of FILENAME as the value of the site's
2677 ** "mainmenu" setting, overriding the contents (for this
2678 ** request) of the db-side setting or the hard-coded default.
2679 */
2680 g.zMainMenuFile = fossil_strdup(blob_str(&value));
2681 blob_reset(&value);
2682 continue;
2683 }
2684 if( blob_eq(&key, "cgi-debug:") && blob_token(&line, &value) ){
2685 /* cgi-debug: FILENAME
@@ -2708,11 +2732,11 @@
2732 db_must_be_within_tree();
2733 }else{
2734 const char *zRepo = g.argv[arg];
2735 int isDir = file_isdir(zRepo, ExtFILE);
2736 if( isDir==1 ){
2737 g.zRepositoryName = fossil_strdup(zRepo);
2738 file_simplify_name(g.zRepositoryName, -1, 0);
2739 }else{
2740 if( isDir==0 && fCreate ){
2741 const char *zPassword;
2742 db_create_repository(zRepo);
@@ -2920,11 +2944,11 @@
2944 ** the argument being URL encoded, to avoid wildcard expansion in the
2945 ** shell. This option is for internal use and is undocumented.
2946 */
2947 zFileGlob = find_option("files-urlenc",0,1);
2948 if( zFileGlob ){
2949 char *z = fossil_strdup(zFileGlob);
2950 dehttpize(z);
2951 zFileGlob = z;
2952 }else{
2953 zFileGlob = find_option("files",0,1);
2954 }
@@ -3330,11 +3354,11 @@
3354 g.zExtRoot = find_option("extroot",0,1);
3355 zJsMode = find_option("jsmode",0,1);
3356 builtin_set_js_delivery_mode(zJsMode,0);
3357 zFileGlob = find_option("files-urlenc",0,1);
3358 if( zFileGlob ){
3359 char *z = fossil_strdup(zFileGlob);
3360 dehttpize(z);
3361 zFileGlob = z;
3362 }else{
3363 zFileGlob = find_option("files",0,1);
3364 }
@@ -3511,11 +3535,11 @@
3535 ** "fossil ui --nobrowser" on the remote system and to set up a
3536 ** tunnel from the local machine to the remote. */
3537 FILE *sshIn;
3538 Blob ssh;
3539 int bRunning = 0; /* True when fossil starts up on the remote */
3540 int isRetry; /* True if on the second attempt */
3541 char zLine[1000];
3542
3543 blob_init(&ssh, 0, 0);
3544 for(isRetry=0; isRetry<2 && !bRunning; isRetry++){
3545 blob_reset(&ssh);
@@ -3550,11 +3574,11 @@
3574 if( fCreate ) blob_appendf(&ssh, " --create");
3575 blob_appendf(&ssh, " %$", g.argv[2]);
3576 if( isRetry ){
3577 fossil_print("First attempt to run \"fossil\" on %s failed\n"
3578 "Retry: ", zRemote);
3579 }
3580 fossil_print("%s\n", blob_str(&ssh));
3581 sshIn = popen(blob_str(&ssh), "r");
3582 if( sshIn==0 ){
3583 fossil_fatal("unable to %s", blob_str(&ssh));
3584 }
3585
+12
--- src/main.mk
+++ src/main.mk
@@ -119,10 +119,11 @@
119119
$(SRCDIR)/purge.c \
120120
$(SRCDIR)/rebuild.c \
121121
$(SRCDIR)/regexp.c \
122122
$(SRCDIR)/repolist.c \
123123
$(SRCDIR)/report.c \
124
+ $(SRCDIR)/robot.c \
124125
$(SRCDIR)/rss.c \
125126
$(SRCDIR)/schema.c \
126127
$(SRCDIR)/search.c \
127128
$(SRCDIR)/security_audit.c \
128129
$(SRCDIR)/setup.c \
@@ -385,10 +386,11 @@
385386
$(OBJDIR)/purge_.c \
386387
$(OBJDIR)/rebuild_.c \
387388
$(OBJDIR)/regexp_.c \
388389
$(OBJDIR)/repolist_.c \
389390
$(OBJDIR)/report_.c \
391
+ $(OBJDIR)/robot_.c \
390392
$(OBJDIR)/rss_.c \
391393
$(OBJDIR)/schema_.c \
392394
$(OBJDIR)/search_.c \
393395
$(OBJDIR)/security_audit_.c \
394396
$(OBJDIR)/setup_.c \
@@ -535,10 +537,11 @@
535537
$(OBJDIR)/purge.o \
536538
$(OBJDIR)/rebuild.o \
537539
$(OBJDIR)/regexp.o \
538540
$(OBJDIR)/repolist.o \
539541
$(OBJDIR)/report.o \
542
+ $(OBJDIR)/robot.o \
540543
$(OBJDIR)/rss.o \
541544
$(OBJDIR)/schema.o \
542545
$(OBJDIR)/search.o \
543546
$(OBJDIR)/security_audit.o \
544547
$(OBJDIR)/setup.o \
@@ -878,10 +881,11 @@
878881
$(OBJDIR)/purge_.c:$(OBJDIR)/purge.h \
879882
$(OBJDIR)/rebuild_.c:$(OBJDIR)/rebuild.h \
880883
$(OBJDIR)/regexp_.c:$(OBJDIR)/regexp.h \
881884
$(OBJDIR)/repolist_.c:$(OBJDIR)/repolist.h \
882885
$(OBJDIR)/report_.c:$(OBJDIR)/report.h \
886
+ $(OBJDIR)/robot_.c:$(OBJDIR)/robot.h \
883887
$(OBJDIR)/rss_.c:$(OBJDIR)/rss.h \
884888
$(OBJDIR)/schema_.c:$(OBJDIR)/schema.h \
885889
$(OBJDIR)/search_.c:$(OBJDIR)/search.h \
886890
$(OBJDIR)/security_audit_.c:$(OBJDIR)/security_audit.h \
887891
$(OBJDIR)/setup_.c:$(OBJDIR)/setup.h \
@@ -1768,10 +1772,18 @@
17681772
17691773
$(OBJDIR)/report.o: $(OBJDIR)/report_.c $(OBJDIR)/report.h $(SRCDIR)/config.h
17701774
$(XTCC) -o $(OBJDIR)/report.o -c $(OBJDIR)/report_.c
17711775
17721776
$(OBJDIR)/report.h: $(OBJDIR)/headers
1777
+
1778
+$(OBJDIR)/robot_.c: $(SRCDIR)/robot.c $(OBJDIR)/translate
1779
+ $(OBJDIR)/translate $(SRCDIR)/robot.c >$@
1780
+
1781
+$(OBJDIR)/robot.o: $(OBJDIR)/robot_.c $(OBJDIR)/robot.h $(SRCDIR)/config.h
1782
+ $(XTCC) -o $(OBJDIR)/robot.o -c $(OBJDIR)/robot_.c
1783
+
1784
+$(OBJDIR)/robot.h: $(OBJDIR)/headers
17731785
17741786
$(OBJDIR)/rss_.c: $(SRCDIR)/rss.c $(OBJDIR)/translate
17751787
$(OBJDIR)/translate $(SRCDIR)/rss.c >$@
17761788
17771789
$(OBJDIR)/rss.o: $(OBJDIR)/rss_.c $(OBJDIR)/rss.h $(SRCDIR)/config.h
17781790
--- src/main.mk
+++ src/main.mk
@@ -119,10 +119,11 @@
119 $(SRCDIR)/purge.c \
120 $(SRCDIR)/rebuild.c \
121 $(SRCDIR)/regexp.c \
122 $(SRCDIR)/repolist.c \
123 $(SRCDIR)/report.c \
 
124 $(SRCDIR)/rss.c \
125 $(SRCDIR)/schema.c \
126 $(SRCDIR)/search.c \
127 $(SRCDIR)/security_audit.c \
128 $(SRCDIR)/setup.c \
@@ -385,10 +386,11 @@
385 $(OBJDIR)/purge_.c \
386 $(OBJDIR)/rebuild_.c \
387 $(OBJDIR)/regexp_.c \
388 $(OBJDIR)/repolist_.c \
389 $(OBJDIR)/report_.c \
 
390 $(OBJDIR)/rss_.c \
391 $(OBJDIR)/schema_.c \
392 $(OBJDIR)/search_.c \
393 $(OBJDIR)/security_audit_.c \
394 $(OBJDIR)/setup_.c \
@@ -535,10 +537,11 @@
535 $(OBJDIR)/purge.o \
536 $(OBJDIR)/rebuild.o \
537 $(OBJDIR)/regexp.o \
538 $(OBJDIR)/repolist.o \
539 $(OBJDIR)/report.o \
 
540 $(OBJDIR)/rss.o \
541 $(OBJDIR)/schema.o \
542 $(OBJDIR)/search.o \
543 $(OBJDIR)/security_audit.o \
544 $(OBJDIR)/setup.o \
@@ -878,10 +881,11 @@
878 $(OBJDIR)/purge_.c:$(OBJDIR)/purge.h \
879 $(OBJDIR)/rebuild_.c:$(OBJDIR)/rebuild.h \
880 $(OBJDIR)/regexp_.c:$(OBJDIR)/regexp.h \
881 $(OBJDIR)/repolist_.c:$(OBJDIR)/repolist.h \
882 $(OBJDIR)/report_.c:$(OBJDIR)/report.h \
 
883 $(OBJDIR)/rss_.c:$(OBJDIR)/rss.h \
884 $(OBJDIR)/schema_.c:$(OBJDIR)/schema.h \
885 $(OBJDIR)/search_.c:$(OBJDIR)/search.h \
886 $(OBJDIR)/security_audit_.c:$(OBJDIR)/security_audit.h \
887 $(OBJDIR)/setup_.c:$(OBJDIR)/setup.h \
@@ -1768,10 +1772,18 @@
1768
1769 $(OBJDIR)/report.o: $(OBJDIR)/report_.c $(OBJDIR)/report.h $(SRCDIR)/config.h
1770 $(XTCC) -o $(OBJDIR)/report.o -c $(OBJDIR)/report_.c
1771
1772 $(OBJDIR)/report.h: $(OBJDIR)/headers
 
 
 
 
 
 
 
 
1773
1774 $(OBJDIR)/rss_.c: $(SRCDIR)/rss.c $(OBJDIR)/translate
1775 $(OBJDIR)/translate $(SRCDIR)/rss.c >$@
1776
1777 $(OBJDIR)/rss.o: $(OBJDIR)/rss_.c $(OBJDIR)/rss.h $(SRCDIR)/config.h
1778
--- src/main.mk
+++ src/main.mk
@@ -119,10 +119,11 @@
119 $(SRCDIR)/purge.c \
120 $(SRCDIR)/rebuild.c \
121 $(SRCDIR)/regexp.c \
122 $(SRCDIR)/repolist.c \
123 $(SRCDIR)/report.c \
124 $(SRCDIR)/robot.c \
125 $(SRCDIR)/rss.c \
126 $(SRCDIR)/schema.c \
127 $(SRCDIR)/search.c \
128 $(SRCDIR)/security_audit.c \
129 $(SRCDIR)/setup.c \
@@ -385,10 +386,11 @@
386 $(OBJDIR)/purge_.c \
387 $(OBJDIR)/rebuild_.c \
388 $(OBJDIR)/regexp_.c \
389 $(OBJDIR)/repolist_.c \
390 $(OBJDIR)/report_.c \
391 $(OBJDIR)/robot_.c \
392 $(OBJDIR)/rss_.c \
393 $(OBJDIR)/schema_.c \
394 $(OBJDIR)/search_.c \
395 $(OBJDIR)/security_audit_.c \
396 $(OBJDIR)/setup_.c \
@@ -535,10 +537,11 @@
537 $(OBJDIR)/purge.o \
538 $(OBJDIR)/rebuild.o \
539 $(OBJDIR)/regexp.o \
540 $(OBJDIR)/repolist.o \
541 $(OBJDIR)/report.o \
542 $(OBJDIR)/robot.o \
543 $(OBJDIR)/rss.o \
544 $(OBJDIR)/schema.o \
545 $(OBJDIR)/search.o \
546 $(OBJDIR)/security_audit.o \
547 $(OBJDIR)/setup.o \
@@ -878,10 +881,11 @@
881 $(OBJDIR)/purge_.c:$(OBJDIR)/purge.h \
882 $(OBJDIR)/rebuild_.c:$(OBJDIR)/rebuild.h \
883 $(OBJDIR)/regexp_.c:$(OBJDIR)/regexp.h \
884 $(OBJDIR)/repolist_.c:$(OBJDIR)/repolist.h \
885 $(OBJDIR)/report_.c:$(OBJDIR)/report.h \
886 $(OBJDIR)/robot_.c:$(OBJDIR)/robot.h \
887 $(OBJDIR)/rss_.c:$(OBJDIR)/rss.h \
888 $(OBJDIR)/schema_.c:$(OBJDIR)/schema.h \
889 $(OBJDIR)/search_.c:$(OBJDIR)/search.h \
890 $(OBJDIR)/security_audit_.c:$(OBJDIR)/security_audit.h \
891 $(OBJDIR)/setup_.c:$(OBJDIR)/setup.h \
@@ -1768,10 +1772,18 @@
1772
1773 $(OBJDIR)/report.o: $(OBJDIR)/report_.c $(OBJDIR)/report.h $(SRCDIR)/config.h
1774 $(XTCC) -o $(OBJDIR)/report.o -c $(OBJDIR)/report_.c
1775
1776 $(OBJDIR)/report.h: $(OBJDIR)/headers
1777
1778 $(OBJDIR)/robot_.c: $(SRCDIR)/robot.c $(OBJDIR)/translate
1779 $(OBJDIR)/translate $(SRCDIR)/robot.c >$@
1780
1781 $(OBJDIR)/robot.o: $(OBJDIR)/robot_.c $(OBJDIR)/robot.h $(SRCDIR)/config.h
1782 $(XTCC) -o $(OBJDIR)/robot.o -c $(OBJDIR)/robot_.c
1783
1784 $(OBJDIR)/robot.h: $(OBJDIR)/headers
1785
1786 $(OBJDIR)/rss_.c: $(SRCDIR)/rss.c $(OBJDIR)/translate
1787 $(OBJDIR)/translate $(SRCDIR)/rss.c >$@
1788
1789 $(OBJDIR)/rss.o: $(OBJDIR)/rss_.c $(OBJDIR)/rss.h $(SRCDIR)/config.h
1790
+1 -1
--- src/merge.c
+++ src/merge.c
@@ -481,11 +481,11 @@
481481
const char *zTagList = db_column_text(&q, 4);
482482
char *zCom;
483483
if( zTagList && zTagList[0] ){
484484
zCom = mprintf("%s (%s)", db_column_text(&q, 2), zTagList);
485485
}else{
486
- zCom = mprintf("%s", db_column_text(&q,2));
486
+ zCom = fossil_strdup(db_column_text(&q,2));
487487
}
488488
fossil_print("%-*s [%S] by %s on %s\n%*s",
489489
indent-1, zLabel,
490490
db_column_text(&q, 3),
491491
db_column_text(&q, 1),
492492
--- src/merge.c
+++ src/merge.c
@@ -481,11 +481,11 @@
481 const char *zTagList = db_column_text(&q, 4);
482 char *zCom;
483 if( zTagList && zTagList[0] ){
484 zCom = mprintf("%s (%s)", db_column_text(&q, 2), zTagList);
485 }else{
486 zCom = mprintf("%s", db_column_text(&q,2));
487 }
488 fossil_print("%-*s [%S] by %s on %s\n%*s",
489 indent-1, zLabel,
490 db_column_text(&q, 3),
491 db_column_text(&q, 1),
492
--- src/merge.c
+++ src/merge.c
@@ -481,11 +481,11 @@
481 const char *zTagList = db_column_text(&q, 4);
482 char *zCom;
483 if( zTagList && zTagList[0] ){
484 zCom = mprintf("%s (%s)", db_column_text(&q, 2), zTagList);
485 }else{
486 zCom = fossil_strdup(db_column_text(&q,2));
487 }
488 fossil_print("%-*s [%S] by %s on %s\n%*s",
489 indent-1, zLabel,
490 db_column_text(&q, 3),
491 db_column_text(&q, 1),
492
+2 -2
--- src/name.c
+++ src/name.c
@@ -581,11 +581,11 @@
581581
nTag = strlen(zTag);
582582
for(i=0; i<nTag-8 && zTag[i]!=':'; i++){}
583583
if( zTag[i]==':'
584584
&& (fossil_isdate(&zTag[i+1]) || fossil_expand_datetime(&zTag[i+1],0,0)!=0)
585585
){
586
- char *zDate = mprintf("%s", &zTag[i+1]);
586
+ char *zDate = fossil_strdup(&zTag[i+1]);
587587
char *zTagBase = mprintf("%.*s", i, zTag);
588588
char *zXDate;
589589
int nDate = strlen(zDate);
590590
if( sqlite3_strnicmp(&zDate[nDate-3],"utc",3)==0 ){
591591
zDate[nDate-3] = 'z';
@@ -986,11 +986,11 @@
986986
}
987987
style_header("Ambiguous Artifact ID");
988988
@ <p>The artifact hash prefix <b>%h(zName)</b> is ambiguous and might
989989
@ mean any of the following:
990990
@ <ol>
991
- z = mprintf("%s", zName);
991
+ z = fossil_strdup(zName);
992992
canonical16(z, strlen(z));
993993
db_prepare(&q, "SELECT uuid, rid FROM blob WHERE uuid GLOB '%q*'", z);
994994
while( db_step(&q)==SQLITE_ROW ){
995995
const char *zUuid = db_column_text(&q, 0);
996996
int rid = db_column_int(&q, 1);
997997
--- src/name.c
+++ src/name.c
@@ -581,11 +581,11 @@
581 nTag = strlen(zTag);
582 for(i=0; i<nTag-8 && zTag[i]!=':'; i++){}
583 if( zTag[i]==':'
584 && (fossil_isdate(&zTag[i+1]) || fossil_expand_datetime(&zTag[i+1],0,0)!=0)
585 ){
586 char *zDate = mprintf("%s", &zTag[i+1]);
587 char *zTagBase = mprintf("%.*s", i, zTag);
588 char *zXDate;
589 int nDate = strlen(zDate);
590 if( sqlite3_strnicmp(&zDate[nDate-3],"utc",3)==0 ){
591 zDate[nDate-3] = 'z';
@@ -986,11 +986,11 @@
986 }
987 style_header("Ambiguous Artifact ID");
988 @ <p>The artifact hash prefix <b>%h(zName)</b> is ambiguous and might
989 @ mean any of the following:
990 @ <ol>
991 z = mprintf("%s", zName);
992 canonical16(z, strlen(z));
993 db_prepare(&q, "SELECT uuid, rid FROM blob WHERE uuid GLOB '%q*'", z);
994 while( db_step(&q)==SQLITE_ROW ){
995 const char *zUuid = db_column_text(&q, 0);
996 int rid = db_column_int(&q, 1);
997
--- src/name.c
+++ src/name.c
@@ -581,11 +581,11 @@
581 nTag = strlen(zTag);
582 for(i=0; i<nTag-8 && zTag[i]!=':'; i++){}
583 if( zTag[i]==':'
584 && (fossil_isdate(&zTag[i+1]) || fossil_expand_datetime(&zTag[i+1],0,0)!=0)
585 ){
586 char *zDate = fossil_strdup(&zTag[i+1]);
587 char *zTagBase = mprintf("%.*s", i, zTag);
588 char *zXDate;
589 int nDate = strlen(zDate);
590 if( sqlite3_strnicmp(&zDate[nDate-3],"utc",3)==0 ){
591 zDate[nDate-3] = 'z';
@@ -986,11 +986,11 @@
986 }
987 style_header("Ambiguous Artifact ID");
988 @ <p>The artifact hash prefix <b>%h(zName)</b> is ambiguous and might
989 @ mean any of the following:
990 @ <ol>
991 z = fossil_strdup(zName);
992 canonical16(z, strlen(z));
993 db_prepare(&q, "SELECT uuid, rid FROM blob WHERE uuid GLOB '%q*'", z);
994 while( db_step(&q)==SQLITE_ROW ){
995 const char *zUuid = db_column_text(&q, 0);
996 int rid = db_column_int(&q, 1);
997
--- src/pikchrshow.c
+++ src/pikchrshow.c
@@ -462,12 +462,12 @@
462462
} CX("</fieldset><!-- .zone-wrapper.input -->");
463463
CX("<fieldset class='zone-wrapper output'>"); {
464464
CX("<legend><div class='button-bar'>");
465465
CX("<button id='btn-render-mode'>Render Mode</button> ");
466466
CX("<span style='white-space:nowrap'>"
467
- "<span id='preview-copy-button' "
468
- "title='Tap to copy to clipboard.'></span>"
467
+ "<button id='preview-copy-button' "
468
+ "title='Tap to copy to clipboard.'><span></span></button>"
469469
"<label for='preview-copy-button' "
470470
"title='Tap to copy to clipboard.'></label>"
471471
"</span>");
472472
CX("</div></legend>");
473473
CX("<div id='pikchr-output-wrapper'>");
474474
--- src/pikchrshow.c
+++ src/pikchrshow.c
@@ -462,12 +462,12 @@
462 } CX("</fieldset><!-- .zone-wrapper.input -->");
463 CX("<fieldset class='zone-wrapper output'>"); {
464 CX("<legend><div class='button-bar'>");
465 CX("<button id='btn-render-mode'>Render Mode</button> ");
466 CX("<span style='white-space:nowrap'>"
467 "<span id='preview-copy-button' "
468 "title='Tap to copy to clipboard.'></span>"
469 "<label for='preview-copy-button' "
470 "title='Tap to copy to clipboard.'></label>"
471 "</span>");
472 CX("</div></legend>");
473 CX("<div id='pikchr-output-wrapper'>");
474
--- src/pikchrshow.c
+++ src/pikchrshow.c
@@ -462,12 +462,12 @@
462 } CX("</fieldset><!-- .zone-wrapper.input -->");
463 CX("<fieldset class='zone-wrapper output'>"); {
464 CX("<legend><div class='button-bar'>");
465 CX("<button id='btn-render-mode'>Render Mode</button> ");
466 CX("<span style='white-space:nowrap'>"
467 "<button id='preview-copy-button' "
468 "title='Tap to copy to clipboard.'><span></span></button>"
469 "<label for='preview-copy-button' "
470 "title='Tap to copy to clipboard.'></label>"
471 "</span>");
472 CX("</div></legend>");
473 CX("<div id='pikchr-output-wrapper'>");
474
+3 -3
--- src/repolist.c
+++ src/repolist.c
@@ -247,22 +247,22 @@
247247
if( zName[0]=='/'
248248
#ifdef _WIN32
249249
|| sqlite3_strglob("[a-zA-Z]:/*", zName)==0
250250
#endif
251251
){
252
- zFull = mprintf("%s", zName);
252
+ zFull = fossil_strdup(zName);
253253
}else if ( allRepo ){
254254
zFull = mprintf("/%s", zName);
255255
}else{
256256
zFull = mprintf("%s/%s", g.zRepositoryName, zName);
257257
}
258258
x.zRepoName = zFull;
259259
remote_repo_info(&x);
260260
if( x.isRepolistSkin ){
261261
if( zSkinRepo==0 ){
262
- zSkinRepo = mprintf("%s", x.zRepoName);
263
- zSkinUrl = mprintf("%s", zUrl);
262
+ zSkinRepo = fossil_strdup(x.zRepoName);
263
+ zSkinUrl = fossil_strdup(zUrl);
264264
}
265265
}
266266
fossil_free(zFull);
267267
if( !x.isValid
268268
#if USE_SEE
269269
270270
ADDED src/robot.c
--- src/repolist.c
+++ src/repolist.c
@@ -247,22 +247,22 @@
247 if( zName[0]=='/'
248 #ifdef _WIN32
249 || sqlite3_strglob("[a-zA-Z]:/*", zName)==0
250 #endif
251 ){
252 zFull = mprintf("%s", zName);
253 }else if ( allRepo ){
254 zFull = mprintf("/%s", zName);
255 }else{
256 zFull = mprintf("%s/%s", g.zRepositoryName, zName);
257 }
258 x.zRepoName = zFull;
259 remote_repo_info(&x);
260 if( x.isRepolistSkin ){
261 if( zSkinRepo==0 ){
262 zSkinRepo = mprintf("%s", x.zRepoName);
263 zSkinUrl = mprintf("%s", zUrl);
264 }
265 }
266 fossil_free(zFull);
267 if( !x.isValid
268 #if USE_SEE
269
270 DDED src/robot.c
--- src/repolist.c
+++ src/repolist.c
@@ -247,22 +247,22 @@
247 if( zName[0]=='/'
248 #ifdef _WIN32
249 || sqlite3_strglob("[a-zA-Z]:/*", zName)==0
250 #endif
251 ){
252 zFull = fossil_strdup(zName);
253 }else if ( allRepo ){
254 zFull = mprintf("/%s", zName);
255 }else{
256 zFull = mprintf("%s/%s", g.zRepositoryName, zName);
257 }
258 x.zRepoName = zFull;
259 remote_repo_info(&x);
260 if( x.isRepolistSkin ){
261 if( zSkinRepo==0 ){
262 zSkinRepo = fossil_strdup(x.zRepoName);
263 zSkinUrl = fossil_strdup(zUrl);
264 }
265 }
266 fossil_free(zFull);
267 if( !x.isValid
268 #if USE_SEE
269
270 DDED src/robot.c
--- a/src/robot.c
+++ b/src/robot.c
@@ -0,0 +1 @@
1
+t t
--- a/src/robot.c
+++ b/src/robot.c
@@ -0,0 +1 @@
 
--- a/src/robot.c
+++ b/src/robot.c
@@ -0,0 +1 @@
1 t t
+5 -5
--- src/search.c
+++ src/search.c
@@ -130,14 +130,14 @@
130130
search_end(p);
131131
}else{
132132
p = fossil_malloc(sizeof(*p));
133133
memset(p, 0, sizeof(*p));
134134
}
135
- p->zPattern = z = mprintf("%s", zPattern);
136
- p->zMarkBegin = mprintf("%s", zMarkBegin);
137
- p->zMarkEnd = mprintf("%s", zMarkEnd);
138
- p->zMarkGap = mprintf("%s", zMarkGap);
135
+ p->zPattern = z = fossil_strdup(zPattern);
136
+ p->zMarkBegin = fossil_strdup(zMarkBegin);
137
+ p->zMarkEnd = fossil_strdup(zMarkEnd);
138
+ p->zMarkGap = fossil_strdup(zMarkGap);
139139
p->fSrchFlg = fSrchFlg;
140140
blob_init(&p->snip, 0, 0);
141141
while( *z && p->nTerm<SEARCH_MAX_TERM ){
142142
while( *z && !ISALNUM(*z) ){ z++; }
143143
if( *z==0 ) break;
@@ -1076,11 +1076,11 @@
10761076
** causing errors in FTS5 searches with inputs which contain AND, OR,
10771077
** and symbols like #. The caller is responsible for passing the
10781078
** result to fossil_free().
10791079
*/
10801080
char *search_simplify_pattern(const char * zPattern){
1081
- char *zPat = mprintf("%s",zPattern);
1081
+ char *zPat = fossil_strdup(zPattern);
10821082
int i;
10831083
for(i=0; zPat[i]; i++){
10841084
if( (zPat[i]&0x80)==0 && !fossil_isalnum(zPat[i]) ) zPat[i] = ' ';
10851085
if( fossil_isupper(zPat[i]) ) zPat[i] = fossil_tolower(zPat[i]);
10861086
}
10871087
--- src/search.c
+++ src/search.c
@@ -130,14 +130,14 @@
130 search_end(p);
131 }else{
132 p = fossil_malloc(sizeof(*p));
133 memset(p, 0, sizeof(*p));
134 }
135 p->zPattern = z = mprintf("%s", zPattern);
136 p->zMarkBegin = mprintf("%s", zMarkBegin);
137 p->zMarkEnd = mprintf("%s", zMarkEnd);
138 p->zMarkGap = mprintf("%s", zMarkGap);
139 p->fSrchFlg = fSrchFlg;
140 blob_init(&p->snip, 0, 0);
141 while( *z && p->nTerm<SEARCH_MAX_TERM ){
142 while( *z && !ISALNUM(*z) ){ z++; }
143 if( *z==0 ) break;
@@ -1076,11 +1076,11 @@
1076 ** causing errors in FTS5 searches with inputs which contain AND, OR,
1077 ** and symbols like #. The caller is responsible for passing the
1078 ** result to fossil_free().
1079 */
1080 char *search_simplify_pattern(const char * zPattern){
1081 char *zPat = mprintf("%s",zPattern);
1082 int i;
1083 for(i=0; zPat[i]; i++){
1084 if( (zPat[i]&0x80)==0 && !fossil_isalnum(zPat[i]) ) zPat[i] = ' ';
1085 if( fossil_isupper(zPat[i]) ) zPat[i] = fossil_tolower(zPat[i]);
1086 }
1087
--- src/search.c
+++ src/search.c
@@ -130,14 +130,14 @@
130 search_end(p);
131 }else{
132 p = fossil_malloc(sizeof(*p));
133 memset(p, 0, sizeof(*p));
134 }
135 p->zPattern = z = fossil_strdup(zPattern);
136 p->zMarkBegin = fossil_strdup(zMarkBegin);
137 p->zMarkEnd = fossil_strdup(zMarkEnd);
138 p->zMarkGap = fossil_strdup(zMarkGap);
139 p->fSrchFlg = fSrchFlg;
140 blob_init(&p->snip, 0, 0);
141 while( *z && p->nTerm<SEARCH_MAX_TERM ){
142 while( *z && !ISALNUM(*z) ){ z++; }
143 if( *z==0 ) break;
@@ -1076,11 +1076,11 @@
1076 ** causing errors in FTS5 searches with inputs which contain AND, OR,
1077 ** and symbols like #. The caller is responsible for passing the
1078 ** result to fossil_free().
1079 */
1080 char *search_simplify_pattern(const char * zPattern){
1081 char *zPat = fossil_strdup(zPattern);
1082 int i;
1083 for(i=0; zPat[i]; i++){
1084 if( (zPat[i]&0x80)==0 && !fossil_isalnum(zPat[i]) ) zPat[i] = ' ';
1085 if( fossil_isupper(zPat[i]) ) zPat[i] = fossil_tolower(zPat[i]);
1086 }
1087
+55 -62
--- src/setup.c
+++ src/setup.c
@@ -421,56 +421,38 @@
421421
};
422422
multiple_choice_attribute(
423423
"Enable hyperlinks base on User-Agent and/or Javascript",
424424
"auto-hyperlink", "autohyperlink", "1",
425425
count(azDefenseOpts)/2, azDefenseOpts);
426
- @ <p>Enable hyperlinks (the equivalent of the "h" permission) for all users,
427
- @ including user "nobody", as long as the User-Agent string in the
428
- @ HTTP header indicates that the request is coming from an actual human
429
- @ being. If this setting is "UserAgent only" (2) then the
430
- @ UserAgent string is the only factor considered. If the value of this
431
- @ setting is "UserAgent And Javascript" (1) then Javascript is added that
432
- @ runs after the page loads and fills in the href= values of &lt;a&gt;
433
- @ elements. In either case, &lt;a&gt; tags are only generated if the
434
- @ UserAgent string indicates that the request is coming from a human and
435
- @ not a robot.
436
- @
437
- @ <p>This setting is designed to give easy access to humans while
438
- @ keeping out robots.
439
- @ You do not normally want a robot to walk your entire repository because
440
- @ if it does, your server will end up computing diffs and annotations for
441
- @ every historical version of every file and creating ZIPs and tarballs of
442
- @ every historical check-in, which can use a lot of CPU and bandwidth
443
- @ even for relatively small projects.</p>
444
- @
445
- @ <p>The "UserAgent and Javascript" value for this setting provides
446
- @ superior protection from robots. However, that setting also prevents
447
- @ the visited/unvisited colors on hyperlinks from displaying correctly
448
- @ on Safari-derived browsers. (Chrome and Firefox work fine.) Since
449
- @ Safari is the underlying rendering engine on all iPhones and iPads,
450
- @ this means that hyperlink visited/unvisited colors will not operate
451
- @ on those platforms when "UserAgent and Javascript" is selected.</p>
452
- @
453
- @ <p>Additional parameters that control the behavior of Javascript:</p>
454
- @ <blockquote>
426
+ @ <br>
455427
entry_attribute("Delay in milliseconds before enabling hyperlinks", 5,
456428
"auto-hyperlink-delay", "ah-delay", "50", 0);
457429
@ <br>
458430
onoff_attribute("Also require a mouse event before enabling hyperlinks",
459431
"auto-hyperlink-mouseover", "ahmo", 0, 0);
460
- @ </blockquote>
432
+ @ <p>Enable hyperlinks (the equivalent of the "h" permission) for all users,
433
+ @ including user "nobody" if the request appears to be from a human.
434
+ @ Disabling hyperlinks helps prevent robots from walking your site and
435
+ @ soaking up all your CPU and bandwidth.
436
+ @ If this setting is "UserAgent only" (2) then the
437
+ @ UserAgent string is the only factor considered. If the value of this
438
+ @ setting is "UserAgent And Javascript" (1) then Javascript is added that
439
+ @ runs after the page loads and fills in the href= values of &lt;a&gt;
440
+ @ elements. In either case, &lt;a&gt; tags are not generated if the
441
+ @ UserAgent string indicates that the client is a robot.
442
+ @ (Property: "auto-hyperlink")</p>
443
+ @
461444
@ <p>For maximum robot defense, "Delay" should be at least 50 milliseconds
462445
@ and "require a mouse event" should be turned on. These values only come
463446
@ into play when the main auto-hyperlink settings is 2 ("UserAgent and
464
- @ Javascript").</p>
447
+ @ Javascript").
448
+ @ (Properties: "auto-hyperlink-delay" and "auto-hyperlink-mouseover")</p>
465449
@
466450
@ <p>To see if Javascript-base hyperlink enabling mechanism is working,
467
- @ visit the <a href="%R/test-env">/test-env</a> page (from a separate
468
- @ web browser that is not logged in, even as "anonymous") and verify
451
+ @ visit the <a href="%R/test-env">/test-env</a> page from a separate
452
+ @ web browser that is not logged in, even as "anonymous" and verify
469453
@ that the "g.jsHref" value is "1".</p>
470
- @ <p>(Properties: "auto-hyperlink", "auto-hyperlink-delay", and
471
- @ "auto-hyperlink-mouseover"")</p>
472454
}
473455
474456
/*
475457
** WEBPAGE: setup_robot
476458
**
@@ -494,13 +476,43 @@
494476
@ defend the site against robots.
495477
@
496478
@ <form action="%R/setup_robot" method="post"><div>
497479
login_insert_csrf_secret();
498480
@ <input type="submit" name="submit" value="Apply Changes"></p>
481
+ @ <hr>
482
+ @ <p><b>Do not allow robots access to these pages.</b>
483
+ @ <p> If the page name matches the GLOB pattern of this setting, and the
484
+ @ users is "nobody", and the client has not previously passed a captcha
485
+ @ test to show that it is not a robot, then the page is not displayed.
486
+ @ A captcha test is is rendered instead.
487
+ @ The recommended value for this setting is:
488
+ @ <p>
489
+ @ &emsp;&emsp;&emsp;<tt>%h(robot_restrict_default())</tt>
490
+ @ <p>
491
+ @ The "diff" tag covers all diffing pages such as /vdiff, /fdiff, and
492
+ @ /vpatch. The "annotate" tag covers /annotate and also /blame and
493
+ @ /praise. The "zip" covers itself and also /tarball and /sqlar. If a
494
+ @ tag has an "X" character appended, then it only applies if query
495
+ @ parameters are such that the page is particularly difficult to compute.
496
+ @ In all other case, the tag should exactly match the page name.
497
+ @
498
+ @ To disable robot restrictions, change this setting to "off".
499
+ @ (Property: robot-restrict)
500
+ @ <br>
501
+ textarea_attribute("", 2, 80,
502
+ "robot-restrict", "rbrestrict", robot_restrict_default(), 0);
503
+
499504
@ <hr>
500505
addAutoHyperlinkSettings();
501506
507
+ @ <hr>
508
+ entry_attribute("Anonymous Login Validity", 11, "anon-cookie-lifespan",
509
+ "anoncookls", "840", 0);
510
+ @ <p>The number of minutes for which an anonymous login cookie is valid.
511
+ @ Set to zero to disable anonymous login.
512
+ @ (property: anon-cookie-lifespan)
513
+
502514
@ <hr>
503515
entry_attribute("Server Load Average Limit", 11, "max-loadavg", "mxldavg",
504516
"0.0", 0);
505517
@ <p>Some expensive operations (such as computing tarballs, zip archives,
506518
@ or annotation/blame pages) are prohibited if the load average on the host
@@ -508,37 +520,11 @@
508520
@ computations here. Set this to 0.0 to disable the load average limit.
509521
@ This limit is only enforced on Unix servers. On Linux systems,
510522
@ access to the /proc virtual filesystem is required, which means this limit
511523
@ might not work inside a chroot() jail.
512524
@ (Property: "max-loadavg")</p>
513
-
514
- @ <hr>
515
- @ <p><b>Do not allow robots to make complex requests
516
- @ against the following pages.</b>
517
- @ <p> A "complex request" is an HTTP request that has one or more query
518
- @ parameters. Some robots will spend hours juggling around query parameters
519
- @ or even forging fake query parameters in an effort to discover new
520
- @ behavior or to find an SQL injection opportunity or similar. This can
521
- @ waste hours of CPU time and gigabytes of bandwidth on the server. A
522
- @ suggested value for this setting is:
523
- @ "<tt>timeline,*diff,vpatch,annotate,blame,praise,dir,tree</tt>".
524
- @ (Property: robot-restrict)
525
- @ <br>
526
- textarea_attribute("", 2, 80,
527
- "robot-restrict", "rbrestrict", "", 0);
528
- @ <br> The following comma-separated GLOB pattern allows for exceptions
529
- @ in the maximum number of query parameters before a request is considered
530
- @ complex. If this GLOB pattern exists and is non-empty and if it
531
- @ matches against the pagename followed by "/" and the number of query
532
- @ parameters, then the request is allowed through. For example, the
533
- @ suggested pattern of "timeline/[012]" allows the /timeline page to
534
- @ pass with up to 2 query parameters besides "name".
535
- @ (Property: robot-restrict-qp)
536
- @ <br>
537
- textarea_attribute("", 2, 80,
538
- "robot-restrict-qp", "rbrestrictqp", "", 0);
539
-
525
+ @
540526
@ <hr>
541527
@ <p><input type="submit" name="submit" value="Apply Changes"></p>
542528
@ </div></form>
543529
db_end_transaction(0);
544530
style_finish_page();
@@ -774,10 +760,17 @@
774760
@ "anonymous" that will automatically fill in the CAPTCHA password.
775761
@ This is less secure than forcing the user to do it manually, but is
776762
@ probably secure enough and it is certainly more convenient for
777763
@ anonymous users. (Property: "auto-captcha")</p>
778764
765
+ @ <hr>
766
+ entry_attribute("Anonymous Login Validity", 11, "anon-cookie-lifespan",
767
+ "anoncookls", "840", 0);
768
+ @ <p>The number of minutes for which an anonymous login cookie is valid.
769
+ @ Set to zero to disable anonymous logins.
770
+ @ (property: anon-cookie-lifespan)
771
+
779772
@ <hr>
780773
@ <p><input type="submit" name="submit" value="Apply Changes"></p>
781774
@ </div></form>
782775
db_end_transaction(0);
783776
style_finish_page();
784777
--- src/setup.c
+++ src/setup.c
@@ -421,56 +421,38 @@
421 };
422 multiple_choice_attribute(
423 "Enable hyperlinks base on User-Agent and/or Javascript",
424 "auto-hyperlink", "autohyperlink", "1",
425 count(azDefenseOpts)/2, azDefenseOpts);
426 @ <p>Enable hyperlinks (the equivalent of the "h" permission) for all users,
427 @ including user "nobody", as long as the User-Agent string in the
428 @ HTTP header indicates that the request is coming from an actual human
429 @ being. If this setting is "UserAgent only" (2) then the
430 @ UserAgent string is the only factor considered. If the value of this
431 @ setting is "UserAgent And Javascript" (1) then Javascript is added that
432 @ runs after the page loads and fills in the href= values of &lt;a&gt;
433 @ elements. In either case, &lt;a&gt; tags are only generated if the
434 @ UserAgent string indicates that the request is coming from a human and
435 @ not a robot.
436 @
437 @ <p>This setting is designed to give easy access to humans while
438 @ keeping out robots.
439 @ You do not normally want a robot to walk your entire repository because
440 @ if it does, your server will end up computing diffs and annotations for
441 @ every historical version of every file and creating ZIPs and tarballs of
442 @ every historical check-in, which can use a lot of CPU and bandwidth
443 @ even for relatively small projects.</p>
444 @
445 @ <p>The "UserAgent and Javascript" value for this setting provides
446 @ superior protection from robots. However, that setting also prevents
447 @ the visited/unvisited colors on hyperlinks from displaying correctly
448 @ on Safari-derived browsers. (Chrome and Firefox work fine.) Since
449 @ Safari is the underlying rendering engine on all iPhones and iPads,
450 @ this means that hyperlink visited/unvisited colors will not operate
451 @ on those platforms when "UserAgent and Javascript" is selected.</p>
452 @
453 @ <p>Additional parameters that control the behavior of Javascript:</p>
454 @ <blockquote>
455 entry_attribute("Delay in milliseconds before enabling hyperlinks", 5,
456 "auto-hyperlink-delay", "ah-delay", "50", 0);
457 @ <br>
458 onoff_attribute("Also require a mouse event before enabling hyperlinks",
459 "auto-hyperlink-mouseover", "ahmo", 0, 0);
460 @ </blockquote>
 
 
 
 
 
 
 
 
 
 
 
461 @ <p>For maximum robot defense, "Delay" should be at least 50 milliseconds
462 @ and "require a mouse event" should be turned on. These values only come
463 @ into play when the main auto-hyperlink settings is 2 ("UserAgent and
464 @ Javascript").</p>
 
465 @
466 @ <p>To see if Javascript-base hyperlink enabling mechanism is working,
467 @ visit the <a href="%R/test-env">/test-env</a> page (from a separate
468 @ web browser that is not logged in, even as "anonymous") and verify
469 @ that the "g.jsHref" value is "1".</p>
470 @ <p>(Properties: "auto-hyperlink", "auto-hyperlink-delay", and
471 @ "auto-hyperlink-mouseover"")</p>
472 }
473
474 /*
475 ** WEBPAGE: setup_robot
476 **
@@ -494,13 +476,43 @@
494 @ defend the site against robots.
495 @
496 @ <form action="%R/setup_robot" method="post"><div>
497 login_insert_csrf_secret();
498 @ <input type="submit" name="submit" value="Apply Changes"></p>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
499 @ <hr>
500 addAutoHyperlinkSettings();
501
 
 
 
 
 
 
 
502 @ <hr>
503 entry_attribute("Server Load Average Limit", 11, "max-loadavg", "mxldavg",
504 "0.0", 0);
505 @ <p>Some expensive operations (such as computing tarballs, zip archives,
506 @ or annotation/blame pages) are prohibited if the load average on the host
@@ -508,37 +520,11 @@
508 @ computations here. Set this to 0.0 to disable the load average limit.
509 @ This limit is only enforced on Unix servers. On Linux systems,
510 @ access to the /proc virtual filesystem is required, which means this limit
511 @ might not work inside a chroot() jail.
512 @ (Property: "max-loadavg")</p>
513
514 @ <hr>
515 @ <p><b>Do not allow robots to make complex requests
516 @ against the following pages.</b>
517 @ <p> A "complex request" is an HTTP request that has one or more query
518 @ parameters. Some robots will spend hours juggling around query parameters
519 @ or even forging fake query parameters in an effort to discover new
520 @ behavior or to find an SQL injection opportunity or similar. This can
521 @ waste hours of CPU time and gigabytes of bandwidth on the server. A
522 @ suggested value for this setting is:
523 @ "<tt>timeline,*diff,vpatch,annotate,blame,praise,dir,tree</tt>".
524 @ (Property: robot-restrict)
525 @ <br>
526 textarea_attribute("", 2, 80,
527 "robot-restrict", "rbrestrict", "", 0);
528 @ <br> The following comma-separated GLOB pattern allows for exceptions
529 @ in the maximum number of query parameters before a request is considered
530 @ complex. If this GLOB pattern exists and is non-empty and if it
531 @ matches against the pagename followed by "/" and the number of query
532 @ parameters, then the request is allowed through. For example, the
533 @ suggested pattern of "timeline/[012]" allows the /timeline page to
534 @ pass with up to 2 query parameters besides "name".
535 @ (Property: robot-restrict-qp)
536 @ <br>
537 textarea_attribute("", 2, 80,
538 "robot-restrict-qp", "rbrestrictqp", "", 0);
539
540 @ <hr>
541 @ <p><input type="submit" name="submit" value="Apply Changes"></p>
542 @ </div></form>
543 db_end_transaction(0);
544 style_finish_page();
@@ -774,10 +760,17 @@
774 @ "anonymous" that will automatically fill in the CAPTCHA password.
775 @ This is less secure than forcing the user to do it manually, but is
776 @ probably secure enough and it is certainly more convenient for
777 @ anonymous users. (Property: "auto-captcha")</p>
778
 
 
 
 
 
 
 
779 @ <hr>
780 @ <p><input type="submit" name="submit" value="Apply Changes"></p>
781 @ </div></form>
782 db_end_transaction(0);
783 style_finish_page();
784
--- src/setup.c
+++ src/setup.c
@@ -421,56 +421,38 @@
421 };
422 multiple_choice_attribute(
423 "Enable hyperlinks base on User-Agent and/or Javascript",
424 "auto-hyperlink", "autohyperlink", "1",
425 count(azDefenseOpts)/2, azDefenseOpts);
426 @ <br>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
427 entry_attribute("Delay in milliseconds before enabling hyperlinks", 5,
428 "auto-hyperlink-delay", "ah-delay", "50", 0);
429 @ <br>
430 onoff_attribute("Also require a mouse event before enabling hyperlinks",
431 "auto-hyperlink-mouseover", "ahmo", 0, 0);
432 @ <p>Enable hyperlinks (the equivalent of the "h" permission) for all users,
433 @ including user "nobody" if the request appears to be from a human.
434 @ Disabling hyperlinks helps prevent robots from walking your site and
435 @ soaking up all your CPU and bandwidth.
436 @ If this setting is "UserAgent only" (2) then the
437 @ UserAgent string is the only factor considered. If the value of this
438 @ setting is "UserAgent And Javascript" (1) then Javascript is added that
439 @ runs after the page loads and fills in the href= values of &lt;a&gt;
440 @ elements. In either case, &lt;a&gt; tags are not generated if the
441 @ UserAgent string indicates that the client is a robot.
442 @ (Property: "auto-hyperlink")</p>
443 @
444 @ <p>For maximum robot defense, "Delay" should be at least 50 milliseconds
445 @ and "require a mouse event" should be turned on. These values only come
446 @ into play when the main auto-hyperlink settings is 2 ("UserAgent and
447 @ Javascript").
448 @ (Properties: "auto-hyperlink-delay" and "auto-hyperlink-mouseover")</p>
449 @
450 @ <p>To see if Javascript-base hyperlink enabling mechanism is working,
451 @ visit the <a href="%R/test-env">/test-env</a> page from a separate
452 @ web browser that is not logged in, even as "anonymous" and verify
453 @ that the "g.jsHref" value is "1".</p>
 
 
454 }
455
456 /*
457 ** WEBPAGE: setup_robot
458 **
@@ -494,13 +476,43 @@
476 @ defend the site against robots.
477 @
478 @ <form action="%R/setup_robot" method="post"><div>
479 login_insert_csrf_secret();
480 @ <input type="submit" name="submit" value="Apply Changes"></p>
481 @ <hr>
482 @ <p><b>Do not allow robots access to these pages.</b>
483 @ <p> If the page name matches the GLOB pattern of this setting, and the
484 @ users is "nobody", and the client has not previously passed a captcha
485 @ test to show that it is not a robot, then the page is not displayed.
486 @ A captcha test is is rendered instead.
487 @ The recommended value for this setting is:
488 @ <p>
489 @ &emsp;&emsp;&emsp;<tt>%h(robot_restrict_default())</tt>
490 @ <p>
491 @ The "diff" tag covers all diffing pages such as /vdiff, /fdiff, and
492 @ /vpatch. The "annotate" tag covers /annotate and also /blame and
493 @ /praise. The "zip" covers itself and also /tarball and /sqlar. If a
494 @ tag has an "X" character appended, then it only applies if query
495 @ parameters are such that the page is particularly difficult to compute.
496 @ In all other case, the tag should exactly match the page name.
497 @
498 @ To disable robot restrictions, change this setting to "off".
499 @ (Property: robot-restrict)
500 @ <br>
501 textarea_attribute("", 2, 80,
502 "robot-restrict", "rbrestrict", robot_restrict_default(), 0);
503
504 @ <hr>
505 addAutoHyperlinkSettings();
506
507 @ <hr>
508 entry_attribute("Anonymous Login Validity", 11, "anon-cookie-lifespan",
509 "anoncookls", "840", 0);
510 @ <p>The number of minutes for which an anonymous login cookie is valid.
511 @ Set to zero to disable anonymous login.
512 @ (property: anon-cookie-lifespan)
513
514 @ <hr>
515 entry_attribute("Server Load Average Limit", 11, "max-loadavg", "mxldavg",
516 "0.0", 0);
517 @ <p>Some expensive operations (such as computing tarballs, zip archives,
518 @ or annotation/blame pages) are prohibited if the load average on the host
@@ -508,37 +520,11 @@
520 @ computations here. Set this to 0.0 to disable the load average limit.
521 @ This limit is only enforced on Unix servers. On Linux systems,
522 @ access to the /proc virtual filesystem is required, which means this limit
523 @ might not work inside a chroot() jail.
524 @ (Property: "max-loadavg")</p>
525 @
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
526 @ <hr>
527 @ <p><input type="submit" name="submit" value="Apply Changes"></p>
528 @ </div></form>
529 db_end_transaction(0);
530 style_finish_page();
@@ -774,10 +760,17 @@
760 @ "anonymous" that will automatically fill in the CAPTCHA password.
761 @ This is less secure than forcing the user to do it manually, but is
762 @ probably secure enough and it is certainly more convenient for
763 @ anonymous users. (Property: "auto-captcha")</p>
764
765 @ <hr>
766 entry_attribute("Anonymous Login Validity", 11, "anon-cookie-lifespan",
767 "anoncookls", "840", 0);
768 @ <p>The number of minutes for which an anonymous login cookie is valid.
769 @ Set to zero to disable anonymous logins.
770 @ (property: anon-cookie-lifespan)
771
772 @ <hr>
773 @ <p><input type="submit" name="submit" value="Apply Changes"></p>
774 @ </div></form>
775 db_end_transaction(0);
776 style_finish_page();
777
+3 -3
--- src/sha1.c
+++ src/sha1.c
@@ -420,11 +420,11 @@
420420
421421
SHA1Init(&ctx);
422422
SHA1Update(&ctx, (unsigned const char*)zIn, strlen(zIn));
423423
SHA1Final(zResult, &ctx);
424424
DigestToBase16(zResult, zDigest);
425
- return mprintf("%s", zDigest);
425
+ return fossil_strdup(zDigest);
426426
}
427427
428428
/*
429429
** Convert a cleartext password for a specific user into a SHA1 hash.
430430
**
@@ -459,11 +459,11 @@
459459
460460
/* On the first xfer request of a clone, the project-code is not yet
461461
** known. Use the cleartext password, since that is all we have.
462462
*/
463463
if( zProjectId==0 ){
464
- return mprintf("%s", zPw);
464
+ return fossil_strdup(zPw);
465465
}
466466
}
467467
zProjCode = zProjectId;
468468
}
469469
SHA1Update(&ctx, (unsigned char*)zProjCode, strlen(zProjCode));
@@ -471,11 +471,11 @@
471471
SHA1Update(&ctx, (unsigned char*)zLogin, strlen(zLogin));
472472
SHA1Update(&ctx, (unsigned char*)"/", 1);
473473
SHA1Update(&ctx, (unsigned const char*)zPw, strlen(zPw));
474474
SHA1Final(zResult, &ctx);
475475
DigestToBase16(zResult, zDigest);
476
- return mprintf("%s", zDigest);
476
+ return fossil_strdup(zDigest);
477477
}
478478
479479
/*
480480
** Implement the shared_secret() SQL function. shared_secret() takes two or
481481
** three arguments; the third argument is optional.
482482
--- src/sha1.c
+++ src/sha1.c
@@ -420,11 +420,11 @@
420
421 SHA1Init(&ctx);
422 SHA1Update(&ctx, (unsigned const char*)zIn, strlen(zIn));
423 SHA1Final(zResult, &ctx);
424 DigestToBase16(zResult, zDigest);
425 return mprintf("%s", zDigest);
426 }
427
428 /*
429 ** Convert a cleartext password for a specific user into a SHA1 hash.
430 **
@@ -459,11 +459,11 @@
459
460 /* On the first xfer request of a clone, the project-code is not yet
461 ** known. Use the cleartext password, since that is all we have.
462 */
463 if( zProjectId==0 ){
464 return mprintf("%s", zPw);
465 }
466 }
467 zProjCode = zProjectId;
468 }
469 SHA1Update(&ctx, (unsigned char*)zProjCode, strlen(zProjCode));
@@ -471,11 +471,11 @@
471 SHA1Update(&ctx, (unsigned char*)zLogin, strlen(zLogin));
472 SHA1Update(&ctx, (unsigned char*)"/", 1);
473 SHA1Update(&ctx, (unsigned const char*)zPw, strlen(zPw));
474 SHA1Final(zResult, &ctx);
475 DigestToBase16(zResult, zDigest);
476 return mprintf("%s", zDigest);
477 }
478
479 /*
480 ** Implement the shared_secret() SQL function. shared_secret() takes two or
481 ** three arguments; the third argument is optional.
482
--- src/sha1.c
+++ src/sha1.c
@@ -420,11 +420,11 @@
420
421 SHA1Init(&ctx);
422 SHA1Update(&ctx, (unsigned const char*)zIn, strlen(zIn));
423 SHA1Final(zResult, &ctx);
424 DigestToBase16(zResult, zDigest);
425 return fossil_strdup(zDigest);
426 }
427
428 /*
429 ** Convert a cleartext password for a specific user into a SHA1 hash.
430 **
@@ -459,11 +459,11 @@
459
460 /* On the first xfer request of a clone, the project-code is not yet
461 ** known. Use the cleartext password, since that is all we have.
462 */
463 if( zProjectId==0 ){
464 return fossil_strdup(zPw);
465 }
466 }
467 zProjCode = zProjectId;
468 }
469 SHA1Update(&ctx, (unsigned char*)zProjCode, strlen(zProjCode));
@@ -471,11 +471,11 @@
471 SHA1Update(&ctx, (unsigned char*)zLogin, strlen(zLogin));
472 SHA1Update(&ctx, (unsigned char*)"/", 1);
473 SHA1Update(&ctx, (unsigned const char*)zPw, strlen(zPw));
474 SHA1Final(zResult, &ctx);
475 DigestToBase16(zResult, zDigest);
476 return fossil_strdup(zDigest);
477 }
478
479 /*
480 ** Implement the shared_secret() SQL function. shared_secret() takes two or
481 ** three arguments; the third argument is optional.
482
+1 -1
--- src/sha3.c
+++ src/sha3.c
@@ -612,11 +612,11 @@
612612
char zDigest[132];
613613
614614
SHA3Init(&ctx, iSize);
615615
SHA3Update(&ctx, (unsigned const char*)zIn, strlen(zIn));
616616
DigestToBase16(SHA3Final(&ctx), zDigest, iSize/8);
617
- return mprintf("%s", zDigest);
617
+ return fossil_strdup(zDigest);
618618
}
619619
#endif
620620
621621
/*
622622
** COMMAND: sha3sum*
623623
--- src/sha3.c
+++ src/sha3.c
@@ -612,11 +612,11 @@
612 char zDigest[132];
613
614 SHA3Init(&ctx, iSize);
615 SHA3Update(&ctx, (unsigned const char*)zIn, strlen(zIn));
616 DigestToBase16(SHA3Final(&ctx), zDigest, iSize/8);
617 return mprintf("%s", zDigest);
618 }
619 #endif
620
621 /*
622 ** COMMAND: sha3sum*
623
--- src/sha3.c
+++ src/sha3.c
@@ -612,11 +612,11 @@
612 char zDigest[132];
613
614 SHA3Init(&ctx, iSize);
615 SHA3Update(&ctx, (unsigned const char*)zIn, strlen(zIn));
616 DigestToBase16(SHA3Final(&ctx), zDigest, iSize/8);
617 return fossil_strdup(zDigest);
618 }
619 #endif
620
621 /*
622 ** COMMAND: sha3sum*
623
+11 -1
--- src/stash.c
+++ src/stash.c
@@ -562,10 +562,14 @@
562562
** > fossil stash gdiff ?STASHID? ?DIFF-OPTIONS?
563563
**
564564
** Show diffs of the current working directory and what that
565565
** directory would be if STASHID were applied. With gdiff,
566566
** gdiff-command is used instead of internal diff logic.
567
+**
568
+** > fossil stash rename STASHID NEW-NAME
569
+**
570
+** Change the description of the given STASHID entry to NEW-NAME.
567571
*/
568572
void stash_cmd(void){
569573
const char *zCmd;
570574
int nCmd;
571575
int stashid = 0;
@@ -771,11 +775,17 @@
771775
}
772776
diff_options(&DCfg, zCmd[0]=='g', 0);
773777
stashid = stash_get_id(g.argc==4 ? g.argv[3] : 0);
774778
stash_diff(stashid, fBaseline, &DCfg);
775779
}else
776
- if( strncmp(zCmd, "help", nCmd)==0 ){
780
+ if( strncmp(zCmd, "rename", nCmd)==0 ){
781
+ if( g.argc!=5 ) usage("rename STASHID NAME");
782
+ stashid = stash_get_id(g.argv[3]);
783
+ db_multi_exec("UPDATE STASH SET COMMENT=%Q WHERE stashid=%d",
784
+ g.argv[4], stashid);
785
+ }
786
+ else if( strncmp(zCmd, "help", nCmd)==0 ){
777787
g.argv[1] = "help";
778788
g.argv[2] = "stash";
779789
g.argc = 3;
780790
help_cmd();
781791
}else
782792
--- src/stash.c
+++ src/stash.c
@@ -562,10 +562,14 @@
562 ** > fossil stash gdiff ?STASHID? ?DIFF-OPTIONS?
563 **
564 ** Show diffs of the current working directory and what that
565 ** directory would be if STASHID were applied. With gdiff,
566 ** gdiff-command is used instead of internal diff logic.
 
 
 
 
567 */
568 void stash_cmd(void){
569 const char *zCmd;
570 int nCmd;
571 int stashid = 0;
@@ -771,11 +775,17 @@
771 }
772 diff_options(&DCfg, zCmd[0]=='g', 0);
773 stashid = stash_get_id(g.argc==4 ? g.argv[3] : 0);
774 stash_diff(stashid, fBaseline, &DCfg);
775 }else
776 if( strncmp(zCmd, "help", nCmd)==0 ){
 
 
 
 
 
 
777 g.argv[1] = "help";
778 g.argv[2] = "stash";
779 g.argc = 3;
780 help_cmd();
781 }else
782
--- src/stash.c
+++ src/stash.c
@@ -562,10 +562,14 @@
562 ** > fossil stash gdiff ?STASHID? ?DIFF-OPTIONS?
563 **
564 ** Show diffs of the current working directory and what that
565 ** directory would be if STASHID were applied. With gdiff,
566 ** gdiff-command is used instead of internal diff logic.
567 **
568 ** > fossil stash rename STASHID NEW-NAME
569 **
570 ** Change the description of the given STASHID entry to NEW-NAME.
571 */
572 void stash_cmd(void){
573 const char *zCmd;
574 int nCmd;
575 int stashid = 0;
@@ -771,11 +775,17 @@
775 }
776 diff_options(&DCfg, zCmd[0]=='g', 0);
777 stashid = stash_get_id(g.argc==4 ? g.argv[3] : 0);
778 stash_diff(stashid, fBaseline, &DCfg);
779 }else
780 if( strncmp(zCmd, "rename", nCmd)==0 ){
781 if( g.argc!=5 ) usage("rename STASHID NAME");
782 stashid = stash_get_id(g.argv[3]);
783 db_multi_exec("UPDATE STASH SET COMMENT=%Q WHERE stashid=%d",
784 g.argv[4], stashid);
785 }
786 else if( strncmp(zCmd, "help", nCmd)==0 ){
787 g.argv[1] = "help";
788 g.argv[2] = "stash";
789 g.argc = 3;
790 help_cmd();
791 }else
792
+21 -62
--- src/style.c
+++ src/style.c
@@ -478,11 +478,11 @@
478478
/*
479479
** Output TEXT with a click-to-copy button next to it. Loads the copybtn.js
480480
** Javascript module, and generates HTML elements with the following IDs:
481481
**
482482
** TARGETID: The <span> wrapper around TEXT.
483
-** copy-TARGETID: The <span> for the copy button.
483
+** copy-TARGETID: The <button> for the copy button.
484484
**
485485
** If the FLIPPED argument is non-zero, the copy button is displayed after TEXT.
486486
**
487487
** The COPYLENGTH argument defines the length of the substring of TEXT copied to
488488
** clipboard:
@@ -510,18 +510,20 @@
510510
if( cchLength==1 ) cchLength = hash_digits(0);
511511
else if( cchLength==2 ) cchLength = hash_digits(1);
512512
if( !bFlipped ){
513513
const char *zBtnFmt =
514514
"<span class=\"nobr\">"
515
- "<span "
516
- "class=\"copy-button\" "
517
- "id=\"copy-%h\" "
518
- "data-copytarget=\"%h\" "
519
- "data-copylength=\"%d\">"
520
- "</span>"
515
+ "<button "
516
+ "class=\"copy-button\" "
517
+ "id=\"copy-%h\" "
518
+ "data-copytarget=\"%h\" "
519
+ "data-copylength=\"%d\">"
520
+ "<span>"
521
+ "</span>"
522
+ "</button>"
521523
"<span id=\"%h\">"
522
- "%s"
524
+ "%s"
523525
"</span>"
524526
"</span>";
525527
if( bOutputCGI ){
526528
cgi_printf(
527529
zBtnFmt/*works-like:"%h%h%d%h%s"*/,
@@ -533,18 +535,20 @@
533535
}
534536
}else{
535537
const char *zBtnFmt =
536538
"<span class=\"nobr\">"
537539
"<span id=\"%h\">"
538
- "%s"
539
- "</span>"
540
- "<span "
541
- "class=\"copy-button copy-button-flipped\" "
542
- "id=\"copy-%h\" "
543
- "data-copytarget=\"%h\" "
544
- "data-copylength=\"%d\">"
545
- "</span>"
540
+ "%s"
541
+ "</span>"
542
+ "<button "
543
+ "class=\"copy-button copy-button-flipped\" "
544
+ "id=\"copy-%h\" "
545
+ "data-copytarget=\"%h\" "
546
+ "data-copylength=\"%d\">"
547
+ "<span>"
548
+ "</span>"
549
+ "</button>"
546550
"</span>";
547551
if( bOutputCGI ){
548552
cgi_printf(
549553
zBtnFmt/*works-like:"%h%s%h%h%d"*/,
550554
zTargetId,zText,zTargetId,zTargetId,cchLength);
@@ -1386,55 +1390,10 @@
13861390
*/
13871391
void page_test_env(void){
13881392
webpage_error("");
13891393
}
13901394
1391
-/*
1392
-** WEBPAGE: honeypot
1393
-** This page is a honeypot for spiders and bots.
1394
-*/
1395
-void honeypot_page(void){
1396
- unsigned int uSeed = captcha_seed();
1397
- const char *zDecoded = captcha_decode(uSeed, 0);
1398
- int bAutoCaptcha = db_get_boolean("auto-captcha", 0);
1399
- char *zCaptcha = captcha_render(zDecoded);
1400
- style_header("I think you are a robot");
1401
- @ <p>You seem like a robot.</p>
1402
- @
1403
- @ <p>Is that incorrect? Are you really human?
1404
- @ If so, please prove it by transcribing the captcha text
1405
- @ into the entry box below and pressing "Submit".
1406
- @ <form action="%R/login" method="post">
1407
- @ <input type="hidden" id="u" name="u" value="anonymous">
1408
- @ <p>
1409
- @ Captcha: <input type="text" id="p" name="p" value="">
1410
- @ <input type="submit" name="in" value="Submit">
1411
- @
1412
- @ <p>Alternatively, you can <a href="%R/login">log in</a> using an
1413
- @ existing userid.
1414
- @
1415
- @ <p><input type="hidden" name="cs" value="%u(uSeed)">
1416
- @ <div class="captcha"><table class="captcha"><tr><td>\
1417
- @ <pre class="captcha">
1418
- @ %h(zCaptcha)
1419
- @ </pre></td></tr></table>
1420
- if( bAutoCaptcha ) {
1421
- @ <input type="button" value="Fill out captcha" id='autofillButton' \
1422
- @ data-af='%s(zDecoded)'>
1423
- builtin_request_js("login.js");
1424
- }
1425
- @ </div>
1426
- free(zCaptcha);
1427
- @
1428
- @ <p>We regret this inconvenience. However, robots have become so
1429
- @ prolific and so aggressive that they will soak up too much CPU time
1430
- @ and network bandwidth on our servers if allowed to run unchecked.
1431
- @ Your cooperation in demonstrating that you are human is
1432
- @ appreciated.
1433
- style_finish_page();
1434
-}
1435
-
14361395
/*
14371396
** Webpages that encounter an error due to missing or incorrect
14381397
** query parameters can jump to this routine to render an error
14391398
** message screen.
14401399
**
@@ -1483,11 +1442,11 @@
14831442
@ g.zHttpsURL = %h(g.zHttpsURL)<br>
14841443
@ g.zTop = %h(g.zTop)<br>
14851444
@ g.zPath = %h(g.zPath)<br>
14861445
@ g.userUid = %d(g.userUid)<br>
14871446
@ g.zLogin = %h(g.zLogin)<br>
1488
- @ g.isHuman = %d(g.isHuman)<br>
1447
+ @ g.isRobot = %d(g.isRobot)<br>
14891448
@ g.jsHref = %d(g.jsHref)<br>
14901449
if( g.zLocalRoot ){
14911450
@ g.zLocalRoot = %h(g.zLocalRoot)<br>
14921451
}else{
14931452
@ g.zLocalRoot = <i>none</i><br>
14941453
--- src/style.c
+++ src/style.c
@@ -478,11 +478,11 @@
478 /*
479 ** Output TEXT with a click-to-copy button next to it. Loads the copybtn.js
480 ** Javascript module, and generates HTML elements with the following IDs:
481 **
482 ** TARGETID: The <span> wrapper around TEXT.
483 ** copy-TARGETID: The <span> for the copy button.
484 **
485 ** If the FLIPPED argument is non-zero, the copy button is displayed after TEXT.
486 **
487 ** The COPYLENGTH argument defines the length of the substring of TEXT copied to
488 ** clipboard:
@@ -510,18 +510,20 @@
510 if( cchLength==1 ) cchLength = hash_digits(0);
511 else if( cchLength==2 ) cchLength = hash_digits(1);
512 if( !bFlipped ){
513 const char *zBtnFmt =
514 "<span class=\"nobr\">"
515 "<span "
516 "class=\"copy-button\" "
517 "id=\"copy-%h\" "
518 "data-copytarget=\"%h\" "
519 "data-copylength=\"%d\">"
520 "</span>"
 
 
521 "<span id=\"%h\">"
522 "%s"
523 "</span>"
524 "</span>";
525 if( bOutputCGI ){
526 cgi_printf(
527 zBtnFmt/*works-like:"%h%h%d%h%s"*/,
@@ -533,18 +535,20 @@
533 }
534 }else{
535 const char *zBtnFmt =
536 "<span class=\"nobr\">"
537 "<span id=\"%h\">"
538 "%s"
539 "</span>"
540 "<span "
541 "class=\"copy-button copy-button-flipped\" "
542 "id=\"copy-%h\" "
543 "data-copytarget=\"%h\" "
544 "data-copylength=\"%d\">"
545 "</span>"
 
 
546 "</span>";
547 if( bOutputCGI ){
548 cgi_printf(
549 zBtnFmt/*works-like:"%h%s%h%h%d"*/,
550 zTargetId,zText,zTargetId,zTargetId,cchLength);
@@ -1386,55 +1390,10 @@
1386 */
1387 void page_test_env(void){
1388 webpage_error("");
1389 }
1390
1391 /*
1392 ** WEBPAGE: honeypot
1393 ** This page is a honeypot for spiders and bots.
1394 */
1395 void honeypot_page(void){
1396 unsigned int uSeed = captcha_seed();
1397 const char *zDecoded = captcha_decode(uSeed, 0);
1398 int bAutoCaptcha = db_get_boolean("auto-captcha", 0);
1399 char *zCaptcha = captcha_render(zDecoded);
1400 style_header("I think you are a robot");
1401 @ <p>You seem like a robot.</p>
1402 @
1403 @ <p>Is that incorrect? Are you really human?
1404 @ If so, please prove it by transcribing the captcha text
1405 @ into the entry box below and pressing "Submit".
1406 @ <form action="%R/login" method="post">
1407 @ <input type="hidden" id="u" name="u" value="anonymous">
1408 @ <p>
1409 @ Captcha: <input type="text" id="p" name="p" value="">
1410 @ <input type="submit" name="in" value="Submit">
1411 @
1412 @ <p>Alternatively, you can <a href="%R/login">log in</a> using an
1413 @ existing userid.
1414 @
1415 @ <p><input type="hidden" name="cs" value="%u(uSeed)">
1416 @ <div class="captcha"><table class="captcha"><tr><td>\
1417 @ <pre class="captcha">
1418 @ %h(zCaptcha)
1419 @ </pre></td></tr></table>
1420 if( bAutoCaptcha ) {
1421 @ <input type="button" value="Fill out captcha" id='autofillButton' \
1422 @ data-af='%s(zDecoded)'>
1423 builtin_request_js("login.js");
1424 }
1425 @ </div>
1426 free(zCaptcha);
1427 @
1428 @ <p>We regret this inconvenience. However, robots have become so
1429 @ prolific and so aggressive that they will soak up too much CPU time
1430 @ and network bandwidth on our servers if allowed to run unchecked.
1431 @ Your cooperation in demonstrating that you are human is
1432 @ appreciated.
1433 style_finish_page();
1434 }
1435
1436 /*
1437 ** Webpages that encounter an error due to missing or incorrect
1438 ** query parameters can jump to this routine to render an error
1439 ** message screen.
1440 **
@@ -1483,11 +1442,11 @@
1483 @ g.zHttpsURL = %h(g.zHttpsURL)<br>
1484 @ g.zTop = %h(g.zTop)<br>
1485 @ g.zPath = %h(g.zPath)<br>
1486 @ g.userUid = %d(g.userUid)<br>
1487 @ g.zLogin = %h(g.zLogin)<br>
1488 @ g.isHuman = %d(g.isHuman)<br>
1489 @ g.jsHref = %d(g.jsHref)<br>
1490 if( g.zLocalRoot ){
1491 @ g.zLocalRoot = %h(g.zLocalRoot)<br>
1492 }else{
1493 @ g.zLocalRoot = <i>none</i><br>
1494
--- src/style.c
+++ src/style.c
@@ -478,11 +478,11 @@
478 /*
479 ** Output TEXT with a click-to-copy button next to it. Loads the copybtn.js
480 ** Javascript module, and generates HTML elements with the following IDs:
481 **
482 ** TARGETID: The <span> wrapper around TEXT.
483 ** copy-TARGETID: The <button> for the copy button.
484 **
485 ** If the FLIPPED argument is non-zero, the copy button is displayed after TEXT.
486 **
487 ** The COPYLENGTH argument defines the length of the substring of TEXT copied to
488 ** clipboard:
@@ -510,18 +510,20 @@
510 if( cchLength==1 ) cchLength = hash_digits(0);
511 else if( cchLength==2 ) cchLength = hash_digits(1);
512 if( !bFlipped ){
513 const char *zBtnFmt =
514 "<span class=\"nobr\">"
515 "<button "
516 "class=\"copy-button\" "
517 "id=\"copy-%h\" "
518 "data-copytarget=\"%h\" "
519 "data-copylength=\"%d\">"
520 "<span>"
521 "</span>"
522 "</button>"
523 "<span id=\"%h\">"
524 "%s"
525 "</span>"
526 "</span>";
527 if( bOutputCGI ){
528 cgi_printf(
529 zBtnFmt/*works-like:"%h%h%d%h%s"*/,
@@ -533,18 +535,20 @@
535 }
536 }else{
537 const char *zBtnFmt =
538 "<span class=\"nobr\">"
539 "<span id=\"%h\">"
540 "%s"
541 "</span>"
542 "<button "
543 "class=\"copy-button copy-button-flipped\" "
544 "id=\"copy-%h\" "
545 "data-copytarget=\"%h\" "
546 "data-copylength=\"%d\">"
547 "<span>"
548 "</span>"
549 "</button>"
550 "</span>";
551 if( bOutputCGI ){
552 cgi_printf(
553 zBtnFmt/*works-like:"%h%s%h%h%d"*/,
554 zTargetId,zText,zTargetId,zTargetId,cchLength);
@@ -1386,55 +1390,10 @@
1390 */
1391 void page_test_env(void){
1392 webpage_error("");
1393 }
1394
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1395 /*
1396 ** Webpages that encounter an error due to missing or incorrect
1397 ** query parameters can jump to this routine to render an error
1398 ** message screen.
1399 **
@@ -1483,11 +1442,11 @@
1442 @ g.zHttpsURL = %h(g.zHttpsURL)<br>
1443 @ g.zTop = %h(g.zTop)<br>
1444 @ g.zPath = %h(g.zPath)<br>
1445 @ g.userUid = %d(g.userUid)<br>
1446 @ g.zLogin = %h(g.zLogin)<br>
1447 @ g.isRobot = %d(g.isRobot)<br>
1448 @ g.jsHref = %d(g.jsHref)<br>
1449 if( g.zLocalRoot ){
1450 @ g.zLocalRoot = %h(g.zLocalRoot)<br>
1451 }else{
1452 @ g.zLocalRoot = <i>none</i><br>
1453
+2 -2
--- src/tag.c
+++ src/tag.c
@@ -218,12 +218,12 @@
218218
}
219219
}
220220
if( zCol ){
221221
db_multi_exec("UPDATE event SET \"%w\"=%Q WHERE objid=%d",
222222
zCol, zValue, rid);
223
- if( tagid==TAG_COMMENT ){
224
- char *zCopy = mprintf("%s", zValue);
223
+ if( tagid==TAG_COMMENT && zValue!=0 ){
224
+ char *zCopy = fossil_strdup(zValue);
225225
backlink_extract(zCopy, MT_NONE, rid, BKLNK_COMMENT, mtime, 1);
226226
free(zCopy);
227227
}
228228
}
229229
if( tagid==TAG_DATE ){
230230
--- src/tag.c
+++ src/tag.c
@@ -218,12 +218,12 @@
218 }
219 }
220 if( zCol ){
221 db_multi_exec("UPDATE event SET \"%w\"=%Q WHERE objid=%d",
222 zCol, zValue, rid);
223 if( tagid==TAG_COMMENT ){
224 char *zCopy = mprintf("%s", zValue);
225 backlink_extract(zCopy, MT_NONE, rid, BKLNK_COMMENT, mtime, 1);
226 free(zCopy);
227 }
228 }
229 if( tagid==TAG_DATE ){
230
--- src/tag.c
+++ src/tag.c
@@ -218,12 +218,12 @@
218 }
219 }
220 if( zCol ){
221 db_multi_exec("UPDATE event SET \"%w\"=%Q WHERE objid=%d",
222 zCol, zValue, rid);
223 if( tagid==TAG_COMMENT && zValue!=0 ){
224 char *zCopy = fossil_strdup(zValue);
225 backlink_extract(zCopy, MT_NONE, rid, BKLNK_COMMENT, mtime, 1);
226 free(zCopy);
227 }
228 }
229 if( tagid==TAG_DATE ){
230
+1
--- src/tar.c
+++ src/tar.c
@@ -760,10 +760,11 @@
760760
Blob tarball; /* Tarball accumulated here */
761761
const char *z;
762762
763763
login_check_credentials();
764764
if( !g.perm.Zip ){ login_needed(g.anon.Zip); return; }
765
+ if( robot_restrict("zip") ) return;
765766
fossil_nice_default();
766767
zName = fossil_strdup(PD("name",""));
767768
z = P("r");
768769
if( z==0 ) z = P("uuid");
769770
if( z==0 ) z = tar_uuid_from_name(&zName);
770771
--- src/tar.c
+++ src/tar.c
@@ -760,10 +760,11 @@
760 Blob tarball; /* Tarball accumulated here */
761 const char *z;
762
763 login_check_credentials();
764 if( !g.perm.Zip ){ login_needed(g.anon.Zip); return; }
 
765 fossil_nice_default();
766 zName = fossil_strdup(PD("name",""));
767 z = P("r");
768 if( z==0 ) z = P("uuid");
769 if( z==0 ) z = tar_uuid_from_name(&zName);
770
--- src/tar.c
+++ src/tar.c
@@ -760,10 +760,11 @@
760 Blob tarball; /* Tarball accumulated here */
761 const char *z;
762
763 login_check_credentials();
764 if( !g.perm.Zip ){ login_needed(g.anon.Zip); return; }
765 if( robot_restrict("zip") ) return;
766 fossil_nice_default();
767 zName = fossil_strdup(PD("name",""));
768 z = P("r");
769 if( z==0 ) z = P("uuid");
770 if( z==0 ) z = tar_uuid_from_name(&zName);
771
+1 -1
--- src/th_lang.c
+++ src/th_lang.c
@@ -956,11 +956,11 @@
956956
**
957957
*/
958958
static int string_match_command(
959959
Th_Interp *interp, void *ctx, int argc, const char **argv, int *argl
960960
){
961
- extern char *fossil_strndup(const char*,int);
961
+ extern char *fossil_strndup(const char*,ssize_t);
962962
extern void fossil_free(void*);
963963
char *zPat, *zStr;
964964
int rc;
965965
if( argc!=4 ){
966966
return Th_WrongNumArgs(interp, "string match pattern string");
967967
--- src/th_lang.c
+++ src/th_lang.c
@@ -956,11 +956,11 @@
956 **
957 */
958 static int string_match_command(
959 Th_Interp *interp, void *ctx, int argc, const char **argv, int *argl
960 ){
961 extern char *fossil_strndup(const char*,int);
962 extern void fossil_free(void*);
963 char *zPat, *zStr;
964 int rc;
965 if( argc!=4 ){
966 return Th_WrongNumArgs(interp, "string match pattern string");
967
--- src/th_lang.c
+++ src/th_lang.c
@@ -956,11 +956,11 @@
956 **
957 */
958 static int string_match_command(
959 Th_Interp *interp, void *ctx, int argc, const char **argv, int *argl
960 ){
961 extern char *fossil_strndup(const char*,ssize_t);
962 extern void fossil_free(void*);
963 char *zPat, *zStr;
964 int rc;
965 if( argc!=4 ){
966 return Th_WrongNumArgs(interp, "string match pattern string");
967
+2 -2
--- src/th_main.c
+++ src/th_main.c
@@ -1432,11 +1432,11 @@
14321432
int *argl
14331433
){
14341434
if( argc!=3 ){
14351435
return Th_WrongNumArgs(interp, "setParameter NAME VALUE");
14361436
}
1437
- cgi_replace_parameter(mprintf("%s", argv[1]), mprintf("%s", argv[2]));
1437
+ cgi_replace_parameter(fossil_strdup(argv[1]), fossil_strdup(argv[2]));
14381438
return TH_OK;
14391439
}
14401440
14411441
/*
14421442
** TH1 command: reinitialize ?FLAGS?
@@ -2965,11 +2965,11 @@
29652965
if( rc!=TH_OK ) break;
29662966
z += i;
29672967
if( z[0] ){ z += 6; }
29682968
i = 0;
29692969
}else{
2970
- i++;
2970
+ i += strcspn(&z[i+1], "<$") + 1;
29712971
}
29722972
}
29732973
if( rc==TH_ERROR ){
29742974
zResult = (char*)Th_GetResult(g.interp, &n);
29752975
sendError(pOut,zResult, n, 1);
29762976
--- src/th_main.c
+++ src/th_main.c
@@ -1432,11 +1432,11 @@
1432 int *argl
1433 ){
1434 if( argc!=3 ){
1435 return Th_WrongNumArgs(interp, "setParameter NAME VALUE");
1436 }
1437 cgi_replace_parameter(mprintf("%s", argv[1]), mprintf("%s", argv[2]));
1438 return TH_OK;
1439 }
1440
1441 /*
1442 ** TH1 command: reinitialize ?FLAGS?
@@ -2965,11 +2965,11 @@
2965 if( rc!=TH_OK ) break;
2966 z += i;
2967 if( z[0] ){ z += 6; }
2968 i = 0;
2969 }else{
2970 i++;
2971 }
2972 }
2973 if( rc==TH_ERROR ){
2974 zResult = (char*)Th_GetResult(g.interp, &n);
2975 sendError(pOut,zResult, n, 1);
2976
--- src/th_main.c
+++ src/th_main.c
@@ -1432,11 +1432,11 @@
1432 int *argl
1433 ){
1434 if( argc!=3 ){
1435 return Th_WrongNumArgs(interp, "setParameter NAME VALUE");
1436 }
1437 cgi_replace_parameter(fossil_strdup(argv[1]), fossil_strdup(argv[2]));
1438 return TH_OK;
1439 }
1440
1441 /*
1442 ** TH1 command: reinitialize ?FLAGS?
@@ -2965,11 +2965,11 @@
2965 if( rc!=TH_OK ) break;
2966 z += i;
2967 if( z[0] ){ z += 6; }
2968 i = 0;
2969 }else{
2970 i += strcspn(&z[i+1], "<$") + 1;
2971 }
2972 }
2973 if( rc==TH_ERROR ){
2974 zResult = (char*)Th_GetResult(g.interp, &n);
2975 sendError(pOut,zResult, n, 1);
2976
+4 -2
--- src/timeline.c
+++ src/timeline.c
@@ -1830,10 +1830,11 @@
18301830
|| (bisectLocal && !g.perm.Setup)
18311831
){
18321832
login_needed(g.anon.Read && g.anon.RdTkt && g.anon.RdWiki);
18331833
return;
18341834
}
1835
+ if( zBefore && robot_restrict("timelineX") ) return;
18351836
if( !bisectLocal ){
18361837
etag_check(ETAG_QUERY|ETAG_COOKIE|ETAG_DATA|ETAG_CONFIG, 0);
18371838
}
18381839
cookie_read_parameter("y","y");
18391840
zType = P("y");
@@ -1969,10 +1970,11 @@
19691970
}
19701971
if( showSql ) db_append_dml_to_blob(&allSql);
19711972
if( zUses!=0 ){
19721973
int ufid = db_int(0, "SELECT rid FROM blob WHERE uuid GLOB '%q*'", zUses);
19731974
if( ufid ){
1975
+ if( robot_restrict("timelineX") ) return;
19741976
zUses = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", ufid);
19751977
db_multi_exec("CREATE TEMP TABLE usesfile(rid INTEGER PRIMARY KEY)");
19761978
compute_uses_file("usesfile", ufid, 0);
19771979
zType = "ci";
19781980
disableY = 1;
@@ -3039,11 +3041,11 @@
30393041
"brlist", "List"
30403042
};
30413043
double rDate;
30423044
zDate = db_text(0, "SELECT min(timestamp) FROM timeline /*scan*/");
30433045
if( (!zDate || !zDate[0]) && ( zAfter || zBefore ) ){
3044
- zDate = mprintf("%s", (zAfter ? zAfter : zBefore));
3046
+ zDate = fossil_strdup((zAfter ? zAfter : zBefore));
30453047
}
30463048
if( zDate ){
30473049
rDate = symbolic_name_to_mtime(zDate, 0, 0);
30483050
if( db_int(0,
30493051
"SELECT EXISTS (SELECT 1 FROM event CROSS JOIN blob"
@@ -3055,11 +3057,11 @@
30553057
}
30563058
free(zDate);
30573059
}
30583060
zDate = db_text(0, "SELECT max(timestamp) FROM timeline /*scan*/");
30593061
if( (!zDate || !zDate[0]) && ( zAfter || zBefore ) ){
3060
- zDate = mprintf("%s", (zBefore ? zBefore : zAfter));
3062
+ zDate = fossil_strdup((zBefore ? zBefore : zAfter));
30613063
}
30623064
if( zDate ){
30633065
rDate = symbolic_name_to_mtime(zDate, 0, 0);
30643066
if( db_int(0,
30653067
"SELECT EXISTS (SELECT 1 FROM event CROSS JOIN blob"
30663068
--- src/timeline.c
+++ src/timeline.c
@@ -1830,10 +1830,11 @@
1830 || (bisectLocal && !g.perm.Setup)
1831 ){
1832 login_needed(g.anon.Read && g.anon.RdTkt && g.anon.RdWiki);
1833 return;
1834 }
 
1835 if( !bisectLocal ){
1836 etag_check(ETAG_QUERY|ETAG_COOKIE|ETAG_DATA|ETAG_CONFIG, 0);
1837 }
1838 cookie_read_parameter("y","y");
1839 zType = P("y");
@@ -1969,10 +1970,11 @@
1969 }
1970 if( showSql ) db_append_dml_to_blob(&allSql);
1971 if( zUses!=0 ){
1972 int ufid = db_int(0, "SELECT rid FROM blob WHERE uuid GLOB '%q*'", zUses);
1973 if( ufid ){
 
1974 zUses = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", ufid);
1975 db_multi_exec("CREATE TEMP TABLE usesfile(rid INTEGER PRIMARY KEY)");
1976 compute_uses_file("usesfile", ufid, 0);
1977 zType = "ci";
1978 disableY = 1;
@@ -3039,11 +3041,11 @@
3039 "brlist", "List"
3040 };
3041 double rDate;
3042 zDate = db_text(0, "SELECT min(timestamp) FROM timeline /*scan*/");
3043 if( (!zDate || !zDate[0]) && ( zAfter || zBefore ) ){
3044 zDate = mprintf("%s", (zAfter ? zAfter : zBefore));
3045 }
3046 if( zDate ){
3047 rDate = symbolic_name_to_mtime(zDate, 0, 0);
3048 if( db_int(0,
3049 "SELECT EXISTS (SELECT 1 FROM event CROSS JOIN blob"
@@ -3055,11 +3057,11 @@
3055 }
3056 free(zDate);
3057 }
3058 zDate = db_text(0, "SELECT max(timestamp) FROM timeline /*scan*/");
3059 if( (!zDate || !zDate[0]) && ( zAfter || zBefore ) ){
3060 zDate = mprintf("%s", (zBefore ? zBefore : zAfter));
3061 }
3062 if( zDate ){
3063 rDate = symbolic_name_to_mtime(zDate, 0, 0);
3064 if( db_int(0,
3065 "SELECT EXISTS (SELECT 1 FROM event CROSS JOIN blob"
3066
--- src/timeline.c
+++ src/timeline.c
@@ -1830,10 +1830,11 @@
1830 || (bisectLocal && !g.perm.Setup)
1831 ){
1832 login_needed(g.anon.Read && g.anon.RdTkt && g.anon.RdWiki);
1833 return;
1834 }
1835 if( zBefore && robot_restrict("timelineX") ) return;
1836 if( !bisectLocal ){
1837 etag_check(ETAG_QUERY|ETAG_COOKIE|ETAG_DATA|ETAG_CONFIG, 0);
1838 }
1839 cookie_read_parameter("y","y");
1840 zType = P("y");
@@ -1969,10 +1970,11 @@
1970 }
1971 if( showSql ) db_append_dml_to_blob(&allSql);
1972 if( zUses!=0 ){
1973 int ufid = db_int(0, "SELECT rid FROM blob WHERE uuid GLOB '%q*'", zUses);
1974 if( ufid ){
1975 if( robot_restrict("timelineX") ) return;
1976 zUses = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", ufid);
1977 db_multi_exec("CREATE TEMP TABLE usesfile(rid INTEGER PRIMARY KEY)");
1978 compute_uses_file("usesfile", ufid, 0);
1979 zType = "ci";
1980 disableY = 1;
@@ -3039,11 +3041,11 @@
3041 "brlist", "List"
3042 };
3043 double rDate;
3044 zDate = db_text(0, "SELECT min(timestamp) FROM timeline /*scan*/");
3045 if( (!zDate || !zDate[0]) && ( zAfter || zBefore ) ){
3046 zDate = fossil_strdup((zAfter ? zAfter : zBefore));
3047 }
3048 if( zDate ){
3049 rDate = symbolic_name_to_mtime(zDate, 0, 0);
3050 if( db_int(0,
3051 "SELECT EXISTS (SELECT 1 FROM event CROSS JOIN blob"
@@ -3055,11 +3057,11 @@
3057 }
3058 free(zDate);
3059 }
3060 zDate = db_text(0, "SELECT max(timestamp) FROM timeline /*scan*/");
3061 if( (!zDate || !zDate[0]) && ( zAfter || zBefore ) ){
3062 zDate = fossil_strdup((zBefore ? zBefore : zAfter));
3063 }
3064 if( zDate ){
3065 rDate = symbolic_name_to_mtime(zDate, 0, 0);
3066 if( db_int(0,
3067 "SELECT EXISTS (SELECT 1 FROM event CROSS JOIN blob"
3068
+4 -4
--- src/tkt.c
+++ src/tkt.c
@@ -102,11 +102,11 @@
102102
if( strchr(zFieldName,' ')!=0 ) continue;
103103
if( nField%10==0 ){
104104
aField = fossil_realloc(aField, sizeof(aField[0])*(nField+10) );
105105
}
106106
aField[nField].zBsln = 0;
107
- aField[nField].zName = mprintf("%s", zFieldName);
107
+ aField[nField].zName = fossil_strdup(zFieldName);
108108
aField[nField].mUsed = USEDBY_TICKET;
109109
nField++;
110110
}
111111
db_finalize(&q);
112112
if( nBaselines ){
@@ -145,11 +145,11 @@
145145
}
146146
if( nField%10==0 ){
147147
aField = fossil_realloc(aField, sizeof(aField[0])*(nField+10) );
148148
}
149149
aField[nField].zBsln = 0;
150
- aField[nField].zName = mprintf("%s", zFieldName);
150
+ aField[nField].zName = fossil_strdup(zFieldName);
151151
aField[nField].mUsed = USEDBY_TICKETCHNG;
152152
nField++;
153153
}
154154
db_finalize(&q);
155155
qsort(aField, nField, sizeof(aField[0]), nameCmpr);
@@ -210,11 +210,11 @@
210210
zVal = "";
211211
}else if( strncmp(zName, "private_", 8)==0 ){
212212
zVal = zRevealed = db_reveal(zVal);
213213
}
214214
if( (j = fieldId(zName))>=0 ){
215
- aField[j].zValue = mprintf("%s", zVal);
215
+ aField[j].zValue = fossil_strdup(zVal);
216216
}else if( memcmp(zName, "tkt_", 4)==0 && Th_Fetch(zName, &size)==0 ){
217217
/* TICKET table columns that begin with "tkt_" are always safe */
218218
Th_Store(zName, zVal);
219219
}
220220
free(zRevealed);
@@ -1787,11 +1787,11 @@
17871787
if( i==g.argc ){
17881788
fossil_fatal("missing value for '%s'!",zFName);
17891789
}
17901790
zFValue = g.argv[i++];
17911791
if( tktEncoding == tktFossilize ){
1792
- zFValue=mprintf("%s",zFValue);
1792
+ zFValue=fossil_strdup(zFValue);
17931793
defossilize(zFValue);
17941794
}
17951795
append = (zFName[0] == '+');
17961796
if( append ){
17971797
zFName++;
17981798
--- src/tkt.c
+++ src/tkt.c
@@ -102,11 +102,11 @@
102 if( strchr(zFieldName,' ')!=0 ) continue;
103 if( nField%10==0 ){
104 aField = fossil_realloc(aField, sizeof(aField[0])*(nField+10) );
105 }
106 aField[nField].zBsln = 0;
107 aField[nField].zName = mprintf("%s", zFieldName);
108 aField[nField].mUsed = USEDBY_TICKET;
109 nField++;
110 }
111 db_finalize(&q);
112 if( nBaselines ){
@@ -145,11 +145,11 @@
145 }
146 if( nField%10==0 ){
147 aField = fossil_realloc(aField, sizeof(aField[0])*(nField+10) );
148 }
149 aField[nField].zBsln = 0;
150 aField[nField].zName = mprintf("%s", zFieldName);
151 aField[nField].mUsed = USEDBY_TICKETCHNG;
152 nField++;
153 }
154 db_finalize(&q);
155 qsort(aField, nField, sizeof(aField[0]), nameCmpr);
@@ -210,11 +210,11 @@
210 zVal = "";
211 }else if( strncmp(zName, "private_", 8)==0 ){
212 zVal = zRevealed = db_reveal(zVal);
213 }
214 if( (j = fieldId(zName))>=0 ){
215 aField[j].zValue = mprintf("%s", zVal);
216 }else if( memcmp(zName, "tkt_", 4)==0 && Th_Fetch(zName, &size)==0 ){
217 /* TICKET table columns that begin with "tkt_" are always safe */
218 Th_Store(zName, zVal);
219 }
220 free(zRevealed);
@@ -1787,11 +1787,11 @@
1787 if( i==g.argc ){
1788 fossil_fatal("missing value for '%s'!",zFName);
1789 }
1790 zFValue = g.argv[i++];
1791 if( tktEncoding == tktFossilize ){
1792 zFValue=mprintf("%s",zFValue);
1793 defossilize(zFValue);
1794 }
1795 append = (zFName[0] == '+');
1796 if( append ){
1797 zFName++;
1798
--- src/tkt.c
+++ src/tkt.c
@@ -102,11 +102,11 @@
102 if( strchr(zFieldName,' ')!=0 ) continue;
103 if( nField%10==0 ){
104 aField = fossil_realloc(aField, sizeof(aField[0])*(nField+10) );
105 }
106 aField[nField].zBsln = 0;
107 aField[nField].zName = fossil_strdup(zFieldName);
108 aField[nField].mUsed = USEDBY_TICKET;
109 nField++;
110 }
111 db_finalize(&q);
112 if( nBaselines ){
@@ -145,11 +145,11 @@
145 }
146 if( nField%10==0 ){
147 aField = fossil_realloc(aField, sizeof(aField[0])*(nField+10) );
148 }
149 aField[nField].zBsln = 0;
150 aField[nField].zName = fossil_strdup(zFieldName);
151 aField[nField].mUsed = USEDBY_TICKETCHNG;
152 nField++;
153 }
154 db_finalize(&q);
155 qsort(aField, nField, sizeof(aField[0]), nameCmpr);
@@ -210,11 +210,11 @@
210 zVal = "";
211 }else if( strncmp(zName, "private_", 8)==0 ){
212 zVal = zRevealed = db_reveal(zVal);
213 }
214 if( (j = fieldId(zName))>=0 ){
215 aField[j].zValue = fossil_strdup(zVal);
216 }else if( memcmp(zName, "tkt_", 4)==0 && Th_Fetch(zName, &size)==0 ){
217 /* TICKET table columns that begin with "tkt_" are always safe */
218 Th_Store(zName, zVal);
219 }
220 free(zRevealed);
@@ -1787,11 +1787,11 @@
1787 if( i==g.argc ){
1788 fossil_fatal("missing value for '%s'!",zFName);
1789 }
1790 zFValue = g.argv[i++];
1791 if( tktEncoding == tktFossilize ){
1792 zFValue=fossil_strdup(zFValue);
1793 defossilize(zFValue);
1794 }
1795 append = (zFName[0] == '+');
1796 if( append ){
1797 zFName++;
1798
+4 -4
--- src/tktsetup.c
+++ src/tktsetup.c
@@ -603,11 +603,11 @@
603603
@ }
604604
@ }
605605
@ }
606606
@ set seenRow 0
607607
@ set alwaysPlaintext [info exists plaintext]
608
-@ query {SELECT datetime(tkt_mtime) AS xdate, login AS xlogin,
608
+@ query {SELECT datetime(tkt_mtime,toLocal()) AS xdate, login AS xlogin,
609609
@ mimetype as xmimetype, icomment AS xcomment,
610610
@ username AS xusername
611611
@ FROM ticketchng
612612
@ WHERE tkt_id=$tkt_id AND length(icomment)>0} {
613613
@ if {$seenRow} {
@@ -789,11 +789,11 @@
789789
@ </tr>
790790
@
791791
@ <th1>
792792
@ set seenRow 0
793793
@ set alwaysPlaintext [info exists plaintext]
794
-@ query {SELECT datetime(tkt_mtime) AS xdate, login AS xlogin,
794
+@ query {SELECT datetime(tkt_mtime,toLocal()) AS xdate, login AS xlogin,
795795
@ mimetype as xmimetype, icomment AS xcomment,
796796
@ username AS xusername
797797
@ FROM ticketchng
798798
@ WHERE tkt_id=$tkt_id AND length(icomment)>0} {
799799
@ if {$seenRow} {
@@ -926,12 +926,12 @@
926926
@ WHEN status='Fixed' THEN '#cfe8bd'
927927
@ WHEN status='Tested' THEN '#bde5d6'
928928
@ WHEN status='Deferred' THEN '#cacae5'
929929
@ ELSE '#c8c8c8' END AS 'bgcolor',
930930
@ substr(tkt_uuid,1,10) AS '#',
931
-@ datetime(tkt_ctime) AS 'created',
932
-@ datetime(tkt_mtime) AS 'modified',
931
+@ datetime(tkt_ctime,toLocal()) AS 'created',
932
+@ datetime(tkt_mtime,toLocal()) AS 'modified',
933933
@ type,
934934
@ status,
935935
@ subsystem,
936936
@ title,
937937
@ comment AS '_comments'
938938
--- src/tktsetup.c
+++ src/tktsetup.c
@@ -603,11 +603,11 @@
603 @ }
604 @ }
605 @ }
606 @ set seenRow 0
607 @ set alwaysPlaintext [info exists plaintext]
608 @ query {SELECT datetime(tkt_mtime) AS xdate, login AS xlogin,
609 @ mimetype as xmimetype, icomment AS xcomment,
610 @ username AS xusername
611 @ FROM ticketchng
612 @ WHERE tkt_id=$tkt_id AND length(icomment)>0} {
613 @ if {$seenRow} {
@@ -789,11 +789,11 @@
789 @ </tr>
790 @
791 @ <th1>
792 @ set seenRow 0
793 @ set alwaysPlaintext [info exists plaintext]
794 @ query {SELECT datetime(tkt_mtime) AS xdate, login AS xlogin,
795 @ mimetype as xmimetype, icomment AS xcomment,
796 @ username AS xusername
797 @ FROM ticketchng
798 @ WHERE tkt_id=$tkt_id AND length(icomment)>0} {
799 @ if {$seenRow} {
@@ -926,12 +926,12 @@
926 @ WHEN status='Fixed' THEN '#cfe8bd'
927 @ WHEN status='Tested' THEN '#bde5d6'
928 @ WHEN status='Deferred' THEN '#cacae5'
929 @ ELSE '#c8c8c8' END AS 'bgcolor',
930 @ substr(tkt_uuid,1,10) AS '#',
931 @ datetime(tkt_ctime) AS 'created',
932 @ datetime(tkt_mtime) AS 'modified',
933 @ type,
934 @ status,
935 @ subsystem,
936 @ title,
937 @ comment AS '_comments'
938
--- src/tktsetup.c
+++ src/tktsetup.c
@@ -603,11 +603,11 @@
603 @ }
604 @ }
605 @ }
606 @ set seenRow 0
607 @ set alwaysPlaintext [info exists plaintext]
608 @ query {SELECT datetime(tkt_mtime,toLocal()) AS xdate, login AS xlogin,
609 @ mimetype as xmimetype, icomment AS xcomment,
610 @ username AS xusername
611 @ FROM ticketchng
612 @ WHERE tkt_id=$tkt_id AND length(icomment)>0} {
613 @ if {$seenRow} {
@@ -789,11 +789,11 @@
789 @ </tr>
790 @
791 @ <th1>
792 @ set seenRow 0
793 @ set alwaysPlaintext [info exists plaintext]
794 @ query {SELECT datetime(tkt_mtime,toLocal()) AS xdate, login AS xlogin,
795 @ mimetype as xmimetype, icomment AS xcomment,
796 @ username AS xusername
797 @ FROM ticketchng
798 @ WHERE tkt_id=$tkt_id AND length(icomment)>0} {
799 @ if {$seenRow} {
@@ -926,12 +926,12 @@
926 @ WHEN status='Fixed' THEN '#cfe8bd'
927 @ WHEN status='Tested' THEN '#bde5d6'
928 @ WHEN status='Deferred' THEN '#cacae5'
929 @ ELSE '#c8c8c8' END AS 'bgcolor',
930 @ substr(tkt_uuid,1,10) AS '#',
931 @ datetime(tkt_ctime,toLocal()) AS 'created',
932 @ datetime(tkt_mtime,toLocal()) AS 'modified',
933 @ type,
934 @ status,
935 @ subsystem,
936 @ title,
937 @ comment AS '_comments'
938
--- src/unversioned.c
+++ src/unversioned.c
@@ -147,10 +147,12 @@
147147
}else{
148148
db_bind_int(&ins, ":encoding", 0);
149149
db_bind_blob(&ins, ":content", pContent);
150150
}
151151
db_step(&ins);
152
+ admin_log("Wrote unversioned file \"%w\" with hash %!S",
153
+ zUVFile, blob_str(&hash));
152154
blob_reset(&compressed);
153155
blob_reset(&hash);
154156
db_finalize(&ins);
155157
db_unset("uv-hash", 0);
156158
}
@@ -318,10 +320,13 @@
318320
const char *zError = 0;
319321
const char *zIn;
320322
const char *zAs;
321323
Blob file;
322324
int i;
325
+ i64 mxSize = sqlite3_limit(g.db,SQLITE_LIMIT_LENGTH,-1) - 850;
326
+ /* Extra space for other fields ------^^^ */
327
+ /* of the UNVESIONED table row. */
323328
324329
zAs = find_option("as",0,1);
325330
verify_all_options();
326331
if( zAs && g.argc!=4 ) usage("add DISKFILE --as UVFILE");
327332
db_begin_transaction();
@@ -334,13 +339,19 @@
334339
zError = "be absolute";
335340
}else if ( !file_is_simple_pathname(zIn,1) ){
336341
zError = "contain complex paths";
337342
}else if( contains_whitespace(zIn) ){
338343
zError = "contain whitespace";
344
+ }else if( strlen(zIn)>500 ){
345
+ zError = "be more than 500 bytes long";
339346
}
340347
if( zError ){
341348
fossil_fatal("unversioned filenames may not %s: %Q", zError, zIn);
349
+ }
350
+ if( file_size(g.argv[i], ExtFILE)>mxSize ){
351
+ fossil_fatal("file \"%s\" is too big; max size: %,lld bytes",
352
+ g.argv[i], mxSize);
342353
}
343354
blob_init(&file,0,0);
344355
blob_read_from_file(&file, g.argv[i], ExtFILE);
345356
unversioned_write(zIn, &file, mtime);
346357
blob_reset(&file);
347358
--- src/unversioned.c
+++ src/unversioned.c
@@ -147,10 +147,12 @@
147 }else{
148 db_bind_int(&ins, ":encoding", 0);
149 db_bind_blob(&ins, ":content", pContent);
150 }
151 db_step(&ins);
 
 
152 blob_reset(&compressed);
153 blob_reset(&hash);
154 db_finalize(&ins);
155 db_unset("uv-hash", 0);
156 }
@@ -318,10 +320,13 @@
318 const char *zError = 0;
319 const char *zIn;
320 const char *zAs;
321 Blob file;
322 int i;
 
 
 
323
324 zAs = find_option("as",0,1);
325 verify_all_options();
326 if( zAs && g.argc!=4 ) usage("add DISKFILE --as UVFILE");
327 db_begin_transaction();
@@ -334,13 +339,19 @@
334 zError = "be absolute";
335 }else if ( !file_is_simple_pathname(zIn,1) ){
336 zError = "contain complex paths";
337 }else if( contains_whitespace(zIn) ){
338 zError = "contain whitespace";
 
 
339 }
340 if( zError ){
341 fossil_fatal("unversioned filenames may not %s: %Q", zError, zIn);
 
 
 
 
342 }
343 blob_init(&file,0,0);
344 blob_read_from_file(&file, g.argv[i], ExtFILE);
345 unversioned_write(zIn, &file, mtime);
346 blob_reset(&file);
347
--- src/unversioned.c
+++ src/unversioned.c
@@ -147,10 +147,12 @@
147 }else{
148 db_bind_int(&ins, ":encoding", 0);
149 db_bind_blob(&ins, ":content", pContent);
150 }
151 db_step(&ins);
152 admin_log("Wrote unversioned file \"%w\" with hash %!S",
153 zUVFile, blob_str(&hash));
154 blob_reset(&compressed);
155 blob_reset(&hash);
156 db_finalize(&ins);
157 db_unset("uv-hash", 0);
158 }
@@ -318,10 +320,13 @@
320 const char *zError = 0;
321 const char *zIn;
322 const char *zAs;
323 Blob file;
324 int i;
325 i64 mxSize = sqlite3_limit(g.db,SQLITE_LIMIT_LENGTH,-1) - 850;
326 /* Extra space for other fields ------^^^ */
327 /* of the UNVESIONED table row. */
328
329 zAs = find_option("as",0,1);
330 verify_all_options();
331 if( zAs && g.argc!=4 ) usage("add DISKFILE --as UVFILE");
332 db_begin_transaction();
@@ -334,13 +339,19 @@
339 zError = "be absolute";
340 }else if ( !file_is_simple_pathname(zIn,1) ){
341 zError = "contain complex paths";
342 }else if( contains_whitespace(zIn) ){
343 zError = "contain whitespace";
344 }else if( strlen(zIn)>500 ){
345 zError = "be more than 500 bytes long";
346 }
347 if( zError ){
348 fossil_fatal("unversioned filenames may not %s: %Q", zError, zIn);
349 }
350 if( file_size(g.argv[i], ExtFILE)>mxSize ){
351 fossil_fatal("file \"%s\" is too big; max size: %,lld bytes",
352 g.argv[i], mxSize);
353 }
354 blob_init(&file,0,0);
355 blob_read_from_file(&file, g.argv[i], ExtFILE);
356 unversioned_write(zIn, &file, mtime);
357 blob_reset(&file);
358
+5 -5
--- src/url.c
+++ src/url.c
@@ -227,17 +227,17 @@
227227
}else{
228228
pUrlData->port = pUrlData->dfltPort;
229229
pUrlData->hostname = pUrlData->name;
230230
}
231231
dehttpize(pUrlData->name);
232
- pUrlData->path = mprintf("%s", &zUrl[i]);
232
+ pUrlData->path = fossil_strdup(&zUrl[i]);
233233
for(i=0; pUrlData->path[i] && pUrlData->path[i]!='?'; i++){}
234234
if( pUrlData->path[i] ){
235235
pUrlData->path[i] = 0;
236236
i++;
237237
}
238
- zExe = mprintf("");
238
+ zExe = fossil_strdup("");
239239
while( pUrlData->path[i]!=0 ){
240240
char *zName, *zValue;
241241
zName = &pUrlData->path[i];
242242
zValue = zName;
243243
while( pUrlData->path[i] && pUrlData->path[i]!='=' ){ i++; }
@@ -275,11 +275,11 @@
275275
pUrlData->path, zExe
276276
);
277277
}
278278
if( pUrlData->isSsh && pUrlData->path[1] ){
279279
char *zOld = pUrlData->path;
280
- pUrlData->path = mprintf("%s", zOld+1);
280
+ pUrlData->path = fossil_strdup(zOld+1);
281281
fossil_free(zOld);
282282
}
283283
free(zLogin);
284284
}else if( strncmp(zUrl, "file:", 5)==0 ){
285285
pUrlData->isFile = 1;
@@ -286,14 +286,14 @@
286286
if( zUrl[5]=='/' && zUrl[6]=='/' ){
287287
i = 7;
288288
}else{
289289
i = 5;
290290
}
291
- zFile = mprintf("%s", &zUrl[i]);
291
+ zFile = fossil_strdup(&zUrl[i]);
292292
}else if( file_isfile(zUrl, ExtFILE) ){
293293
pUrlData->isFile = 1;
294
- zFile = mprintf("%s", zUrl);
294
+ zFile = fossil_strdup(zUrl);
295295
}else if( file_isdir(zUrl, ExtFILE)==1 ){
296296
zFile = mprintf("%s/FOSSIL", zUrl);
297297
if( file_isfile(zFile, ExtFILE) ){
298298
pUrlData->isFile = 1;
299299
}else{
300300
--- src/url.c
+++ src/url.c
@@ -227,17 +227,17 @@
227 }else{
228 pUrlData->port = pUrlData->dfltPort;
229 pUrlData->hostname = pUrlData->name;
230 }
231 dehttpize(pUrlData->name);
232 pUrlData->path = mprintf("%s", &zUrl[i]);
233 for(i=0; pUrlData->path[i] && pUrlData->path[i]!='?'; i++){}
234 if( pUrlData->path[i] ){
235 pUrlData->path[i] = 0;
236 i++;
237 }
238 zExe = mprintf("");
239 while( pUrlData->path[i]!=0 ){
240 char *zName, *zValue;
241 zName = &pUrlData->path[i];
242 zValue = zName;
243 while( pUrlData->path[i] && pUrlData->path[i]!='=' ){ i++; }
@@ -275,11 +275,11 @@
275 pUrlData->path, zExe
276 );
277 }
278 if( pUrlData->isSsh && pUrlData->path[1] ){
279 char *zOld = pUrlData->path;
280 pUrlData->path = mprintf("%s", zOld+1);
281 fossil_free(zOld);
282 }
283 free(zLogin);
284 }else if( strncmp(zUrl, "file:", 5)==0 ){
285 pUrlData->isFile = 1;
@@ -286,14 +286,14 @@
286 if( zUrl[5]=='/' && zUrl[6]=='/' ){
287 i = 7;
288 }else{
289 i = 5;
290 }
291 zFile = mprintf("%s", &zUrl[i]);
292 }else if( file_isfile(zUrl, ExtFILE) ){
293 pUrlData->isFile = 1;
294 zFile = mprintf("%s", zUrl);
295 }else if( file_isdir(zUrl, ExtFILE)==1 ){
296 zFile = mprintf("%s/FOSSIL", zUrl);
297 if( file_isfile(zFile, ExtFILE) ){
298 pUrlData->isFile = 1;
299 }else{
300
--- src/url.c
+++ src/url.c
@@ -227,17 +227,17 @@
227 }else{
228 pUrlData->port = pUrlData->dfltPort;
229 pUrlData->hostname = pUrlData->name;
230 }
231 dehttpize(pUrlData->name);
232 pUrlData->path = fossil_strdup(&zUrl[i]);
233 for(i=0; pUrlData->path[i] && pUrlData->path[i]!='?'; i++){}
234 if( pUrlData->path[i] ){
235 pUrlData->path[i] = 0;
236 i++;
237 }
238 zExe = fossil_strdup("");
239 while( pUrlData->path[i]!=0 ){
240 char *zName, *zValue;
241 zName = &pUrlData->path[i];
242 zValue = zName;
243 while( pUrlData->path[i] && pUrlData->path[i]!='=' ){ i++; }
@@ -275,11 +275,11 @@
275 pUrlData->path, zExe
276 );
277 }
278 if( pUrlData->isSsh && pUrlData->path[1] ){
279 char *zOld = pUrlData->path;
280 pUrlData->path = fossil_strdup(zOld+1);
281 fossil_free(zOld);
282 }
283 free(zLogin);
284 }else if( strncmp(zUrl, "file:", 5)==0 ){
285 pUrlData->isFile = 1;
@@ -286,14 +286,14 @@
286 if( zUrl[5]=='/' && zUrl[6]=='/' ){
287 i = 7;
288 }else{
289 i = 5;
290 }
291 zFile = fossil_strdup(&zUrl[i]);
292 }else if( file_isfile(zUrl, ExtFILE) ){
293 pUrlData->isFile = 1;
294 zFile = fossil_strdup(zUrl);
295 }else if( file_isdir(zUrl, ExtFILE)==1 ){
296 zFile = mprintf("%s/FOSSIL", zUrl);
297 if( file_isfile(zFile, ExtFILE) ){
298 pUrlData->isFile = 1;
299 }else{
300
+6 -5
--- src/user.c
+++ src/user.c
@@ -109,10 +109,12 @@
109109
}
110110
void freepass(){
111111
if( !zPwdBuffer ) return;
112112
assert( nPwdBuffer>0 );
113113
fossil_secure_free_page(zPwdBuffer, nPwdBuffer);
114
+ zPwdBuffer = 0;
115
+ nPwdBuffer = 0;
114116
}
115117
#endif
116118
117119
/*
118120
** Scramble substitution matrix:
@@ -286,13 +288,12 @@
286288
char *zPrompt = mprintf("\rpassword for %s: ", zUser);
287289
char *zPw;
288290
Blob x;
289291
fossil_force_newline();
290292
prompt_for_password(zPrompt, &x, 0);
291
- free(zPrompt);
292
- zPw = mprintf("%b", &x);
293
- blob_reset(&x);
293
+ fossil_free(zPrompt);
294
+ zPw = blob_str(&x)/*transfer ownership*/;
294295
return zPw;
295296
}
296297
297298
/*
298299
** Prompt the user to enter a single line of text.
@@ -465,11 +466,11 @@
465466
char *zSecret = sha1_shared_secret(blob_str(&pw), g.argv[3], 0);
466467
db_unprotect(PROTECT_USER);
467468
db_multi_exec("UPDATE user SET pw=%Q, mtime=now() WHERE uid=%d",
468469
zSecret, uid);
469470
db_protect_pop();
470
- free(zSecret);
471
+ fossil_free(zSecret);
471472
}
472473
}else if( n>=2 && strncmp(g.argv[2],"capabilities",2)==0 ){
473474
int uid;
474475
if( g.argc!=4 && g.argc!=5 ){
475476
usage("capabilities USERNAME ?PERMISSIONS?");
@@ -521,11 +522,11 @@
521522
return 0;
522523
}
523524
uid = db_int(0, "SELECT uid FROM user WHERE login=%Q", zLogin);
524525
if( uid ){
525526
g.userUid = uid;
526
- g.zLogin = mprintf("%s", zLogin);
527
+ g.zLogin = fossil_strdup(zLogin);
527528
return 1;
528529
}
529530
return 0;
530531
}
531532
532533
--- src/user.c
+++ src/user.c
@@ -109,10 +109,12 @@
109 }
110 void freepass(){
111 if( !zPwdBuffer ) return;
112 assert( nPwdBuffer>0 );
113 fossil_secure_free_page(zPwdBuffer, nPwdBuffer);
 
 
114 }
115 #endif
116
117 /*
118 ** Scramble substitution matrix:
@@ -286,13 +288,12 @@
286 char *zPrompt = mprintf("\rpassword for %s: ", zUser);
287 char *zPw;
288 Blob x;
289 fossil_force_newline();
290 prompt_for_password(zPrompt, &x, 0);
291 free(zPrompt);
292 zPw = mprintf("%b", &x);
293 blob_reset(&x);
294 return zPw;
295 }
296
297 /*
298 ** Prompt the user to enter a single line of text.
@@ -465,11 +466,11 @@
465 char *zSecret = sha1_shared_secret(blob_str(&pw), g.argv[3], 0);
466 db_unprotect(PROTECT_USER);
467 db_multi_exec("UPDATE user SET pw=%Q, mtime=now() WHERE uid=%d",
468 zSecret, uid);
469 db_protect_pop();
470 free(zSecret);
471 }
472 }else if( n>=2 && strncmp(g.argv[2],"capabilities",2)==0 ){
473 int uid;
474 if( g.argc!=4 && g.argc!=5 ){
475 usage("capabilities USERNAME ?PERMISSIONS?");
@@ -521,11 +522,11 @@
521 return 0;
522 }
523 uid = db_int(0, "SELECT uid FROM user WHERE login=%Q", zLogin);
524 if( uid ){
525 g.userUid = uid;
526 g.zLogin = mprintf("%s", zLogin);
527 return 1;
528 }
529 return 0;
530 }
531
532
--- src/user.c
+++ src/user.c
@@ -109,10 +109,12 @@
109 }
110 void freepass(){
111 if( !zPwdBuffer ) return;
112 assert( nPwdBuffer>0 );
113 fossil_secure_free_page(zPwdBuffer, nPwdBuffer);
114 zPwdBuffer = 0;
115 nPwdBuffer = 0;
116 }
117 #endif
118
119 /*
120 ** Scramble substitution matrix:
@@ -286,13 +288,12 @@
288 char *zPrompt = mprintf("\rpassword for %s: ", zUser);
289 char *zPw;
290 Blob x;
291 fossil_force_newline();
292 prompt_for_password(zPrompt, &x, 0);
293 fossil_free(zPrompt);
294 zPw = blob_str(&x)/*transfer ownership*/;
 
295 return zPw;
296 }
297
298 /*
299 ** Prompt the user to enter a single line of text.
@@ -465,11 +466,11 @@
466 char *zSecret = sha1_shared_secret(blob_str(&pw), g.argv[3], 0);
467 db_unprotect(PROTECT_USER);
468 db_multi_exec("UPDATE user SET pw=%Q, mtime=now() WHERE uid=%d",
469 zSecret, uid);
470 db_protect_pop();
471 fossil_free(zSecret);
472 }
473 }else if( n>=2 && strncmp(g.argv[2],"capabilities",2)==0 ){
474 int uid;
475 if( g.argc!=4 && g.argc!=5 ){
476 usage("capabilities USERNAME ?PERMISSIONS?");
@@ -521,11 +522,11 @@
522 return 0;
523 }
524 uid = db_int(0, "SELECT uid FROM user WHERE login=%Q", zLogin);
525 if( uid ){
526 g.userUid = uid;
527 g.zLogin = fossil_strdup(zLogin);
528 return 1;
529 }
530 return 0;
531 }
532
533
+42
--- src/util.c
+++ src/util.c
@@ -899,10 +899,52 @@
899899
}else{
900900
fossil_print("%s\n", zPassword);
901901
}
902902
fossil_free(zPassword);
903903
}
904
+
905
+/*
906
+** Generate a version 4 ("random"), variant 1 UUID (RFC 9562, Section 5.4).
907
+**
908
+** Format: xxxxxxxx-xxxx-Mxxx-Nxxx-xxxxxxxxxxxx
909
+** where M=4 and N=8, 9, a, or b (this leaves 122 random bits)
910
+*/
911
+char* fossil_generate_uuid() {
912
+ static const char zDigits[] = "0123456789abcdef";
913
+ unsigned char aBlob[16];
914
+ unsigned char zStr[37];
915
+ unsigned char *p = zStr;
916
+ int i, k;
917
+
918
+ sqlite3_randomness(16, aBlob);
919
+ aBlob[6] = (aBlob[6]&0x0f) + 0x40; /* Version byte: 0100 xxxx */
920
+ aBlob[8] = (aBlob[8]&0x3f) + 0x80; /* Variant byte: 1000 xxxx */
921
+
922
+ for(i=0, k=0x550; i<16; i++, k=k>>1){
923
+ if( k&1 ){
924
+ *p++ = '-'; /* Add a dash after byte 4, 6, 8, and 12 */
925
+ }
926
+ *p++ = zDigits[aBlob[i]>>4];
927
+ *p++ = zDigits[aBlob[i]&0xf];
928
+ }
929
+ *p = 0;
930
+
931
+ return fossil_strdup((char*)zStr);
932
+}
933
+
934
+/*
935
+** COMMAND: test-generate-uuid
936
+**
937
+** Usage: %fossil test-generate-uuid
938
+**
939
+** Generate a version 4 ("random"), variant 1 UUID (RFC 9562, Section 5.4):
940
+**
941
+** xxxxxxxx-xxxx-Mxxx-Nxxx-xxxxxxxxxxxx - where M=4 and N=8, 9, a, or b
942
+*/
943
+void test_generate_uuid(void){
944
+ fossil_print("%s\n", fossil_generate_uuid());
945
+}
904946
905947
/*
906948
** Return the number of decimal digits in a nonnegative integer. This is useful
907949
** when formatting text.
908950
*/
909951
--- src/util.c
+++ src/util.c
@@ -899,10 +899,52 @@
899 }else{
900 fossil_print("%s\n", zPassword);
901 }
902 fossil_free(zPassword);
903 }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
904
905 /*
906 ** Return the number of decimal digits in a nonnegative integer. This is useful
907 ** when formatting text.
908 */
909
--- src/util.c
+++ src/util.c
@@ -899,10 +899,52 @@
899 }else{
900 fossil_print("%s\n", zPassword);
901 }
902 fossil_free(zPassword);
903 }
904
905 /*
906 ** Generate a version 4 ("random"), variant 1 UUID (RFC 9562, Section 5.4).
907 **
908 ** Format: xxxxxxxx-xxxx-Mxxx-Nxxx-xxxxxxxxxxxx
909 ** where M=4 and N=8, 9, a, or b (this leaves 122 random bits)
910 */
911 char* fossil_generate_uuid() {
912 static const char zDigits[] = "0123456789abcdef";
913 unsigned char aBlob[16];
914 unsigned char zStr[37];
915 unsigned char *p = zStr;
916 int i, k;
917
918 sqlite3_randomness(16, aBlob);
919 aBlob[6] = (aBlob[6]&0x0f) + 0x40; /* Version byte: 0100 xxxx */
920 aBlob[8] = (aBlob[8]&0x3f) + 0x80; /* Variant byte: 1000 xxxx */
921
922 for(i=0, k=0x550; i<16; i++, k=k>>1){
923 if( k&1 ){
924 *p++ = '-'; /* Add a dash after byte 4, 6, 8, and 12 */
925 }
926 *p++ = zDigits[aBlob[i]>>4];
927 *p++ = zDigits[aBlob[i]&0xf];
928 }
929 *p = 0;
930
931 return fossil_strdup((char*)zStr);
932 }
933
934 /*
935 ** COMMAND: test-generate-uuid
936 **
937 ** Usage: %fossil test-generate-uuid
938 **
939 ** Generate a version 4 ("random"), variant 1 UUID (RFC 9562, Section 5.4):
940 **
941 ** xxxxxxxx-xxxx-Mxxx-Nxxx-xxxxxxxxxxxx - where M=4 and N=8, 9, a, or b
942 */
943 void test_generate_uuid(void){
944 fossil_print("%s\n", fossil_generate_uuid());
945 }
946
947 /*
948 ** Return the number of decimal digits in a nonnegative integer. This is useful
949 ** when formatting text.
950 */
951
+2 -2
--- src/vfile.c
+++ src/vfile.c
@@ -643,11 +643,11 @@
643643
if( pEntry->d_name[0]=='.' ){
644644
if( (scanFlags & SCAN_ALL)==0 ) continue;
645645
if( pEntry->d_name[1]==0 ) continue;
646646
if( pEntry->d_name[1]=='.' && pEntry->d_name[2]==0 ) continue;
647647
}
648
- zOrigPath = mprintf("%s", blob_str(pPath));
648
+ zOrigPath = fossil_strdup(blob_str(pPath));
649649
zUtf8 = fossil_path_to_utf8(pEntry->d_name);
650650
blob_appendf(pPath, "/%s", zUtf8);
651651
zPath = blob_str(pPath);
652652
if( glob_match(pIgnore1, &zPath[nPrefix+1]) ||
653653
glob_match(pIgnore2, &zPath[nPrefix+1]) ){
@@ -657,11 +657,11 @@
657657
? (file_isdir(zPath, eFType)==1) : (pEntry->d_type==DT_DIR) ){
658658
#else
659659
}else if( file_isdir(zPath, eFType)==1 ){
660660
#endif
661661
if( (scanFlags & SCAN_NESTED) || !vfile_top_of_checkout(zPath) ){
662
- char *zSavePath = mprintf("%s", zPath);
662
+ char *zSavePath = fossil_strdup(zPath);
663663
int count = vfile_dir_scan(pPath, nPrefix, scanFlags, pIgnore1,
664664
pIgnore2, eFType);
665665
db_bind_text(&ins, ":file", &zSavePath[nPrefix+1]);
666666
db_bind_int(&ins, ":count", count);
667667
db_step(&ins);
668668
--- src/vfile.c
+++ src/vfile.c
@@ -643,11 +643,11 @@
643 if( pEntry->d_name[0]=='.' ){
644 if( (scanFlags & SCAN_ALL)==0 ) continue;
645 if( pEntry->d_name[1]==0 ) continue;
646 if( pEntry->d_name[1]=='.' && pEntry->d_name[2]==0 ) continue;
647 }
648 zOrigPath = mprintf("%s", blob_str(pPath));
649 zUtf8 = fossil_path_to_utf8(pEntry->d_name);
650 blob_appendf(pPath, "/%s", zUtf8);
651 zPath = blob_str(pPath);
652 if( glob_match(pIgnore1, &zPath[nPrefix+1]) ||
653 glob_match(pIgnore2, &zPath[nPrefix+1]) ){
@@ -657,11 +657,11 @@
657 ? (file_isdir(zPath, eFType)==1) : (pEntry->d_type==DT_DIR) ){
658 #else
659 }else if( file_isdir(zPath, eFType)==1 ){
660 #endif
661 if( (scanFlags & SCAN_NESTED) || !vfile_top_of_checkout(zPath) ){
662 char *zSavePath = mprintf("%s", zPath);
663 int count = vfile_dir_scan(pPath, nPrefix, scanFlags, pIgnore1,
664 pIgnore2, eFType);
665 db_bind_text(&ins, ":file", &zSavePath[nPrefix+1]);
666 db_bind_int(&ins, ":count", count);
667 db_step(&ins);
668
--- src/vfile.c
+++ src/vfile.c
@@ -643,11 +643,11 @@
643 if( pEntry->d_name[0]=='.' ){
644 if( (scanFlags & SCAN_ALL)==0 ) continue;
645 if( pEntry->d_name[1]==0 ) continue;
646 if( pEntry->d_name[1]=='.' && pEntry->d_name[2]==0 ) continue;
647 }
648 zOrigPath = fossil_strdup(blob_str(pPath));
649 zUtf8 = fossil_path_to_utf8(pEntry->d_name);
650 blob_appendf(pPath, "/%s", zUtf8);
651 zPath = blob_str(pPath);
652 if( glob_match(pIgnore1, &zPath[nPrefix+1]) ||
653 glob_match(pIgnore2, &zPath[nPrefix+1]) ){
@@ -657,11 +657,11 @@
657 ? (file_isdir(zPath, eFType)==1) : (pEntry->d_type==DT_DIR) ){
658 #else
659 }else if( file_isdir(zPath, eFType)==1 ){
660 #endif
661 if( (scanFlags & SCAN_NESTED) || !vfile_top_of_checkout(zPath) ){
662 char *zSavePath = fossil_strdup(zPath);
663 int count = vfile_dir_scan(pPath, nPrefix, scanFlags, pIgnore1,
664 pIgnore2, eFType);
665 db_bind_text(&ins, ":file", &zSavePath[nPrefix+1]);
666 db_bind_int(&ins, ":count", count);
667 db_step(&ins);
668
+1 -1
--- src/wiki.c
+++ src/wiki.c
@@ -574,11 +574,11 @@
574574
Blob wiki;
575575
Manifest *pWiki = 0;
576576
const char *zPageName;
577577
const char *zMimetype = 0;
578578
int isPopup = P("popup")!=0;
579
- char *zBody = mprintf("%s","<i>Empty Page</i>");
579
+ char *zBody = fossil_strdup("<i>Empty Page</i>");
580580
int noSubmenu = P("nsm")!=0 || g.isHome;
581581
582582
login_check_credentials();
583583
if( !g.perm.RdWiki ){ login_needed(g.anon.RdWiki); return; }
584584
zPageName = P("name");
585585
--- src/wiki.c
+++ src/wiki.c
@@ -574,11 +574,11 @@
574 Blob wiki;
575 Manifest *pWiki = 0;
576 const char *zPageName;
577 const char *zMimetype = 0;
578 int isPopup = P("popup")!=0;
579 char *zBody = mprintf("%s","<i>Empty Page</i>");
580 int noSubmenu = P("nsm")!=0 || g.isHome;
581
582 login_check_credentials();
583 if( !g.perm.RdWiki ){ login_needed(g.anon.RdWiki); return; }
584 zPageName = P("name");
585
--- src/wiki.c
+++ src/wiki.c
@@ -574,11 +574,11 @@
574 Blob wiki;
575 Manifest *pWiki = 0;
576 const char *zPageName;
577 const char *zMimetype = 0;
578 int isPopup = P("popup")!=0;
579 char *zBody = fossil_strdup("<i>Empty Page</i>");
580 int noSubmenu = P("nsm")!=0 || g.isHome;
581
582 login_check_credentials();
583 if( !g.perm.RdWiki ){ login_needed(g.anon.RdWiki); return; }
584 zPageName = P("name");
585
+1 -1
--- src/winhttp.c
+++ src/winhttp.c
@@ -1154,11 +1154,11 @@
11541154
"port number must be in the range 1 - 65535.");
11551155
}
11561156
if( !zRepository ){
11571157
db_must_be_within_tree();
11581158
}else if( file_isdir(zRepository, ExtFILE)==1 ){
1159
- g.zRepositoryName = mprintf("%s", zRepository);
1159
+ g.zRepositoryName = fossil_strdup(zRepository);
11601160
file_simplify_name(g.zRepositoryName, -1, 0);
11611161
}else{
11621162
db_open_repository(zRepository);
11631163
}
11641164
db_close(0);
11651165
--- src/winhttp.c
+++ src/winhttp.c
@@ -1154,11 +1154,11 @@
1154 "port number must be in the range 1 - 65535.");
1155 }
1156 if( !zRepository ){
1157 db_must_be_within_tree();
1158 }else if( file_isdir(zRepository, ExtFILE)==1 ){
1159 g.zRepositoryName = mprintf("%s", zRepository);
1160 file_simplify_name(g.zRepositoryName, -1, 0);
1161 }else{
1162 db_open_repository(zRepository);
1163 }
1164 db_close(0);
1165
--- src/winhttp.c
+++ src/winhttp.c
@@ -1154,11 +1154,11 @@
1154 "port number must be in the range 1 - 65535.");
1155 }
1156 if( !zRepository ){
1157 db_must_be_within_tree();
1158 }else if( file_isdir(zRepository, ExtFILE)==1 ){
1159 g.zRepositoryName = fossil_strdup(zRepository);
1160 file_simplify_name(g.zRepositoryName, -1, 0);
1161 }else{
1162 db_open_repository(zRepository);
1163 }
1164 db_close(0);
1165
+55 -13
--- src/xfer.c
+++ src/xfer.c
@@ -827,11 +827,11 @@
827827
int rc = -1;
828828
char *zLogin = blob_terminate(pLogin);
829829
defossilize(zLogin);
830830
831831
if( fossil_strcmp(zLogin, "nobody")==0
832
- || fossil_strcmp(zLogin,"anonymous")==0
832
+ || fossil_strcmp(zLogin, "anonymous")==0
833833
){
834834
return 0; /* Anybody is allowed to sync as "nobody" or "anonymous" */
835835
}
836836
if( fossil_strcmp(P("REMOTE_USER"), zLogin)==0
837837
&& db_get_boolean("remote_user_ok",0) ){
@@ -866,11 +866,11 @@
866866
const char *zPw = db_column_text(&q, 0);
867867
char *zSecret = sha1_shared_secret(zPw, blob_str(pLogin), 0);
868868
blob_zero(&combined);
869869
blob_copy(&combined, pNonce);
870870
blob_append(&combined, zSecret, -1);
871
- free(zSecret);
871
+ fossil_free(zSecret);
872872
sha1sum_blob(&combined, &hash);
873873
rc = blob_constant_time_cmp(&hash, pSig);
874874
blob_reset(&hash);
875875
blob_reset(&combined);
876876
}
@@ -1241,10 +1241,25 @@
12411241
/*
12421242
** If this variable is set, disable login checks. Used for debugging
12431243
** only.
12441244
*/
12451245
static int disableLogin = 0;
1246
+
1247
+/*
1248
+** Must be passed the version info from pragmas
1249
+** client-version/server-version cards. If the version info is "new
1250
+** enough" then the loginCardMode is ORd into the X-Fossil-Xfer-Login
1251
+** card flag, else this is a no-op.
1252
+*/
1253
+static void xfer_xflc_check(int iRemoteVersion, int iDate, int iTime,
1254
+ int fLoginCardMode){
1255
+ if( iRemoteVersion>=22700
1256
+ && (iDate > 20250727
1257
+ || (iDate == 20250727 && iTime >= 110500)) ){
1258
+ g.syncInfo.fLoginCardMode |= fLoginCardMode;
1259
+ }
1260
+}
12461261
12471262
/*
12481263
** The CGI/HTTP preprocessor always redirects requests with a content-type
12491264
** of application/x-fossil or application/x-fossil-debug to this page,
12501265
** regardless of what path was specified in the HTTP header. This allows
@@ -1273,10 +1288,11 @@
12731288
int nUuidList = 0;
12741289
char **pzUuidList = 0;
12751290
int *pnUuidList = 0;
12761291
int uvCatalogSent = 0;
12771292
int bSendLinks = 0;
1293
+ int nLogin = 0;
12781294
12791295
if( fossil_strcmp(PD("REQUEST_METHOD","POST"),"POST") ){
12801296
fossil_redirect_home();
12811297
}
12821298
g.zLogin = "anonymous";
@@ -1314,10 +1330,24 @@
13141330
}
13151331
zScript = xfer_push_code();
13161332
if( zScript ){ /* NOTE: Are TH1 transfer hooks enabled? */
13171333
pzUuidList = &zUuidList;
13181334
pnUuidList = &nUuidList;
1335
+ }
1336
+ if( g.syncInfo.zLoginCard ){
1337
+ /* Login card received via HTTP Cookie header */
1338
+ assert( g.syncInfo.fLoginCardMode && "Set via HTTP cookie" );
1339
+ blob_zero(&xfer.line);
1340
+ blob_append(&xfer.line, g.syncInfo.zLoginCard, -1);
1341
+ xfer.nToken = blob_tokenize(&xfer.line, xfer.aToken,
1342
+ count(xfer.aToken));
1343
+ fossil_free( g.syncInfo.zLoginCard );
1344
+ g.syncInfo.zLoginCard = 0;
1345
+ if( xfer.nToken==4
1346
+ && blob_eq(&xfer.aToken[0], "login") ){
1347
+ goto handle_login_card;
1348
+ }
13191349
}
13201350
while( blob_line(xfer.pIn, &xfer.line) ){
13211351
if( blob_buffer(&xfer.line)[0]=='#' ) continue;
13221352
if( blob_size(&xfer.line)==0 ) continue;
13231353
xfer.nToken = blob_tokenize(&xfer.line, xfer.aToken, count(xfer.aToken));
@@ -1550,17 +1580,28 @@
15501580
15511581
/* login USER NONCE SIGNATURE
15521582
**
15531583
** The client has sent login credentials to the server.
15541584
** Validate the login. This has to happen before anything else.
1555
- ** The client can send multiple logins. Permissions are cumulative.
1585
+ **
1586
+ ** For many years, Fossil would accept multiple login cards with
1587
+ ** cumulative permissions. But that feature was never used. Hence
1588
+ ** it is now prohibited. Any login card after the first generates
1589
+ ** a fatal error.
15561590
*/
15571591
if( blob_eq(&xfer.aToken[0], "login")
15581592
&& xfer.nToken==4
15591593
){
1594
+ handle_login_card:
1595
+ nLogin++;
15601596
if( disableLogin ){
15611597
g.perm.Read = g.perm.Write = g.perm.Private = g.perm.Admin = 1;
1598
+ }else if( nLogin > 1 ){
1599
+ cgi_reset_content();
1600
+ @ error multiple\slogin\cards
1601
+ nErr++;
1602
+ break;
15621603
}else{
15631604
if( check_tail_hash(&xfer.aToken[2], xfer.pIn)
15641605
|| check_login(&xfer.aToken[1], &xfer.aToken[2], &xfer.aToken[3])
15651606
){
15661607
cgi_reset_content();
@@ -1651,11 +1692,10 @@
16511692
}else{
16521693
xfer.nextIsPrivate = 1;
16531694
}
16541695
}else
16551696
1656
-
16571697
/* pragma NAME VALUE...
16581698
**
16591699
** The client issues pragmas to try to influence the behavior of the
16601700
** server. These are requests only. Unknown pragmas are silently
16611701
** ignored.
@@ -1694,17 +1734,20 @@
16941734
** The client announces to the server what version of Fossil it
16951735
** is running. The DATE and TIME are a pure numeric ISO8601 time
16961736
** for the specific check-in of the client.
16971737
*/
16981738
if( xfer.nToken>=3 && blob_eq(&xfer.aToken[1], "client-version") ){
1699
- xfer.remoteVersion = atoi(blob_str(&xfer.aToken[2]));
1739
+ xfer.remoteVersion = g.syncInfo.remoteVersion =
1740
+ atoi(blob_str(&xfer.aToken[2]));
17001741
if( xfer.nToken>=5 ){
17011742
xfer.remoteDate = atoi(blob_str(&xfer.aToken[3]));
17021743
xfer.remoteTime = atoi(blob_str(&xfer.aToken[4]));
17031744
@ pragma server-version %d(RELEASE_VERSION_NUMBER) \
17041745
@ %d(MANIFEST_NUMERIC_DATE) %d(MANIFEST_NUMERIC_TIME)
17051746
}
1747
+ xfer_xflc_check( xfer.remoteVersion, xfer.remoteDate,
1748
+ xfer.remoteTime, 0x04 );
17061749
}else
17071750
17081751
/* pragma uv-hash HASH
17091752
**
17101753
** The client wants to make sure that unversioned files are all synced.
@@ -2341,18 +2384,17 @@
23412384
blob_appendf(&send, "pragma ci-lock %s %s\n", zCkinLock, zClientId);
23422385
zCkinLock = 0;
23432386
}else if( zClientId ){
23442387
blob_appendf(&send, "pragma ci-unlock %s\n", zClientId);
23452388
}
2346
-
23472389
/* Append randomness to the end of the uplink message. This makes all
23482390
** messages unique so that that the login-card nonce will always
23492391
** be unique.
23502392
*/
23512393
zRandomness = db_text(0, "SELECT hex(randomblob(20))");
23522394
blob_appendf(&send, "# %s\n", zRandomness);
2353
- free(zRandomness);
2395
+ fossil_free(zRandomness);
23542396
23552397
if( (syncFlags & SYNC_VERBOSE)!=0
23562398
&& (syncFlags & SYNC_XVERBOSE)==0
23572399
){
23582400
fossil_print("waiting for server...");
@@ -2725,13 +2767,10 @@
27252767
27262768
/* message MESSAGE
27272769
**
27282770
** A message is received from the server. Print it.
27292771
** Similar to "error" but does not stop processing.
2730
- **
2731
- ** If the "login failed" message is seen, clear the sync password prior
2732
- ** to the next cycle.
27332772
*/
27342773
if( blob_eq(&xfer.aToken[0],"message") && xfer.nToken==2 ){
27352774
char *zMsg = blob_terminate(&xfer.aToken[1]);
27362775
defossilize(zMsg);
27372776
if( (syncFlags & SYNC_PUSH) && zMsg
@@ -2757,15 +2796,18 @@
27572796
** The server announces to the server what version of Fossil it
27582797
** is running. The DATE and TIME are a pure numeric ISO8601 time
27592798
** for the specific check-in of the client.
27602799
*/
27612800
if( xfer.nToken>=3 && blob_eq(&xfer.aToken[1], "server-version") ){
2762
- xfer.remoteVersion = atoi(blob_str(&xfer.aToken[2]));
2801
+ xfer.remoteVersion = g.syncInfo.remoteVersion =
2802
+ atoi(blob_str(&xfer.aToken[2]));
27632803
if( xfer.nToken>=5 ){
27642804
xfer.remoteDate = atoi(blob_str(&xfer.aToken[3]));
27652805
xfer.remoteTime = atoi(blob_str(&xfer.aToken[4]));
27662806
}
2807
+ xfer_xflc_check( xfer.remoteVersion, xfer.remoteDate,
2808
+ xfer.remoteTime, 0x08 );
27672809
}
27682810
27692811
/* pragma uv-pull-only
27702812
** pragma uv-push-ok
27712813
**
@@ -2896,11 +2938,11 @@
28962938
&recv
28972939
);
28982940
nErr++;
28992941
break;
29002942
}
2901
- blob_appendf(&xfer.err, "unknown command: [%b]\n", &xfer.aToken[0]);
2943
+ blob_appendf(&xfer.err, "unknown command: [%b]\n", &xfer.line);
29022944
}
29032945
29042946
if( blob_size(&xfer.err) ){
29052947
fossil_force_newline();
29062948
fossil_warning("%b", &xfer.err);
@@ -2963,11 +3005,11 @@
29633005
}else{
29643006
manifest_crosslink_end(MC_PERMIT_HOOKS);
29653007
content_enable_dephantomize(1);
29663008
}
29673009
db_end_transaction(0);
2968
- };
3010
+ }; /* while(go) */
29693011
transport_stats(&nSent, &nRcvd, 1);
29703012
if( pnRcvd ) *pnRcvd = nArtifactRcvd;
29713013
if( (rSkew*24.0*3600.0) > 10.0 ){
29723014
fossil_warning("*** time skew *** server is fast by %s",
29733015
db_timespan_name(rSkew));
29743016
--- src/xfer.c
+++ src/xfer.c
@@ -827,11 +827,11 @@
827 int rc = -1;
828 char *zLogin = blob_terminate(pLogin);
829 defossilize(zLogin);
830
831 if( fossil_strcmp(zLogin, "nobody")==0
832 || fossil_strcmp(zLogin,"anonymous")==0
833 ){
834 return 0; /* Anybody is allowed to sync as "nobody" or "anonymous" */
835 }
836 if( fossil_strcmp(P("REMOTE_USER"), zLogin)==0
837 && db_get_boolean("remote_user_ok",0) ){
@@ -866,11 +866,11 @@
866 const char *zPw = db_column_text(&q, 0);
867 char *zSecret = sha1_shared_secret(zPw, blob_str(pLogin), 0);
868 blob_zero(&combined);
869 blob_copy(&combined, pNonce);
870 blob_append(&combined, zSecret, -1);
871 free(zSecret);
872 sha1sum_blob(&combined, &hash);
873 rc = blob_constant_time_cmp(&hash, pSig);
874 blob_reset(&hash);
875 blob_reset(&combined);
876 }
@@ -1241,10 +1241,25 @@
1241 /*
1242 ** If this variable is set, disable login checks. Used for debugging
1243 ** only.
1244 */
1245 static int disableLogin = 0;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1246
1247 /*
1248 ** The CGI/HTTP preprocessor always redirects requests with a content-type
1249 ** of application/x-fossil or application/x-fossil-debug to this page,
1250 ** regardless of what path was specified in the HTTP header. This allows
@@ -1273,10 +1288,11 @@
1273 int nUuidList = 0;
1274 char **pzUuidList = 0;
1275 int *pnUuidList = 0;
1276 int uvCatalogSent = 0;
1277 int bSendLinks = 0;
 
1278
1279 if( fossil_strcmp(PD("REQUEST_METHOD","POST"),"POST") ){
1280 fossil_redirect_home();
1281 }
1282 g.zLogin = "anonymous";
@@ -1314,10 +1330,24 @@
1314 }
1315 zScript = xfer_push_code();
1316 if( zScript ){ /* NOTE: Are TH1 transfer hooks enabled? */
1317 pzUuidList = &zUuidList;
1318 pnUuidList = &nUuidList;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1319 }
1320 while( blob_line(xfer.pIn, &xfer.line) ){
1321 if( blob_buffer(&xfer.line)[0]=='#' ) continue;
1322 if( blob_size(&xfer.line)==0 ) continue;
1323 xfer.nToken = blob_tokenize(&xfer.line, xfer.aToken, count(xfer.aToken));
@@ -1550,17 +1580,28 @@
1550
1551 /* login USER NONCE SIGNATURE
1552 **
1553 ** The client has sent login credentials to the server.
1554 ** Validate the login. This has to happen before anything else.
1555 ** The client can send multiple logins. Permissions are cumulative.
 
 
 
 
1556 */
1557 if( blob_eq(&xfer.aToken[0], "login")
1558 && xfer.nToken==4
1559 ){
 
 
1560 if( disableLogin ){
1561 g.perm.Read = g.perm.Write = g.perm.Private = g.perm.Admin = 1;
 
 
 
 
 
1562 }else{
1563 if( check_tail_hash(&xfer.aToken[2], xfer.pIn)
1564 || check_login(&xfer.aToken[1], &xfer.aToken[2], &xfer.aToken[3])
1565 ){
1566 cgi_reset_content();
@@ -1651,11 +1692,10 @@
1651 }else{
1652 xfer.nextIsPrivate = 1;
1653 }
1654 }else
1655
1656
1657 /* pragma NAME VALUE...
1658 **
1659 ** The client issues pragmas to try to influence the behavior of the
1660 ** server. These are requests only. Unknown pragmas are silently
1661 ** ignored.
@@ -1694,17 +1734,20 @@
1694 ** The client announces to the server what version of Fossil it
1695 ** is running. The DATE and TIME are a pure numeric ISO8601 time
1696 ** for the specific check-in of the client.
1697 */
1698 if( xfer.nToken>=3 && blob_eq(&xfer.aToken[1], "client-version") ){
1699 xfer.remoteVersion = atoi(blob_str(&xfer.aToken[2]));
 
1700 if( xfer.nToken>=5 ){
1701 xfer.remoteDate = atoi(blob_str(&xfer.aToken[3]));
1702 xfer.remoteTime = atoi(blob_str(&xfer.aToken[4]));
1703 @ pragma server-version %d(RELEASE_VERSION_NUMBER) \
1704 @ %d(MANIFEST_NUMERIC_DATE) %d(MANIFEST_NUMERIC_TIME)
1705 }
 
 
1706 }else
1707
1708 /* pragma uv-hash HASH
1709 **
1710 ** The client wants to make sure that unversioned files are all synced.
@@ -2341,18 +2384,17 @@
2341 blob_appendf(&send, "pragma ci-lock %s %s\n", zCkinLock, zClientId);
2342 zCkinLock = 0;
2343 }else if( zClientId ){
2344 blob_appendf(&send, "pragma ci-unlock %s\n", zClientId);
2345 }
2346
2347 /* Append randomness to the end of the uplink message. This makes all
2348 ** messages unique so that that the login-card nonce will always
2349 ** be unique.
2350 */
2351 zRandomness = db_text(0, "SELECT hex(randomblob(20))");
2352 blob_appendf(&send, "# %s\n", zRandomness);
2353 free(zRandomness);
2354
2355 if( (syncFlags & SYNC_VERBOSE)!=0
2356 && (syncFlags & SYNC_XVERBOSE)==0
2357 ){
2358 fossil_print("waiting for server...");
@@ -2725,13 +2767,10 @@
2725
2726 /* message MESSAGE
2727 **
2728 ** A message is received from the server. Print it.
2729 ** Similar to "error" but does not stop processing.
2730 **
2731 ** If the "login failed" message is seen, clear the sync password prior
2732 ** to the next cycle.
2733 */
2734 if( blob_eq(&xfer.aToken[0],"message") && xfer.nToken==2 ){
2735 char *zMsg = blob_terminate(&xfer.aToken[1]);
2736 defossilize(zMsg);
2737 if( (syncFlags & SYNC_PUSH) && zMsg
@@ -2757,15 +2796,18 @@
2757 ** The server announces to the server what version of Fossil it
2758 ** is running. The DATE and TIME are a pure numeric ISO8601 time
2759 ** for the specific check-in of the client.
2760 */
2761 if( xfer.nToken>=3 && blob_eq(&xfer.aToken[1], "server-version") ){
2762 xfer.remoteVersion = atoi(blob_str(&xfer.aToken[2]));
 
2763 if( xfer.nToken>=5 ){
2764 xfer.remoteDate = atoi(blob_str(&xfer.aToken[3]));
2765 xfer.remoteTime = atoi(blob_str(&xfer.aToken[4]));
2766 }
 
 
2767 }
2768
2769 /* pragma uv-pull-only
2770 ** pragma uv-push-ok
2771 **
@@ -2896,11 +2938,11 @@
2896 &recv
2897 );
2898 nErr++;
2899 break;
2900 }
2901 blob_appendf(&xfer.err, "unknown command: [%b]\n", &xfer.aToken[0]);
2902 }
2903
2904 if( blob_size(&xfer.err) ){
2905 fossil_force_newline();
2906 fossil_warning("%b", &xfer.err);
@@ -2963,11 +3005,11 @@
2963 }else{
2964 manifest_crosslink_end(MC_PERMIT_HOOKS);
2965 content_enable_dephantomize(1);
2966 }
2967 db_end_transaction(0);
2968 };
2969 transport_stats(&nSent, &nRcvd, 1);
2970 if( pnRcvd ) *pnRcvd = nArtifactRcvd;
2971 if( (rSkew*24.0*3600.0) > 10.0 ){
2972 fossil_warning("*** time skew *** server is fast by %s",
2973 db_timespan_name(rSkew));
2974
--- src/xfer.c
+++ src/xfer.c
@@ -827,11 +827,11 @@
827 int rc = -1;
828 char *zLogin = blob_terminate(pLogin);
829 defossilize(zLogin);
830
831 if( fossil_strcmp(zLogin, "nobody")==0
832 || fossil_strcmp(zLogin, "anonymous")==0
833 ){
834 return 0; /* Anybody is allowed to sync as "nobody" or "anonymous" */
835 }
836 if( fossil_strcmp(P("REMOTE_USER"), zLogin)==0
837 && db_get_boolean("remote_user_ok",0) ){
@@ -866,11 +866,11 @@
866 const char *zPw = db_column_text(&q, 0);
867 char *zSecret = sha1_shared_secret(zPw, blob_str(pLogin), 0);
868 blob_zero(&combined);
869 blob_copy(&combined, pNonce);
870 blob_append(&combined, zSecret, -1);
871 fossil_free(zSecret);
872 sha1sum_blob(&combined, &hash);
873 rc = blob_constant_time_cmp(&hash, pSig);
874 blob_reset(&hash);
875 blob_reset(&combined);
876 }
@@ -1241,10 +1241,25 @@
1241 /*
1242 ** If this variable is set, disable login checks. Used for debugging
1243 ** only.
1244 */
1245 static int disableLogin = 0;
1246
1247 /*
1248 ** Must be passed the version info from pragmas
1249 ** client-version/server-version cards. If the version info is "new
1250 ** enough" then the loginCardMode is ORd into the X-Fossil-Xfer-Login
1251 ** card flag, else this is a no-op.
1252 */
1253 static void xfer_xflc_check(int iRemoteVersion, int iDate, int iTime,
1254 int fLoginCardMode){
1255 if( iRemoteVersion>=22700
1256 && (iDate > 20250727
1257 || (iDate == 20250727 && iTime >= 110500)) ){
1258 g.syncInfo.fLoginCardMode |= fLoginCardMode;
1259 }
1260 }
1261
1262 /*
1263 ** The CGI/HTTP preprocessor always redirects requests with a content-type
1264 ** of application/x-fossil or application/x-fossil-debug to this page,
1265 ** regardless of what path was specified in the HTTP header. This allows
@@ -1273,10 +1288,11 @@
1288 int nUuidList = 0;
1289 char **pzUuidList = 0;
1290 int *pnUuidList = 0;
1291 int uvCatalogSent = 0;
1292 int bSendLinks = 0;
1293 int nLogin = 0;
1294
1295 if( fossil_strcmp(PD("REQUEST_METHOD","POST"),"POST") ){
1296 fossil_redirect_home();
1297 }
1298 g.zLogin = "anonymous";
@@ -1314,10 +1330,24 @@
1330 }
1331 zScript = xfer_push_code();
1332 if( zScript ){ /* NOTE: Are TH1 transfer hooks enabled? */
1333 pzUuidList = &zUuidList;
1334 pnUuidList = &nUuidList;
1335 }
1336 if( g.syncInfo.zLoginCard ){
1337 /* Login card received via HTTP Cookie header */
1338 assert( g.syncInfo.fLoginCardMode && "Set via HTTP cookie" );
1339 blob_zero(&xfer.line);
1340 blob_append(&xfer.line, g.syncInfo.zLoginCard, -1);
1341 xfer.nToken = blob_tokenize(&xfer.line, xfer.aToken,
1342 count(xfer.aToken));
1343 fossil_free( g.syncInfo.zLoginCard );
1344 g.syncInfo.zLoginCard = 0;
1345 if( xfer.nToken==4
1346 && blob_eq(&xfer.aToken[0], "login") ){
1347 goto handle_login_card;
1348 }
1349 }
1350 while( blob_line(xfer.pIn, &xfer.line) ){
1351 if( blob_buffer(&xfer.line)[0]=='#' ) continue;
1352 if( blob_size(&xfer.line)==0 ) continue;
1353 xfer.nToken = blob_tokenize(&xfer.line, xfer.aToken, count(xfer.aToken));
@@ -1550,17 +1580,28 @@
1580
1581 /* login USER NONCE SIGNATURE
1582 **
1583 ** The client has sent login credentials to the server.
1584 ** Validate the login. This has to happen before anything else.
1585 **
1586 ** For many years, Fossil would accept multiple login cards with
1587 ** cumulative permissions. But that feature was never used. Hence
1588 ** it is now prohibited. Any login card after the first generates
1589 ** a fatal error.
1590 */
1591 if( blob_eq(&xfer.aToken[0], "login")
1592 && xfer.nToken==4
1593 ){
1594 handle_login_card:
1595 nLogin++;
1596 if( disableLogin ){
1597 g.perm.Read = g.perm.Write = g.perm.Private = g.perm.Admin = 1;
1598 }else if( nLogin > 1 ){
1599 cgi_reset_content();
1600 @ error multiple\slogin\cards
1601 nErr++;
1602 break;
1603 }else{
1604 if( check_tail_hash(&xfer.aToken[2], xfer.pIn)
1605 || check_login(&xfer.aToken[1], &xfer.aToken[2], &xfer.aToken[3])
1606 ){
1607 cgi_reset_content();
@@ -1651,11 +1692,10 @@
1692 }else{
1693 xfer.nextIsPrivate = 1;
1694 }
1695 }else
1696
 
1697 /* pragma NAME VALUE...
1698 **
1699 ** The client issues pragmas to try to influence the behavior of the
1700 ** server. These are requests only. Unknown pragmas are silently
1701 ** ignored.
@@ -1694,17 +1734,20 @@
1734 ** The client announces to the server what version of Fossil it
1735 ** is running. The DATE and TIME are a pure numeric ISO8601 time
1736 ** for the specific check-in of the client.
1737 */
1738 if( xfer.nToken>=3 && blob_eq(&xfer.aToken[1], "client-version") ){
1739 xfer.remoteVersion = g.syncInfo.remoteVersion =
1740 atoi(blob_str(&xfer.aToken[2]));
1741 if( xfer.nToken>=5 ){
1742 xfer.remoteDate = atoi(blob_str(&xfer.aToken[3]));
1743 xfer.remoteTime = atoi(blob_str(&xfer.aToken[4]));
1744 @ pragma server-version %d(RELEASE_VERSION_NUMBER) \
1745 @ %d(MANIFEST_NUMERIC_DATE) %d(MANIFEST_NUMERIC_TIME)
1746 }
1747 xfer_xflc_check( xfer.remoteVersion, xfer.remoteDate,
1748 xfer.remoteTime, 0x04 );
1749 }else
1750
1751 /* pragma uv-hash HASH
1752 **
1753 ** The client wants to make sure that unversioned files are all synced.
@@ -2341,18 +2384,17 @@
2384 blob_appendf(&send, "pragma ci-lock %s %s\n", zCkinLock, zClientId);
2385 zCkinLock = 0;
2386 }else if( zClientId ){
2387 blob_appendf(&send, "pragma ci-unlock %s\n", zClientId);
2388 }
 
2389 /* Append randomness to the end of the uplink message. This makes all
2390 ** messages unique so that that the login-card nonce will always
2391 ** be unique.
2392 */
2393 zRandomness = db_text(0, "SELECT hex(randomblob(20))");
2394 blob_appendf(&send, "# %s\n", zRandomness);
2395 fossil_free(zRandomness);
2396
2397 if( (syncFlags & SYNC_VERBOSE)!=0
2398 && (syncFlags & SYNC_XVERBOSE)==0
2399 ){
2400 fossil_print("waiting for server...");
@@ -2725,13 +2767,10 @@
2767
2768 /* message MESSAGE
2769 **
2770 ** A message is received from the server. Print it.
2771 ** Similar to "error" but does not stop processing.
 
 
 
2772 */
2773 if( blob_eq(&xfer.aToken[0],"message") && xfer.nToken==2 ){
2774 char *zMsg = blob_terminate(&xfer.aToken[1]);
2775 defossilize(zMsg);
2776 if( (syncFlags & SYNC_PUSH) && zMsg
@@ -2757,15 +2796,18 @@
2796 ** The server announces to the server what version of Fossil it
2797 ** is running. The DATE and TIME are a pure numeric ISO8601 time
2798 ** for the specific check-in of the client.
2799 */
2800 if( xfer.nToken>=3 && blob_eq(&xfer.aToken[1], "server-version") ){
2801 xfer.remoteVersion = g.syncInfo.remoteVersion =
2802 atoi(blob_str(&xfer.aToken[2]));
2803 if( xfer.nToken>=5 ){
2804 xfer.remoteDate = atoi(blob_str(&xfer.aToken[3]));
2805 xfer.remoteTime = atoi(blob_str(&xfer.aToken[4]));
2806 }
2807 xfer_xflc_check( xfer.remoteVersion, xfer.remoteDate,
2808 xfer.remoteTime, 0x08 );
2809 }
2810
2811 /* pragma uv-pull-only
2812 ** pragma uv-push-ok
2813 **
@@ -2896,11 +2938,11 @@
2938 &recv
2939 );
2940 nErr++;
2941 break;
2942 }
2943 blob_appendf(&xfer.err, "unknown command: [%b]\n", &xfer.line);
2944 }
2945
2946 if( blob_size(&xfer.err) ){
2947 fossil_force_newline();
2948 fossil_warning("%b", &xfer.err);
@@ -2963,11 +3005,11 @@
3005 }else{
3006 manifest_crosslink_end(MC_PERMIT_HOOKS);
3007 content_enable_dephantomize(1);
3008 }
3009 db_end_transaction(0);
3010 }; /* while(go) */
3011 transport_stats(&nSent, &nRcvd, 1);
3012 if( pnRcvd ) *pnRcvd = nArtifactRcvd;
3013 if( (rSkew*24.0*3600.0) > 10.0 ){
3014 fossil_warning("*** time skew *** server is fast by %s",
3015 db_timespan_name(rSkew));
3016
+2 -1
--- src/zip.c
+++ src/zip.c
@@ -496,11 +496,11 @@
496496
if( fossil_strcmp(zName, azDir[j])==0 ) break;
497497
}
498498
if( j>=nDir ){
499499
nDir++;
500500
azDir = fossil_realloc(azDir, sizeof(azDir[0])*nDir);
501
- azDir[j] = mprintf("%s", zName);
501
+ azDir[j] = fossil_strdup(zName);
502502
zip_add_file(p, zName, 0, 0);
503503
}
504504
zName[i+1] = c;
505505
}
506506
}
@@ -1012,10 +1012,11 @@
10121012
int eType = ARCHIVE_ZIP; /* Type of archive to generate */
10131013
char *zType; /* Human-readable archive type */
10141014
10151015
login_check_credentials();
10161016
if( !g.perm.Zip ){ login_needed(g.anon.Zip); return; }
1017
+ if( robot_restrict("zip") ) return;
10171018
if( fossil_strcmp(g.zPath, "sqlar")==0 ){
10181019
eType = ARCHIVE_SQLAR;
10191020
zType = "SQL";
10201021
/* For some reason, SQL-archives are like catnip for robots. So
10211022
** don't allow them to be downloaded by user "nobody" */
10221023
--- src/zip.c
+++ src/zip.c
@@ -496,11 +496,11 @@
496 if( fossil_strcmp(zName, azDir[j])==0 ) break;
497 }
498 if( j>=nDir ){
499 nDir++;
500 azDir = fossil_realloc(azDir, sizeof(azDir[0])*nDir);
501 azDir[j] = mprintf("%s", zName);
502 zip_add_file(p, zName, 0, 0);
503 }
504 zName[i+1] = c;
505 }
506 }
@@ -1012,10 +1012,11 @@
1012 int eType = ARCHIVE_ZIP; /* Type of archive to generate */
1013 char *zType; /* Human-readable archive type */
1014
1015 login_check_credentials();
1016 if( !g.perm.Zip ){ login_needed(g.anon.Zip); return; }
 
1017 if( fossil_strcmp(g.zPath, "sqlar")==0 ){
1018 eType = ARCHIVE_SQLAR;
1019 zType = "SQL";
1020 /* For some reason, SQL-archives are like catnip for robots. So
1021 ** don't allow them to be downloaded by user "nobody" */
1022
--- src/zip.c
+++ src/zip.c
@@ -496,11 +496,11 @@
496 if( fossil_strcmp(zName, azDir[j])==0 ) break;
497 }
498 if( j>=nDir ){
499 nDir++;
500 azDir = fossil_realloc(azDir, sizeof(azDir[0])*nDir);
501 azDir[j] = fossil_strdup(zName);
502 zip_add_file(p, zName, 0, 0);
503 }
504 zName[i+1] = c;
505 }
506 }
@@ -1012,10 +1012,11 @@
1012 int eType = ARCHIVE_ZIP; /* Type of archive to generate */
1013 char *zType; /* Human-readable archive type */
1014
1015 login_check_credentials();
1016 if( !g.perm.Zip ){ login_needed(g.anon.Zip); return; }
1017 if( robot_restrict("zip") ) return;
1018 if( fossil_strcmp(g.zPath, "sqlar")==0 ){
1019 eType = ARCHIVE_SQLAR;
1020 zType = "SQL";
1021 /* For some reason, SQL-archives are like catnip for robots. So
1022 ** don't allow them to be downloaded by user "nobody" */
1023
--- tools/makemake.tcl
+++ tools/makemake.tcl
@@ -152,10 +152,11 @@
152152
purge
153153
rebuild
154154
regexp
155155
repolist
156156
report
157
+ robot
157158
rss
158159
schema
159160
search
160161
security_audit
161162
setup
162163
--- tools/makemake.tcl
+++ tools/makemake.tcl
@@ -152,10 +152,11 @@
152 purge
153 rebuild
154 regexp
155 repolist
156 report
 
157 rss
158 schema
159 search
160 security_audit
161 setup
162
--- tools/makemake.tcl
+++ tools/makemake.tcl
@@ -152,10 +152,11 @@
152 purge
153 rebuild
154 regexp
155 repolist
156 report
157 robot
158 rss
159 schema
160 search
161 security_audit
162 setup
163
+10 -4
--- win/Makefile.dmc
+++ win/Makefile.dmc
@@ -32,13 +32,13 @@
3232
3333
SHELL_OPTIONS = -DNDEBUG=1 -DSQLITE_DQS=0 -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_PROGRESS_CALLBACK -DSQLITE_OMIT_SHARED_CACHE -DSQLITE_OMIT_LOAD_EXTENSION -DSQLITE_MAX_EXPR_DEPTH=0 -DSQLITE_ENABLE_LOCKING_STYLE=0 -DSQLITE_DEFAULT_FILE_FORMAT=4 -DSQLITE_ENABLE_DBSTAT_VTAB -DSQLITE_ENABLE_EXPLAIN_COMMENTS -DSQLITE_ENABLE_FTS4 -DSQLITE_ENABLE_FTS5 -DSQLITE_ENABLE_MATH_FUNCTIONS -DSQLITE_ENABLE_SETLK_TIMEOUT -DSQLITE_ENABLE_STMTVTAB -DSQLITE_HAVE_ZLIB -DSQLITE_ENABLE_DBPAGE_VTAB -DSQLITE_TRUSTED_SCHEMA=0 -DHAVE_USLEEP -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
3434
3535
PIKCHR_OPTIONS = -DPIKCHR_TOKEN_LIMIT=10000
3636
37
-SRC = add_.c ajax_.c alerts_.c allrepo_.c attach_.c backlink_.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 chat_.c checkin_.c checkout_.c clearsign_.c clone_.c color_.c comformat_.c configure_.c content_.c cookies_.c db_.c delta_.c deltacmd_.c deltafunc_.c descendants_.c diff_.c diffcmd_.c dispatch_.c doc_.c encode_.c etag_.c event_.c export_.c extcgi_.c file_.c fileedit_.c finfo_.c foci_.c forum_.c fshell_.c fusefs_.c fuzz_.c glob_.c graph_.c gzip_.c hname_.c hook_.c http_.c http_socket_.c http_ssl_.c http_transport_.c import_.c info_.c interwiki_.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 match_.c md5_.c merge_.c merge3_.c moderate_.c name_.c patch_.c path_.c piechart_.c pikchrshow_.c pivot_.c popen_.c pqueue_.c printf_.c publish_.c purge_.c rebuild_.c regexp_.c repolist_.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 terminal_.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 wiki_.c wikiformat_.c winfile_.c winhttp_.c xfer_.c xfersetup_.c zip_.c
37
+SRC = add_.c ajax_.c alerts_.c allrepo_.c attach_.c backlink_.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 chat_.c checkin_.c checkout_.c clearsign_.c clone_.c color_.c comformat_.c configure_.c content_.c cookies_.c db_.c delta_.c deltacmd_.c deltafunc_.c descendants_.c diff_.c diffcmd_.c dispatch_.c doc_.c encode_.c etag_.c event_.c export_.c extcgi_.c file_.c fileedit_.c finfo_.c foci_.c forum_.c fshell_.c fusefs_.c fuzz_.c glob_.c graph_.c gzip_.c hname_.c hook_.c http_.c http_socket_.c http_ssl_.c http_transport_.c import_.c info_.c interwiki_.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 match_.c md5_.c merge_.c merge3_.c moderate_.c name_.c patch_.c path_.c piechart_.c pikchrshow_.c pivot_.c popen_.c pqueue_.c printf_.c publish_.c purge_.c rebuild_.c regexp_.c repolist_.c report_.c robot_.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 terminal_.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 wiki_.c wikiformat_.c winfile_.c winhttp_.c xfer_.c xfersetup_.c zip_.c
3838
39
-OBJ = $(OBJDIR)\add$O $(OBJDIR)\ajax$O $(OBJDIR)\alerts$O $(OBJDIR)\allrepo$O $(OBJDIR)\attach$O $(OBJDIR)\backlink$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)\chat$O $(OBJDIR)\checkin$O $(OBJDIR)\checkout$O $(OBJDIR)\clearsign$O $(OBJDIR)\clone$O $(OBJDIR)\color$O $(OBJDIR)\comformat$O $(OBJDIR)\configure$O $(OBJDIR)\content$O $(OBJDIR)\cookies$O $(OBJDIR)\db$O $(OBJDIR)\delta$O $(OBJDIR)\deltacmd$O $(OBJDIR)\deltafunc$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)\extcgi$O $(OBJDIR)\file$O $(OBJDIR)\fileedit$O $(OBJDIR)\finfo$O $(OBJDIR)\foci$O $(OBJDIR)\forum$O $(OBJDIR)\fshell$O $(OBJDIR)\fusefs$O $(OBJDIR)\fuzz$O $(OBJDIR)\glob$O $(OBJDIR)\graph$O $(OBJDIR)\gzip$O $(OBJDIR)\hname$O $(OBJDIR)\hook$O $(OBJDIR)\http$O $(OBJDIR)\http_socket$O $(OBJDIR)\http_ssl$O $(OBJDIR)\http_transport$O $(OBJDIR)\import$O $(OBJDIR)\info$O $(OBJDIR)\interwiki$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)\match$O $(OBJDIR)\md5$O $(OBJDIR)\merge$O $(OBJDIR)\merge3$O $(OBJDIR)\moderate$O $(OBJDIR)\name$O $(OBJDIR)\patch$O $(OBJDIR)\path$O $(OBJDIR)\piechart$O $(OBJDIR)\pikchrshow$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)\repolist$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)\terminal$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)\wiki$O $(OBJDIR)\wikiformat$O $(OBJDIR)\winfile$O $(OBJDIR)\winhttp$O $(OBJDIR)\xfer$O $(OBJDIR)\xfersetup$O $(OBJDIR)\zip$O $(OBJDIR)\shell$O $(OBJDIR)\sqlite3$O $(OBJDIR)\th$O $(OBJDIR)\th_lang$O
39
+OBJ = $(OBJDIR)\add$O $(OBJDIR)\ajax$O $(OBJDIR)\alerts$O $(OBJDIR)\allrepo$O $(OBJDIR)\attach$O $(OBJDIR)\backlink$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)\chat$O $(OBJDIR)\checkin$O $(OBJDIR)\checkout$O $(OBJDIR)\clearsign$O $(OBJDIR)\clone$O $(OBJDIR)\color$O $(OBJDIR)\comformat$O $(OBJDIR)\configure$O $(OBJDIR)\content$O $(OBJDIR)\cookies$O $(OBJDIR)\db$O $(OBJDIR)\delta$O $(OBJDIR)\deltacmd$O $(OBJDIR)\deltafunc$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)\extcgi$O $(OBJDIR)\file$O $(OBJDIR)\fileedit$O $(OBJDIR)\finfo$O $(OBJDIR)\foci$O $(OBJDIR)\forum$O $(OBJDIR)\fshell$O $(OBJDIR)\fusefs$O $(OBJDIR)\fuzz$O $(OBJDIR)\glob$O $(OBJDIR)\graph$O $(OBJDIR)\gzip$O $(OBJDIR)\hname$O $(OBJDIR)\hook$O $(OBJDIR)\http$O $(OBJDIR)\http_socket$O $(OBJDIR)\http_ssl$O $(OBJDIR)\http_transport$O $(OBJDIR)\import$O $(OBJDIR)\info$O $(OBJDIR)\interwiki$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)\match$O $(OBJDIR)\md5$O $(OBJDIR)\merge$O $(OBJDIR)\merge3$O $(OBJDIR)\moderate$O $(OBJDIR)\name$O $(OBJDIR)\patch$O $(OBJDIR)\path$O $(OBJDIR)\piechart$O $(OBJDIR)\pikchrshow$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)\repolist$O $(OBJDIR)\report$O $(OBJDIR)\robot$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)\terminal$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)\wiki$O $(OBJDIR)\wikiformat$O $(OBJDIR)\winfile$O $(OBJDIR)\winhttp$O $(OBJDIR)\xfer$O $(OBJDIR)\xfersetup$O $(OBJDIR)\zip$O $(OBJDIR)\shell$O $(OBJDIR)\sqlite3$O $(OBJDIR)\th$O $(OBJDIR)\th_lang$O
4040
4141
4242
RC=$(DMDIR)\bin\rcc
4343
RCFLAGS=-32 -w1 -I$(SRCDIR) /D__DMC__
4444
@@ -53,11 +53,11 @@
5353
5454
$(OBJDIR)\fossil.res: $B\win\fossil.rc
5555
$(RC) $(RCFLAGS) -o$@ $**
5656
5757
$(OBJDIR)\link: $B\win\Makefile.dmc $(OBJDIR)\fossil.res
58
- +echo add ajax alerts allrepo attach backlink backoffice bag bisect blob branch browse builtin bundle cache capabilities captcha cgi chat checkin checkout clearsign clone color comformat configure content cookies db delta deltacmd deltafunc descendants diff diffcmd dispatch doc encode etag event export extcgi file fileedit finfo foci forum fshell fusefs fuzz glob graph gzip hname hook http http_socket http_ssl http_transport import info interwiki 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 match md5 merge merge3 moderate name patch path piechart pikchrshow pivot popen pqueue printf publish purge rebuild regexp repolist report rss schema search security_audit setup setupuser sha1 sha1hard sha3 shun sitemap skins smtp sqlcmd stash stat statrep style sync tag tar terminal th_main timeline tkt tktsetup undo unicode unversioned update url user utf8 util verify vfile wiki wikiformat winfile winhttp xfer xfersetup zip shell sqlite3 th th_lang > $@
58
+ +echo add ajax alerts allrepo attach backlink backoffice bag bisect blob branch browse builtin bundle cache capabilities captcha cgi chat checkin checkout clearsign clone color comformat configure content cookies db delta deltacmd deltafunc descendants diff diffcmd dispatch doc encode etag event export extcgi file fileedit finfo foci forum fshell fusefs fuzz glob graph gzip hname hook http http_socket http_ssl http_transport import info interwiki 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 match md5 merge merge3 moderate name patch path piechart pikchrshow pivot popen pqueue printf publish purge rebuild regexp repolist report robot rss schema search security_audit setup setupuser sha1 sha1hard sha3 shun sitemap skins smtp sqlcmd stash stat statrep style sync tag tar terminal th_main timeline tkt tktsetup undo unicode unversioned update url user utf8 util verify vfile wiki wikiformat winfile winhttp xfer xfersetup zip shell sqlite3 th th_lang > $@
5959
+echo fossil >> $@
6060
+echo fossil >> $@
6161
+echo $(LIBS) >> $@
6262
+echo. >> $@
6363
+echo fossil >> $@
@@ -755,10 +755,16 @@
755755
$(OBJDIR)\report$O : report_.c report.h
756756
$(TCC) -o$@ -c report_.c
757757
758758
report_.c : $(SRCDIR)\report.c
759759
+translate$E $** > $@
760
+
761
+$(OBJDIR)\robot$O : robot_.c robot.h
762
+ $(TCC) -o$@ -c robot_.c
763
+
764
+robot_.c : $(SRCDIR)\robot.c
765
+ +translate$E $** > $@
760766
761767
$(OBJDIR)\rss$O : rss_.c rss.h
762768
$(TCC) -o$@ -c rss_.c
763769
764770
rss_.c : $(SRCDIR)\rss.c
@@ -1015,7 +1021,7 @@
10151021
10161022
zip_.c : $(SRCDIR)\zip.c
10171023
+translate$E $** > $@
10181024
10191025
headers: makeheaders$E page_index.h builtin_data.h VERSION.h
1020
- +makeheaders$E add_.c:add.h ajax_.c:ajax.h alerts_.c:alerts.h allrepo_.c:allrepo.h attach_.c:attach.h backlink_.c:backlink.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 chat_.c:chat.h checkin_.c:checkin.h checkout_.c:checkout.h clearsign_.c:clearsign.h clone_.c:clone.h color_.c:color.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 deltafunc_.c:deltafunc.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 extcgi_.c:extcgi.h file_.c:file.h fileedit_.c:fileedit.h finfo_.c:finfo.h foci_.c:foci.h forum_.c:forum.h fshell_.c:fshell.h fusefs_.c:fusefs.h fuzz_.c:fuzz.h glob_.c:glob.h graph_.c:graph.h gzip_.c:gzip.h hname_.c:hname.h hook_.c:hook.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 interwiki_.c:interwiki.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 match_.c:match.h md5_.c:md5.h merge_.c:merge.h merge3_.c:merge3.h moderate_.c:moderate.h name_.c:name.h patch_.c:patch.h path_.c:path.h piechart_.c:piechart.h pikchrshow_.c:pikchrshow.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 repolist_.c:repolist.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 terminal_.c:terminal.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 wiki_.c:wiki.h wikiformat_.c:wikiformat.h winfile_.c:winfile.h winhttp_.c:winhttp.h xfer_.c:xfer.h xfersetup_.c:xfersetup.h zip_.c:zip.h $(SRCDIR_extsrc)\pikchr.c:pikchr.h $(SRCDIR_extsrc)\sqlite3.h $(SRCDIR)\th.h VERSION.h $(SRCDIR_extsrc)\cson_amalgamation.h
1026
+ +makeheaders$E add_.c:add.h ajax_.c:ajax.h alerts_.c:alerts.h allrepo_.c:allrepo.h attach_.c:attach.h backlink_.c:backlink.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 chat_.c:chat.h checkin_.c:checkin.h checkout_.c:checkout.h clearsign_.c:clearsign.h clone_.c:clone.h color_.c:color.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 deltafunc_.c:deltafunc.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 extcgi_.c:extcgi.h file_.c:file.h fileedit_.c:fileedit.h finfo_.c:finfo.h foci_.c:foci.h forum_.c:forum.h fshell_.c:fshell.h fusefs_.c:fusefs.h fuzz_.c:fuzz.h glob_.c:glob.h graph_.c:graph.h gzip_.c:gzip.h hname_.c:hname.h hook_.c:hook.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 interwiki_.c:interwiki.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 match_.c:match.h md5_.c:md5.h merge_.c:merge.h merge3_.c:merge3.h moderate_.c:moderate.h name_.c:name.h patch_.c:patch.h path_.c:path.h piechart_.c:piechart.h pikchrshow_.c:pikchrshow.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 repolist_.c:repolist.h report_.c:report.h robot_.c:robot.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 terminal_.c:terminal.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 wiki_.c:wiki.h wikiformat_.c:wikiformat.h winfile_.c:winfile.h winhttp_.c:winhttp.h xfer_.c:xfer.h xfersetup_.c:xfersetup.h zip_.c:zip.h $(SRCDIR_extsrc)\pikchr.c:pikchr.h $(SRCDIR_extsrc)\sqlite3.h $(SRCDIR)\th.h VERSION.h $(SRCDIR_extsrc)\cson_amalgamation.h
10211027
@copy /Y nul: headers
10221028
--- win/Makefile.dmc
+++ win/Makefile.dmc
@@ -32,13 +32,13 @@
32
33 SHELL_OPTIONS = -DNDEBUG=1 -DSQLITE_DQS=0 -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_PROGRESS_CALLBACK -DSQLITE_OMIT_SHARED_CACHE -DSQLITE_OMIT_LOAD_EXTENSION -DSQLITE_MAX_EXPR_DEPTH=0 -DSQLITE_ENABLE_LOCKING_STYLE=0 -DSQLITE_DEFAULT_FILE_FORMAT=4 -DSQLITE_ENABLE_DBSTAT_VTAB -DSQLITE_ENABLE_EXPLAIN_COMMENTS -DSQLITE_ENABLE_FTS4 -DSQLITE_ENABLE_FTS5 -DSQLITE_ENABLE_MATH_FUNCTIONS -DSQLITE_ENABLE_SETLK_TIMEOUT -DSQLITE_ENABLE_STMTVTAB -DSQLITE_HAVE_ZLIB -DSQLITE_ENABLE_DBPAGE_VTAB -DSQLITE_TRUSTED_SCHEMA=0 -DHAVE_USLEEP -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
34
35 PIKCHR_OPTIONS = -DPIKCHR_TOKEN_LIMIT=10000
36
37 SRC = add_.c ajax_.c alerts_.c allrepo_.c attach_.c backlink_.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 chat_.c checkin_.c checkout_.c clearsign_.c clone_.c color_.c comformat_.c configure_.c content_.c cookies_.c db_.c delta_.c deltacmd_.c deltafunc_.c descendants_.c diff_.c diffcmd_.c dispatch_.c doc_.c encode_.c etag_.c event_.c export_.c extcgi_.c file_.c fileedit_.c finfo_.c foci_.c forum_.c fshell_.c fusefs_.c fuzz_.c glob_.c graph_.c gzip_.c hname_.c hook_.c http_.c http_socket_.c http_ssl_.c http_transport_.c import_.c info_.c interwiki_.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 match_.c md5_.c merge_.c merge3_.c moderate_.c name_.c patch_.c path_.c piechart_.c pikchrshow_.c pivot_.c popen_.c pqueue_.c printf_.c publish_.c purge_.c rebuild_.c regexp_.c repolist_.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 terminal_.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 wiki_.c wikiformat_.c winfile_.c winhttp_.c xfer_.c xfersetup_.c zip_.c
38
39 OBJ = $(OBJDIR)\add$O $(OBJDIR)\ajax$O $(OBJDIR)\alerts$O $(OBJDIR)\allrepo$O $(OBJDIR)\attach$O $(OBJDIR)\backlink$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)\chat$O $(OBJDIR)\checkin$O $(OBJDIR)\checkout$O $(OBJDIR)\clearsign$O $(OBJDIR)\clone$O $(OBJDIR)\color$O $(OBJDIR)\comformat$O $(OBJDIR)\configure$O $(OBJDIR)\content$O $(OBJDIR)\cookies$O $(OBJDIR)\db$O $(OBJDIR)\delta$O $(OBJDIR)\deltacmd$O $(OBJDIR)\deltafunc$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)\extcgi$O $(OBJDIR)\file$O $(OBJDIR)\fileedit$O $(OBJDIR)\finfo$O $(OBJDIR)\foci$O $(OBJDIR)\forum$O $(OBJDIR)\fshell$O $(OBJDIR)\fusefs$O $(OBJDIR)\fuzz$O $(OBJDIR)\glob$O $(OBJDIR)\graph$O $(OBJDIR)\gzip$O $(OBJDIR)\hname$O $(OBJDIR)\hook$O $(OBJDIR)\http$O $(OBJDIR)\http_socket$O $(OBJDIR)\http_ssl$O $(OBJDIR)\http_transport$O $(OBJDIR)\import$O $(OBJDIR)\info$O $(OBJDIR)\interwiki$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)\match$O $(OBJDIR)\md5$O $(OBJDIR)\merge$O $(OBJDIR)\merge3$O $(OBJDIR)\moderate$O $(OBJDIR)\name$O $(OBJDIR)\patch$O $(OBJDIR)\path$O $(OBJDIR)\piechart$O $(OBJDIR)\pikchrshow$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)\repolist$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)\terminal$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)\wiki$O $(OBJDIR)\wikiformat$O $(OBJDIR)\winfile$O $(OBJDIR)\winhttp$O $(OBJDIR)\xfer$O $(OBJDIR)\xfersetup$O $(OBJDIR)\zip$O $(OBJDIR)\shell$O $(OBJDIR)\sqlite3$O $(OBJDIR)\th$O $(OBJDIR)\th_lang$O
40
41
42 RC=$(DMDIR)\bin\rcc
43 RCFLAGS=-32 -w1 -I$(SRCDIR) /D__DMC__
44
@@ -53,11 +53,11 @@
53
54 $(OBJDIR)\fossil.res: $B\win\fossil.rc
55 $(RC) $(RCFLAGS) -o$@ $**
56
57 $(OBJDIR)\link: $B\win\Makefile.dmc $(OBJDIR)\fossil.res
58 +echo add ajax alerts allrepo attach backlink backoffice bag bisect blob branch browse builtin bundle cache capabilities captcha cgi chat checkin checkout clearsign clone color comformat configure content cookies db delta deltacmd deltafunc descendants diff diffcmd dispatch doc encode etag event export extcgi file fileedit finfo foci forum fshell fusefs fuzz glob graph gzip hname hook http http_socket http_ssl http_transport import info interwiki 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 match md5 merge merge3 moderate name patch path piechart pikchrshow pivot popen pqueue printf publish purge rebuild regexp repolist report rss schema search security_audit setup setupuser sha1 sha1hard sha3 shun sitemap skins smtp sqlcmd stash stat statrep style sync tag tar terminal th_main timeline tkt tktsetup undo unicode unversioned update url user utf8 util verify vfile wiki wikiformat winfile winhttp xfer xfersetup zip shell sqlite3 th th_lang > $@
59 +echo fossil >> $@
60 +echo fossil >> $@
61 +echo $(LIBS) >> $@
62 +echo. >> $@
63 +echo fossil >> $@
@@ -755,10 +755,16 @@
755 $(OBJDIR)\report$O : report_.c report.h
756 $(TCC) -o$@ -c report_.c
757
758 report_.c : $(SRCDIR)\report.c
759 +translate$E $** > $@
 
 
 
 
 
 
760
761 $(OBJDIR)\rss$O : rss_.c rss.h
762 $(TCC) -o$@ -c rss_.c
763
764 rss_.c : $(SRCDIR)\rss.c
@@ -1015,7 +1021,7 @@
1015
1016 zip_.c : $(SRCDIR)\zip.c
1017 +translate$E $** > $@
1018
1019 headers: makeheaders$E page_index.h builtin_data.h VERSION.h
1020 +makeheaders$E add_.c:add.h ajax_.c:ajax.h alerts_.c:alerts.h allrepo_.c:allrepo.h attach_.c:attach.h backlink_.c:backlink.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 chat_.c:chat.h checkin_.c:checkin.h checkout_.c:checkout.h clearsign_.c:clearsign.h clone_.c:clone.h color_.c:color.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 deltafunc_.c:deltafunc.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 extcgi_.c:extcgi.h file_.c:file.h fileedit_.c:fileedit.h finfo_.c:finfo.h foci_.c:foci.h forum_.c:forum.h fshell_.c:fshell.h fusefs_.c:fusefs.h fuzz_.c:fuzz.h glob_.c:glob.h graph_.c:graph.h gzip_.c:gzip.h hname_.c:hname.h hook_.c:hook.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 interwiki_.c:interwiki.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 match_.c:match.h md5_.c:md5.h merge_.c:merge.h merge3_.c:merge3.h moderate_.c:moderate.h name_.c:name.h patch_.c:patch.h path_.c:path.h piechart_.c:piechart.h pikchrshow_.c:pikchrshow.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 repolist_.c:repolist.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 terminal_.c:terminal.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 wiki_.c:wiki.h wikiformat_.c:wikiformat.h winfile_.c:winfile.h winhttp_.c:winhttp.h xfer_.c:xfer.h xfersetup_.c:xfersetup.h zip_.c:zip.h $(SRCDIR_extsrc)\pikchr.c:pikchr.h $(SRCDIR_extsrc)\sqlite3.h $(SRCDIR)\th.h VERSION.h $(SRCDIR_extsrc)\cson_amalgamation.h
1021 @copy /Y nul: headers
1022
--- win/Makefile.dmc
+++ win/Makefile.dmc
@@ -32,13 +32,13 @@
32
33 SHELL_OPTIONS = -DNDEBUG=1 -DSQLITE_DQS=0 -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_PROGRESS_CALLBACK -DSQLITE_OMIT_SHARED_CACHE -DSQLITE_OMIT_LOAD_EXTENSION -DSQLITE_MAX_EXPR_DEPTH=0 -DSQLITE_ENABLE_LOCKING_STYLE=0 -DSQLITE_DEFAULT_FILE_FORMAT=4 -DSQLITE_ENABLE_DBSTAT_VTAB -DSQLITE_ENABLE_EXPLAIN_COMMENTS -DSQLITE_ENABLE_FTS4 -DSQLITE_ENABLE_FTS5 -DSQLITE_ENABLE_MATH_FUNCTIONS -DSQLITE_ENABLE_SETLK_TIMEOUT -DSQLITE_ENABLE_STMTVTAB -DSQLITE_HAVE_ZLIB -DSQLITE_ENABLE_DBPAGE_VTAB -DSQLITE_TRUSTED_SCHEMA=0 -DHAVE_USLEEP -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
34
35 PIKCHR_OPTIONS = -DPIKCHR_TOKEN_LIMIT=10000
36
37 SRC = add_.c ajax_.c alerts_.c allrepo_.c attach_.c backlink_.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 chat_.c checkin_.c checkout_.c clearsign_.c clone_.c color_.c comformat_.c configure_.c content_.c cookies_.c db_.c delta_.c deltacmd_.c deltafunc_.c descendants_.c diff_.c diffcmd_.c dispatch_.c doc_.c encode_.c etag_.c event_.c export_.c extcgi_.c file_.c fileedit_.c finfo_.c foci_.c forum_.c fshell_.c fusefs_.c fuzz_.c glob_.c graph_.c gzip_.c hname_.c hook_.c http_.c http_socket_.c http_ssl_.c http_transport_.c import_.c info_.c interwiki_.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 match_.c md5_.c merge_.c merge3_.c moderate_.c name_.c patch_.c path_.c piechart_.c pikchrshow_.c pivot_.c popen_.c pqueue_.c printf_.c publish_.c purge_.c rebuild_.c regexp_.c repolist_.c report_.c robot_.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 terminal_.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 wiki_.c wikiformat_.c winfile_.c winhttp_.c xfer_.c xfersetup_.c zip_.c
38
39 OBJ = $(OBJDIR)\add$O $(OBJDIR)\ajax$O $(OBJDIR)\alerts$O $(OBJDIR)\allrepo$O $(OBJDIR)\attach$O $(OBJDIR)\backlink$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)\chat$O $(OBJDIR)\checkin$O $(OBJDIR)\checkout$O $(OBJDIR)\clearsign$O $(OBJDIR)\clone$O $(OBJDIR)\color$O $(OBJDIR)\comformat$O $(OBJDIR)\configure$O $(OBJDIR)\content$O $(OBJDIR)\cookies$O $(OBJDIR)\db$O $(OBJDIR)\delta$O $(OBJDIR)\deltacmd$O $(OBJDIR)\deltafunc$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)\extcgi$O $(OBJDIR)\file$O $(OBJDIR)\fileedit$O $(OBJDIR)\finfo$O $(OBJDIR)\foci$O $(OBJDIR)\forum$O $(OBJDIR)\fshell$O $(OBJDIR)\fusefs$O $(OBJDIR)\fuzz$O $(OBJDIR)\glob$O $(OBJDIR)\graph$O $(OBJDIR)\gzip$O $(OBJDIR)\hname$O $(OBJDIR)\hook$O $(OBJDIR)\http$O $(OBJDIR)\http_socket$O $(OBJDIR)\http_ssl$O $(OBJDIR)\http_transport$O $(OBJDIR)\import$O $(OBJDIR)\info$O $(OBJDIR)\interwiki$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)\match$O $(OBJDIR)\md5$O $(OBJDIR)\merge$O $(OBJDIR)\merge3$O $(OBJDIR)\moderate$O $(OBJDIR)\name$O $(OBJDIR)\patch$O $(OBJDIR)\path$O $(OBJDIR)\piechart$O $(OBJDIR)\pikchrshow$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)\repolist$O $(OBJDIR)\report$O $(OBJDIR)\robot$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)\terminal$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)\wiki$O $(OBJDIR)\wikiformat$O $(OBJDIR)\winfile$O $(OBJDIR)\winhttp$O $(OBJDIR)\xfer$O $(OBJDIR)\xfersetup$O $(OBJDIR)\zip$O $(OBJDIR)\shell$O $(OBJDIR)\sqlite3$O $(OBJDIR)\th$O $(OBJDIR)\th_lang$O
40
41
42 RC=$(DMDIR)\bin\rcc
43 RCFLAGS=-32 -w1 -I$(SRCDIR) /D__DMC__
44
@@ -53,11 +53,11 @@
53
54 $(OBJDIR)\fossil.res: $B\win\fossil.rc
55 $(RC) $(RCFLAGS) -o$@ $**
56
57 $(OBJDIR)\link: $B\win\Makefile.dmc $(OBJDIR)\fossil.res
58 +echo add ajax alerts allrepo attach backlink backoffice bag bisect blob branch browse builtin bundle cache capabilities captcha cgi chat checkin checkout clearsign clone color comformat configure content cookies db delta deltacmd deltafunc descendants diff diffcmd dispatch doc encode etag event export extcgi file fileedit finfo foci forum fshell fusefs fuzz glob graph gzip hname hook http http_socket http_ssl http_transport import info interwiki 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 match md5 merge merge3 moderate name patch path piechart pikchrshow pivot popen pqueue printf publish purge rebuild regexp repolist report robot rss schema search security_audit setup setupuser sha1 sha1hard sha3 shun sitemap skins smtp sqlcmd stash stat statrep style sync tag tar terminal th_main timeline tkt tktsetup undo unicode unversioned update url user utf8 util verify vfile wiki wikiformat winfile winhttp xfer xfersetup zip shell sqlite3 th th_lang > $@
59 +echo fossil >> $@
60 +echo fossil >> $@
61 +echo $(LIBS) >> $@
62 +echo. >> $@
63 +echo fossil >> $@
@@ -755,10 +755,16 @@
755 $(OBJDIR)\report$O : report_.c report.h
756 $(TCC) -o$@ -c report_.c
757
758 report_.c : $(SRCDIR)\report.c
759 +translate$E $** > $@
760
761 $(OBJDIR)\robot$O : robot_.c robot.h
762 $(TCC) -o$@ -c robot_.c
763
764 robot_.c : $(SRCDIR)\robot.c
765 +translate$E $** > $@
766
767 $(OBJDIR)\rss$O : rss_.c rss.h
768 $(TCC) -o$@ -c rss_.c
769
770 rss_.c : $(SRCDIR)\rss.c
@@ -1015,7 +1021,7 @@
1021
1022 zip_.c : $(SRCDIR)\zip.c
1023 +translate$E $** > $@
1024
1025 headers: makeheaders$E page_index.h builtin_data.h VERSION.h
1026 +makeheaders$E add_.c:add.h ajax_.c:ajax.h alerts_.c:alerts.h allrepo_.c:allrepo.h attach_.c:attach.h backlink_.c:backlink.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 chat_.c:chat.h checkin_.c:checkin.h checkout_.c:checkout.h clearsign_.c:clearsign.h clone_.c:clone.h color_.c:color.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 deltafunc_.c:deltafunc.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 extcgi_.c:extcgi.h file_.c:file.h fileedit_.c:fileedit.h finfo_.c:finfo.h foci_.c:foci.h forum_.c:forum.h fshell_.c:fshell.h fusefs_.c:fusefs.h fuzz_.c:fuzz.h glob_.c:glob.h graph_.c:graph.h gzip_.c:gzip.h hname_.c:hname.h hook_.c:hook.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 interwiki_.c:interwiki.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 match_.c:match.h md5_.c:md5.h merge_.c:merge.h merge3_.c:merge3.h moderate_.c:moderate.h name_.c:name.h patch_.c:patch.h path_.c:path.h piechart_.c:piechart.h pikchrshow_.c:pikchrshow.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 repolist_.c:repolist.h report_.c:report.h robot_.c:robot.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 terminal_.c:terminal.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 wiki_.c:wiki.h wikiformat_.c:wikiformat.h winfile_.c:winfile.h winhttp_.c:winhttp.h xfer_.c:xfer.h xfersetup_.c:xfersetup.h zip_.c:zip.h $(SRCDIR_extsrc)\pikchr.c:pikchr.h $(SRCDIR_extsrc)\sqlite3.h $(SRCDIR)\th.h VERSION.h $(SRCDIR_extsrc)\cson_amalgamation.h
1027 @copy /Y nul: headers
1028
--- win/Makefile.mingw
+++ win/Makefile.mingw
@@ -505,10 +505,11 @@
505505
$(SRCDIR)/purge.c \
506506
$(SRCDIR)/rebuild.c \
507507
$(SRCDIR)/regexp.c \
508508
$(SRCDIR)/repolist.c \
509509
$(SRCDIR)/report.c \
510
+ $(SRCDIR)/robot.c \
510511
$(SRCDIR)/rss.c \
511512
$(SRCDIR)/schema.c \
512513
$(SRCDIR)/search.c \
513514
$(SRCDIR)/security_audit.c \
514515
$(SRCDIR)/setup.c \
@@ -771,10 +772,11 @@
771772
$(OBJDIR)/purge_.c \
772773
$(OBJDIR)/rebuild_.c \
773774
$(OBJDIR)/regexp_.c \
774775
$(OBJDIR)/repolist_.c \
775776
$(OBJDIR)/report_.c \
777
+ $(OBJDIR)/robot_.c \
776778
$(OBJDIR)/rss_.c \
777779
$(OBJDIR)/schema_.c \
778780
$(OBJDIR)/search_.c \
779781
$(OBJDIR)/security_audit_.c \
780782
$(OBJDIR)/setup_.c \
@@ -921,10 +923,11 @@
921923
$(OBJDIR)/purge.o \
922924
$(OBJDIR)/rebuild.o \
923925
$(OBJDIR)/regexp.o \
924926
$(OBJDIR)/repolist.o \
925927
$(OBJDIR)/report.o \
928
+ $(OBJDIR)/robot.o \
926929
$(OBJDIR)/rss.o \
927930
$(OBJDIR)/schema.o \
928931
$(OBJDIR)/search.o \
929932
$(OBJDIR)/security_audit.o \
930933
$(OBJDIR)/setup.o \
@@ -1275,10 +1278,11 @@
12751278
$(OBJDIR)/purge_.c:$(OBJDIR)/purge.h \
12761279
$(OBJDIR)/rebuild_.c:$(OBJDIR)/rebuild.h \
12771280
$(OBJDIR)/regexp_.c:$(OBJDIR)/regexp.h \
12781281
$(OBJDIR)/repolist_.c:$(OBJDIR)/repolist.h \
12791282
$(OBJDIR)/report_.c:$(OBJDIR)/report.h \
1283
+ $(OBJDIR)/robot_.c:$(OBJDIR)/robot.h \
12801284
$(OBJDIR)/rss_.c:$(OBJDIR)/rss.h \
12811285
$(OBJDIR)/schema_.c:$(OBJDIR)/schema.h \
12821286
$(OBJDIR)/search_.c:$(OBJDIR)/search.h \
12831287
$(OBJDIR)/security_audit_.c:$(OBJDIR)/security_audit.h \
12841288
$(OBJDIR)/setup_.c:$(OBJDIR)/setup.h \
@@ -2167,10 +2171,18 @@
21672171
21682172
$(OBJDIR)/report.o: $(OBJDIR)/report_.c $(OBJDIR)/report.h $(SRCDIR)/config.h
21692173
$(XTCC) -o $(OBJDIR)/report.o -c $(OBJDIR)/report_.c
21702174
21712175
$(OBJDIR)/report.h: $(OBJDIR)/headers
2176
+
2177
+$(OBJDIR)/robot_.c: $(SRCDIR)/robot.c $(TRANSLATE)
2178
+ $(TRANSLATE) $(SRCDIR)/robot.c >$@
2179
+
2180
+$(OBJDIR)/robot.o: $(OBJDIR)/robot_.c $(OBJDIR)/robot.h $(SRCDIR)/config.h
2181
+ $(XTCC) -o $(OBJDIR)/robot.o -c $(OBJDIR)/robot_.c
2182
+
2183
+$(OBJDIR)/robot.h: $(OBJDIR)/headers
21722184
21732185
$(OBJDIR)/rss_.c: $(SRCDIR)/rss.c $(TRANSLATE)
21742186
$(TRANSLATE) $(SRCDIR)/rss.c >$@
21752187
21762188
$(OBJDIR)/rss.o: $(OBJDIR)/rss_.c $(OBJDIR)/rss.h $(SRCDIR)/config.h
21772189
--- win/Makefile.mingw
+++ win/Makefile.mingw
@@ -505,10 +505,11 @@
505 $(SRCDIR)/purge.c \
506 $(SRCDIR)/rebuild.c \
507 $(SRCDIR)/regexp.c \
508 $(SRCDIR)/repolist.c \
509 $(SRCDIR)/report.c \
 
510 $(SRCDIR)/rss.c \
511 $(SRCDIR)/schema.c \
512 $(SRCDIR)/search.c \
513 $(SRCDIR)/security_audit.c \
514 $(SRCDIR)/setup.c \
@@ -771,10 +772,11 @@
771 $(OBJDIR)/purge_.c \
772 $(OBJDIR)/rebuild_.c \
773 $(OBJDIR)/regexp_.c \
774 $(OBJDIR)/repolist_.c \
775 $(OBJDIR)/report_.c \
 
776 $(OBJDIR)/rss_.c \
777 $(OBJDIR)/schema_.c \
778 $(OBJDIR)/search_.c \
779 $(OBJDIR)/security_audit_.c \
780 $(OBJDIR)/setup_.c \
@@ -921,10 +923,11 @@
921 $(OBJDIR)/purge.o \
922 $(OBJDIR)/rebuild.o \
923 $(OBJDIR)/regexp.o \
924 $(OBJDIR)/repolist.o \
925 $(OBJDIR)/report.o \
 
926 $(OBJDIR)/rss.o \
927 $(OBJDIR)/schema.o \
928 $(OBJDIR)/search.o \
929 $(OBJDIR)/security_audit.o \
930 $(OBJDIR)/setup.o \
@@ -1275,10 +1278,11 @@
1275 $(OBJDIR)/purge_.c:$(OBJDIR)/purge.h \
1276 $(OBJDIR)/rebuild_.c:$(OBJDIR)/rebuild.h \
1277 $(OBJDIR)/regexp_.c:$(OBJDIR)/regexp.h \
1278 $(OBJDIR)/repolist_.c:$(OBJDIR)/repolist.h \
1279 $(OBJDIR)/report_.c:$(OBJDIR)/report.h \
 
1280 $(OBJDIR)/rss_.c:$(OBJDIR)/rss.h \
1281 $(OBJDIR)/schema_.c:$(OBJDIR)/schema.h \
1282 $(OBJDIR)/search_.c:$(OBJDIR)/search.h \
1283 $(OBJDIR)/security_audit_.c:$(OBJDIR)/security_audit.h \
1284 $(OBJDIR)/setup_.c:$(OBJDIR)/setup.h \
@@ -2167,10 +2171,18 @@
2167
2168 $(OBJDIR)/report.o: $(OBJDIR)/report_.c $(OBJDIR)/report.h $(SRCDIR)/config.h
2169 $(XTCC) -o $(OBJDIR)/report.o -c $(OBJDIR)/report_.c
2170
2171 $(OBJDIR)/report.h: $(OBJDIR)/headers
 
 
 
 
 
 
 
 
2172
2173 $(OBJDIR)/rss_.c: $(SRCDIR)/rss.c $(TRANSLATE)
2174 $(TRANSLATE) $(SRCDIR)/rss.c >$@
2175
2176 $(OBJDIR)/rss.o: $(OBJDIR)/rss_.c $(OBJDIR)/rss.h $(SRCDIR)/config.h
2177
--- win/Makefile.mingw
+++ win/Makefile.mingw
@@ -505,10 +505,11 @@
505 $(SRCDIR)/purge.c \
506 $(SRCDIR)/rebuild.c \
507 $(SRCDIR)/regexp.c \
508 $(SRCDIR)/repolist.c \
509 $(SRCDIR)/report.c \
510 $(SRCDIR)/robot.c \
511 $(SRCDIR)/rss.c \
512 $(SRCDIR)/schema.c \
513 $(SRCDIR)/search.c \
514 $(SRCDIR)/security_audit.c \
515 $(SRCDIR)/setup.c \
@@ -771,10 +772,11 @@
772 $(OBJDIR)/purge_.c \
773 $(OBJDIR)/rebuild_.c \
774 $(OBJDIR)/regexp_.c \
775 $(OBJDIR)/repolist_.c \
776 $(OBJDIR)/report_.c \
777 $(OBJDIR)/robot_.c \
778 $(OBJDIR)/rss_.c \
779 $(OBJDIR)/schema_.c \
780 $(OBJDIR)/search_.c \
781 $(OBJDIR)/security_audit_.c \
782 $(OBJDIR)/setup_.c \
@@ -921,10 +923,11 @@
923 $(OBJDIR)/purge.o \
924 $(OBJDIR)/rebuild.o \
925 $(OBJDIR)/regexp.o \
926 $(OBJDIR)/repolist.o \
927 $(OBJDIR)/report.o \
928 $(OBJDIR)/robot.o \
929 $(OBJDIR)/rss.o \
930 $(OBJDIR)/schema.o \
931 $(OBJDIR)/search.o \
932 $(OBJDIR)/security_audit.o \
933 $(OBJDIR)/setup.o \
@@ -1275,10 +1278,11 @@
1278 $(OBJDIR)/purge_.c:$(OBJDIR)/purge.h \
1279 $(OBJDIR)/rebuild_.c:$(OBJDIR)/rebuild.h \
1280 $(OBJDIR)/regexp_.c:$(OBJDIR)/regexp.h \
1281 $(OBJDIR)/repolist_.c:$(OBJDIR)/repolist.h \
1282 $(OBJDIR)/report_.c:$(OBJDIR)/report.h \
1283 $(OBJDIR)/robot_.c:$(OBJDIR)/robot.h \
1284 $(OBJDIR)/rss_.c:$(OBJDIR)/rss.h \
1285 $(OBJDIR)/schema_.c:$(OBJDIR)/schema.h \
1286 $(OBJDIR)/search_.c:$(OBJDIR)/search.h \
1287 $(OBJDIR)/security_audit_.c:$(OBJDIR)/security_audit.h \
1288 $(OBJDIR)/setup_.c:$(OBJDIR)/setup.h \
@@ -2167,10 +2171,18 @@
2171
2172 $(OBJDIR)/report.o: $(OBJDIR)/report_.c $(OBJDIR)/report.h $(SRCDIR)/config.h
2173 $(XTCC) -o $(OBJDIR)/report.o -c $(OBJDIR)/report_.c
2174
2175 $(OBJDIR)/report.h: $(OBJDIR)/headers
2176
2177 $(OBJDIR)/robot_.c: $(SRCDIR)/robot.c $(TRANSLATE)
2178 $(TRANSLATE) $(SRCDIR)/robot.c >$@
2179
2180 $(OBJDIR)/robot.o: $(OBJDIR)/robot_.c $(OBJDIR)/robot.h $(SRCDIR)/config.h
2181 $(XTCC) -o $(OBJDIR)/robot.o -c $(OBJDIR)/robot_.c
2182
2183 $(OBJDIR)/robot.h: $(OBJDIR)/headers
2184
2185 $(OBJDIR)/rss_.c: $(SRCDIR)/rss.c $(TRANSLATE)
2186 $(TRANSLATE) $(SRCDIR)/rss.c >$@
2187
2188 $(OBJDIR)/rss.o: $(OBJDIR)/rss_.c $(OBJDIR)/rss.h $(SRCDIR)/config.h
2189
--- win/Makefile.msc
+++ win/Makefile.msc
@@ -467,10 +467,11 @@
467467
"$(OX)\purge_.c" \
468468
"$(OX)\rebuild_.c" \
469469
"$(OX)\regexp_.c" \
470470
"$(OX)\repolist_.c" \
471471
"$(OX)\report_.c" \
472
+ "$(OX)\robot_.c" \
472473
"$(OX)\rss_.c" \
473474
"$(OX)\schema_.c" \
474475
"$(OX)\search_.c" \
475476
"$(OX)\security_audit_.c" \
476477
"$(OX)\setup_.c" \
@@ -734,10 +735,11 @@
734735
"$(OX)\purge$O" \
735736
"$(OX)\rebuild$O" \
736737
"$(OX)\regexp$O" \
737738
"$(OX)\repolist$O" \
738739
"$(OX)\report$O" \
740
+ "$(OX)\robot$O" \
739741
"$(OX)\rss$O" \
740742
"$(OX)\schema$O" \
741743
"$(OX)\search$O" \
742744
"$(OX)\security_audit$O" \
743745
"$(OX)\setup$O" \
@@ -984,10 +986,11 @@
984986
echo "$(OX)\purge.obj" >> $@
985987
echo "$(OX)\rebuild.obj" >> $@
986988
echo "$(OX)\regexp.obj" >> $@
987989
echo "$(OX)\repolist.obj" >> $@
988990
echo "$(OX)\report.obj" >> $@
991
+ echo "$(OX)\robot.obj" >> $@
989992
echo "$(OX)\rss.obj" >> $@
990993
echo "$(OX)\schema.obj" >> $@
991994
echo "$(OX)\search.obj" >> $@
992995
echo "$(OX)\security_audit.obj" >> $@
993996
echo "$(OX)\setup.obj" >> $@
@@ -1889,10 +1892,16 @@
18891892
"$(OX)\report$O" : "$(OX)\report_.c" "$(OX)\report.h"
18901893
$(TCC) /Fo$@ /Fd$(@D)\ -c "$(OX)\report_.c"
18911894
18921895
"$(OX)\report_.c" : "$(SRCDIR)\report.c"
18931896
"$(OBJDIR)\translate$E" $** > $@
1897
+
1898
+"$(OX)\robot$O" : "$(OX)\robot_.c" "$(OX)\robot.h"
1899
+ $(TCC) /Fo$@ /Fd$(@D)\ -c "$(OX)\robot_.c"
1900
+
1901
+"$(OX)\robot_.c" : "$(SRCDIR)\robot.c"
1902
+ "$(OBJDIR)\translate$E" $** > $@
18941903
18951904
"$(OX)\rss$O" : "$(OX)\rss_.c" "$(OX)\rss.h"
18961905
$(TCC) /Fo$@ /Fd$(@D)\ -c "$(OX)\rss_.c"
18971906
18981907
"$(OX)\rss_.c" : "$(SRCDIR)\rss.c"
@@ -2257,10 +2266,11 @@
22572266
"$(OX)\purge_.c":"$(OX)\purge.h" \
22582267
"$(OX)\rebuild_.c":"$(OX)\rebuild.h" \
22592268
"$(OX)\regexp_.c":"$(OX)\regexp.h" \
22602269
"$(OX)\repolist_.c":"$(OX)\repolist.h" \
22612270
"$(OX)\report_.c":"$(OX)\report.h" \
2271
+ "$(OX)\robot_.c":"$(OX)\robot.h" \
22622272
"$(OX)\rss_.c":"$(OX)\rss.h" \
22632273
"$(OX)\schema_.c":"$(OX)\schema.h" \
22642274
"$(OX)\search_.c":"$(OX)\search.h" \
22652275
"$(OX)\security_audit_.c":"$(OX)\security_audit.h" \
22662276
"$(OX)\setup_.c":"$(OX)\setup.h" \
22672277
--- win/Makefile.msc
+++ win/Makefile.msc
@@ -467,10 +467,11 @@
467 "$(OX)\purge_.c" \
468 "$(OX)\rebuild_.c" \
469 "$(OX)\regexp_.c" \
470 "$(OX)\repolist_.c" \
471 "$(OX)\report_.c" \
 
472 "$(OX)\rss_.c" \
473 "$(OX)\schema_.c" \
474 "$(OX)\search_.c" \
475 "$(OX)\security_audit_.c" \
476 "$(OX)\setup_.c" \
@@ -734,10 +735,11 @@
734 "$(OX)\purge$O" \
735 "$(OX)\rebuild$O" \
736 "$(OX)\regexp$O" \
737 "$(OX)\repolist$O" \
738 "$(OX)\report$O" \
 
739 "$(OX)\rss$O" \
740 "$(OX)\schema$O" \
741 "$(OX)\search$O" \
742 "$(OX)\security_audit$O" \
743 "$(OX)\setup$O" \
@@ -984,10 +986,11 @@
984 echo "$(OX)\purge.obj" >> $@
985 echo "$(OX)\rebuild.obj" >> $@
986 echo "$(OX)\regexp.obj" >> $@
987 echo "$(OX)\repolist.obj" >> $@
988 echo "$(OX)\report.obj" >> $@
 
989 echo "$(OX)\rss.obj" >> $@
990 echo "$(OX)\schema.obj" >> $@
991 echo "$(OX)\search.obj" >> $@
992 echo "$(OX)\security_audit.obj" >> $@
993 echo "$(OX)\setup.obj" >> $@
@@ -1889,10 +1892,16 @@
1889 "$(OX)\report$O" : "$(OX)\report_.c" "$(OX)\report.h"
1890 $(TCC) /Fo$@ /Fd$(@D)\ -c "$(OX)\report_.c"
1891
1892 "$(OX)\report_.c" : "$(SRCDIR)\report.c"
1893 "$(OBJDIR)\translate$E" $** > $@
 
 
 
 
 
 
1894
1895 "$(OX)\rss$O" : "$(OX)\rss_.c" "$(OX)\rss.h"
1896 $(TCC) /Fo$@ /Fd$(@D)\ -c "$(OX)\rss_.c"
1897
1898 "$(OX)\rss_.c" : "$(SRCDIR)\rss.c"
@@ -2257,10 +2266,11 @@
2257 "$(OX)\purge_.c":"$(OX)\purge.h" \
2258 "$(OX)\rebuild_.c":"$(OX)\rebuild.h" \
2259 "$(OX)\regexp_.c":"$(OX)\regexp.h" \
2260 "$(OX)\repolist_.c":"$(OX)\repolist.h" \
2261 "$(OX)\report_.c":"$(OX)\report.h" \
 
2262 "$(OX)\rss_.c":"$(OX)\rss.h" \
2263 "$(OX)\schema_.c":"$(OX)\schema.h" \
2264 "$(OX)\search_.c":"$(OX)\search.h" \
2265 "$(OX)\security_audit_.c":"$(OX)\security_audit.h" \
2266 "$(OX)\setup_.c":"$(OX)\setup.h" \
2267
--- win/Makefile.msc
+++ win/Makefile.msc
@@ -467,10 +467,11 @@
467 "$(OX)\purge_.c" \
468 "$(OX)\rebuild_.c" \
469 "$(OX)\regexp_.c" \
470 "$(OX)\repolist_.c" \
471 "$(OX)\report_.c" \
472 "$(OX)\robot_.c" \
473 "$(OX)\rss_.c" \
474 "$(OX)\schema_.c" \
475 "$(OX)\search_.c" \
476 "$(OX)\security_audit_.c" \
477 "$(OX)\setup_.c" \
@@ -734,10 +735,11 @@
735 "$(OX)\purge$O" \
736 "$(OX)\rebuild$O" \
737 "$(OX)\regexp$O" \
738 "$(OX)\repolist$O" \
739 "$(OX)\report$O" \
740 "$(OX)\robot$O" \
741 "$(OX)\rss$O" \
742 "$(OX)\schema$O" \
743 "$(OX)\search$O" \
744 "$(OX)\security_audit$O" \
745 "$(OX)\setup$O" \
@@ -984,10 +986,11 @@
986 echo "$(OX)\purge.obj" >> $@
987 echo "$(OX)\rebuild.obj" >> $@
988 echo "$(OX)\regexp.obj" >> $@
989 echo "$(OX)\repolist.obj" >> $@
990 echo "$(OX)\report.obj" >> $@
991 echo "$(OX)\robot.obj" >> $@
992 echo "$(OX)\rss.obj" >> $@
993 echo "$(OX)\schema.obj" >> $@
994 echo "$(OX)\search.obj" >> $@
995 echo "$(OX)\security_audit.obj" >> $@
996 echo "$(OX)\setup.obj" >> $@
@@ -1889,10 +1892,16 @@
1892 "$(OX)\report$O" : "$(OX)\report_.c" "$(OX)\report.h"
1893 $(TCC) /Fo$@ /Fd$(@D)\ -c "$(OX)\report_.c"
1894
1895 "$(OX)\report_.c" : "$(SRCDIR)\report.c"
1896 "$(OBJDIR)\translate$E" $** > $@
1897
1898 "$(OX)\robot$O" : "$(OX)\robot_.c" "$(OX)\robot.h"
1899 $(TCC) /Fo$@ /Fd$(@D)\ -c "$(OX)\robot_.c"
1900
1901 "$(OX)\robot_.c" : "$(SRCDIR)\robot.c"
1902 "$(OBJDIR)\translate$E" $** > $@
1903
1904 "$(OX)\rss$O" : "$(OX)\rss_.c" "$(OX)\rss.h"
1905 $(TCC) /Fo$@ /Fd$(@D)\ -c "$(OX)\rss_.c"
1906
1907 "$(OX)\rss_.c" : "$(SRCDIR)\rss.c"
@@ -2257,10 +2266,11 @@
2266 "$(OX)\purge_.c":"$(OX)\purge.h" \
2267 "$(OX)\rebuild_.c":"$(OX)\rebuild.h" \
2268 "$(OX)\regexp_.c":"$(OX)\regexp.h" \
2269 "$(OX)\repolist_.c":"$(OX)\repolist.h" \
2270 "$(OX)\report_.c":"$(OX)\report.h" \
2271 "$(OX)\robot_.c":"$(OX)\robot.h" \
2272 "$(OX)\rss_.c":"$(OX)\rss.h" \
2273 "$(OX)\schema_.c":"$(OX)\schema.h" \
2274 "$(OX)\search_.c":"$(OX)\search.h" \
2275 "$(OX)\security_audit_.c":"$(OX)\security_audit.h" \
2276 "$(OX)\setup_.c":"$(OX)\setup.h" \
2277
--- www/changes.wiki
+++ www/changes.wiki
@@ -13,10 +13,13 @@
1313
<li> Enable the --editor option on the [/help?cmd=amend|fossil amend] command.
1414
<li> Require at least an anonymous login to access the /blame page and similar,
1515
to help prevent robots from soaking up excess CPU time on such pages.
1616
<li> When walking the filesystem looking for Fossil repositories, avoid descending
1717
into directories named "/proc".
18
+ <li> Reduce memory requirements for sending authenticated sync protocol
19
+ messages.
20
+ <li> Add the [/help?cmd=stash | stash rename] subcommand.
1821
</ol>
1922
2023
<h2 id='v2_26'>Changes for version 2.26 (2025-04-30)</h2><ol>
2124
<li>Enhancements to [/help?cmd=diff|fossil diff] and similar:
2225
<ol type="a">
2326
--- www/changes.wiki
+++ www/changes.wiki
@@ -13,10 +13,13 @@
13 <li> Enable the --editor option on the [/help?cmd=amend|fossil amend] command.
14 <li> Require at least an anonymous login to access the /blame page and similar,
15 to help prevent robots from soaking up excess CPU time on such pages.
16 <li> When walking the filesystem looking for Fossil repositories, avoid descending
17 into directories named "/proc".
 
 
 
18 </ol>
19
20 <h2 id='v2_26'>Changes for version 2.26 (2025-04-30)</h2><ol>
21 <li>Enhancements to [/help?cmd=diff|fossil diff] and similar:
22 <ol type="a">
23
--- www/changes.wiki
+++ www/changes.wiki
@@ -13,10 +13,13 @@
13 <li> Enable the --editor option on the [/help?cmd=amend|fossil amend] command.
14 <li> Require at least an anonymous login to access the /blame page and similar,
15 to help prevent robots from soaking up excess CPU time on such pages.
16 <li> When walking the filesystem looking for Fossil repositories, avoid descending
17 into directories named "/proc".
18 <li> Reduce memory requirements for sending authenticated sync protocol
19 messages.
20 <li> Add the [/help?cmd=stash | stash rename] subcommand.
21 </ol>
22
23 <h2 id='v2_26'>Changes for version 2.26 (2025-04-30)</h2><ol>
24 <li>Enhancements to [/help?cmd=diff|fossil diff] and similar:
25 <ol type="a">
26
+28 -16
--- www/sync.wiki
+++ www/sync.wiki
@@ -220,26 +220,38 @@
220220
Every message from client to server begins with one or more login
221221
cards. Each login card has the following format:
222222
223223
<pre><b>login</b> <i>userid nonce signature</i></pre>
224224
225
-The userid is the name of the user that is requesting service
226
-from the server. The nonce is the SHA1 hash of the remainder of
227
-the message - all text that follows the newline character that
228
-terminates the login card. The signature is the SHA1 hash of
229
-the concatenation of the nonce and the users password.
230
-
231
-For each login card, the server looks up the user and verifies
232
-that the nonce matches the SHA1 hash of the remainder of the
233
-message. It then checks the signature hash to make sure the
234
-signature matches. If everything
235
-checks out, then the client is granted all privileges of the
236
-specified user.
237
-
238
-Privileges are cumulative. There can be multiple successful
239
-login cards. The session privilege is the union of all
240
-privileges from all login cards.
225
+The userid is the name of the user that is requesting service from the
226
+server, encoded in "fossilized" form (exactly as described for <a
227
+href="#error">the error card</a>). The nonce is the SHA1 hash of the
228
+remainder of the message - all text that follows the newline character
229
+that terminates the login card. The signature is the SHA1 hash of the
230
+concatenation of the nonce and the users password.
231
+
232
+When receving a login card, the server looks up the user and verifies
233
+that the nonce matches the SHA1 hash of the remainder of the message.
234
+It then checks the signature hash to make sure the signature matches.
235
+If everything checks out, then the client is granted all privileges of
236
+the specified user.
237
+
238
+Only one login card is permitted. A second login card will trigger
239
+a sync error. (Prior to 2025-07-21, the protocol permitted multiple
240
+logins, treating the login as the union of all privileges from all
241
+login cards. That capability was never used and has been removed.)
242
+
243
+As of version 2.27, Fossil supports transfering of the login card
244
+externally to the request payload via a Cookie HTTP header:
245
+
246
+<verbatim>
247
+ Cookie: x-f-x-l=...
248
+</verbatim>
249
+
250
+Where "..." is the URL-encoded login cookie. <code>x-f-x-l</code> is
251
+short for X-Fossil-Xfer-Login.
252
+
241253
242254
<h3 id="file">3.3 File Cards</h3>
243255
244256
Artifacts are transferred using either "file" cards, or "cfile"
245257
or "uvfile" cards.
246258
--- www/sync.wiki
+++ www/sync.wiki
@@ -220,26 +220,38 @@
220 Every message from client to server begins with one or more login
221 cards. Each login card has the following format:
222
223 <pre><b>login</b> <i>userid nonce signature</i></pre>
224
225 The userid is the name of the user that is requesting service
226 from the server. The nonce is the SHA1 hash of the remainder of
227 the message - all text that follows the newline character that
228 terminates the login card. The signature is the SHA1 hash of
229 the concatenation of the nonce and the users password.
230
231 For each login card, the server looks up the user and verifies
232 that the nonce matches the SHA1 hash of the remainder of the
233 message. It then checks the signature hash to make sure the
234 signature matches. If everything
235 checks out, then the client is granted all privileges of the
236 specified user.
237
238 Privileges are cumulative. There can be multiple successful
239 login cards. The session privilege is the union of all
240 privileges from all login cards.
 
 
 
 
 
 
 
 
 
 
 
 
241
242 <h3 id="file">3.3 File Cards</h3>
243
244 Artifacts are transferred using either "file" cards, or "cfile"
245 or "uvfile" cards.
246
--- www/sync.wiki
+++ www/sync.wiki
@@ -220,26 +220,38 @@
220 Every message from client to server begins with one or more login
221 cards. Each login card has the following format:
222
223 <pre><b>login</b> <i>userid nonce signature</i></pre>
224
225 The userid is the name of the user that is requesting service from the
226 server, encoded in "fossilized" form (exactly as described for <a
227 href="#error">the error card</a>). The nonce is the SHA1 hash of the
228 remainder of the message - all text that follows the newline character
229 that terminates the login card. The signature is the SHA1 hash of the
230 concatenation of the nonce and the users password.
231
232 When receving a login card, the server looks up the user and verifies
233 that the nonce matches the SHA1 hash of the remainder of the message.
234 It then checks the signature hash to make sure the signature matches.
235 If everything checks out, then the client is granted all privileges of
236 the specified user.
237
238 Only one login card is permitted. A second login card will trigger
239 a sync error. (Prior to 2025-07-21, the protocol permitted multiple
240 logins, treating the login as the union of all privileges from all
241 login cards. That capability was never used and has been removed.)
242
243 As of version 2.27, Fossil supports transfering of the login card
244 externally to the request payload via a Cookie HTTP header:
245
246 <verbatim>
247 Cookie: x-f-x-l=...
248 </verbatim>
249
250 Where "..." is the URL-encoded login cookie. <code>x-f-x-l</code> is
251 short for X-Fossil-Xfer-Login.
252
253
254 <h3 id="file">3.3 File Cards</h3>
255
256 Artifacts are transferred using either "file" cards, or "cfile"
257 or "uvfile" cards.
258

Keyboard Shortcuts

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