Fossil SCM
Sync with trunk.
Commit
a0377ebb9d1bb4740606d9b63742c072aef042973e2a36a7f51f82da59dbd213
Parent
931e7065bbc2731…
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
~
extsrc/shell.c
~
extsrc/sqlite3.c
~
extsrc/sqlite3.h
~
src/bisect.c
~
src/browse.c
~
src/captcha.c
~
src/cgi.c
~
src/chat.c
~
src/checkin.c
~
src/clearsign.c
~
src/clone.c
~
src/comformat.c
~
src/content.c
~
src/cookies.c
~
src/copybtn.js
~
src/db.c
~
src/default.css
~
src/default.css
~
src/diff.c
~
src/diff.c
~
src/diff.tcl
~
src/diffcmd.c
~
src/diffcmd.c
~
src/event.c
~
src/export.c
~
src/extcgi.c
~
src/file.c
~
src/fileedit.c
~
src/fossil.copybutton.js
~
src/fossil.numbered-lines.js
~
src/fossil.page.chat.js
~
src/fossil.page.pikchrshow.js
~
src/fossil.page.pikchrshowasm.js
~
src/fossil.page.wikiedit.js
~
src/graph.c
~
src/href.js
~
src/http.c
~
src/http_socket.c
~
src/http_ssl.c
~
src/http_transport.c
~
src/import.c
~
src/info.c
~
src/json.c
~
src/json_report.c
~
src/login.c
~
src/main.c
~
src/main.mk
~
src/merge.c
~
src/name.c
~
src/pikchrshow.c
~
src/repolist.c
~
src/robot.c
~
src/search.c
~
src/setup.c
~
src/sha1.c
~
src/sha3.c
~
src/stash.c
~
src/style.c
~
src/tag.c
~
src/tar.c
~
src/th_lang.c
~
src/th_main.c
~
src/timeline.c
~
src/tkt.c
~
src/tktsetup.c
~
src/unversioned.c
~
src/url.c
~
src/user.c
~
src/util.c
~
src/vfile.c
~
src/wiki.c
~
src/winhttp.c
~
src/xfer.c
~
src/zip.c
~
tools/makemake.tcl
~
win/Makefile.dmc
~
win/Makefile.mingw
~
win/Makefile.msc
~
www/changes.wiki
~
www/sync.wiki
+89
-59
| --- extsrc/shell.c | ||
| +++ extsrc/shell.c | ||
| @@ -122,10 +122,13 @@ | ||
| 122 | 122 | typedef sqlite3_int64 i64; |
| 123 | 123 | typedef sqlite3_uint64 u64; |
| 124 | 124 | typedef unsigned char u8; |
| 125 | 125 | #include <ctype.h> |
| 126 | 126 | #include <stdarg.h> |
| 127 | +#ifndef _WIN32 | |
| 128 | +# include <sys/time.h> | |
| 129 | +#endif | |
| 127 | 130 | |
| 128 | 131 | #if !defined(_WIN32) && !defined(WIN32) |
| 129 | 132 | # include <signal.h> |
| 130 | 133 | # if !defined(__RTP__) && !defined(_WRS_KERNEL) && !defined(SQLITE_WASI) |
| 131 | 134 | # include <pwd.h> |
| @@ -646,24 +649,27 @@ | ||
| 646 | 649 | if( a==0 ) a = ""; |
| 647 | 650 | if( b==0 ) b = ""; |
| 648 | 651 | return strncmp(a,b,n); |
| 649 | 652 | } |
| 650 | 653 | |
| 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 | +*/ | |
| 652 | 657 | 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; | |
| 664 | 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 | |
| 665 | 671 | } |
| 666 | 672 | |
| 667 | 673 | #if !defined(_WIN32) && !defined(WIN32) && !defined(__minux) |
| 668 | 674 | #include <sys/time.h> |
| 669 | 675 | #include <sys/resource.h> |
| @@ -704,12 +710,12 @@ | ||
| 704 | 710 | static void endTimer(FILE *out){ |
| 705 | 711 | if( enableTimer ){ |
| 706 | 712 | sqlite3_int64 iEnd = timeOfDay(); |
| 707 | 713 | struct rusage sEnd; |
| 708 | 714 | 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, | |
| 711 | 717 | timeDiff(&sBegin.ru_utime, &sEnd.ru_utime), |
| 712 | 718 | timeDiff(&sBegin.ru_stime, &sEnd.ru_stime)); |
| 713 | 719 | } |
| 714 | 720 | } |
| 715 | 721 | |
| @@ -783,12 +789,12 @@ | ||
| 783 | 789 | static void endTimer(FILE *out){ |
| 784 | 790 | if( enableTimer && getProcessTimesAddr){ |
| 785 | 791 | FILETIME ftCreation, ftExit, ftKernelEnd, ftUserEnd; |
| 786 | 792 | sqlite3_int64 ftWallEnd = timeOfDay(); |
| 787 | 793 | 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, | |
| 790 | 796 | timeDiff(&ftUserBegin, &ftUserEnd), |
| 791 | 797 | timeDiff(&ftKernelBegin, &ftKernelEnd)); |
| 792 | 798 | } |
| 793 | 799 | } |
| 794 | 800 | |
| @@ -32888,76 +32894,95 @@ | ||
| 32888 | 32894 | |
| 32889 | 32895 | return home_dir; |
| 32890 | 32896 | } |
| 32891 | 32897 | |
| 32892 | 32898 | /* |
| 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. | |
| 32898 | 32917 | ** |
| 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. | |
| 32901 | 32920 | */ |
| 32902 | -static char *find_xdg_config(void){ | |
| 32921 | +static char *find_xdg_file(const char *zEnvVar, const char *zSubdir, | |
| 32922 | + const char *zBaseName){ | |
| 32903 | 32923 | #if defined(_WIN32) || defined(WIN32) || defined(_WIN32_WCE) \ |
| 32904 | 32924 | || defined(__RTP__) || defined(_WRS_KERNEL) |
| 32905 | 32925 | return 0; |
| 32906 | 32926 | #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); | |
| 32915 | 32933 | }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; | |
| 32924 | 32946 | #endif |
| 32925 | 32947 | } |
| 32926 | 32948 | |
| 32927 | 32949 | /* |
| 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. | |
| 32931 | 32953 | ** |
| 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. | |
| 32933 | 32958 | */ |
| 32934 | 32959 | static void process_sqliterc( |
| 32935 | 32960 | ShellState *p, /* Configuration data */ |
| 32936 | 32961 | const char *sqliterc_override /* Name of config file. NULL to use default */ |
| 32937 | 32962 | ){ |
| 32938 | 32963 | char *home_dir = NULL; |
| 32939 | - const char *sqliterc = sqliterc_override; | |
| 32940 | - char *zBuf = 0; | |
| 32964 | + char *sqliterc = (char*)sqliterc_override; | |
| 32941 | 32965 | FILE *inSaved = p->in; |
| 32942 | 32966 | int savedLineno = p->lineno; |
| 32943 | 32967 | |
| 32944 | 32968 | if( sqliterc == NULL ){ |
| 32945 | - sqliterc = zBuf = find_xdg_config(); | |
| 32969 | + sqliterc = find_xdg_file("XDG_CONFIG_HOME", | |
| 32970 | + ".config", | |
| 32971 | + "sqlite3/sqliterc"); | |
| 32946 | 32972 | } |
| 32947 | 32973 | if( sqliterc == NULL ){ |
| 32948 | 32974 | home_dir = find_home_dir(0); |
| 32949 | 32975 | if( home_dir==0 ){ |
| 32950 | 32976 | eputz("-- warning: cannot find home directory;" |
| 32951 | 32977 | " cannot read ~/.sqliterc\n"); |
| 32952 | 32978 | return; |
| 32953 | 32979 | } |
| 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); | |
| 32957 | 32982 | } |
| 32958 | - p->in = sqlite3_fopen(sqliterc,"rb"); | |
| 32983 | + p->in = sqliterc ? sqlite3_fopen(sqliterc,"rb") : 0; | |
| 32959 | 32984 | if( p->in ){ |
| 32960 | 32985 | if( stdin_is_interactive ){ |
| 32961 | 32986 | sqlite3_fprintf(stderr,"-- Loading resources from %s\n", sqliterc); |
| 32962 | 32987 | } |
| 32963 | 32988 | if( process_input(p) && bail_on_error ) exit(1); |
| @@ -32966,11 +32991,13 @@ | ||
| 32966 | 32991 | sqlite3_fprintf(stderr,"cannot open: \"%s\"\n", sqliterc); |
| 32967 | 32992 | if( bail_on_error ) exit(1); |
| 32968 | 32993 | } |
| 32969 | 32994 | p->in = inSaved; |
| 32970 | 32995 | p->lineno = savedLineno; |
| 32971 | - sqlite3_free(zBuf); | |
| 32996 | + if( sqliterc != sqliterc_override ){ | |
| 32997 | + sqlite3_free(sqliterc); | |
| 32998 | + } | |
| 32972 | 32999 | } |
| 32973 | 33000 | |
| 32974 | 33001 | /* |
| 32975 | 33002 | ** Show available command line options |
| 32976 | 33003 | */ |
| @@ -33734,11 +33761,10 @@ | ||
| 33734 | 33761 | /* Run commands received from standard input |
| 33735 | 33762 | */ |
| 33736 | 33763 | if( stdin_is_interactive ){ |
| 33737 | 33764 | char *zHome; |
| 33738 | 33765 | char *zHistory; |
| 33739 | - int nHistory; | |
| 33740 | 33766 | sqlite3_fprintf(stdout, |
| 33741 | 33767 | "SQLite version %s %.19s\n" /*extra-version-info*/ |
| 33742 | 33768 | "Enter \".help\" for usage hints.\n", |
| 33743 | 33769 | sqlite3_libversion(), sqlite3_sourceid()); |
| 33744 | 33770 | if( warnInmemoryDb ){ |
| @@ -33747,15 +33773,19 @@ | ||
| 33747 | 33773 | sputz(stdout, ".\nUse \".open FILENAME\" to reopen on a" |
| 33748 | 33774 | " persistent database.\n"); |
| 33749 | 33775 | } |
| 33750 | 33776 | zHistory = getenv("SQLITE_HISTORY"); |
| 33751 | 33777 | 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); | |
| 33757 | 33787 | } |
| 33758 | 33788 | } |
| 33759 | 33789 | if( zHistory ){ shell_read_history(zHistory); } |
| 33760 | 33790 | #if (HAVE_READLINE || HAVE_EDITLINE) && !defined(SQLITE_OMIT_READLINE_COMPLETION) |
| 33761 | 33791 | rl_attempted_completion_function = readline_completion; |
| @@ -33767,11 +33797,11 @@ | ||
| 33767 | 33797 | data.in = 0; |
| 33768 | 33798 | rc = process_input(&data); |
| 33769 | 33799 | if( zHistory ){ |
| 33770 | 33800 | shell_stifle_history(2000); |
| 33771 | 33801 | shell_write_history(zHistory); |
| 33772 | - free(zHistory); | |
| 33802 | + sqlite3_free(zHistory); | |
| 33773 | 33803 | } |
| 33774 | 33804 | }else{ |
| 33775 | 33805 | data.in = stdin; |
| 33776 | 33806 | rc = process_input(&data); |
| 33777 | 33807 | } |
| 33778 | 33808 |
| --- 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 @@ | ||
| 16 | 16 | ** if you want a wrapper to interface SQLite with your choice of programming |
| 17 | 17 | ** language. The code for the "sqlite3" command-line shell is also in a |
| 18 | 18 | ** separate file. This file contains only code for the core SQLite library. |
| 19 | 19 | ** |
| 20 | 20 | ** The content in this amalgamation comes from Fossil check-in |
| 21 | -** 9f184f8dfa5ef6d57e10376adc30e0060ced with changes in files: | |
| 21 | +** cf7163f82ca380958a79350473b2c5a2cebd with changes in files: | |
| 22 | 22 | ** |
| 23 | 23 | ** |
| 24 | 24 | */ |
| 25 | 25 | #ifndef SQLITE_AMALGAMATION |
| 26 | 26 | #define SQLITE_CORE 1 |
| @@ -465,11 +465,11 @@ | ||
| 465 | 465 | ** [sqlite3_libversion_number()], [sqlite3_sourceid()], |
| 466 | 466 | ** [sqlite_version()] and [sqlite_source_id()]. |
| 467 | 467 | */ |
| 468 | 468 | #define SQLITE_VERSION "3.51.0" |
| 469 | 469 | #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" | |
| 471 | 471 | |
| 472 | 472 | /* |
| 473 | 473 | ** CAPI3REF: Run-Time Library Version Numbers |
| 474 | 474 | ** KEYWORDS: sqlite3_version sqlite3_sourceid |
| 475 | 475 | ** |
| @@ -814,10 +814,13 @@ | ||
| 814 | 814 | ** [sqlite3_extended_errcode()]. |
| 815 | 815 | */ |
| 816 | 816 | #define SQLITE_ERROR_MISSING_COLLSEQ (SQLITE_ERROR | (1<<8)) |
| 817 | 817 | #define SQLITE_ERROR_RETRY (SQLITE_ERROR | (2<<8)) |
| 818 | 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)) | |
| 819 | 822 | #define SQLITE_IOERR_READ (SQLITE_IOERR | (1<<8)) |
| 820 | 823 | #define SQLITE_IOERR_SHORT_READ (SQLITE_IOERR | (2<<8)) |
| 821 | 824 | #define SQLITE_IOERR_WRITE (SQLITE_IOERR | (3<<8)) |
| 822 | 825 | #define SQLITE_IOERR_FSYNC (SQLITE_IOERR | (4<<8)) |
| 823 | 826 | #define SQLITE_IOERR_DIR_FSYNC (SQLITE_IOERR | (5<<8)) |
| @@ -848,10 +851,12 @@ | ||
| 848 | 851 | #define SQLITE_IOERR_COMMIT_ATOMIC (SQLITE_IOERR | (30<<8)) |
| 849 | 852 | #define SQLITE_IOERR_ROLLBACK_ATOMIC (SQLITE_IOERR | (31<<8)) |
| 850 | 853 | #define SQLITE_IOERR_DATA (SQLITE_IOERR | (32<<8)) |
| 851 | 854 | #define SQLITE_IOERR_CORRUPTFS (SQLITE_IOERR | (33<<8)) |
| 852 | 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)) | |
| 853 | 858 | #define SQLITE_LOCKED_SHAREDCACHE (SQLITE_LOCKED | (1<<8)) |
| 854 | 859 | #define SQLITE_LOCKED_VTAB (SQLITE_LOCKED | (2<<8)) |
| 855 | 860 | #define SQLITE_BUSY_RECOVERY (SQLITE_BUSY | (1<<8)) |
| 856 | 861 | #define SQLITE_BUSY_SNAPSHOT (SQLITE_BUSY | (2<<8)) |
| 857 | 862 | #define SQLITE_BUSY_TIMEOUT (SQLITE_BUSY | (3<<8)) |
| @@ -19503,10 +19508,11 @@ | ||
| 19503 | 19508 | AggInfo *pAggInfo; /* Used by TK_AGG_COLUMN and TK_AGG_FUNCTION */ |
| 19504 | 19509 | union { |
| 19505 | 19510 | Table *pTab; /* TK_COLUMN: Table containing column. Can be NULL |
| 19506 | 19511 | ** for a column of an index on an expression */ |
| 19507 | 19512 | Window *pWin; /* EP_WinFunc: Window/Filter defn for a function */ |
| 19513 | + int nReg; /* TK_NULLS: Number of registers to NULL out */ | |
| 19508 | 19514 | struct { /* TK_IN, TK_SELECT, and TK_EXISTS */ |
| 19509 | 19515 | int iAddr; /* Subroutine entry address */ |
| 19510 | 19516 | int regReturn; /* Register used to hold return address */ |
| 19511 | 19517 | } sub; |
| 19512 | 19518 | } y; |
| @@ -21540,10 +21546,11 @@ | ||
| 21540 | 21546 | SQLITE_PRIVATE void sqlite3ExprCodeGeneratedColumn(Parse*, Table*, Column*, int); |
| 21541 | 21547 | #endif |
| 21542 | 21548 | SQLITE_PRIVATE void sqlite3ExprCodeCopy(Parse*, Expr*, int); |
| 21543 | 21549 | SQLITE_PRIVATE void sqlite3ExprCodeFactorable(Parse*, Expr*, int); |
| 21544 | 21550 | SQLITE_PRIVATE int sqlite3ExprCodeRunJustOnce(Parse*, Expr*, int); |
| 21551 | +SQLITE_PRIVATE void sqlite3ExprNullRegisterRange(Parse*, int, int); | |
| 21545 | 21552 | SQLITE_PRIVATE int sqlite3ExprCodeTemp(Parse*, Expr*, int*); |
| 21546 | 21553 | SQLITE_PRIVATE int sqlite3ExprCodeTarget(Parse*, Expr*, int); |
| 21547 | 21554 | SQLITE_PRIVATE int sqlite3ExprCodeExprList(Parse*, ExprList*, int, int, u8); |
| 21548 | 21555 | #define SQLITE_ECEL_DUP 0x01 /* Deep, not shallow copies */ |
| 21549 | 21556 | #define SQLITE_ECEL_FACTOR 0x02 /* Factor out constant terms */ |
| @@ -24366,13 +24373,15 @@ | ||
| 24366 | 24373 | SQLITE_PRIVATE void sqlite3VdbeMemAboutToChange(Vdbe*,Mem*); |
| 24367 | 24374 | SQLITE_PRIVATE int sqlite3VdbeCheckMemInvariants(Mem*); |
| 24368 | 24375 | #endif |
| 24369 | 24376 | |
| 24370 | 24377 | #ifndef SQLITE_OMIT_FOREIGN_KEY |
| 24371 | -SQLITE_PRIVATE int sqlite3VdbeCheckFk(Vdbe *, int); | |
| 24378 | +SQLITE_PRIVATE int sqlite3VdbeCheckFkImmediate(Vdbe*); | |
| 24379 | +SQLITE_PRIVATE int sqlite3VdbeCheckFkDeferred(Vdbe*); | |
| 24372 | 24380 | #else |
| 24373 | -# define sqlite3VdbeCheckFk(p,i) 0 | |
| 24381 | +# define sqlite3VdbeCheckFkImmediate(p) 0 | |
| 24382 | +# define sqlite3VdbeCheckFkDeferred(p) 0 | |
| 24374 | 24383 | #endif |
| 24375 | 24384 | |
| 24376 | 24385 | #ifdef SQLITE_DEBUG |
| 24377 | 24386 | SQLITE_PRIVATE void sqlite3VdbePrintSql(Vdbe*); |
| 24378 | 24387 | SQLITE_PRIVATE void sqlite3VdbeMemPrettyPrint(Mem *pMem, StrAccum *pStr); |
| @@ -32252,11 +32261,25 @@ | ||
| 32252 | 32261 | length = sqlite3Strlen30(bufpt); |
| 32253 | 32262 | break; |
| 32254 | 32263 | } |
| 32255 | 32264 | } |
| 32256 | 32265 | 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 | + } | |
| 32258 | 32281 | }else{ |
| 32259 | 32282 | prefix = flag_prefix; |
| 32260 | 32283 | } |
| 32261 | 32284 | |
| 32262 | 32285 | exp = s.iDP-1; |
| @@ -51192,10 +51215,107 @@ | ||
| 51192 | 51215 | ** on allocation size granularity boundaries. |
| 51193 | 51216 | ** During sqlite3_os_init() we do a GetSystemInfo() |
| 51194 | 51217 | ** to get the granularity size. |
| 51195 | 51218 | */ |
| 51196 | 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 | +} | |
| 51197 | 51317 | |
| 51198 | 51318 | #ifndef SQLITE_OMIT_WAL |
| 51199 | 51319 | |
| 51200 | 51320 | /* |
| 51201 | 51321 | ** Helper functions to obtain and relinquish the global mutex. The |
| @@ -51387,107 +51507,10 @@ | ||
| 51387 | 51507 | |
| 51388 | 51508 | return rc; |
| 51389 | 51509 | } |
| 51390 | 51510 | |
| 51391 | 51511 | |
| 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 | 51512 | /* |
| 51490 | 51513 | ** This function is used to open a handle on a *-shm file. |
| 51491 | 51514 | ** |
| 51492 | 51515 | ** If SQLITE_ENABLE_SETLK_TIMEOUT is defined at build time, then the file |
| 51493 | 51516 | ** is opened with FILE_FLAG_OVERLAPPED specified. If not, it is not. |
| @@ -89078,14 +89101,16 @@ | ||
| 89078 | 89101 | ** simple case then too. |
| 89079 | 89102 | */ |
| 89080 | 89103 | if( 0==sqlite3Strlen30(sqlite3BtreeGetFilename(db->aDb[0].pBt)) |
| 89081 | 89104 | || nTrans<=1 |
| 89082 | 89105 | ){ |
| 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 | + } | |
| 89087 | 89112 | } |
| 89088 | 89113 | } |
| 89089 | 89114 | |
| 89090 | 89115 | /* Do the commit only if all databases successfully complete phase 1. |
| 89091 | 89116 | ** If one of the BtreeCommitPhaseOne() calls fails, this indicates an |
| @@ -89092,11 +89117,13 @@ | ||
| 89092 | 89117 | ** IO error while deleting or truncating a journal file. It is unlikely, |
| 89093 | 89118 | ** but could happen. In this case abandon processing and return the error. |
| 89094 | 89119 | */ |
| 89095 | 89120 | for(i=0; rc==SQLITE_OK && i<db->nDb; i++){ |
| 89096 | 89121 | 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 ); | |
| 89098 | 89125 | rc = sqlite3BtreeCommitPhaseTwo(pBt, 0); |
| 89099 | 89126 | } |
| 89100 | 89127 | } |
| 89101 | 89128 | if( rc==SQLITE_OK ){ |
| 89102 | 89129 | sqlite3VtabCommit(db); |
| @@ -89347,32 +89374,35 @@ | ||
| 89347 | 89374 | return SQLITE_OK; |
| 89348 | 89375 | } |
| 89349 | 89376 | |
| 89350 | 89377 | |
| 89351 | 89378 | /* |
| 89352 | -** This function is called when a transaction opened by the database | |
| 89379 | +** These functions are called when a transaction opened by the database | |
| 89353 | 89380 | ** 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. | |
| 89356 | 89383 | ** |
| 89357 | 89384 | ** 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. | |
| 89360 | 89387 | */ |
| 89361 | 89388 | #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){ | |
| 89363 | 89401 | 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); | |
| 89374 | 89404 | } |
| 89375 | 89405 | #endif |
| 89376 | 89406 | |
| 89377 | 89407 | /* |
| 89378 | 89408 | ** This routine is called the when a VDBE tries to halt. If the VDBE |
| @@ -89462,11 +89492,11 @@ | ||
| 89462 | 89492 | } |
| 89463 | 89493 | } |
| 89464 | 89494 | |
| 89465 | 89495 | /* Check for immediate foreign key violations. */ |
| 89466 | 89496 | if( p->rc==SQLITE_OK || (p->errorAction==OE_Fail && !isSpecialError) ){ |
| 89467 | - (void)sqlite3VdbeCheckFk(p, 0); | |
| 89497 | + (void)sqlite3VdbeCheckFkImmediate(p); | |
| 89468 | 89498 | } |
| 89469 | 89499 | |
| 89470 | 89500 | /* If the auto-commit flag is set and this is the only active writer |
| 89471 | 89501 | ** VM, then we do either a commit or rollback of the current transaction. |
| 89472 | 89502 | ** |
| @@ -89476,11 +89506,11 @@ | ||
| 89476 | 89506 | if( !sqlite3VtabInSync(db) |
| 89477 | 89507 | && db->autoCommit |
| 89478 | 89508 | && db->nVdbeWrite==(p->readOnly==0) |
| 89479 | 89509 | ){ |
| 89480 | 89510 | if( p->rc==SQLITE_OK || (p->errorAction==OE_Fail && !isSpecialError) ){ |
| 89481 | - rc = sqlite3VdbeCheckFk(p, 1); | |
| 89511 | + rc = sqlite3VdbeCheckFkDeferred(p); | |
| 89482 | 89512 | if( rc!=SQLITE_OK ){ |
| 89483 | 89513 | if( NEVER(p->readOnly) ){ |
| 89484 | 89514 | sqlite3VdbeLeave(p); |
| 89485 | 89515 | return SQLITE_ERROR; |
| 89486 | 89516 | } |
| @@ -90341,19 +90371,19 @@ | ||
| 90341 | 90371 | /* pMem->flags = 0; // sqlite3VdbeSerialGet() will set this for us */ |
| 90342 | 90372 | pMem->szMalloc = 0; |
| 90343 | 90373 | pMem->z = 0; |
| 90344 | 90374 | sqlite3VdbeSerialGet(&aKey[d], serial_type, pMem); |
| 90345 | 90375 | d += sqlite3VdbeSerialTypeLen(serial_type); |
| 90346 | - pMem++; | |
| 90347 | 90376 | if( (++u)>=p->nField ) break; |
| 90377 | + pMem++; | |
| 90348 | 90378 | } |
| 90349 | 90379 | if( d>(u32)nKey && u ){ |
| 90350 | 90380 | assert( CORRUPT_DB ); |
| 90351 | 90381 | /* In a corrupt record entry, the last pMem might have been set up using |
| 90352 | 90382 | ** uninitialized memory. Overwrite its value with NULL, to prevent |
| 90353 | 90383 | ** warnings from MSAN. */ |
| 90354 | - sqlite3VdbeMemSetNull(pMem-1); | |
| 90384 | + sqlite3VdbeMemSetNull(pMem-(u<p->nField)); | |
| 90355 | 90385 | } |
| 90356 | 90386 | testcase( u == pKeyInfo->nKeyField + 1 ); |
| 90357 | 90387 | testcase( u < pKeyInfo->nKeyField + 1 ); |
| 90358 | 90388 | assert( u<=pKeyInfo->nKeyField + 1 ); |
| 90359 | 90389 | p->nField = u; |
| @@ -90520,10 +90550,36 @@ | ||
| 90520 | 90550 | ** Both *pMem1 and *pMem2 contain string values. Compare the two values |
| 90521 | 90551 | ** using the collation sequence pColl. As usual, return a negative , zero |
| 90522 | 90552 | ** or positive value if *pMem1 is less than, equal to or greater than |
| 90523 | 90553 | ** *pMem2, respectively. Similar in spirit to "rc = (*pMem1) - (*pMem2);". |
| 90524 | 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 | +} | |
| 90525 | 90581 | static int vdbeCompareMemString( |
| 90526 | 90582 | const Mem *pMem1, |
| 90527 | 90583 | const Mem *pMem2, |
| 90528 | 90584 | const CollSeq *pColl, |
| 90529 | 90585 | u8 *prcErr /* If an OOM occurs, set to SQLITE_NOMEM */ |
| @@ -90531,29 +90587,11 @@ | ||
| 90531 | 90587 | if( pMem1->enc==pColl->enc ){ |
| 90532 | 90588 | /* The strings are already in the correct encoding. Call the |
| 90533 | 90589 | ** comparison function directly */ |
| 90534 | 90590 | return pColl->xCmp(pColl->pUser,pMem1->n,pMem1->z,pMem2->n,pMem2->z); |
| 90535 | 90591 | }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); | |
| 90555 | 90593 | } |
| 90556 | 90594 | } |
| 90557 | 90595 | |
| 90558 | 90596 | /* |
| 90559 | 90597 | ** The input pBlob is guaranteed to be a Blob that is not marked |
| @@ -96256,11 +96294,11 @@ | ||
| 96256 | 96294 | ** exits. This opcode is used to raise foreign key constraint errors prior |
| 96257 | 96295 | ** to returning results such as a row change count or the result of a |
| 96258 | 96296 | ** RETURNING clause. |
| 96259 | 96297 | */ |
| 96260 | 96298 | case OP_FkCheck: { |
| 96261 | - if( (rc = sqlite3VdbeCheckFk(p,0))!=SQLITE_OK ){ | |
| 96299 | + if( (rc = sqlite3VdbeCheckFkImmediate(p))!=SQLITE_OK ){ | |
| 96262 | 96300 | goto abort_due_to_error; |
| 96263 | 96301 | } |
| 96264 | 96302 | break; |
| 96265 | 96303 | } |
| 96266 | 96304 | |
| @@ -98440,11 +98478,11 @@ | ||
| 98440 | 98478 | ** and this is a RELEASE command, then the current transaction |
| 98441 | 98479 | ** is committed. |
| 98442 | 98480 | */ |
| 98443 | 98481 | int isTransaction = pSavepoint->pNext==0 && db->isTransactionSavepoint; |
| 98444 | 98482 | if( isTransaction && p1==SAVEPOINT_RELEASE ){ |
| 98445 | - if( (rc = sqlite3VdbeCheckFk(p, 1))!=SQLITE_OK ){ | |
| 98483 | + if( (rc = sqlite3VdbeCheckFkDeferred(p))!=SQLITE_OK ){ | |
| 98446 | 98484 | goto vdbe_return; |
| 98447 | 98485 | } |
| 98448 | 98486 | db->autoCommit = 1; |
| 98449 | 98487 | if( sqlite3VdbeHalt(p)==SQLITE_BUSY ){ |
| 98450 | 98488 | p->pc = (int)(pOp - aOp); |
| @@ -98558,11 +98596,11 @@ | ||
| 98558 | 98596 | */ |
| 98559 | 98597 | sqlite3VdbeError(p, "cannot commit transaction - " |
| 98560 | 98598 | "SQL statements in progress"); |
| 98561 | 98599 | rc = SQLITE_BUSY; |
| 98562 | 98600 | goto abort_due_to_error; |
| 98563 | - }else if( (rc = sqlite3VdbeCheckFk(p, 1))!=SQLITE_OK ){ | |
| 98601 | + }else if( (rc = sqlite3VdbeCheckFkDeferred(p))!=SQLITE_OK ){ | |
| 98564 | 98602 | goto vdbe_return; |
| 98565 | 98603 | }else{ |
| 98566 | 98604 | db->autoCommit = (u8)desiredAutoCommit; |
| 98567 | 98605 | } |
| 98568 | 98606 | if( sqlite3VdbeHalt(p)==SQLITE_BUSY ){ |
| @@ -109256,12 +109294,12 @@ | ||
| 109256 | 109294 | assert( ((X)&~(NC_IsCheck|NC_PartIdx|NC_IdxExpr|NC_GenCol))==0 ); \ |
| 109257 | 109295 | if( ((N)->ncFlags & (X))!=0 ) notValidImpl(P,N,M,E,R); |
| 109258 | 109296 | |
| 109259 | 109297 | /* |
| 109260 | 109298 | ** 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. | |
| 109263 | 109301 | */ |
| 109264 | 109302 | static int exprProbability(Expr *p){ |
| 109265 | 109303 | double r = -1.0; |
| 109266 | 109304 | if( p->op!=TK_FLOAT ) return -1; |
| 109267 | 109305 | assert( !ExprHasProperty(p, EP_IntValue) ); |
| @@ -115633,10 +115671,16 @@ | ||
| 115633 | 115671 | case TK_STRING: { |
| 115634 | 115672 | assert( !ExprHasProperty(pExpr, EP_IntValue) ); |
| 115635 | 115673 | sqlite3VdbeLoadString(v, target, pExpr->u.zToken); |
| 115636 | 115674 | return target; |
| 115637 | 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 | + } | |
| 115638 | 115682 | default: { |
| 115639 | 115683 | /* Make NULL the default case so that if a bug causes an illegal |
| 115640 | 115684 | ** Expr node to be passed into this function, it will be handled |
| 115641 | 115685 | ** sanely and not crash. But keep the assert() to bring the problem |
| 115642 | 115686 | ** to the attention of the developers. */ |
| @@ -116341,10 +116385,29 @@ | ||
| 116341 | 116385 | } |
| 116342 | 116386 | pParse->pConstExpr = p; |
| 116343 | 116387 | } |
| 116344 | 116388 | return regDest; |
| 116345 | 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 | +} | |
| 116346 | 116409 | |
| 116347 | 116410 | /* |
| 116348 | 116411 | ** Generate code to evaluate an expression and store the results |
| 116349 | 116412 | ** into a register. Return the register number where the results |
| 116350 | 116413 | ** are stored. |
| @@ -152746,10 +152809,11 @@ | ||
| 152746 | 152809 | Select *pSub = pWhere->x.pSelect; |
| 152747 | 152810 | Expr *pSubWhere = pSub->pWhere; |
| 152748 | 152811 | if( pSub->pSrc->nSrc==1 |
| 152749 | 152812 | && (pSub->selFlags & SF_Aggregate)==0 |
| 152750 | 152813 | && !pSub->pSrc->a[0].fg.isSubquery |
| 152814 | + && pSub->pLimit==0 | |
| 152751 | 152815 | ){ |
| 152752 | 152816 | memset(pWhere, 0, sizeof(*pWhere)); |
| 152753 | 152817 | pWhere->op = TK_INTEGER; |
| 152754 | 152818 | pWhere->u.iValue = 1; |
| 152755 | 152819 | ExprSetProperty(pWhere, EP_IntValue); |
| @@ -153766,10 +153830,11 @@ | ||
| 153766 | 153830 | iBMem = pParse->nMem + 1; |
| 153767 | 153831 | pParse->nMem += pGroupBy->nExpr; |
| 153768 | 153832 | sqlite3VdbeAddOp2(v, OP_Integer, 0, iAbortFlag); |
| 153769 | 153833 | VdbeComment((v, "clear abort flag")); |
| 153770 | 153834 | sqlite3VdbeAddOp3(v, OP_Null, 0, iAMem, iAMem+pGroupBy->nExpr-1); |
| 153835 | + sqlite3ExprNullRegisterRange(pParse, iAMem, pGroupBy->nExpr); | |
| 153771 | 153836 | |
| 153772 | 153837 | /* Begin a loop that will extract all source rows in GROUP BY order. |
| 153773 | 153838 | ** This might involve two separate loops with an OP_Sort in between, or |
| 153774 | 153839 | ** it might be a single loop that uses an index to extract information |
| 153775 | 153840 | ** in the right order to begin with. |
| @@ -169084,10 +169149,11 @@ | ||
| 169084 | 169149 | if( pProbe->bNoQuery ) continue; |
| 169085 | 169150 | rSize = pProbe->aiRowLogEst[0]; |
| 169086 | 169151 | pNew->u.btree.nEq = 0; |
| 169087 | 169152 | pNew->u.btree.nBtm = 0; |
| 169088 | 169153 | pNew->u.btree.nTop = 0; |
| 169154 | + pNew->u.btree.nDistinctCol = 0; | |
| 169089 | 169155 | pNew->nSkip = 0; |
| 169090 | 169156 | pNew->nLTerm = 0; |
| 169091 | 169157 | pNew->iSortIdx = 0; |
| 169092 | 169158 | pNew->rSetup = 0; |
| 169093 | 169159 | pNew->prereq = mPrereq; |
| @@ -170152,12 +170218,10 @@ | ||
| 170152 | 170218 | && ((wctrlFlags&(WHERE_DISTINCTBY|WHERE_SORTBYGROUP))!=WHERE_DISTINCTBY) |
| 170153 | 170219 | ){ |
| 170154 | 170220 | obSat = obDone; |
| 170155 | 170221 | } |
| 170156 | 170222 | break; |
| 170157 | - }else if( wctrlFlags & WHERE_DISTINCTBY ){ | |
| 170158 | - pLoop->u.btree.nDistinctCol = 0; | |
| 170159 | 170223 | } |
| 170160 | 170224 | iCur = pWInfo->pTabList->a[pLoop->iTab].iCursor; |
| 170161 | 170225 | |
| 170162 | 170226 | /* Mark off any ORDER BY term X that is a column in the table of |
| 170163 | 170227 | ** the current loop for which there is term in the WHERE |
| @@ -258113,11 +258177,11 @@ | ||
| 258113 | 258177 | int nArg, /* Number of args */ |
| 258114 | 258178 | sqlite3_value **apUnused /* Function arguments */ |
| 258115 | 258179 | ){ |
| 258116 | 258180 | assert( nArg==0 ); |
| 258117 | 258181 | 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); | |
| 258119 | 258183 | } |
| 258120 | 258184 | |
| 258121 | 258185 | /* |
| 258122 | 258186 | ** Implementation of fts5_locale(LOCALE, TEXT) function. |
| 258123 | 258187 | ** |
| 258124 | 258188 |
| --- 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 |
+6
-1
| --- extsrc/sqlite3.h | ||
| +++ extsrc/sqlite3.h | ||
| @@ -146,11 +146,11 @@ | ||
| 146 | 146 | ** [sqlite3_libversion_number()], [sqlite3_sourceid()], |
| 147 | 147 | ** [sqlite_version()] and [sqlite_source_id()]. |
| 148 | 148 | */ |
| 149 | 149 | #define SQLITE_VERSION "3.51.0" |
| 150 | 150 | #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" | |
| 152 | 152 | |
| 153 | 153 | /* |
| 154 | 154 | ** CAPI3REF: Run-Time Library Version Numbers |
| 155 | 155 | ** KEYWORDS: sqlite3_version sqlite3_sourceid |
| 156 | 156 | ** |
| @@ -495,10 +495,13 @@ | ||
| 495 | 495 | ** [sqlite3_extended_errcode()]. |
| 496 | 496 | */ |
| 497 | 497 | #define SQLITE_ERROR_MISSING_COLLSEQ (SQLITE_ERROR | (1<<8)) |
| 498 | 498 | #define SQLITE_ERROR_RETRY (SQLITE_ERROR | (2<<8)) |
| 499 | 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)) | |
| 500 | 503 | #define SQLITE_IOERR_READ (SQLITE_IOERR | (1<<8)) |
| 501 | 504 | #define SQLITE_IOERR_SHORT_READ (SQLITE_IOERR | (2<<8)) |
| 502 | 505 | #define SQLITE_IOERR_WRITE (SQLITE_IOERR | (3<<8)) |
| 503 | 506 | #define SQLITE_IOERR_FSYNC (SQLITE_IOERR | (4<<8)) |
| 504 | 507 | #define SQLITE_IOERR_DIR_FSYNC (SQLITE_IOERR | (5<<8)) |
| @@ -529,10 +532,12 @@ | ||
| 529 | 532 | #define SQLITE_IOERR_COMMIT_ATOMIC (SQLITE_IOERR | (30<<8)) |
| 530 | 533 | #define SQLITE_IOERR_ROLLBACK_ATOMIC (SQLITE_IOERR | (31<<8)) |
| 531 | 534 | #define SQLITE_IOERR_DATA (SQLITE_IOERR | (32<<8)) |
| 532 | 535 | #define SQLITE_IOERR_CORRUPTFS (SQLITE_IOERR | (33<<8)) |
| 533 | 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)) | |
| 534 | 539 | #define SQLITE_LOCKED_SHAREDCACHE (SQLITE_LOCKED | (1<<8)) |
| 535 | 540 | #define SQLITE_LOCKED_VTAB (SQLITE_LOCKED | (2<<8)) |
| 536 | 541 | #define SQLITE_BUSY_RECOVERY (SQLITE_BUSY | (1<<8)) |
| 537 | 542 | #define SQLITE_BUSY_SNAPSHOT (SQLITE_BUSY | (2<<8)) |
| 538 | 543 | #define SQLITE_BUSY_TIMEOUT (SQLITE_BUSY | (3<<8)) |
| 539 | 544 |
| --- 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 @@ | ||
| 336 | 336 | } |
| 337 | 337 | zUuid = db_text(0,"SELECT lower(uuid) FROM blob WHERE rid=%d", rid); |
| 338 | 338 | if( blob_size(&link)>0 ) blob_append(&link, "-", 1); |
| 339 | 339 | blob_appendf(&link, "%c%.10s", cPrefix, zUuid); |
| 340 | 340 | } |
| 341 | - zResult = mprintf("%s", blob_str(&link)); | |
| 341 | + zResult = fossil_strdup(blob_str(&link)); | |
| 342 | 342 | blob_reset(&link); |
| 343 | 343 | blob_reset(&log); |
| 344 | 344 | blob_reset(&id); |
| 345 | 345 | return zResult; |
| 346 | 346 | } |
| 347 | 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 = 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 |
-1
| --- src/browse.c | ||
| +++ src/browse.c | ||
| @@ -1162,11 +1162,10 @@ | ||
| 1162 | 1162 | int showId = PB("showid"); |
| 1163 | 1163 | Stmt q1, q2; |
| 1164 | 1164 | double baseTime; |
| 1165 | 1165 | login_check_credentials(); |
| 1166 | 1166 | if( !g.perm.Read ){ login_needed(g.anon.Read); return; } |
| 1167 | - if( exclude_spiders(0) ) return; | |
| 1168 | 1167 | zName = P("name"); |
| 1169 | 1168 | if( zName==0 ) zName = "tip"; |
| 1170 | 1169 | rid = symbolic_name_to_rid(zName, "ci"); |
| 1171 | 1170 | if( rid==0 ){ |
| 1172 | 1171 | fossil_fatal("not a valid check-in: %s", zName); |
| 1173 | 1172 |
| --- 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 @@ | ||
| 744 | 744 | (void)exclude_spiders(1); |
| 745 | 745 | @ <hr><p>The captcha is shown above. Add a name=HEX query parameter |
| 746 | 746 | @ to see how HEX would be rendered in the current captcha font. |
| 747 | 747 | @ <h2>Debug/Testing Values:</h2> |
| 748 | 748 | @ <ul> |
| 749 | - @ <li> g.isHuman = %d(g.isHuman) | |
| 749 | + @ <li> g.isRobot = %d(g.isRobot) | |
| 750 | 750 | @ <li> g.zLogin = %h(g.zLogin) |
| 751 | 751 | @ <li> login_cookie_welformed() = %d(login_cookie_wellformed()) |
| 752 | 752 | @ <li> captcha_is_correct(1) = %d(captcha_is_correct(1)). |
| 753 | 753 | @ </ul> |
| 754 | 754 | style_finish_page(); |
| @@ -759,10 +759,18 @@ | ||
| 759 | 759 | @ %s(captcha_render(zPw)) |
| 760 | 760 | @ </pre> |
| 761 | 761 | style_finish_page(); |
| 762 | 762 | } |
| 763 | 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 | +} | |
| 764 | 772 | |
| 765 | 773 | /* |
| 766 | 774 | ** Check to see if the current request is coming from an agent that |
| 767 | 775 | ** self-identifies as a spider. |
| 768 | 776 | ** |
| @@ -776,27 +784,23 @@ | ||
| 776 | 784 | ** If the bTest argument is non-zero, then show the captcha regardless of |
| 777 | 785 | ** how the agent identifies. This is used for testing only. |
| 778 | 786 | */ |
| 779 | 787 | int exclude_spiders(int bTest){ |
| 780 | 788 | if( !bTest ){ |
| 781 | - if( g.isHuman ) return 0; /* This user has already proven human */ | |
| 782 | 789 | if( g.zLogin!=0 ) return 0; /* Logged in. Consider them human */ |
| 783 | 790 | if( login_cookie_wellformed() ){ |
| 784 | 791 | /* Logged into another member of the login group */ |
| 785 | 792 | return 0; |
| 786 | 793 | } |
| 787 | 794 | } |
| 788 | 795 | |
| 789 | 796 | /* This appears to be a spider. Offer the captcha */ |
| 790 | 797 | style_set_current_feature("captcha"); |
| 791 | - style_header("I think you are a robot"); | |
| 798 | + style_header("Captcha"); | |
| 792 | 799 | style_submenu_enable(0); |
| 793 | 800 | @ <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: | |
| 798 | 802 | if( bTest ){ |
| 799 | 803 | @ <input type="hidden" name="istest" value="1"> |
| 800 | 804 | } |
| 801 | 805 | captcha_generate(3); |
| 802 | 806 | @ </form> |
| @@ -830,11 +834,11 @@ | ||
| 830 | 834 | } |
| 831 | 835 | cgi_append_header("X-Robot: 0\r\n"); |
| 832 | 836 | } |
| 833 | 837 | login_redirect_to_g(); |
| 834 | 838 | }else{ |
| 835 | - g.isHuman = 0; | |
| 839 | + g.isRobot = 1; | |
| 836 | 840 | (void)exclude_spiders(bTest); |
| 837 | 841 | if( bTest ){ |
| 838 | 842 | @ <hr><p>Wrong code. Try again |
| 839 | 843 | style_finish_page(); |
| 840 | 844 | } |
| 841 | 845 |
| --- 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 @@ | ||
| 964 | 964 | ** * it is impossible for a cookie or query parameter to |
| 965 | 965 | ** override the value of an environment variable since |
| 966 | 966 | ** environment variables always have uppercase names. |
| 967 | 967 | ** |
| 968 | 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 | |
| 969 | +** other than [-a-zA-Z0-9_]. There are no known exploits involving unusual | |
| 970 | 970 | ** names that contain characters outside that set, but it never hurts to |
| 971 | 971 | ** be extra cautious when sanitizing inputs. |
| 972 | 972 | ** |
| 973 | 973 | ** Parameters are separated by the "terminator" character. Whitespace |
| 974 | 974 | ** before the NAME is ignored. |
| @@ -1280,36 +1280,49 @@ | ||
| 1280 | 1280 | |
| 1281 | 1281 | /* Forward declaration */ |
| 1282 | 1282 | static NORETURN void malformed_request(const char *zMsg, ...); |
| 1283 | 1283 | |
| 1284 | 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()). | |
| 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(). | |
| 1290 | 1295 | */ |
| 1291 | 1296 | int cgi_setup_query_string(void){ |
| 1292 | 1297 | int rc = 0; |
| 1293 | 1298 | char * z = (char*)P("QUERY_STRING"); |
| 1294 | 1299 | if( z ){ |
| 1295 | - ++rc; | |
| 1300 | + rc = 0x01; | |
| 1296 | 1301 | z = fossil_strdup(z); |
| 1297 | 1302 | add_param_list(z, '&'); |
| 1298 | 1303 | z = (char*)P("skin"); |
| 1299 | 1304 | if( z ){ |
| 1300 | 1305 | char *zErr = skin_use_alternative(z, 2, SKIN_FROM_QPARAM); |
| 1301 | - ++rc; | |
| 1306 | + rc |= 0x02; | |
| 1302 | 1307 | if( !zErr && P("once")==0 ){ |
| 1303 | 1308 | cookie_write_parameter("skin","skin",z); |
| 1304 | 1309 | /* Per /chat discussion, passing ?skin=... without "once" |
| 1305 | 1310 | ** implies the "udc" argument, so we force that into the |
| 1306 | 1311 | ** environment here. */ |
| 1307 | 1312 | cgi_set_parameter_nocopy("udc", "1", 1); |
| 1308 | 1313 | } |
| 1309 | 1314 | fossil_free(zErr); |
| 1310 | 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"); | |
| 1311 | 1324 | } |
| 1312 | 1325 | return rc; |
| 1313 | 1326 | } |
| 1314 | 1327 | |
| 1315 | 1328 | /* |
| @@ -1599,10 +1612,25 @@ | ||
| 1599 | 1612 | } |
| 1600 | 1613 | } |
| 1601 | 1614 | CGIDEBUG(("no-match [%s]\n", zName)); |
| 1602 | 1615 | return zDefault; |
| 1603 | 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 | +} | |
| 1604 | 1632 | |
| 1605 | 1633 | /* |
| 1606 | 1634 | ** Renders the "begone, spider" page and exits. |
| 1607 | 1635 | */ |
| 1608 | 1636 | static void cgi_begone_spider(const char *zName){ |
| @@ -2125,10 +2153,11 @@ | ||
| 2125 | 2153 | int i; |
| 2126 | 2154 | const char *zScheme = "http"; |
| 2127 | 2155 | char zLine[2000]; /* A single line of input. */ |
| 2128 | 2156 | g.fullHttpReply = 1; |
| 2129 | 2157 | g.zReqType = "HTTP"; |
| 2158 | + | |
| 2130 | 2159 | if( cgi_fgets(zLine, sizeof(zLine))==0 ){ |
| 2131 | 2160 | malformed_request("missing header"); |
| 2132 | 2161 | } |
| 2133 | 2162 | blob_append(&g.httpHeader, zLine, -1); |
| 2134 | 2163 | cgi_trace(zLine); |
| @@ -2160,11 +2189,10 @@ | ||
| 2160 | 2189 | } |
| 2161 | 2190 | if( zIpAddr ){ |
| 2162 | 2191 | cgi_setenv("REMOTE_ADDR", zIpAddr); |
| 2163 | 2192 | g.zIpAddr = fossil_strdup(zIpAddr); |
| 2164 | 2193 | } |
| 2165 | - | |
| 2166 | 2194 | |
| 2167 | 2195 | /* Get all the optional fields that follow the first line. |
| 2168 | 2196 | */ |
| 2169 | 2197 | while( cgi_fgets(zLine,sizeof(zLine)) ){ |
| 2170 | 2198 | char *zFieldName; |
| 2171 | 2199 |
| --- 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 @@ | ||
| 1321 | 1321 | if( zMsg && zMsg[0] ){ |
| 1322 | 1322 | blob_appendf(&up,"\r\nContent-Disposition: form-data; name=\"msg\"\r\n" |
| 1323 | 1323 | "\r\n%s\r\n%s", zMsg, zBoundary); |
| 1324 | 1324 | } |
| 1325 | 1325 | 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)); | |
| 1327 | 1327 | int i; |
| 1328 | 1328 | const char *zMime = mimetype_from_name(zFN); |
| 1329 | 1329 | for(i=0; zFN[i]; i++){ |
| 1330 | 1330 | char c = zFN[i]; |
| 1331 | 1331 | if( fossil_isalnum(c) ) continue; |
| 1332 | 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 = 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 @@ | ||
| 1386 | 1386 | file_relative_name(g.zLocalRoot, &fname, 1); |
| 1387 | 1387 | zFile = db_text(0, "SELECT '%qci-comment-'||hex(randomblob(6))||'.txt'", |
| 1388 | 1388 | blob_str(&fname)); |
| 1389 | 1389 | }else{ |
| 1390 | 1390 | file_tempname(&fname, "ci-comment",0); |
| 1391 | - zFile = mprintf("%s", blob_str(&fname)); | |
| 1391 | + zFile = fossil_strdup(blob_str(&fname)); | |
| 1392 | 1392 | } |
| 1393 | 1393 | blob_reset(&fname); |
| 1394 | 1394 | } |
| 1395 | 1395 | #if defined(_WIN32) |
| 1396 | 1396 | blob_add_cr(pPrompt); |
| @@ -1502,11 +1502,11 @@ | ||
| 1502 | 1502 | "# * All other text will be displayed as written\n", -1); |
| 1503 | 1503 | }else{ |
| 1504 | 1504 | blob_append(&prompt, |
| 1505 | 1505 | "# * Hyperlinks: [target] or [target|display-text]\n" |
| 1506 | 1506 | "# * 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 | |
| 1508 | 1508 | ); |
| 1509 | 1509 | } |
| 1510 | 1510 | blob_append(&prompt, "#\n", 2); |
| 1511 | 1511 | |
| 1512 | 1512 | if( dryRunFlag ){ |
| 1513 | 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 = 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 @@ | ||
| 65 | 65 | blob_appendf(pOut, "%s", "-----BEGIN SSH SIGNED MESSAGE-----\n\n"); |
| 66 | 66 | blob_appendf(pOut, "%s", blob_str(&tmpBlob)); |
| 67 | 67 | blob_zero(&tmpBlob); |
| 68 | 68 | blob_read_from_file(&tmpBlob, zIn, ExtFILE); |
| 69 | 69 | /* Add signature - already armored by SSH */ |
| 70 | - blob_appendf(pOut, "%s", blob_str(&tmpBlob)); | |
| 70 | + blob_appendb(pOut, &tmpBlob); | |
| 71 | 71 | }else{ |
| 72 | 72 | /* Assume that the external command creates non-detached signatures */ |
| 73 | 73 | blob_read_from_file(pOut, zIn, ExtFILE); |
| 74 | 74 | } |
| 75 | 75 | }else{ |
| 76 | 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_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 @@ | ||
| 348 | 348 | const char *zHttpAuth, /* Credentials in the form "user:password" */ |
| 349 | 349 | int fRemember, /* True to remember credentials for later reuse */ |
| 350 | 350 | const char *zUrl /* URL for which these credentials apply */ |
| 351 | 351 | ){ |
| 352 | 352 | if( zHttpAuth && zHttpAuth[0] ){ |
| 353 | - g.zHttpAuth = mprintf("%s", zHttpAuth); | |
| 353 | + g.zHttpAuth = fossil_strdup(zHttpAuth); | |
| 354 | 354 | } |
| 355 | 355 | if( fRemember ){ |
| 356 | 356 | if( g.zHttpAuth && g.zHttpAuth[0] ){ |
| 357 | 357 | set_httpauth(g.zHttpAuth); |
| 358 | 358 | }else if( zUrl && zUrl[0] ){ |
| @@ -388,11 +388,11 @@ | ||
| 388 | 388 | void clone_ssh_find_options(void){ |
| 389 | 389 | const char *zSshCmd; /* SSH command string */ |
| 390 | 390 | |
| 391 | 391 | zSshCmd = find_option("ssh-command","c",1); |
| 392 | 392 | if( zSshCmd && zSshCmd[0] ){ |
| 393 | - g.zSshCmd = mprintf("%s", zSshCmd); | |
| 393 | + g.zSshCmd = fossil_strdup(zSshCmd); | |
| 394 | 394 | } |
| 395 | 395 | } |
| 396 | 396 | |
| 397 | 397 | /* |
| 398 | 398 | ** Set SSH options discovered in global variables (set from command line |
| 399 | 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 = 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 @@ | ||
| 807 | 807 | verify_all_options(); |
| 808 | 808 | zPrefix = zText = zOrigText = 0; |
| 809 | 809 | if( fromFile ){ |
| 810 | 810 | Blob fileData; |
| 811 | 811 | blob_read_from_file(&fileData, fromFile, ExtFILE); |
| 812 | - zText = mprintf("%s", blob_str(&fileData)); | |
| 812 | + zText = fossil_strdup(blob_str(&fileData)); | |
| 813 | 813 | blob_reset(&fileData); |
| 814 | 814 | } |
| 815 | 815 | if( fromOrig ){ |
| 816 | 816 | Blob fileData; |
| 817 | 817 | blob_read_from_file(&fileData, fromOrig, ExtFILE); |
| 818 | - zOrigText = mprintf("%s", blob_str(&fileData)); | |
| 818 | + zOrigText = fossil_strdup(blob_str(&fileData)); | |
| 819 | 819 | blob_reset(&fileData); |
| 820 | 820 | } |
| 821 | 821 | for(i=2; i<g.argc; i++){ |
| 822 | 822 | if( zText==0 ){ |
| 823 | 823 | zText = g.argv[i]; |
| 824 | 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 = 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 |
+1
| --- src/content.c | ||
| +++ src/content.c | ||
| @@ -600,10 +600,11 @@ | ||
| 600 | 600 | ); |
| 601 | 601 | db_bind_blob(&s1, ":data", &cmpr); |
| 602 | 602 | db_exec(&s1); |
| 603 | 603 | rid = db_last_insert_rowid(); |
| 604 | 604 | if( !pBlob ){ |
| 605 | + assert(!"cannot happen: pBlob is always non-NULL"); | |
| 605 | 606 | db_multi_exec("INSERT OR IGNORE INTO phantom VALUES(%d)", rid); |
| 606 | 607 | } |
| 607 | 608 | } |
| 608 | 609 | if( g.markPrivate || isPrivate ){ |
| 609 | 610 | db_multi_exec("INSERT OR IGNORE INTO private VALUES(%d)", rid); |
| 610 | 611 |
| --- 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 @@ | ||
| 86 | 86 | void cookie_parse(void){ |
| 87 | 87 | char *z; |
| 88 | 88 | if( cookies.bIsInit ) return; |
| 89 | 89 | z = (char*)P(DISPLAY_SETTINGS_COOKIE); |
| 90 | 90 | if( z==0 ) z = ""; |
| 91 | - cookies.zCookieValue = z = mprintf("%s", z); | |
| 91 | + cookies.zCookieValue = z = fossil_strdup(z); | |
| 92 | 92 | cookies.bIsInit = 1; |
| 93 | 93 | while( cookies.nParam<COOKIE_NPARAM ){ |
| 94 | 94 | while( fossil_isspace(z[0]) ) z++; |
| 95 | 95 | if( z[0]==0 ) break; |
| 96 | 96 | cookies.aParam[cookies.nParam].zPName = z; |
| @@ -282,10 +282,14 @@ | ||
| 282 | 282 | && hex_prefix_length(&zName[7])==16 |
| 283 | 283 | && hex_prefix_length(zValue)>24 |
| 284 | 284 | ){ |
| 285 | 285 | @ <p>This appears to be a login cookie for another Fossil repository |
| 286 | 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. | |
| 287 | 291 | } |
| 288 | 292 | else { |
| 289 | 293 | @ <p>This cookie was not generated by Fossil. It might be something |
| 290 | 294 | @ from another program on the same website. |
| 291 | 295 | } |
| 292 | 296 |
| --- 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 @@ | ||
| 1 | 1 | /* Manage "Copy Buttons" linked to target elements, to copy the text (or, parts |
| 2 | 2 | ** thereof) of the target elements to the clipboard. |
| 3 | 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). | |
| 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>". | |
| 11 | 7 | ** |
| 12 | 8 | ** For HTML-defined buttons, either initCopyButtonById(), or initCopyButton(), |
| 13 | 9 | ** 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). | |
| 15 | 15 | ** |
| 16 | 16 | ** The initialization functions do not overwrite the "data-copytarget" and |
| 17 | 17 | ** "data-copylength" attributes with empty or null values for <idTarget> and |
| 18 | 18 | ** <cchLength>, respectively. Set <cchLength> to "-1" to explicitly remove the |
| 19 | 19 | ** previous copy length limit. |
| 20 | 20 | ** |
| 21 | 21 | ** HTML snippet for statically created buttons: |
| 22 | 22 | ** |
| 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> | |
| 25 | 27 | */ |
| 26 | 28 | function makeCopyButton(idTarget,bFlipped,cchLength){ |
| 27 | - var elButton = document.createElement("span"); | |
| 29 | + var elButton = document.createElement("button"); | |
| 28 | 30 | elButton.className = "copy-button"; |
| 29 | 31 | if( bFlipped ) elButton.className += " copy-button-flipped"; |
| 30 | 32 | elButton.id = "copy-" + idTarget; |
| 31 | 33 | initCopyButton(elButton,idTarget,cchLength); |
| 32 | 34 | return elButton; |
| @@ -36,15 +38,18 @@ | ||
| 36 | 38 | var elButton = document.getElementById(idButton); |
| 37 | 39 | if( elButton ) initCopyButton(elButton,idTarget,cchLength); |
| 38 | 40 | return elButton; |
| 39 | 41 | } |
| 40 | 42 | function initCopyButton(elButton,idTarget,cchLength){ |
| 41 | - elButton.style.transition = ""; | |
| 42 | - elButton.style.opacity = 1; | |
| 43 | 43 | if( idTarget ) elButton.setAttribute("data-copytarget",idTarget); |
| 44 | 44 | if( cchLength ) elButton.setAttribute("data-copylength",cchLength); |
| 45 | 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 | + } | |
| 46 | 51 | return elButton; |
| 47 | 52 | } |
| 48 | 53 | setTimeout(function(){ |
| 49 | 54 | var elButtons = document.getElementsByClassName("copy-button"); |
| 50 | 55 | for ( var i=0; i<elButtons.length; i++ ){ |
| @@ -53,14 +58,11 @@ | ||
| 53 | 58 | },1); |
| 54 | 59 | /* The onclick handler for the "Copy Button". */ |
| 55 | 60 | function clickCopyButton(e){ |
| 56 | 61 | e.preventDefault(); /* Mandatory for <a> and <button>. */ |
| 57 | 62 | 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. */ | |
| 62 | 64 | var idTarget = this.getAttribute("data-copytarget"); |
| 63 | 65 | var elTarget = document.getElementById(idTarget); |
| 64 | 66 | if( elTarget ){ |
| 65 | 67 | var text = elTarget.innerText.replace(/^\s+|\s+$/g,""); |
| 66 | 68 | var cchLength = parseInt(this.getAttribute("data-copylength")); |
| @@ -67,15 +69,10 @@ | ||
| 67 | 69 | if( !isNaN(cchLength) && cchLength>0 ){ |
| 68 | 70 | text = text.slice(0,cchLength); /* Assume single-byte chars. */ |
| 69 | 71 | } |
| 70 | 72 | copyTextToClipboard(text); |
| 71 | 73 | } |
| 72 | - setTimeout(function(){ | |
| 73 | - this.style.transition = ""; | |
| 74 | - this.style.opacity = 1; | |
| 75 | - this.removeAttribute("data-copylocked"); | |
| 76 | - }.bind(this),400); | |
| 77 | 74 | } |
| 78 | 75 | /* Create a temporary <textarea> element and copy the contents to clipboard. */ |
| 79 | 76 | function copyTextToClipboard(text){ |
| 80 | 77 | if( window.clipboardData && window.clipboardData.setData ){ |
| 81 | 78 | window.clipboardData.setData("Text",text); |
| 82 | 79 |
| --- 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 |
M
src/db.c
+10
| --- src/db.c | ||
| +++ src/db.c | ||
| @@ -125,10 +125,20 @@ | ||
| 125 | 125 | #endif /* FOSSIL_ENABLE_JSON */ |
| 126 | 126 | if( g.xferPanic && g.cgiOutput==1 ){ |
| 127 | 127 | cgi_reset_content(); |
| 128 | 128 | @ error Database\serror:\s%F(z) |
| 129 | 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 | + } | |
| 130 | 140 | } |
| 131 | 141 | fossil_fatal("Database error: %s", z); |
| 132 | 142 | } |
| 133 | 143 | |
| 134 | 144 | /* |
| 135 | 145 |
| --- 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 @@ | ||
| 1 | 1 | /* This CSS file holds the default implementations for all of fossil's |
| 2 | 2 | CSS classes. When /style.css is requested, the rules in this file |
| 3 | 3 | are emitted first, followed by (1) page-specific CSS (if any) and |
| 4 | 4 | (2) skin-specific CSS. |
| 5 | 5 | */ |
| 6 | +body { | |
| 7 | + z-index: 0 /* Used by robot.c:robot_proofofwork() and href.js */; | |
| 8 | +} | |
| 6 | 9 | div.sidebox { |
| 7 | 10 | float: right; |
| 8 | 11 | background-color: white; |
| 9 | 12 | border-width: medium; |
| 10 | 13 | border-style: double; |
| @@ -1098,19 +1101,40 @@ | ||
| 1098 | 1101 | white-space: nowrap; |
| 1099 | 1102 | } |
| 1100 | 1103 | label[for] { |
| 1101 | 1104 | cursor: pointer; |
| 1102 | 1105 | } |
| 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 { | |
| 1105 | 1110 | width: 14px; |
| 1106 | 1111 | height: 14px; |
| 1107 | 1112 | /*Note: .24em is slightly smaller than the average width of a normal space.*/ |
| 1108 | 1113 | margin: -2px .24em 0 0; |
| 1109 | 1114 | padding: 0; |
| 1110 | 1115 | border: 0; |
| 1116 | + outline: 0; | |
| 1117 | + background: none; | |
| 1118 | + font-size: inherit; /* Required for horizontal spacing. */ | |
| 1111 | 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; | |
| 1112 | 1136 | background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' \ |
| 1113 | 1137 | viewBox='0,0,14,14'%3E%3Cpath style='fill:black;opacity:0' \ |
| 1114 | 1138 | d='M14,14H0V0h14v14z'/%3E%3Cpath style='fill:rgb(240,240,240)' \ |
| 1115 | 1139 | d='M1,0h6.6l2,2h1l3.4,3.4v8.6h-10v-2h-3z'/%3E%3Cpath style='fill:rgb(64,64,64)' \ |
| 1116 | 1140 | d='M2,1h5l3,3v7h-8z'/%3E%3Cpath style='fill:rgb(248,248,248)' \ |
| @@ -1121,19 +1145,17 @@ | ||
| 1121 | 1145 | d='M7,8h4v1h-4zm0,2h4v1h-4z'/%3E%3C/svg%3E"); |
| 1122 | 1146 | background-repeat: no-repeat; |
| 1123 | 1147 | background-position: center; |
| 1124 | 1148 | cursor: pointer; |
| 1125 | 1149 | } |
| 1126 | -.copy-button.disabled { | |
| 1150 | +button.copy-button:enabled:active span { | |
| 1151 | + background-size: 90%; | |
| 1152 | +} | |
| 1153 | +button.copy-button:disabled span { | |
| 1127 | 1154 | filter: grayscale(1); |
| 1128 | 1155 | opacity: 0.4; |
| 1129 | 1156 | } |
| 1130 | -.copy-button-flipped { | |
| 1131 | -/*Note: .16em is suitable for element grouping.*/ | |
| 1132 | - margin-left: .16em; | |
| 1133 | - margin-right: 0; | |
| 1134 | -} | |
| 1135 | 1157 | .nobr { |
| 1136 | 1158 | white-space: nowrap; |
| 1137 | 1159 | } |
| 1138 | 1160 | .accordion { |
| 1139 | 1161 | cursor: pointer; |
| 1140 | 1162 |
| --- 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 @@ | ||
| 1 | 1 | /* This CSS file holds the default implementations for all of fossil's |
| 2 | 2 | CSS classes. When /style.css is requested, the rules in this file |
| 3 | 3 | are emitted first, followed by (1) page-specific CSS (if any) and |
| 4 | 4 | (2) skin-specific CSS. |
| 5 | 5 | */ |
| 6 | +body { | |
| 7 | + z-index: 0 /* Used by robot.c:robot_proofofwork() and href.js */; | |
| 8 | +} | |
| 6 | 9 | div.sidebox { |
| 7 | 10 | float: right; |
| 8 | 11 | background-color: white; |
| 9 | 12 | border-width: medium; |
| 10 | 13 | border-style: double; |
| @@ -1098,19 +1101,40 @@ | ||
| 1098 | 1101 | white-space: nowrap; |
| 1099 | 1102 | } |
| 1100 | 1103 | label[for] { |
| 1101 | 1104 | cursor: pointer; |
| 1102 | 1105 | } |
| 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 { | |
| 1105 | 1110 | width: 14px; |
| 1106 | 1111 | height: 14px; |
| 1107 | 1112 | /*Note: .24em is slightly smaller than the average width of a normal space.*/ |
| 1108 | 1113 | margin: -2px .24em 0 0; |
| 1109 | 1114 | padding: 0; |
| 1110 | 1115 | border: 0; |
| 1116 | + outline: 0; | |
| 1117 | + background: none; | |
| 1118 | + font-size: inherit; /* Required for horizontal spacing. */ | |
| 1111 | 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; | |
| 1112 | 1136 | background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' \ |
| 1113 | 1137 | viewBox='0,0,14,14'%3E%3Cpath style='fill:black;opacity:0' \ |
| 1114 | 1138 | d='M14,14H0V0h14v14z'/%3E%3Cpath style='fill:rgb(240,240,240)' \ |
| 1115 | 1139 | d='M1,0h6.6l2,2h1l3.4,3.4v8.6h-10v-2h-3z'/%3E%3Cpath style='fill:rgb(64,64,64)' \ |
| 1116 | 1140 | d='M2,1h5l3,3v7h-8z'/%3E%3Cpath style='fill:rgb(248,248,248)' \ |
| @@ -1121,19 +1145,17 @@ | ||
| 1121 | 1145 | d='M7,8h4v1h-4zm0,2h4v1h-4z'/%3E%3C/svg%3E"); |
| 1122 | 1146 | background-repeat: no-repeat; |
| 1123 | 1147 | background-position: center; |
| 1124 | 1148 | cursor: pointer; |
| 1125 | 1149 | } |
| 1126 | -.copy-button.disabled { | |
| 1150 | +button.copy-button:enabled:active span { | |
| 1151 | + background-size: 90%; | |
| 1152 | +} | |
| 1153 | +button.copy-button:disabled span { | |
| 1127 | 1154 | filter: grayscale(1); |
| 1128 | 1155 | opacity: 0.4; |
| 1129 | 1156 | } |
| 1130 | -.copy-button-flipped { | |
| 1131 | -/*Note: .16em is suitable for element grouping.*/ | |
| 1132 | - margin-left: .16em; | |
| 1133 | - margin-right: 0; | |
| 1134 | -} | |
| 1135 | 1157 | .nobr { |
| 1136 | 1158 | white-space: nowrap; |
| 1137 | 1159 | } |
| 1138 | 1160 | .accordion { |
| 1139 | 1161 | cursor: pointer; |
| 1140 | 1162 |
| --- 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 @@ | ||
| 2970 | 2970 | Blob *pOut, /* Write diff here if not NULL */ |
| 2971 | 2971 | DiffConfig *pCfg /* Configuration options */ |
| 2972 | 2972 | ){ |
| 2973 | 2973 | int ignoreWs; /* Ignore whitespace */ |
| 2974 | 2974 | DContext c; |
| 2975 | + int nDel = 0, nIns = 0; | |
| 2975 | 2976 | |
| 2976 | 2977 | if( pCfg->diffFlags & DIFF_INVERT ){ |
| 2977 | 2978 | Blob *pTemp = pA_Blob; |
| 2978 | 2979 | pA_Blob = pB_Blob; |
| 2979 | 2980 | pB_Blob = pTemp; |
| @@ -3048,22 +3049,27 @@ | ||
| 3048 | 3049 | c.aEdit[i+1] = sum; |
| 3049 | 3050 | for(k=0, sum=0; k<c.aEdit[i+2]; k++) sum += c.aTo[iB++].n; |
| 3050 | 3051 | c.aEdit[i+2] = sum; |
| 3051 | 3052 | } |
| 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 | + } | |
| 3053 | 3067 | |
| 3054 | 3068 | 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)){ | |
| 3063 | 3070 | if( nIns+nDel ){ |
| 3064 | - g.diffCnt[0]++; | |
| 3065 | 3071 | if( !(pCfg->diffFlags & DIFF_BRIEF) ){ |
| 3066 | 3072 | blob_appendf(pOut, "%10d %10d", nIns, nDel); |
| 3067 | 3073 | } |
| 3068 | 3074 | } |
| 3069 | 3075 | }else if( pCfg->diffFlags & (DIFF_RAW|DIFF_BY_TOKEN) ){ |
| @@ -3666,12 +3672,12 @@ | ||
| 3666 | 3672 | unsigned clr1, clr2, clr; |
| 3667 | 3673 | int bBlame = g.zPath[0]!='a';/* True for BLAME output. False for ANNOTATE. */ |
| 3668 | 3674 | |
| 3669 | 3675 | /* Gather query parameters */ |
| 3670 | 3676 | 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; | |
| 3673 | 3679 | fossil_nice_default(); |
| 3674 | 3680 | zFilename = P("filename"); |
| 3675 | 3681 | zRevision = PD("checkin",0); |
| 3676 | 3682 | zOrigin = P("origin"); |
| 3677 | 3683 | zLimit = P("limit"); |
| 3678 | 3684 |
| --- 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 @@ | ||
| 2970 | 2970 | Blob *pOut, /* Write diff here if not NULL */ |
| 2971 | 2971 | DiffConfig *pCfg /* Configuration options */ |
| 2972 | 2972 | ){ |
| 2973 | 2973 | int ignoreWs; /* Ignore whitespace */ |
| 2974 | 2974 | DContext c; |
| 2975 | + int nDel = 0, nIns = 0; | |
| 2975 | 2976 | |
| 2976 | 2977 | if( pCfg->diffFlags & DIFF_INVERT ){ |
| 2977 | 2978 | Blob *pTemp = pA_Blob; |
| 2978 | 2979 | pA_Blob = pB_Blob; |
| 2979 | 2980 | pB_Blob = pTemp; |
| @@ -3048,22 +3049,27 @@ | ||
| 3048 | 3049 | c.aEdit[i+1] = sum; |
| 3049 | 3050 | for(k=0, sum=0; k<c.aEdit[i+2]; k++) sum += c.aTo[iB++].n; |
| 3050 | 3051 | c.aEdit[i+2] = sum; |
| 3051 | 3052 | } |
| 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 | + } | |
| 3053 | 3067 | |
| 3054 | 3068 | 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)){ | |
| 3063 | 3070 | if( nIns+nDel ){ |
| 3064 | - g.diffCnt[0]++; | |
| 3065 | 3071 | if( !(pCfg->diffFlags & DIFF_BRIEF) ){ |
| 3066 | 3072 | blob_appendf(pOut, "%10d %10d", nIns, nDel); |
| 3067 | 3073 | } |
| 3068 | 3074 | } |
| 3069 | 3075 | }else if( pCfg->diffFlags & (DIFF_RAW|DIFF_BY_TOKEN) ){ |
| @@ -3666,12 +3672,12 @@ | ||
| 3666 | 3672 | unsigned clr1, clr2, clr; |
| 3667 | 3673 | int bBlame = g.zPath[0]!='a';/* True for BLAME output. False for ANNOTATE. */ |
| 3668 | 3674 | |
| 3669 | 3675 | /* Gather query parameters */ |
| 3670 | 3676 | 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; | |
| 3673 | 3679 | fossil_nice_default(); |
| 3674 | 3680 | zFilename = P("filename"); |
| 3675 | 3681 | zRevision = PD("checkin",0); |
| 3676 | 3682 | zOrigin = P("origin"); |
| 3677 | 3683 | zLimit = P("limit"); |
| 3678 | 3684 |
| --- 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 @@ | ||
| 281 | 281 | if {$type ne "txt"} { |
| 282 | 282 | $c config -width $widths($type) |
| 283 | 283 | } |
| 284 | 284 | $c config -state disabled |
| 285 | 285 | } |
| 286 | + .wfiles.lb config -height $nDiffs | |
| 286 | 287 | if {$nDiffs <= [.wfiles.lb cget -height]} { |
| 287 | - .wfiles.lb config -height $nDiffs | |
| 288 | 288 | grid remove .wfiles.sb |
| 289 | 289 | } |
| 290 | 290 | |
| 291 | 291 | return $nDiffs |
| 292 | 292 | } |
| 293 | 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 | 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 |
+1
| --- src/diffcmd.c | ||
| +++ src/diffcmd.c | ||
| @@ -1502,10 +1502,11 @@ | ||
| 1502 | 1502 | DiffConfig DCfg; |
| 1503 | 1503 | cgi_check_for_malice(); |
| 1504 | 1504 | login_check_credentials(); |
| 1505 | 1505 | if( !g.perm.Read ){ login_needed(g.anon.Read); return; } |
| 1506 | 1506 | if( zFrom==0 || zTo==0 ) fossil_redirect_home(); |
| 1507 | + if( robot_restrict("diff") ) return; | |
| 1507 | 1508 | |
| 1508 | 1509 | fossil_nice_default(); |
| 1509 | 1510 | cgi_set_content_type("text/plain"); |
| 1510 | 1511 | diff_config_init(&DCfg, DIFF_VERBOSE); |
| 1511 | 1512 | diff_two_versions(zFrom, zTo, &DCfg, 0); |
| 1512 | 1513 |
| --- 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
| --- src/diffcmd.c | ||
| +++ src/diffcmd.c | ||
| @@ -1502,10 +1502,11 @@ | ||
| 1502 | 1502 | DiffConfig DCfg; |
| 1503 | 1503 | cgi_check_for_malice(); |
| 1504 | 1504 | login_check_credentials(); |
| 1505 | 1505 | if( !g.perm.Read ){ login_needed(g.anon.Read); return; } |
| 1506 | 1506 | if( zFrom==0 || zTo==0 ) fossil_redirect_home(); |
| 1507 | + if( robot_restrict("diff") ) return; | |
| 1507 | 1508 | |
| 1508 | 1509 | fossil_nice_default(); |
| 1509 | 1510 | cgi_set_content_type("text/plain"); |
| 1510 | 1511 | diff_config_init(&DCfg, DIFF_VERBOSE); |
| 1511 | 1512 | diff_two_versions(zFrom, zTo, &DCfg, 0); |
| 1512 | 1513 |
| --- 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 @@ | ||
| 382 | 382 | const char *zClr; /* Name of the background color */ |
| 383 | 383 | const char *zMimetype = P("mimetype"); /* Mimetype of zBody */ |
| 384 | 384 | int isNew = 0; |
| 385 | 385 | |
| 386 | 386 | if( zBody ){ |
| 387 | - zBody = mprintf("%s", zBody); | |
| 387 | + zBody = fossil_strdup(zBody); | |
| 388 | 388 | } |
| 389 | 389 | login_check_credentials(); |
| 390 | 390 | zId = P("name"); |
| 391 | 391 | if( zId==0 ){ |
| 392 | 392 | zId = db_text(0, "SELECT lower(hex(randomblob(20)))"); |
| 393 | 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 = 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 @@ | ||
| 58 | 58 | } |
| 59 | 59 | db_static_prepare(&q, "SELECT info FROM user WHERE login=:user"); |
| 60 | 60 | db_bind_text(&q, ":user", zUser); |
| 61 | 61 | if( db_step(&q)!=SQLITE_ROW ){ |
| 62 | 62 | db_reset(&q); |
| 63 | - zName = mprintf("%s", zUser); | |
| 63 | + zName = fossil_strdup(zUser); | |
| 64 | 64 | for(i=j=0; zName[i]; i++){ |
| 65 | 65 | if( zName[i]!='<' && zName[i]!='>' && zName[i]!='"' ){ |
| 66 | 66 | zName[j++] = zName[i]; |
| 67 | 67 | } |
| 68 | 68 | } |
| @@ -102,11 +102,11 @@ | ||
| 102 | 102 | atEmailFirst = i+1; |
| 103 | 103 | } |
| 104 | 104 | } |
| 105 | 105 | if( zContact[i]==0 ){ |
| 106 | 106 | /* 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); | |
| 108 | 108 | for(i=j=0; zName[i]; i++){ |
| 109 | 109 | if( zName[i]!='<' && zName[i]!='>' && zName[i]!='"' ){ |
| 110 | 110 | zName[j++] = zName[i]; |
| 111 | 111 | } |
| 112 | 112 | } |
| @@ -149,11 +149,11 @@ | ||
| 149 | 149 | for(j=0; j<i && zContact[j] && zContact[j]==' '; j++){} |
| 150 | 150 | zName = mprintf("%.*s", i-j+1, &zContact[j]); |
| 151 | 151 | } |
| 152 | 152 | } |
| 153 | 153 | |
| 154 | - if( zName==NULL ) zName = mprintf("%s", zUser); | |
| 154 | + if( zName==NULL ) zName = fossil_strdup(zUser); | |
| 155 | 155 | for(i=j=0; zName[i]; i++){ |
| 156 | 156 | if( zName[i]!='<' && zName[i]!='>' && zName[i]!='"' ){ |
| 157 | 157 | zName[j++] = zName[i]; |
| 158 | 158 | } |
| 159 | 159 | } |
| @@ -172,11 +172,11 @@ | ||
| 172 | 172 | ** https://git-scm.com/docs/git-check-ref-format |
| 173 | 173 | ** This implementation assumes we are only printing |
| 174 | 174 | ** the branch or tag part of the reference. |
| 175 | 175 | */ |
| 176 | 176 | static void print_ref(const char *zRef){ |
| 177 | - char *zEncoded = mprintf("%s", zRef); | |
| 177 | + char *zEncoded = fossil_strdup(zRef); | |
| 178 | 178 | int i, w; |
| 179 | 179 | if (zEncoded[0]=='@' && zEncoded[1]=='\0'){ |
| 180 | 180 | putchar(REFREPLACEMENT); |
| 181 | 181 | return; |
| 182 | 182 | } |
| @@ -1152,11 +1152,11 @@ | ||
| 1152 | 1152 | TAG_BRANCH, rid |
| 1153 | 1153 | ); |
| 1154 | 1154 | if( fossil_strcmp(zBranch,"trunk")==0 ){ |
| 1155 | 1155 | assert( gitmirror_mainbranch!=0 ); |
| 1156 | 1156 | fossil_free(zBranch); |
| 1157 | - zBranch = mprintf("%s",gitmirror_mainbranch); | |
| 1157 | + zBranch = fossil_strdup(gitmirror_mainbranch); | |
| 1158 | 1158 | }else if( zBranch==0 ){ |
| 1159 | 1159 | zBranch = mprintf("unknown"); |
| 1160 | 1160 | }else{ |
| 1161 | 1161 | gitmirror_sanitize_name(zBranch); |
| 1162 | 1162 | } |
| 1163 | 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 = 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 @@ | ||
| 229 | 229 | zFailReason = "path does not match any file or script"; |
| 230 | 230 | goto ext_not_found; |
| 231 | 231 | } |
| 232 | 232 | assert( nScript>=nRoot+1 ); |
| 233 | 233 | 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); | |
| 235 | 236 | if( zMime==0 ) zMime = "application/octet-stream"; |
| 236 | 237 | if( !file_isexe(zScript, ExtFILE) ){ |
| 237 | 238 | /* File is not executable. Must be a regular file. In that case, |
| 238 | 239 | ** disallow extra path elements */ |
| 239 | 240 | if( zPath[nScript]!=0 ){ |
| 240 | 241 |
| --- 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 @@ | ||
| 260 | 260 | int i, nName; |
| 261 | 261 | char *zName, zBuf[1000]; |
| 262 | 262 | |
| 263 | 263 | nName = strlen(zLinkFile); |
| 264 | 264 | if( nName>=(int)sizeof(zBuf) ){ |
| 265 | - zName = mprintf("%s", zLinkFile); | |
| 265 | + zName = fossil_strdup(zLinkFile); | |
| 266 | 266 | }else{ |
| 267 | 267 | zName = zBuf; |
| 268 | 268 | memcpy(zName, zLinkFile, nName+1); |
| 269 | 269 | } |
| 270 | 270 | nName = file_simplify_name(zName, nName, 0); |
| @@ -425,11 +425,11 @@ | ||
| 425 | 425 | */ |
| 426 | 426 | int file_isdir(const char *zFilename, int eFType){ |
| 427 | 427 | int rc; |
| 428 | 428 | char *zFN; |
| 429 | 429 | |
| 430 | - zFN = mprintf("%s", zFilename); | |
| 430 | + zFN = fossil_strdup(zFilename); | |
| 431 | 431 | file_simplify_name(zFN, -1, 0); |
| 432 | 432 | rc = getStat(zFN, eFType); |
| 433 | 433 | if( rc ){ |
| 434 | 434 | rc = 0; /* It does not exist at all. */ |
| 435 | 435 | }else if( S_ISDIR(fx.fileStat.st_mode) ){ |
| @@ -904,11 +904,11 @@ | ||
| 904 | 904 | ){ |
| 905 | 905 | int nName, rc = 0; |
| 906 | 906 | char *zName; |
| 907 | 907 | |
| 908 | 908 | nName = strlen(zFilename); |
| 909 | - zName = mprintf("%s", zFilename); | |
| 909 | + zName = fossil_strdup(zFilename); | |
| 910 | 910 | nName = file_simplify_name(zName, nName, 0); |
| 911 | 911 | while( nName>0 && zName[nName-1]!='/' ){ nName--; } |
| 912 | 912 | if( nName>1 ){ |
| 913 | 913 | zName[nName-1] = 0; |
| 914 | 914 | if( file_isdir(zName, eFType)!=1 ){ |
| @@ -1277,13 +1277,13 @@ | ||
| 1277 | 1277 | const char *zTail; |
| 1278 | 1278 | for(i=2; i<g.argc; i++){ |
| 1279 | 1279 | zTail = file_skip_userhost(g.argv[i]); |
| 1280 | 1280 | if( zTail ){ |
| 1281 | 1281 | fossil_print("... ON REMOTE: %.*s\n", (int)(zTail-g.argv[i]), g.argv[i]); |
| 1282 | - z = mprintf("%s", zTail); | |
| 1282 | + z = fossil_strdup(zTail); | |
| 1283 | 1283 | }else{ |
| 1284 | - z = mprintf("%s", g.argv[i]); | |
| 1284 | + z = fossil_strdup(g.argv[i]); | |
| 1285 | 1285 | } |
| 1286 | 1286 | fossil_print("[%s] -> ", z); |
| 1287 | 1287 | file_simplify_name(z, -1, 0); |
| 1288 | 1288 | fossil_print("[%s]\n", z); |
| 1289 | 1289 | fossil_free(z); |
| 1290 | 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 = 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 @@ | ||
| 825 | 825 | } |
| 826 | 826 | db_begin_transaction(); |
| 827 | 827 | zFilename = g.argv[2]; |
| 828 | 828 | cimi.zFilename = mprintf("%/", zAsFilename ? zAsFilename : zFilename); |
| 829 | 829 | cimi.filePerm = file_perm(zFilename, ExtFILE); |
| 830 | - cimi.zUser = mprintf("%s", zUser ? zUser : login_name()); | |
| 830 | + cimi.zUser = fossil_strdup(zUser ? zUser : login_name()); | |
| 831 | 831 | if(zDate){ |
| 832 | - cimi.zDate = mprintf("%s", zDate); | |
| 832 | + cimi.zDate = fossil_strdup(zDate); | |
| 833 | 833 | } |
| 834 | 834 | if(zRevision==0 || zRevision[0]==0){ |
| 835 | 835 | if(g.localOpen/*check-out*/){ |
| 836 | 836 | zRevision = db_lget("checkout-hash", 0)/*leak*/; |
| 837 | 837 | }else{ |
| @@ -928,11 +928,11 @@ | ||
| 928 | 928 | char * zFileUuid = 0; |
| 929 | 929 | db_prepare(&stmt, "SELECT uuid, perm FROM files_of_checkin " |
| 930 | 930 | "WHERE filename=%Q %s AND checkinID=%d", |
| 931 | 931 | zFilename, filename_collation(), vid); |
| 932 | 932 | if(SQLITE_ROW==db_step(&stmt)){ |
| 933 | - zFileUuid = mprintf("%s",db_column_text(&stmt, 0)); | |
| 933 | + zFileUuid = fossil_strdup(db_column_text(&stmt, 0)); | |
| 934 | 934 | if(pFilePerm){ |
| 935 | 935 | *pFilePerm = mfile_permstr_int(db_column_text(&stmt, 1)); |
| 936 | 936 | } |
| 937 | 937 | } |
| 938 | 938 | db_finalize(&stmt); |
| @@ -1187,11 +1187,11 @@ | ||
| 1187 | 1187 | if(bIsMissingArg){ |
| 1188 | 1188 | *bIsMissingArg = 1; |
| 1189 | 1189 | } |
| 1190 | 1190 | fail((pErr,"Missing required 'filename' parameter.")); |
| 1191 | 1191 | } |
| 1192 | - p->zFilename = mprintf("%s",zFlag); | |
| 1192 | + p->zFilename = fossil_strdup(zFlag); | |
| 1193 | 1193 | |
| 1194 | 1194 | if(0==fileedit_is_editable(p->zFilename)){ |
| 1195 | 1195 | rc = 403; |
| 1196 | 1196 | fail((pErr,"Filename [%h] is disallowed " |
| 1197 | 1197 | "by the [fileedit-glob] repository " |
| @@ -1248,11 +1248,11 @@ | ||
| 1248 | 1248 | if(zFlag!=0 && *zFlag!=0){ |
| 1249 | 1249 | blob_append(&p->comment, zFlag, -1); |
| 1250 | 1250 | } |
| 1251 | 1251 | zFlag = P("comment_mimetype"); |
| 1252 | 1252 | if(zFlag){ |
| 1253 | - p->zCommentMimetype = mprintf("%s",zFlag); | |
| 1253 | + p->zCommentMimetype = fossil_strdup(zFlag); | |
| 1254 | 1254 | zFlag = 0; |
| 1255 | 1255 | } |
| 1256 | 1256 | #define p_int(K) atoi(PD(K,"0")) |
| 1257 | 1257 | if(p_int("dry_run")!=0){ |
| 1258 | 1258 | p->flags |= CIMINI_DRY_RUN; |
| @@ -1284,11 +1284,11 @@ | ||
| 1284 | 1284 | #undef p_int |
| 1285 | 1285 | /* |
| 1286 | 1286 | ** TODO?: date-override date selection field. Maybe use |
| 1287 | 1287 | ** an input[type=datetime-local]. |
| 1288 | 1288 | */ |
| 1289 | - p->zUser = mprintf("%s",g.zLogin); | |
| 1289 | + p->zUser = fossil_strdup(g.zLogin); | |
| 1290 | 1290 | return 0; |
| 1291 | 1291 | end_fail: |
| 1292 | 1292 | #undef fail |
| 1293 | 1293 | fossil_free(zFileUuid); |
| 1294 | 1294 | return rc ? rc : 500; |
| 1295 | 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 = 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 |
+9
-11
| --- src/fossil.copybutton.js | ||
| +++ src/fossil.copybutton.js | ||
| @@ -42,13 +42,11 @@ | ||
| 42 | 42 | |
| 43 | 43 | .oncopy: an optional callback function which is added as an event |
| 44 | 44 | listener for the 'text-copied' event (see below). There is |
| 45 | 45 | functionally no difference from setting this option or adding a |
| 46 | 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(). | |
| 47 | + considered to be a convenience form of that. | |
| 50 | 48 | |
| 51 | 49 | Note that this function's own defaultOptions object holds default |
| 52 | 50 | values for some options. Any changes made to that object affect |
| 53 | 51 | any future calls to this function. |
| 54 | 52 | |
| @@ -62,25 +60,21 @@ | ||
| 62 | 60 | member is an object with a "text" property holding the copied |
| 63 | 61 | text. Other properties may be added in the future. The event is |
| 64 | 62 | not fired if copying to the clipboard fails (e.g. is not |
| 65 | 63 | available in the current environment). |
| 66 | 64 | |
| 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. | |
| 72 | 67 | |
| 73 | 68 | Returns the copy-initialized element. |
| 74 | 69 | |
| 75 | 70 | Example: |
| 76 | 71 | |
| 77 | 72 | const button = fossil.copyButton('#my-copy-button', { |
| 78 | 73 | copyFromId: 'some-other-element-id' |
| 79 | 74 | }); |
| 80 | 75 | button.addEventListener('text-copied',function(ev){ |
| 81 | - fossil.dom.flashOnce(ev.target); | |
| 82 | 76 | console.debug("Copied text:",ev.detail.text); |
| 83 | 77 | }); |
| 84 | 78 | */ |
| 85 | 79 | F.copyButton = function f(e, opt){ |
| 86 | 80 | if('string'===typeof e){ |
| @@ -103,11 +97,11 @@ | ||
| 103 | 97 | e.addEventListener( |
| 104 | 98 | 'click', |
| 105 | 99 | function(ev){ |
| 106 | 100 | ev.preventDefault(); |
| 107 | 101 | ev.stopPropagation(); |
| 108 | - if(e.classList.contains('disabled')) return; | |
| 102 | + if(e.disabled) return; /* This check is probably redundant. */ | |
| 109 | 103 | const txt = extract.call(opt); |
| 110 | 104 | if(txt && D.copyTextToClipboard(txt)){ |
| 111 | 105 | e.dispatchEvent(new CustomEvent('text-copied',{ |
| 112 | 106 | detail: {text: txt} |
| 113 | 107 | })); |
| @@ -116,15 +110,19 @@ | ||
| 116 | 110 | false |
| 117 | 111 | ); |
| 118 | 112 | if('function' === typeof opt.oncopy){ |
| 119 | 113 | e.addEventListener('text-copied', opt.oncopy, false); |
| 120 | 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 | + } | |
| 121 | 119 | return e; |
| 122 | 120 | }; |
| 123 | 121 | |
| 124 | 122 | F.copyButton.defaultOptions = { |
| 125 | 123 | cssClass: 'copy-button', |
| 126 | - oncopy: D.flashOnce.eventHandler, | |
| 124 | + oncopy: undefined, | |
| 127 | 125 | style: {/*properties copied as-is into element.style*/} |
| 128 | 126 | }; |
| 129 | 127 | |
| 130 | 128 | })(window.fossil); |
| 131 | 129 |
| --- 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 |
+4
-8
| --- src/fossil.numbered-lines.js | ||
| +++ src/fossil.numbered-lines.js | ||
| @@ -23,13 +23,10 @@ | ||
| 23 | 23 | .replace(/&?\budc=[^&]*/,'') /* "update display prefs cookie" */ |
| 24 | 24 | .replace(/&?\bln=[^&]*/,'') /* inbound line number/range */ |
| 25 | 25 | .replace('?&','?'); |
| 26 | 26 | const lineState = { urlArgs: urlArgsRaw, start: 0, end: 0 }; |
| 27 | 27 | const lineTip = new F.PopupWidget({ |
| 28 | - style: { | |
| 29 | - cursor: 'pointer' | |
| 30 | - }, | |
| 31 | 28 | refresh: function(){ |
| 32 | 29 | const link = this.state.link; |
| 33 | 30 | D.clearElement(link); |
| 34 | 31 | if(lineState.start){ |
| 35 | 32 | const ls = [lineState.start]; |
| @@ -48,23 +45,22 @@ | ||
| 48 | 45 | D.append(link, "No lines selected."); |
| 49 | 46 | } |
| 50 | 47 | }, |
| 51 | 48 | init: function(){ |
| 52 | 49 | 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'); | |
| 55 | 52 | this.state = {link}; |
| 56 | 53 | F.copyButton(btnCopy,{ |
| 57 | 54 | copyFromElement: link, |
| 58 | 55 | extractText: ()=>link.dataset.url, |
| 59 | 56 | oncopy: (ev)=>{ |
| 60 | - D.flashOnce(ev.target, undefined, ()=>lineTip.hide()); | |
| 57 | + setTimeout(()=>lineTip.hide(), 400); | |
| 61 | 58 | // arguably too snazzy: F.toast.message("Copied link to clipboard."); |
| 62 | 59 | } |
| 63 | 60 | }); |
| 64 | - this.e.addEventListener('click', ()=>btnCopy.click(), false); | |
| 65 | - D.append(this.e, btnCopy, link) | |
| 61 | + D.append(this.e, btnCopy, link); | |
| 66 | 62 | } |
| 67 | 63 | }); |
| 68 | 64 | |
| 69 | 65 | tbl.addEventListener('click', ()=>lineTip.hide(), true); |
| 70 | 66 | |
| 71 | 67 |
| --- 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 |
+2
-7
| --- src/fossil.page.chat.js | ||
| +++ src/fossil.page.chat.js | ||
| @@ -901,14 +901,13 @@ | ||
| 901 | 901 | const cpId = 'copy-to-clipboard-'+id; |
| 902 | 902 | /* ^^^ copy button element ID, needed for LABEL element |
| 903 | 903 | pairing. Recall that we destroy all child elements of |
| 904 | 904 | `content` each time we hit this block, so we can reuse |
| 905 | 905 | 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); | |
| 907 | 907 | F.copyButton(btnCp, {extractText: ()=>child._xmsgRaw}); |
| 908 | 908 | const lblCp = D.label(cpId, "Copy unformatted text"); |
| 909 | - lblCp.addEventListener('click',()=>btnCp.click(), false); | |
| 910 | 909 | D.append(content, D.append(D.addClass(D.span(), 'nobr'), btnCp, lblCp)); |
| 911 | 910 | } |
| 912 | 911 | delete e.$isToggling; |
| 913 | 912 | D.append(content, child); |
| 914 | 913 | return; |
| @@ -1161,11 +1160,11 @@ | ||
| 1161 | 1160 | if(body && !body.style.fontSize){ |
| 1162 | 1161 | /** _Attempt_ to force the iframe to inherit the message's text size |
| 1163 | 1162 | if the body has no explicit size set. On desktop systems |
| 1164 | 1163 | the size is apparently being inherited in that case, but on mobile |
| 1165 | 1164 | not. */ |
| 1166 | - body.style.fontSize = window.getComputedStyle(msgObj.e.content); | |
| 1165 | + body.style.fontSize = window.getComputedStyle(msgObj.e.content).fontSize; | |
| 1167 | 1166 | } |
| 1168 | 1167 | if('' === iframe.style.maxHeight){ |
| 1169 | 1168 | /* Resize iframe height to fit the content. Workaround: if we |
| 1170 | 1169 | adjust the iframe height while it's hidden then its height |
| 1171 | 1170 | is 0, so we must briefly unhide it. */ |
| @@ -1736,14 +1735,10 @@ | ||
| 1736 | 1735 | (k)=>Chat.e.inputX.addEventListener(k, noDragDropEvents, false) |
| 1737 | 1736 | ); |
| 1738 | 1737 | return bxs; |
| 1739 | 1738 | })()/*drag/drop/paste*/; |
| 1740 | 1739 | |
| 1741 | - const tzOffsetToString = function(off){ | |
| 1742 | - const hours = Math.round(off/60), min = Math.round(off % 30); | |
| 1743 | - return ''+(hours + (min ? '.5' : '')); | |
| 1744 | - }; | |
| 1745 | 1740 | const localTime8601 = function(d){ |
| 1746 | 1741 | return [ |
| 1747 | 1742 | d.getYear()+1900, '-', pad2(d.getMonth()+1), '-', pad2(d.getDate()), |
| 1748 | 1743 | 'T', pad2(d.getHours()),':', pad2(d.getMinutes()),':',pad2(d.getSeconds()) |
| 1749 | 1744 | ].join(''); |
| 1750 | 1745 |
| --- 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 |
+3
-4
| --- src/fossil.page.pikchrshow.js | ||
| +++ src/fossil.page.pikchrshow.js | ||
| @@ -47,11 +47,11 @@ | ||
| 47 | 47 | document.body.classList.add('pikchrshow'); |
| 48 | 48 | P.e = { /* various DOM elements we work with... */ |
| 49 | 49 | previewTarget: E('#pikchrshow-output'), |
| 50 | 50 | previewLegend: E('#pikchrshow-output-wrapper > legend'), |
| 51 | 51 | previewCopyButton: D.attr( |
| 52 | - D.addClass(D.span(),'copy-button'), | |
| 52 | + D.addClass(D.button(),'copy-button'), | |
| 53 | 53 | 'id','preview-copy-button' |
| 54 | 54 | ), |
| 55 | 55 | previewModeLabel: D.label('preview-copy-button'), |
| 56 | 56 | btnSubmit: E('#pikchr-submit-preview'), |
| 57 | 57 | btnStash: E('#pikchr-stash'), |
| @@ -119,11 +119,10 @@ | ||
| 119 | 119 | }, false); |
| 120 | 120 | |
| 121 | 121 | //////////////////////////////////////////////////////////// |
| 122 | 122 | // Setup clipboard-copy of markup/SVG... |
| 123 | 123 | F.copyButton(P.e.previewCopyButton, {copyFromElement: P.e.taPreviewText}); |
| 124 | - P.e.previewModeLabel.addEventListener('click', ()=>P.e.previewCopyButton.click(), false); | |
| 125 | 124 | |
| 126 | 125 | //////////////////////////////////////////////////////////// |
| 127 | 126 | // Set up dark mode simulator... |
| 128 | 127 | P.e.cbDarkMode.addEventListener('change', function(ev){ |
| 129 | 128 | if(ev.target.checked) D.addClass(P.e.previewTarget, 'dark-mode'); |
| @@ -348,11 +347,11 @@ | ||
| 348 | 347 | D.addClass(preTgt, 'error'); |
| 349 | 348 | this.e.previewModeLabel.innerText = "Error"; |
| 350 | 349 | return; |
| 351 | 350 | } |
| 352 | 351 | D.removeClass(preTgt, 'error'); |
| 353 | - D.removeClass(this.e.previewCopyButton, 'disabled'); | |
| 352 | + this.e.previewCopyButton.disabled = false; | |
| 354 | 353 | D.removeClass(this.e.markupAlignWrapper, 'hidden'); |
| 355 | 354 | D.enable(this.e.previewModeToggle, this.e.markupAlignRadios); |
| 356 | 355 | let label, svg; |
| 357 | 356 | switch(this.previewMode){ |
| 358 | 357 | case 0: |
| @@ -427,11 +426,11 @@ | ||
| 427 | 426 | P.renderPreview(); |
| 428 | 427 | }; |
| 429 | 428 | } |
| 430 | 429 | D.disable(fp.toDisable, this.e.previewModeToggle, this.e.markupAlignRadios); |
| 431 | 430 | D.addClass(this.e.markupAlignWrapper, 'hidden'); |
| 432 | - D.addClass(this.e.previewCopyButton, 'disabled'); | |
| 431 | + this.e.previewCopyButton.disabled = true; | |
| 433 | 432 | const content = this.e.taContent.value.trim(); |
| 434 | 433 | this.response.raw = this.response.rawSvg = undefined; |
| 435 | 434 | this.response.inputText = content; |
| 436 | 435 | const sampleScript = fp.$_sampleScript; |
| 437 | 436 | delete fp.$_sampleScript; |
| 438 | 437 |
| --- 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 @@ | ||
| 312 | 312 | if(this.e.pikOut.dataset.pikchr){ |
| 313 | 313 | this.render(this.e.pikOut.dataset.pikchr); |
| 314 | 314 | } |
| 315 | 315 | }.bind(PS)); |
| 316 | 316 | F.copyButton(PS.e.previewCopyButton, {copyFromElement: PS.e.outText}); |
| 317 | - PS.e.previewModeLabel.addEventListener('click', ()=>PS.e.previewCopyButton.click(), false); | |
| 318 | 317 | |
| 319 | 318 | PS.addMsgHandler('working',function f(ev){ |
| 320 | 319 | switch(ev.data){ |
| 321 | 320 | case 'start': /* See notes in preStartWork(). */; return; |
| 322 | 321 | case 'end': |
| 323 | 322 |
| --- 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 |
+2
-2
| --- src/fossil.page.wikiedit.js | ||
| +++ src/fossil.page.wikiedit.js | ||
| @@ -1234,13 +1234,13 @@ | ||
| 1234 | 1234 | encodeURIComponent(a.filename) |
| 1235 | 1235 | ].join(''), |
| 1236 | 1236 | "raw/"+a.src |
| 1237 | 1237 | ].forEach(function(url){ |
| 1238 | 1238 | const imgUrl = D.append(D.addClass(D.span(), 'monospace'), url); |
| 1239 | - const urlCopy = D.span(); | |
| 1239 | + const urlCopy = D.button(); | |
| 1240 | 1240 | const li = D.li(ul); |
| 1241 | - D.append(li, urlCopy, " ", imgUrl); | |
| 1241 | + D.append(li, urlCopy, imgUrl); | |
| 1242 | 1242 | F.copyButton(urlCopy, {copyFromElement: imgUrl}); |
| 1243 | 1243 | }); |
| 1244 | 1244 | }); |
| 1245 | 1245 | return this; |
| 1246 | 1246 | }; |
| 1247 | 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.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 @@ | ||
| 226 | 226 | for(i=0; i<p->nBranch; i++){ |
| 227 | 227 | if( fossil_strcmp(zBranch, p->azBranch[i])==0 ) return p->azBranch[i]; |
| 228 | 228 | } |
| 229 | 229 | p->nBranch++; |
| 230 | 230 | 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); | |
| 232 | 232 | return p->azBranch[p->nBranch-1]; |
| 233 | 233 | } |
| 234 | 234 | |
| 235 | 235 | /* |
| 236 | 236 | ** Add a new row to the graph context. Rows are added from top to bottom. |
| 237 | 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] = 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 @@ | ||
| 1 | 1 | /* As an anti-robot defense, <a> elements are initially coded with the |
| 2 | 2 | ** href= set to the honeypot, and <form> elements are initialized with |
| 3 | 3 | ** action= set to the login page. The real values for href= and action= |
| 4 | 4 | ** are held in data-href= and data-action=. The following code moves |
| 5 | 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. | |
| 6 | +** <a> and <form> elements, after CSS has been loaded, and after a delay, | |
| 7 | +** and maybe also after mouse movement is seen. | |
| 8 | 8 | ** |
| 9 | 9 | ** Before sourcing this script, create a separate <script> element |
| 10 | 10 | ** (with type='application/json' to avoid Content Security Policy issues) |
| 11 | 11 | ** containing: |
| 12 | 12 | ** |
| @@ -18,20 +18,23 @@ | ||
| 18 | 18 | ** until the first mousedown event that occurs after the timer expires. |
| 19 | 19 | */ |
| 20 | 20 | var antiRobot = 0; |
| 21 | 21 | function antiRobotGo(){ |
| 22 | 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"); | |
| 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 | + } | |
| 33 | 36 | } |
| 34 | 37 | } |
| 35 | 38 | function antiRobotDefense(){ |
| 36 | 39 | var x = document.getElementById("href-data"); |
| 37 | 40 | var jx = x.textContent || x.innerText; |
| @@ -56,8 +59,9 @@ | ||
| 56 | 59 | antiRobotGo(); |
| 57 | 60 | }, g.delay) |
| 58 | 61 | }else{ |
| 59 | 62 | antiRobot |= 1; |
| 60 | 63 | } |
| 64 | + window.addEventListener('load',antiRobotGo); | |
| 61 | 65 | antiRobotGo(); |
| 62 | 66 | } |
| 63 | 67 | antiRobotDefense(); |
| 64 | 68 |
| --- 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 @@ | ||
| 52 | 52 | ** Construct the "login" card with the client credentials. |
| 53 | 53 | ** |
| 54 | 54 | ** login LOGIN NONCE SIGNATURE |
| 55 | 55 | ** |
| 56 | 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. | |
| 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. | |
| 60 | 62 | ** |
| 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. | |
| 63 | 66 | */ |
| 64 | -static void http_build_login_card(Blob *pPayload, Blob *pLogin){ | |
| 67 | +static void http_build_login_card(Blob * const pPayload, Blob * const pLogin){ | |
| 65 | 68 | Blob nonce; /* The nonce */ |
| 66 | 69 | const char *zLogin; /* The user login name */ |
| 67 | 70 | const char *zPw; /* The user password */ |
| 68 | 71 | Blob pw; /* The nonce with user password appended */ |
| 69 | 72 | Blob sig; /* The signature field */ |
| 70 | 73 | |
| 71 | 74 | blob_zero(pLogin); |
| 72 | 75 | 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" */ | |
| 74 | 77 | } |
| 75 | 78 | if( g.url.isSsh ){ |
| 76 | - return; /* If no login card for SSH: */ | |
| 79 | + return; /* No login card for SSH: */ | |
| 77 | 80 | } |
| 78 | 81 | blob_zero(&nonce); |
| 79 | 82 | blob_zero(&pw); |
| 80 | 83 | sha1sum_blob(pPayload, &nonce); |
| 81 | 84 | blob_copy(&pw, &nonce); |
| @@ -119,32 +122,34 @@ | ||
| 119 | 122 | g.url.passwd = fossil_strdup(zPw); |
| 120 | 123 | } |
| 121 | 124 | |
| 122 | 125 | blob_append(&pw, zPw, -1); |
| 123 | 126 | 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); | |
| 125 | 128 | blob_reset(&pw); |
| 126 | 129 | blob_reset(&sig); |
| 127 | 130 | blob_reset(&nonce); |
| 128 | 131 | } |
| 129 | 132 | |
| 130 | 133 | /* |
| 131 | 134 | ** Construct an appropriate HTTP request header. Write the header |
| 132 | 135 | ** 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. | |
| 134 | 138 | */ |
| 135 | 139 | static void http_build_header( |
| 136 | 140 | Blob *pPayload, /* the payload that will be sent */ |
| 137 | 141 | Blob *pHdr, /* construct the header here */ |
| 142 | + Blob *pLogin, /* Login card header value or NULL */ | |
| 138 | 143 | const char *zAltMimetype /* Alternative mimetype */ |
| 139 | 144 | ){ |
| 140 | 145 | int nPayload = pPayload ? blob_size(pPayload) : 0; |
| 141 | 146 | |
| 142 | 147 | 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 : "/"); | |
| 146 | 151 | if( g.url.proxyAuth ){ |
| 147 | 152 | blob_appendf(pHdr, "Proxy-Authorization: %s\r\n", g.url.proxyAuth); |
| 148 | 153 | } |
| 149 | 154 | if( g.zHttpAuth && g.zHttpAuth[0] ){ |
| 150 | 155 | const char *zCredentials = g.zHttpAuth; |
| @@ -153,10 +158,16 @@ | ||
| 153 | 158 | fossil_free(zEncoded); |
| 154 | 159 | } |
| 155 | 160 | blob_appendf(pHdr, "Host: %s\r\n", g.url.hostname); |
| 156 | 161 | blob_appendf(pHdr, "User-Agent: %s\r\n", get_user_agent()); |
| 157 | 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 | + } | |
| 158 | 169 | if( nPayload ){ |
| 159 | 170 | if( zAltMimetype ){ |
| 160 | 171 | blob_appendf(pHdr, "Content-Type: %s\r\n", zAltMimetype); |
| 161 | 172 | }else if( g.fHttpTrace ){ |
| 162 | 173 | blob_appendf(pHdr, "Content-Type: application/x-fossil-debug\r\n"); |
| @@ -386,11 +397,11 @@ | ||
| 386 | 397 | ** * The test-ssh-needs-path command that shows the settings |
| 387 | 398 | ** that cache whether or not a PATH= is needed for a particular |
| 388 | 399 | ** HOSTNAME. |
| 389 | 400 | */ |
| 390 | 401 | void ssh_add_path_argument(Blob *pCmd){ |
| 391 | - blob_append_escaped_arg(pCmd, | |
| 402 | + blob_append_escaped_arg(pCmd, | |
| 392 | 403 | "PATH=$HOME/bin:/usr/local/bin:/opt/homebrew/bin:$PATH", 1); |
| 393 | 404 | } |
| 394 | 405 | |
| 395 | 406 | /* |
| 396 | 407 | ** Return the complete text of the last HTTP reply as saved in the |
| @@ -457,28 +468,46 @@ | ||
| 457 | 468 | |
| 458 | 469 | if( transport_open(&g.url) ){ |
| 459 | 470 | fossil_warning("%s", transport_errmsg(&g.url)); |
| 460 | 471 | return 1; |
| 461 | 472 | } |
| 462 | - | |
| 463 | 473 | /* Construct the login card and prepare the complete payload */ |
| 474 | + blob_zero(&login); | |
| 464 | 475 | if( blob_size(pSend)==0 ){ |
| 465 | 476 | blob_zero(&payload); |
| 466 | 477 | }else{ |
| 467 | - blob_zero(&login); | |
| 468 | 478 | 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 | + } | |
| 472 | 491 | }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 | + } | |
| 475 | 504 | } |
| 476 | 505 | } |
| 477 | 506 | |
| 478 | 507 | /* Construct the HTTP request header */ |
| 479 | - http_build_header(&payload, &hdr, zAltMimetype); | |
| 508 | + http_build_header(&payload, &hdr, &login, zAltMimetype); | |
| 480 | 509 | |
| 481 | 510 | /* When tracing, write the transmitted HTTP message both to standard |
| 482 | 511 | ** output and into a file. The file can then be used to drive the |
| 483 | 512 | ** server-side like this: |
| 484 | 513 | ** |
| 485 | 514 |
| --- 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 |
+1
-1
| --- src/http_socket.c | ||
| +++ src/http_socket.c | ||
| @@ -175,11 +175,11 @@ | ||
| 175 | 175 | 0, 0, NI_NUMERICHOST); |
| 176 | 176 | if( rc ){ |
| 177 | 177 | socket_set_errmsg("getnameinfo() failed: %s", gai_strerror(rc)); |
| 178 | 178 | goto end_socket_open; |
| 179 | 179 | } |
| 180 | - g.zIpAddr = mprintf("%s", zRemote); | |
| 180 | + g.zIpAddr = fossil_strdup(zRemote); | |
| 181 | 181 | break; |
| 182 | 182 | } |
| 183 | 183 | if( p==0 ){ |
| 184 | 184 | socket_set_errmsg("cannot connect to host %s:%d", pUrlData->name, |
| 185 | 185 | pUrlData->port); |
| 186 | 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 = 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 @@ | ||
| 638 | 638 | ** check here for the correct LIBRESSL_VERSION_NUMBER too. For now: disable |
| 639 | 639 | */ |
| 640 | 640 | #if defined(OPENSSL_VERSION_NUMBER) && OPENSSL_VERSION_NUMBER >= 0x10100000L \ |
| 641 | 641 | && !defined(LIBRESSL_VERSION_NUMBER) |
| 642 | 642 | char *ip = BIO_ADDR_hostname_string(BIO_get_conn_address(iBio),1); |
| 643 | - g.zIpAddr = mprintf("%s", ip); | |
| 643 | + g.zIpAddr = fossil_strdup(ip); | |
| 644 | 644 | OPENSSL_free(ip); |
| 645 | 645 | #else |
| 646 | 646 | /* IPv4 only code */ |
| 647 | 647 | const unsigned char *ip; |
| 648 | 648 | ip = (const unsigned char*)BIO_ptr_ctrl(iBio,BIO_C_GET_CONNECT,2); |
| 649 | 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 = 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 |
+3
-3
| --- src/http_transport.c | ||
| +++ src/http_transport.c | ||
| @@ -121,11 +121,11 @@ | ||
| 121 | 121 | */ |
| 122 | 122 | Blob zCmd; /* The SSH command */ |
| 123 | 123 | char *zHost; /* The host name to contact */ |
| 124 | 124 | |
| 125 | 125 | fossil_free(g.zIpAddr); |
| 126 | - g.zIpAddr = mprintf("%s", pUrlData->name); | |
| 126 | + g.zIpAddr = fossil_strdup(pUrlData->name); | |
| 127 | 127 | transport_ssh_command(&zCmd); |
| 128 | 128 | if( pUrlData->port!=pUrlData->dfltPort && pUrlData->port ){ |
| 129 | 129 | blob_appendf(&zCmd, " -p %d", pUrlData->port); |
| 130 | 130 | } |
| 131 | 131 | blob_appendf(&zCmd, " -T --"); /* End of switches */ |
| @@ -141,11 +141,11 @@ | ||
| 141 | 141 | ){ |
| 142 | 142 | fossil_fatal("the ssh:// URL is asking to run an unsafe command [%s] on " |
| 143 | 143 | "the server.", pUrlData->fossil); |
| 144 | 144 | } |
| 145 | 145 | if( (pUrlData->flags & URL_SSH_EXE)==0 |
| 146 | - && (pUrlData->flags & URL_SSH_PATH)!=0 | |
| 146 | + && (pUrlData->flags & URL_SSH_PATH)!=0 | |
| 147 | 147 | ){ |
| 148 | 148 | ssh_add_path_argument(&zCmd); |
| 149 | 149 | } |
| 150 | 150 | blob_append_escaped_arg(&zCmd, pUrlData->fossil, 1); |
| 151 | 151 | blob_append(&zCmd, " test-http", 10); |
| @@ -245,11 +245,11 @@ | ||
| 245 | 245 | } |
| 246 | 246 | |
| 247 | 247 | /* |
| 248 | 248 | ** Send content over the wire. |
| 249 | 249 | */ |
| 250 | -void transport_send(UrlData *pUrlData, Blob *toSend){ | |
| 250 | +void transport_send(UrlData const *pUrlData, const Blob *toSend){ | |
| 251 | 251 | char *z = blob_buffer(toSend); |
| 252 | 252 | int n = blob_size(toSend); |
| 253 | 253 | transport.nSent += n; |
| 254 | 254 | if( pUrlData->isSsh ){ |
| 255 | 255 | fwrite(z, 1, n, sshOut); |
| 256 | 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 = 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 @@ | ||
| 895 | 895 | char *sep; |
| 896 | 896 | if( zLine[0]=='\n' ) break; |
| 897 | 897 | rec->nHeaders += 1; |
| 898 | 898 | rec->aHeaders = fossil_realloc(rec->aHeaders, |
| 899 | 899 | 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); | |
| 901 | 901 | sep = strchr(rec->aHeaders[rec->nHeaders-1].zKey, ':'); |
| 902 | 902 | if( !sep ){ |
| 903 | 903 | trim_newline(zLine); |
| 904 | 904 | fossil_fatal("bad header line: [%s]", zLine); |
| 905 | 905 | } |
| @@ -1426,12 +1426,12 @@ | ||
| 1426 | 1426 | fossil_free(gsvn.zDate); |
| 1427 | 1427 | bag_clear(&gsvn.newBranches); |
| 1428 | 1428 | } |
| 1429 | 1429 | /* start new revision */ |
| 1430 | 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")); | |
| 1431 | + gsvn.zUser = fossil_strdup(svn_find_prop(rec, "svn:author")); | |
| 1432 | + gsvn.zComment = fossil_strdup(svn_find_prop(rec, "svn:log")); | |
| 1433 | 1433 | zDate = svn_find_prop(rec, "svn:date"); |
| 1434 | 1434 | if( zDate ){ |
| 1435 | 1435 | gsvn.zDate = date_in_standard_format(zDate); |
| 1436 | 1436 | }else{ |
| 1437 | 1437 | gsvn.zDate = date_in_standard_format("now"); |
| 1438 | 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 = 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 @@ | ||
| 1167 | 1167 | } |
| 1168 | 1168 | render_backlink_graph(zUuid, |
| 1169 | 1169 | "<div class=\"section accordion\">References</div>\n"); |
| 1170 | 1170 | @ <div class="section accordion">Context</div><div class="accordion_panel"> |
| 1171 | 1171 | 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> | |
| 1173 | 1173 | @ <div class="accordion_panel"> |
| 1174 | 1174 | @ <div class="sectionmenu info-changes-menu"> |
| 1175 | 1175 | /* ^^^ .info-changes-menu is used by diff scroll sync */ |
| 1176 | 1176 | pCfg = construct_diff_flags(diffType, &DCfg); |
| 1177 | + DCfg.diffFlags |= DIFF_NUMSTAT; /* Show stats in the 'Changes' section */ | |
| 1177 | 1178 | DCfg.pRe = pRe; |
| 1178 | 1179 | zW = (DCfg.diffFlags&DIFF_IGNORE_ALLWS)?"&w":""; |
| 1179 | 1180 | if( diffType!=1 ){ |
| 1180 | 1181 | @ %z(chref("button","%R/%s/%T?diff=1%s",zPage,zName,zW))\ |
| 1181 | 1182 | @ Unified Diff</a> |
| @@ -1227,10 +1228,18 @@ | ||
| 1227 | 1228 | append_file_change_line(zUuid, zName, zOld, zNew, zOldName, |
| 1228 | 1229 | pCfg,mperm); |
| 1229 | 1230 | } |
| 1230 | 1231 | db_finalize(&q3); |
| 1231 | 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 | + } | |
| 1232 | 1241 | append_diff_javascript(diffType); |
| 1233 | 1242 | style_finish_page(); |
| 1234 | 1243 | } |
| 1235 | 1244 | |
| 1236 | 1245 | /* |
| @@ -1414,10 +1423,11 @@ | ||
| 1414 | 1423 | Blob qpGlob; /* glob= query parameter for generated links */ |
| 1415 | 1424 | int bInvert = PB("inv"); |
| 1416 | 1425 | |
| 1417 | 1426 | login_check_credentials(); |
| 1418 | 1427 | if( !g.perm.Read ){ login_needed(g.anon.Read); return; } |
| 1428 | + if( robot_restrict("diff") ) return; | |
| 1419 | 1429 | login_anonymous_available(); |
| 1420 | 1430 | fossil_nice_default(); |
| 1421 | 1431 | blob_init(&qp, 0, 0); |
| 1422 | 1432 | blob_init(&qpGlob, 0, 0); |
| 1423 | 1433 | diffType = preferred_diff_type(); |
| @@ -1918,18 +1928,31 @@ | ||
| 1918 | 1928 | ** * The "preferred-diff-type" setting |
| 1919 | 1929 | ** * 1 for mobile and 2 for desktop, based on the UserAgent |
| 1920 | 1930 | */ |
| 1921 | 1931 | int preferred_diff_type(void){ |
| 1922 | 1932 | int dflt; |
| 1933 | + int res; | |
| 1934 | + int isBot; | |
| 1923 | 1935 | static char zDflt[2] |
| 1924 | 1936 | /*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 | + } | |
| 1927 | 1945 | zDflt[0] = dflt + '0'; |
| 1928 | 1946 | zDflt[1] = 0; |
| 1929 | 1947 | 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; | |
| 1931 | 1954 | } |
| 1932 | 1955 | |
| 1933 | 1956 | |
| 1934 | 1957 | /* |
| 1935 | 1958 | ** WEBPAGE: fdiff |
| @@ -1967,10 +1990,11 @@ | ||
| 1967 | 1990 | int verbose = PB("verbose"); |
| 1968 | 1991 | DiffConfig DCfg; |
| 1969 | 1992 | |
| 1970 | 1993 | login_check_credentials(); |
| 1971 | 1994 | if( !g.perm.Read ){ login_needed(g.anon.Read); return; } |
| 1995 | + if( robot_restrict("diff") ) return; | |
| 1972 | 1996 | diff_config_init(&DCfg, 0); |
| 1973 | 1997 | diffType = preferred_diff_type(); |
| 1974 | 1998 | if( P("from") && P("to") ){ |
| 1975 | 1999 | v1 = artifact_from_ci_and_filename("from"); |
| 1976 | 2000 | v2 = artifact_from_ci_and_filename("to"); |
| @@ -2407,15 +2431,15 @@ | ||
| 2407 | 2431 | object_description(rid, objdescFlags, 0, &downloadName); |
| 2408 | 2432 | style_submenu_element("Download", "%R/raw/%s?at=%T", |
| 2409 | 2433 | zUuid, file_tail(blob_str(&downloadName))); |
| 2410 | 2434 | @ <hr> |
| 2411 | 2435 | content_get(rid, &content); |
| 2412 | - if( !g.isHuman ){ | |
| 2436 | + if( blob_size(&content)>100000 ){ | |
| 2413 | 2437 | /* Prevent robots from running hexdump on megabyte-sized source files |
| 2414 | 2438 | ** and there by eating up lots of CPU time and bandwidth. There is |
| 2415 | 2439 | ** 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. | |
| 2417 | 2441 | @ Please download the raw binary file and generate a hex dump yourself.</p> |
| 2418 | 2442 | }else{ |
| 2419 | 2443 | @ <blockquote><pre> |
| 2420 | 2444 | hexdump(&content); |
| 2421 | 2445 | @ </pre></blockquote> |
| @@ -2886,11 +2910,11 @@ | ||
| 2886 | 2910 | style_set_current_page("doc/%t/%T", zCI, zName); |
| 2887 | 2911 | }else if( zCIUuid && zCIUuid[0] ){ |
| 2888 | 2912 | zHeader = mprintf("%s at [%S]", file_tail(zName), zCIUuid); |
| 2889 | 2913 | style_set_current_page("doc/%S/%T", zCIUuid, zName); |
| 2890 | 2914 | }else{ |
| 2891 | - zHeader = mprintf("%s", file_tail(zName)); | |
| 2915 | + zHeader = fossil_strdup(file_tail(zName)); | |
| 2892 | 2916 | style_set_current_page("doc/tip/%T", zName); |
| 2893 | 2917 | } |
| 2894 | 2918 | }else if( descOnly ){ |
| 2895 | 2919 | zHeader = mprintf("Artifact Description [%S]", zUuid); |
| 2896 | 2920 | }else{ |
| @@ -4240,11 +4264,11 @@ | ||
| 4240 | 4264 | descr->isDirty = -1; |
| 4241 | 4265 | return (rid-1); |
| 4242 | 4266 | } |
| 4243 | 4267 | |
| 4244 | 4268 | zUuid = rid_to_uuid(rid); |
| 4245 | - descr->zCommitHash = mprintf("%s", zUuid); | |
| 4269 | + descr->zCommitHash = fossil_strdup(zUuid); | |
| 4246 | 4270 | descr->isDirty = unsaved_changes(0); |
| 4247 | 4271 | |
| 4248 | 4272 | db_multi_exec( |
| 4249 | 4273 | "DROP TABLE IF EXISTS temp.singletonTag;" |
| 4250 | 4274 | "CREATE TEMP TABLE singletonTag(" |
| @@ -4289,11 +4313,11 @@ | ||
| 4289 | 4313 | rid, rid |
| 4290 | 4314 | ); |
| 4291 | 4315 | |
| 4292 | 4316 | if( db_step(&q)==SQLITE_ROW ){ |
| 4293 | 4317 | const char *lastTag = db_column_text(&q, 0); |
| 4294 | - descr->zRelTagname = mprintf("%s", lastTag); | |
| 4318 | + descr->zRelTagname = fossil_strdup(lastTag); | |
| 4295 | 4319 | descr->nCommitsSince = db_column_int(&q, 1); |
| 4296 | 4320 | nRet = 0; |
| 4297 | 4321 | }else{ |
| 4298 | 4322 | /* no ancestor commit with a fitting singleton tag found */ |
| 4299 | 4323 | descr->zRelTagname = mprintf(""); |
| 4300 | 4324 |
| --- 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 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 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 @@ | ||
| 1643 | 1643 | int json_set_err( int code, char const * fmt, ... ){ |
| 1644 | 1644 | assert( (code>=1000) && (code<=9999) ); |
| 1645 | 1645 | fossil_free(g.zErrMsg); |
| 1646 | 1646 | g.json.resultCode = code; |
| 1647 | 1647 | if(!fmt || !*fmt){ |
| 1648 | - g.zErrMsg = mprintf("%s", json_err_cstr(code)); | |
| 1648 | + g.zErrMsg = fossil_strdup(json_err_cstr(code)); | |
| 1649 | 1649 | }else{ |
| 1650 | 1650 | va_list vargs; |
| 1651 | 1651 | char * msg; |
| 1652 | 1652 | va_start(vargs,fmt); |
| 1653 | 1653 | msg = vmprintf(fmt, vargs); |
| @@ -1961,11 +1961,10 @@ | ||
| 1961 | 1961 | |
| 1962 | 1962 | #define ADD(X,K) cson_object_set(obj, K, cson_value_new_bool(g.perm.X)) |
| 1963 | 1963 | ADD(Setup,"setup"); |
| 1964 | 1964 | ADD(Admin,"admin"); |
| 1965 | 1965 | ADD(Password,"password"); |
| 1966 | - ADD(Query,"query"); /* don't think this one is actually used */ | |
| 1967 | 1966 | ADD(Write,"checkin"); |
| 1968 | 1967 | ADD(Read,"checkout"); |
| 1969 | 1968 | ADD(Hyperlink,"history"); |
| 1970 | 1969 | ADD(Clone,"clone"); |
| 1971 | 1970 | ADD(RdWiki,"readWiki"); |
| 1972 | 1971 |
| --- 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 |
+1
-1
| --- src/json_report.c | ||
| +++ src/json_report.c | ||
| @@ -202,11 +202,11 @@ | ||
| 202 | 202 | limit = json_find_option_int("limit",NULL,"n",-1); |
| 203 | 203 | |
| 204 | 204 | |
| 205 | 205 | /* Copy over report's SQL...*/ |
| 206 | 206 | 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)); | |
| 208 | 208 | db_finalize(&q); |
| 209 | 209 | db_prepare(&q, "%s", blob_sql_text(&sql)); |
| 210 | 210 | |
| 211 | 211 | /** Build the response... */ |
| 212 | 212 | pay = cson_new_object(); |
| 213 | 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 = 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 @@ | ||
| 160 | 160 | |
| 161 | 161 | if( zUsername==0 ) return 0; |
| 162 | 162 | else if( zPassword==0 ) return 0; |
| 163 | 163 | else if( zCS==0 ) return 0; |
| 164 | 164 | else if( fossil_strcmp(zUsername,"anonymous")!=0 ) return 0; |
| 165 | + else if( anon_cookie_lifespan()==0 ) return 0; | |
| 165 | 166 | while( 1/*exit-by-break*/ ){ |
| 166 | 167 | zPw = captcha_decode((unsigned int)atoi(zCS), n); |
| 167 | 168 | if( zPw==0 ) return 0; |
| 168 | 169 | if( fossil_stricmp(zPw, zPassword)==0 ) break; |
| 169 | 170 | n++; |
| @@ -338,33 +339,62 @@ | ||
| 338 | 339 | *zDest = zCookie; |
| 339 | 340 | }else{ |
| 340 | 341 | free(zCookie); |
| 341 | 342 | } |
| 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 | +} | |
| 343 | 368 | |
| 344 | 369 | /* Sets a cookie for an anonymous user login, which looks like this: |
| 345 | 370 | ** |
| 346 | 371 | ** HASH/TIME/anonymous |
| 347 | 372 | ** |
| 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. | |
| 349 | 375 | ** |
| 350 | 376 | ** If zCookieDest is not NULL then the generated cookie is assigned to |
| 351 | 377 | ** *zCookieDest and the caller must eventually free() it. |
| 352 | 378 | ** |
| 353 | 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. | |
| 354 | 382 | */ |
| 355 | 383 | void login_set_anon_cookie(char **zCookieDest, int bSessionCookie){ |
| 356 | 384 | char *zNow; /* Current time (julian day number) */ |
| 357 | 385 | char *zCookie; /* The login cookie */ |
| 386 | + const char *zUserAgent; /* The user agent */ | |
| 358 | 387 | const char *zCookieName; /* Name of the login cookie */ |
| 359 | 388 | Blob b; /* Blob used during cookie construction */ |
| 360 | - int expires = bSessionCookie ? 0 : 6*3600; | |
| 389 | + int expires = bSessionCookie ? 0 : anon_cookie_lifespan(); | |
| 361 | 390 | zCookieName = login_cookie_name(); |
| 362 | 391 | zNow = db_text("0", "SELECT julianday('now')"); |
| 363 | 392 | assert( zCookieName && zNow ); |
| 364 | 393 | 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)); | |
| 366 | 396 | sha1sum_blob(&b, &b); |
| 367 | 397 | zCookie = mprintf("%s/%s/anonymous", blob_buffer(&b), zNow); |
| 368 | 398 | blob_reset(&b); |
| 369 | 399 | cgi_set_cookie(zCookieName, zCookie, login_cookie_path(), expires); |
| 370 | 400 | if( zCookieDest ){ |
| @@ -581,17 +611,35 @@ | ||
| 581 | 611 | /* If the "Reset Password" button in the form was pressed, render |
| 582 | 612 | ** the Request Password Reset page in place of this one. */ |
| 583 | 613 | login_reqpwreset_page(); |
| 584 | 614 | return; |
| 585 | 615 | } |
| 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 | + | |
| 587 | 635 | fossil_redirect_to_https_if_needed(1); |
| 588 | 636 | sqlite3_create_function(g.db, "constant_time_cmp", 2, SQLITE_UTF8, 0, |
| 589 | 637 | constant_time_cmp_function, 0, 0); |
| 590 | 638 | zUsername = P("u"); |
| 591 | 639 | zPasswd = P("p"); |
| 592 | - anonFlag = g.zLogin==0 && PB("anon"); | |
| 640 | + | |
| 593 | 641 | /* Handle log-out requests */ |
| 594 | 642 | if( P("out") && cgi_csrf_safe(2) ){ |
| 595 | 643 | login_clear_login_data(); |
| 596 | 644 | login_redirect_to_g(); |
| 597 | 645 | return; |
| @@ -717,10 +765,11 @@ | ||
| 717 | 765 | login_redirect_to_g(); |
| 718 | 766 | } |
| 719 | 767 | } |
| 720 | 768 | style_set_current_feature("login"); |
| 721 | 769 | style_header("Login/Logout"); |
| 770 | + if( anonFlag==2 ) g.zLogin = 0; | |
| 722 | 771 | style_adunit_config(ADUNIT_OFF); |
| 723 | 772 | @ %s(zErrMsg) |
| 724 | 773 | if( zGoto && !noAnon ){ |
| 725 | 774 | char *zAbbrev = fossil_strdup(zGoto); |
| 726 | 775 | int i; |
| @@ -728,12 +777,12 @@ | ||
| 728 | 777 | zAbbrev[i] = 0; |
| 729 | 778 | if( g.zLogin ){ |
| 730 | 779 | @ <p>Use a different login with greater privilege than <b>%h(g.zLogin)</b> |
| 731 | 780 | @ to access <b>%h(zAbbrev)</b>. |
| 732 | 781 | }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> | |
| 735 | 784 | }else{ |
| 736 | 785 | @ <p>Login as a named user to access page <b>%h(zAbbrev)</b>. |
| 737 | 786 | } |
| 738 | 787 | fossil_free(zAbbrev); |
| 739 | 788 | } |
| @@ -748,26 +797,27 @@ | ||
| 748 | 797 | if( zGoto ){ |
| 749 | 798 | @ <input type="hidden" name="g" value="%h(zGoto)"> |
| 750 | 799 | } |
| 751 | 800 | if( anonFlag ){ |
| 752 | 801 | @ <input type="hidden" name="anon" value="1"> |
| 802 | + @ <input type="hidden" name="u" value="anonymous"> | |
| 753 | 803 | } |
| 754 | 804 | if( g.zLogin ){ |
| 755 | 805 | @ <p>Currently logged in as <b>%h(g.zLogin)</b>. |
| 756 | 806 | @ <input type="submit" name="out" value="Logout" autofocus></p> |
| 757 | 807 | @ </form> |
| 758 | 808 | }else{ |
| 759 | 809 | 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 ){ | |
| 761 | 811 | zAnonPw = db_text(0, "SELECT pw FROM user" |
| 762 | 812 | " WHERE login='anonymous'" |
| 763 | 813 | " AND cap!=''"); |
| 764 | 814 | }else{ |
| 765 | 815 | zAnonPw = 0; |
| 766 | 816 | } |
| 767 | 817 | @ <table class="login_out"> |
| 768 | - if( P("HTTPS")==0 ){ | |
| 818 | + if( P("HTTPS")==0 && !anonFlag ){ | |
| 769 | 819 | @ <tr><td class="form_label">Warning:</td> |
| 770 | 820 | @ <td><span class='securityWarning'> |
| 771 | 821 | @ Login information, including the password, |
| 772 | 822 | @ will be sent in the clear over an unencrypted connection. |
| 773 | 823 | if( !g.sslNotAvailable ){ |
| @@ -774,41 +824,55 @@ | ||
| 774 | 824 | @ Consider logging in at |
| 775 | 825 | @ <a href='%s(g.zHttpsURL)'>%h(g.zHttpsURL)</a> instead. |
| 776 | 826 | } |
| 777 | 827 | @ </span></td></tr> |
| 778 | 828 | } |
| 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 | + } | |
| 784 | 836 | @ <tr> |
| 785 | 837 | @ <td class="form_label" id="pswdlabel">Password:</td> |
| 786 | 838 | @ <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 ){ | |
| 789 | 846 | captcha_speakit_button(uSeed, "Speak password for \"anonymous\""); |
| 790 | 847 | } |
| 791 | 848 | @ </td> |
| 792 | 849 | @ </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) ){ | |
| 804 | 868 | @ <tr> |
| 805 | 869 | @ <td></td> |
| 806 | 870 | @ <td><input type="submit" name="self" value="Create A New Account"> |
| 807 | 871 | @ </tr> |
| 808 | 872 | } |
| 809 | - if( login_self_password_reset_available() ){ | |
| 873 | + if( !anonFlag && login_self_password_reset_available() ){ | |
| 810 | 874 | @ <tr> |
| 811 | 875 | @ <td></td> |
| 812 | 876 | @ <td><input type="submit" name="pwreset" value="Reset My Password"> |
| 813 | 877 | @ </tr> |
| 814 | 878 | } |
| @@ -817,27 +881,29 @@ | ||
| 817 | 881 | const char *zDecoded = captcha_decode(uSeed, 0); |
| 818 | 882 | int bAutoCaptcha = db_get_boolean("auto-captcha", 0); |
| 819 | 883 | char *zCaptcha = captcha_render(zDecoded); |
| 820 | 884 | |
| 821 | 885 | @ <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 | + } | |
| 824 | 890 | @ <div class="captcha"><table class="captcha"><tr><td>\ |
| 825 | 891 | @ <pre class="captcha"> |
| 826 | 892 | @ %h(zCaptcha) |
| 827 | 893 | @ </pre></td></tr></table> |
| 828 | - if( bAutoCaptcha ) { | |
| 894 | + if( bAutoCaptcha && !anonFlag ) { | |
| 829 | 895 | @ <input type="button" value="Fill out captcha" id='autofillButton' \ |
| 830 | 896 | @ data-af='%s(zDecoded)'> |
| 831 | 897 | builtin_request_js("login.js"); |
| 832 | 898 | } |
| 833 | 899 | @ </div> |
| 834 | 900 | free(zCaptcha); |
| 835 | 901 | } |
| 836 | 902 | @ </form> |
| 837 | 903 | } |
| 838 | - if( login_is_individual() ){ | |
| 904 | + if( login_is_individual() && !anonFlag ){ | |
| 839 | 905 | if( g.perm.EmailAlert && alert_enabled() ){ |
| 840 | 906 | @ <hr> |
| 841 | 907 | @ <p>Configure <a href="%R/alerts">Email Alerts</a> |
| 842 | 908 | @ for user <b>%h(g.zLogin)</b></p> |
| 843 | 909 | } |
| @@ -845,15 +911,18 @@ | ||
| 845 | 911 | @ <hr><p> |
| 846 | 912 | @ <a href="%R/timeline?ss=v&y=f&vfx&u=%t(g.zLogin)">Forum |
| 847 | 913 | @ post timeline</a> for user <b>%h(g.zLogin)</b></p> |
| 848 | 914 | } |
| 849 | 915 | } |
| 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 | + } | |
| 855 | 924 | if( login_is_individual() ){ |
| 856 | 925 | if( g.perm.Password ){ |
| 857 | 926 | char *zRPW = fossil_random_password(12); |
| 858 | 927 | @ <hr> |
| 859 | 928 | @ <p>Change Password for user <b>%h(g.zLogin)</b>:</p> |
| @@ -1262,98 +1331,10 @@ | ||
| 1262 | 1331 | } |
| 1263 | 1332 | fossil_free(zDecode); |
| 1264 | 1333 | return uid; |
| 1265 | 1334 | } |
| 1266 | 1335 | |
| 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 | 1336 | /* |
| 1356 | 1337 | ** When this routine is called, we know that the request does not |
| 1357 | 1338 | ** have a login on the present repository. This routine checks to |
| 1358 | 1339 | ** see if their login cookie might be for another member of the |
| 1359 | 1340 | ** login-group. |
| @@ -1388,11 +1369,11 @@ | ||
| 1388 | 1369 | ** |
| 1389 | 1370 | ** g.userUid Database USER.UID value. Might be -1 for "nobody" |
| 1390 | 1371 | ** g.zLogin Database USER.LOGIN value. NULL for user "nobody" |
| 1391 | 1372 | ** g.perm Permissions granted to this user |
| 1392 | 1373 | ** 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 | |
| 1394 | 1375 | ** g.perm Populated based on user account's capabilities |
| 1395 | 1376 | ** |
| 1396 | 1377 | */ |
| 1397 | 1378 | void login_check_credentials(void){ |
| 1398 | 1379 | int uid = 0; /* User id */ |
| @@ -1429,11 +1410,11 @@ | ||
| 1429 | 1410 | uid = db_int(0, "SELECT uid FROM user WHERE cap LIKE '%%s%%'"); |
| 1430 | 1411 | } |
| 1431 | 1412 | g.zLogin = db_text("?", "SELECT login FROM user WHERE uid=%d", uid); |
| 1432 | 1413 | zCap = "sxy"; |
| 1433 | 1414 | g.noPswd = 1; |
| 1434 | - g.isHuman = 1; | |
| 1415 | + g.isRobot = 0; | |
| 1435 | 1416 | zSeed = db_text("??", "SELECT uid||quote(login)||quote(pw)||quote(cookie)" |
| 1436 | 1417 | " FROM user WHERE uid=%d", uid); |
| 1437 | 1418 | login_create_csrf_secret(zSeed); |
| 1438 | 1419 | fossil_free(zSeed); |
| 1439 | 1420 | } |
| @@ -1457,33 +1438,38 @@ | ||
| 1457 | 1438 | } |
| 1458 | 1439 | } |
| 1459 | 1440 | } |
| 1460 | 1441 | if( zUser==0 ){ |
| 1461 | 1442 | /* 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. | |
| 1466 | 1451 | */ |
| 1467 | 1452 | double rTime = atof(zArg); |
| 1453 | + const char *zUserAgent = PD("HTTP_USER_AGENT","nil"); | |
| 1468 | 1454 | Blob b; |
| 1469 | 1455 | char *zSecret; |
| 1470 | 1456 | int n = 0; |
| 1471 | 1457 | |
| 1472 | 1458 | do{ |
| 1473 | 1459 | blob_zero(&b); |
| 1474 | 1460 | zSecret = captcha_secret(n++); |
| 1475 | 1461 | if( zSecret==0 ) break; |
| 1476 | - blob_appendf(&b, "%s/%s", zArg, zSecret); | |
| 1462 | + blob_appendf(&b, "%s/%s/%s", zArg, zUserAgent, zSecret); | |
| 1477 | 1463 | sha1sum_blob(&b, &b); |
| 1478 | 1464 | if( fossil_strcmp(zHash, blob_str(&b))==0 ){ |
| 1479 | 1465 | uid = db_int(0, |
| 1480 | 1466 | "SELECT uid FROM user WHERE login='anonymous'" |
| 1481 | 1467 | " AND octet_length(cap)>0" |
| 1482 | 1468 | " 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 | |
| 1485 | 1471 | ); |
| 1486 | 1472 | } |
| 1487 | 1473 | }while( uid==0 ); |
| 1488 | 1474 | blob_reset(&b); |
| 1489 | 1475 | }else{ |
| @@ -1559,12 +1545,15 @@ | ||
| 1559 | 1545 | login_create_csrf_secret("none"); |
| 1560 | 1546 | } |
| 1561 | 1547 | |
| 1562 | 1548 | login_set_uid(uid, zCap); |
| 1563 | 1549 | |
| 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 | + } | |
| 1566 | 1555 | } |
| 1567 | 1556 | |
| 1568 | 1557 | /* |
| 1569 | 1558 | ** Set the current logged in user to be uid. zCap is precomputed |
| 1570 | 1559 | ** (override) capabilities. If zCap==0, then look up the capabilities |
| @@ -1599,15 +1588,15 @@ | ||
| 1599 | 1588 | g.userUid = uid; |
| 1600 | 1589 | if( fossil_strcmp(g.zLogin,"nobody")==0 ){ |
| 1601 | 1590 | g.zLogin = 0; |
| 1602 | 1591 | } |
| 1603 | 1592 | if( PB("isrobot") ){ |
| 1604 | - g.isHuman = 0; | |
| 1593 | + g.isRobot = 1; | |
| 1605 | 1594 | }else if( g.zLogin==0 ){ |
| 1606 | - g.isHuman = isHuman(P("HTTP_USER_AGENT")); | |
| 1595 | + g.isRobot = !isHuman(P("HTTP_USER_AGENT")); | |
| 1607 | 1596 | }else{ |
| 1608 | - g.isHuman = 1; | |
| 1597 | + g.isRobot = 0; | |
| 1609 | 1598 | } |
| 1610 | 1599 | |
| 1611 | 1600 | /* Set the capabilities */ |
| 1612 | 1601 | login_replace_capabilities(zCap, 0); |
| 1613 | 1602 | |
| @@ -1617,11 +1606,11 @@ | ||
| 1617 | 1606 | ** enabled for this repository and make appropriate adjustments to the |
| 1618 | 1607 | ** permission flags if it is. This should be done before the permissions |
| 1619 | 1608 | ** are (potentially) copied to the anonymous permission set; otherwise, |
| 1620 | 1609 | ** those will be out-of-sync. |
| 1621 | 1610 | */ |
| 1622 | - if( zCap[0] && !g.perm.Hyperlink && g.isHuman ){ | |
| 1611 | + if( zCap[0] && !g.perm.Hyperlink && !g.isRobot ){ | |
| 1623 | 1612 | int autoLink = db_get_int("auto-hyperlink",1); |
| 1624 | 1613 | if( autoLink==1 ){ |
| 1625 | 1614 | g.jsHref = 1; |
| 1626 | 1615 | g.perm.Hyperlink = 1; |
| 1627 | 1616 | }else if( autoLink==2 ){ |
| @@ -1927,11 +1916,11 @@ | ||
| 1927 | 1916 | blob_appendf(&redir, "%R/login?g=%T", zPathInfo); |
| 1928 | 1917 | } |
| 1929 | 1918 | if( zQS && zQS[0] ){ |
| 1930 | 1919 | blob_appendf(&redir, "%%3f%T", zQS); |
| 1931 | 1920 | } |
| 1932 | - if( anonOk ) blob_append(&redir, "&anon", 5); | |
| 1921 | + if( anonOk ) blob_append(&redir, "&anon=1", 7); | |
| 1933 | 1922 | cgi_redirect(blob_str(&redir)); |
| 1934 | 1923 | /* NOTREACHED */ |
| 1935 | 1924 | assert(0); |
| 1936 | 1925 | } |
| 1937 | 1926 | } |
| @@ -1941,11 +1930,11 @@ | ||
| 1941 | 1930 | ** the anonymous user has Hyperlink permission, then paint a mesage |
| 1942 | 1931 | ** to inform the user that much more information is available by |
| 1943 | 1932 | ** logging in as anonymous. |
| 1944 | 1933 | */ |
| 1945 | 1934 | 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 ){ | |
| 1947 | 1936 | const char *zUrl = PD("PATH_INFO", ""); |
| 1948 | 1937 | @ <p>Many <span class="disabled">hyperlinks are disabled.</span><br> |
| 1949 | 1938 | @ Use <a href="%R/login?anon=1&g=%T(zUrl)">anonymous login</a> |
| 1950 | 1939 | @ to enable hyperlinks.</p> |
| 1951 | 1940 | } |
| 1952 | 1941 |
| --- 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&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&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 @@ | ||
| 83 | 83 | */ |
| 84 | 84 | struct FossilUserPerms { |
| 85 | 85 | char Setup; /* s: use Setup screens on web interface */ |
| 86 | 86 | char Admin; /* a: administrative permission */ |
| 87 | 87 | char Password; /* p: change password */ |
| 88 | - char Query; /* q: create new reports */ | |
| 89 | 88 | char Write; /* i: xfer inbound. check-in */ |
| 90 | 89 | char Read; /* o: xfer outbound. check-out */ |
| 91 | 90 | char Hyperlink; /* h: enable the display of hyperlinks */ |
| 92 | 91 | char Clone; /* g: clone */ |
| 93 | 92 | char RdWiki; /* j: view wiki via web */ |
| @@ -234,11 +233,12 @@ | ||
| 234 | 233 | * applicable when using SEE on Windows or Linux. */ |
| 235 | 234 | #endif |
| 236 | 235 | int useLocalauth; /* No login required if from 127.0.0.1 */ |
| 237 | 236 | int noPswd; /* Logged in without password (on 127.0.0.1) */ |
| 238 | 237 | 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 */ | |
| 240 | 240 | int comFmtFlags; /* Zero or more "COMMENT_PRINT_*" bit flags, should be |
| 241 | 241 | ** accessed through get_comment_format(). */ |
| 242 | 242 | const char *zSockName; /* Name of the unix-domain socket file */ |
| 243 | 243 | const char *zSockMode; /* File permissions for unix-domain socket */ |
| 244 | 244 | const char *zSockOwner; /* Owner, or owner:group for unix-domain socket */ |
| @@ -289,10 +289,27 @@ | ||
| 289 | 289 | int allowSymlinks; /* Cached "allow-symlinks" option */ |
| 290 | 290 | int mainTimerId; /* Set to fossil_timer_start() */ |
| 291 | 291 | int nPendingRequest; /* # of HTTP requests in "fossil server" */ |
| 292 | 292 | int nRequest; /* Total # of HTTP request */ |
| 293 | 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; | |
| 294 | 311 | #ifdef FOSSIL_ENABLE_JSON |
| 295 | 312 | struct FossilJsonBits { |
| 296 | 313 | int isJsonMode; /* True if running in JSON mode, else |
| 297 | 314 | false. This changes how errors are |
| 298 | 315 | reported. In JSON mode we try to |
| @@ -759,10 +776,17 @@ | ||
| 759 | 776 | g.tcl.argc = g.argc; |
| 760 | 777 | g.tcl.argv = copy_args(g.argc, g.argv); /* save full arguments */ |
| 761 | 778 | #endif |
| 762 | 779 | g.mainTimerId = fossil_timer_start(); |
| 763 | 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; | |
| 764 | 788 | g.zVfsName = find_option("vfs",0,1); |
| 765 | 789 | if( g.zVfsName==0 ){ |
| 766 | 790 | g.zVfsName = fossil_getenv("FOSSIL_VFS"); |
| 767 | 791 | } |
| 768 | 792 | if( g.zVfsName ){ |
| @@ -1140,11 +1164,11 @@ | ||
| 1140 | 1164 | */ |
| 1141 | 1165 | const char *find_repository_option(){ |
| 1142 | 1166 | const char *zRepository = find_option("repository", "R", 1); |
| 1143 | 1167 | if( zRepository ){ |
| 1144 | 1168 | if( g.zRepositoryOption ) fossil_free(g.zRepositoryOption); |
| 1145 | - g.zRepositoryOption = mprintf("%s", zRepository); | |
| 1169 | + g.zRepositoryOption = fossil_strdup(zRepository); | |
| 1146 | 1170 | } |
| 1147 | 1171 | return g.zRepositoryOption; |
| 1148 | 1172 | } |
| 1149 | 1173 | |
| 1150 | 1174 | /* |
| @@ -1386,20 +1410,20 @@ | ||
| 1386 | 1410 | const char *zCur; |
| 1387 | 1411 | |
| 1388 | 1412 | if( g.zBaseURL!=0 ) return; |
| 1389 | 1413 | if( zAltBase ){ |
| 1390 | 1414 | int i, n, c; |
| 1391 | - g.zTop = g.zBaseURL = mprintf("%s", zAltBase); | |
| 1415 | + g.zTop = g.zBaseURL = fossil_strdup(zAltBase); | |
| 1392 | 1416 | i = (int)strlen(g.zBaseURL); |
| 1393 | 1417 | while( i>3 && g.zBaseURL[i-1]=='/' ){ i--; } |
| 1394 | 1418 | g.zBaseURL[i] = 0; |
| 1395 | 1419 | if( strncmp(g.zTop, "http://", 7)==0 ){ |
| 1396 | 1420 | /* it is HTTP, replace prefix with HTTPS. */ |
| 1397 | 1421 | g.zHttpsURL = mprintf("https://%s", &g.zTop[7]); |
| 1398 | 1422 | }else if( strncmp(g.zTop, "https://", 8)==0 ){ |
| 1399 | 1423 | /* it is already HTTPS, use it. */ |
| 1400 | - g.zHttpsURL = mprintf("%s", g.zTop); | |
| 1424 | + g.zHttpsURL = fossil_strdup(g.zTop); | |
| 1401 | 1425 | }else{ |
| 1402 | 1426 | fossil_fatal("argument to --baseurl should be 'http://host/path'" |
| 1403 | 1427 | " or 'https://host/path'"); |
| 1404 | 1428 | } |
| 1405 | 1429 | for(i=n=0; (c = g.zTop[i])!=0; i++){ |
| @@ -1491,11 +1515,11 @@ | ||
| 1491 | 1515 | /* In order for ?skin=... to work when visiting the site from |
| 1492 | 1516 | ** a typical external link, we have to process it here, as |
| 1493 | 1517 | ** that parameter gets lost during the redirect. We "could" |
| 1494 | 1518 | ** pass the whole query string along instead, but that seems |
| 1495 | 1519 | ** unnecessary. */ |
| 1496 | - if(cgi_setup_query_string()>1){ | |
| 1520 | + if(cgi_setup_query_string() & 0x02){ | |
| 1497 | 1521 | cookie_render(); |
| 1498 | 1522 | } |
| 1499 | 1523 | cgi_redirectf("%R%s", db_get("index-page", "/index")); |
| 1500 | 1524 | } |
| 1501 | 1525 | |
| @@ -1795,22 +1819,22 @@ | ||
| 1795 | 1819 | } |
| 1796 | 1820 | |
| 1797 | 1821 | |
| 1798 | 1822 | /* Restrictions on the URI for security: |
| 1799 | 1823 | ** |
| 1800 | - ** 1. Reject characters that are not ASCII alphanumerics, | |
| 1824 | + ** 1. Reject characters that are not ASCII alphanumerics, | |
| 1801 | 1825 | ** "-", "_", ".", "/", or unicode (above ASCII). |
| 1802 | 1826 | ** In other words: No ASCII punctuation or control characters |
| 1803 | 1827 | ** 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 | |
| 1805 | 1829 | ** alphabetic character at the beginning of the name on windows. |
| 1806 | 1830 | ** 3. "-" may not occur immediately after "/" |
| 1807 | 1831 | ** 4. "." may not be adjacent to another "." or to "/" |
| 1808 | 1832 | ** |
| 1809 | 1833 | ** Any character does not satisfy these constraints a Not Found |
| 1810 | 1834 | ** error is returned. |
| 1811 | - */ | |
| 1835 | + */ | |
| 1812 | 1836 | szFile = 0; |
| 1813 | 1837 | for(j=nBase+1, k=0; zRepo[j] && k<i-1; j++, k++){ |
| 1814 | 1838 | char c = zRepo[j]; |
| 1815 | 1839 | if( c>='a' && c<='z' ) continue; |
| 1816 | 1840 | if( c>='A' && c<='Z' ) continue; |
| @@ -2072,11 +2096,11 @@ | ||
| 2072 | 2096 | cgi_redirectf("%R/ckout"); |
| 2073 | 2097 | }else{ |
| 2074 | 2098 | fossil_redirect_home() /*does not return*/; |
| 2075 | 2099 | } |
| 2076 | 2100 | }else{ |
| 2077 | - zPath = mprintf("%s", zPathInfo); | |
| 2101 | + zPath = fossil_strdup(zPathInfo); | |
| 2078 | 2102 | } |
| 2079 | 2103 | |
| 2080 | 2104 | /* Make g.zPath point to the first element of the path. Make |
| 2081 | 2105 | ** g.zExtra point to everything past that point. |
| 2082 | 2106 | */ |
| @@ -2483,21 +2507,21 @@ | ||
| 2483 | 2507 | ** If repository: is omitted, then terms of the PATH_INFO cgi parameter |
| 2484 | 2508 | ** are appended to DIRECTORY looking for a repository (whose name ends |
| 2485 | 2509 | ** in ".fossil") or a file in "files:". |
| 2486 | 2510 | */ |
| 2487 | 2511 | db_close(1); |
| 2488 | - g.zRepositoryName = mprintf("%s", blob_str(&value)); | |
| 2512 | + g.zRepositoryName = fossil_strdup(blob_str(&value)); | |
| 2489 | 2513 | blob_reset(&value); |
| 2490 | 2514 | continue; |
| 2491 | 2515 | } |
| 2492 | 2516 | if( blob_eq(&key, "notfound:") && blob_token(&line, &value) ){ |
| 2493 | 2517 | /* notfound: URL |
| 2494 | 2518 | ** |
| 2495 | 2519 | ** If using directory: and no suitable repository or file is found, |
| 2496 | 2520 | ** then redirect to URL. |
| 2497 | 2521 | */ |
| 2498 | - zNotFound = mprintf("%s", blob_str(&value)); | |
| 2522 | + zNotFound = fossil_strdup(blob_str(&value)); | |
| 2499 | 2523 | blob_reset(&value); |
| 2500 | 2524 | continue; |
| 2501 | 2525 | } |
| 2502 | 2526 | if( blob_eq(&key, "localauth") ){ |
| 2503 | 2527 | /* localauth |
| @@ -2537,12 +2561,12 @@ | ||
| 2537 | 2561 | && blob_token(&line, &value2) ){ |
| 2538 | 2562 | /* See the header comment on the redirect_web_page() function |
| 2539 | 2563 | ** above for details. */ |
| 2540 | 2564 | nRedirect++; |
| 2541 | 2565 | 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)); | |
| 2544 | 2568 | blob_reset(&value); |
| 2545 | 2569 | blob_reset(&value2); |
| 2546 | 2570 | continue; |
| 2547 | 2571 | } |
| 2548 | 2572 | if( blob_eq(&key, "files:") && blob_token(&line, &value) ){ |
| @@ -2581,20 +2605,20 @@ | ||
| 2581 | 2605 | /* errorlog: FILENAME |
| 2582 | 2606 | ** |
| 2583 | 2607 | ** Causes messages from warnings, errors, and panics to be appended |
| 2584 | 2608 | ** to FILENAME. |
| 2585 | 2609 | */ |
| 2586 | - g.zErrlog = mprintf("%s", blob_str(&value)); | |
| 2610 | + g.zErrlog = fossil_strdup(blob_str(&value)); | |
| 2587 | 2611 | blob_reset(&value); |
| 2588 | 2612 | continue; |
| 2589 | 2613 | } |
| 2590 | 2614 | if( blob_eq(&key, "extroot:") && blob_token(&line, &value) ){ |
| 2591 | 2615 | /* extroot: DIRECTORY |
| 2592 | 2616 | ** |
| 2593 | 2617 | ** Enables the /ext webpage to use sub-cgi rooted at DIRECTORY |
| 2594 | 2618 | */ |
| 2595 | - g.zExtRoot = mprintf("%s", blob_str(&value)); | |
| 2619 | + g.zExtRoot = fossil_strdup(blob_str(&value)); | |
| 2596 | 2620 | blob_reset(&value); |
| 2597 | 2621 | continue; |
| 2598 | 2622 | } |
| 2599 | 2623 | if( blob_eq(&key, "timeout:") && blob_token(&line, &value) ){ |
| 2600 | 2624 | /* timeout: SECONDS |
| @@ -2651,11 +2675,11 @@ | ||
| 2651 | 2675 | ** |
| 2652 | 2676 | ** Use the contents of FILENAME as the value of the site's |
| 2653 | 2677 | ** "mainmenu" setting, overriding the contents (for this |
| 2654 | 2678 | ** request) of the db-side setting or the hard-coded default. |
| 2655 | 2679 | */ |
| 2656 | - g.zMainMenuFile = mprintf("%s", blob_str(&value)); | |
| 2680 | + g.zMainMenuFile = fossil_strdup(blob_str(&value)); | |
| 2657 | 2681 | blob_reset(&value); |
| 2658 | 2682 | continue; |
| 2659 | 2683 | } |
| 2660 | 2684 | if( blob_eq(&key, "cgi-debug:") && blob_token(&line, &value) ){ |
| 2661 | 2685 | /* cgi-debug: FILENAME |
| @@ -2708,11 +2732,11 @@ | ||
| 2708 | 2732 | db_must_be_within_tree(); |
| 2709 | 2733 | }else{ |
| 2710 | 2734 | const char *zRepo = g.argv[arg]; |
| 2711 | 2735 | int isDir = file_isdir(zRepo, ExtFILE); |
| 2712 | 2736 | if( isDir==1 ){ |
| 2713 | - g.zRepositoryName = mprintf("%s", zRepo); | |
| 2737 | + g.zRepositoryName = fossil_strdup(zRepo); | |
| 2714 | 2738 | file_simplify_name(g.zRepositoryName, -1, 0); |
| 2715 | 2739 | }else{ |
| 2716 | 2740 | if( isDir==0 && fCreate ){ |
| 2717 | 2741 | const char *zPassword; |
| 2718 | 2742 | db_create_repository(zRepo); |
| @@ -2920,11 +2944,11 @@ | ||
| 2920 | 2944 | ** the argument being URL encoded, to avoid wildcard expansion in the |
| 2921 | 2945 | ** shell. This option is for internal use and is undocumented. |
| 2922 | 2946 | */ |
| 2923 | 2947 | zFileGlob = find_option("files-urlenc",0,1); |
| 2924 | 2948 | if( zFileGlob ){ |
| 2925 | - char *z = mprintf("%s", zFileGlob); | |
| 2949 | + char *z = fossil_strdup(zFileGlob); | |
| 2926 | 2950 | dehttpize(z); |
| 2927 | 2951 | zFileGlob = z; |
| 2928 | 2952 | }else{ |
| 2929 | 2953 | zFileGlob = find_option("files",0,1); |
| 2930 | 2954 | } |
| @@ -3330,11 +3354,11 @@ | ||
| 3330 | 3354 | g.zExtRoot = find_option("extroot",0,1); |
| 3331 | 3355 | zJsMode = find_option("jsmode",0,1); |
| 3332 | 3356 | builtin_set_js_delivery_mode(zJsMode,0); |
| 3333 | 3357 | zFileGlob = find_option("files-urlenc",0,1); |
| 3334 | 3358 | if( zFileGlob ){ |
| 3335 | - char *z = mprintf("%s", zFileGlob); | |
| 3359 | + char *z = fossil_strdup(zFileGlob); | |
| 3336 | 3360 | dehttpize(z); |
| 3337 | 3361 | zFileGlob = z; |
| 3338 | 3362 | }else{ |
| 3339 | 3363 | zFileGlob = find_option("files",0,1); |
| 3340 | 3364 | } |
| @@ -3511,11 +3535,11 @@ | ||
| 3511 | 3535 | ** "fossil ui --nobrowser" on the remote system and to set up a |
| 3512 | 3536 | ** tunnel from the local machine to the remote. */ |
| 3513 | 3537 | FILE *sshIn; |
| 3514 | 3538 | Blob ssh; |
| 3515 | 3539 | 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 */ | |
| 3517 | 3541 | char zLine[1000]; |
| 3518 | 3542 | |
| 3519 | 3543 | blob_init(&ssh, 0, 0); |
| 3520 | 3544 | for(isRetry=0; isRetry<2 && !bRunning; isRetry++){ |
| 3521 | 3545 | blob_reset(&ssh); |
| @@ -3550,11 +3574,11 @@ | ||
| 3550 | 3574 | if( fCreate ) blob_appendf(&ssh, " --create"); |
| 3551 | 3575 | blob_appendf(&ssh, " %$", g.argv[2]); |
| 3552 | 3576 | if( isRetry ){ |
| 3553 | 3577 | fossil_print("First attempt to run \"fossil\" on %s failed\n" |
| 3554 | 3578 | "Retry: ", zRemote); |
| 3555 | - } | |
| 3579 | + } | |
| 3556 | 3580 | fossil_print("%s\n", blob_str(&ssh)); |
| 3557 | 3581 | sshIn = popen(blob_str(&ssh), "r"); |
| 3558 | 3582 | if( sshIn==0 ){ |
| 3559 | 3583 | fossil_fatal("unable to %s", blob_str(&ssh)); |
| 3560 | 3584 | } |
| 3561 | 3585 |
| --- 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 @@ | ||
| 119 | 119 | $(SRCDIR)/purge.c \ |
| 120 | 120 | $(SRCDIR)/rebuild.c \ |
| 121 | 121 | $(SRCDIR)/regexp.c \ |
| 122 | 122 | $(SRCDIR)/repolist.c \ |
| 123 | 123 | $(SRCDIR)/report.c \ |
| 124 | + $(SRCDIR)/robot.c \ | |
| 124 | 125 | $(SRCDIR)/rss.c \ |
| 125 | 126 | $(SRCDIR)/schema.c \ |
| 126 | 127 | $(SRCDIR)/search.c \ |
| 127 | 128 | $(SRCDIR)/security_audit.c \ |
| 128 | 129 | $(SRCDIR)/setup.c \ |
| @@ -385,10 +386,11 @@ | ||
| 385 | 386 | $(OBJDIR)/purge_.c \ |
| 386 | 387 | $(OBJDIR)/rebuild_.c \ |
| 387 | 388 | $(OBJDIR)/regexp_.c \ |
| 388 | 389 | $(OBJDIR)/repolist_.c \ |
| 389 | 390 | $(OBJDIR)/report_.c \ |
| 391 | + $(OBJDIR)/robot_.c \ | |
| 390 | 392 | $(OBJDIR)/rss_.c \ |
| 391 | 393 | $(OBJDIR)/schema_.c \ |
| 392 | 394 | $(OBJDIR)/search_.c \ |
| 393 | 395 | $(OBJDIR)/security_audit_.c \ |
| 394 | 396 | $(OBJDIR)/setup_.c \ |
| @@ -535,10 +537,11 @@ | ||
| 535 | 537 | $(OBJDIR)/purge.o \ |
| 536 | 538 | $(OBJDIR)/rebuild.o \ |
| 537 | 539 | $(OBJDIR)/regexp.o \ |
| 538 | 540 | $(OBJDIR)/repolist.o \ |
| 539 | 541 | $(OBJDIR)/report.o \ |
| 542 | + $(OBJDIR)/robot.o \ | |
| 540 | 543 | $(OBJDIR)/rss.o \ |
| 541 | 544 | $(OBJDIR)/schema.o \ |
| 542 | 545 | $(OBJDIR)/search.o \ |
| 543 | 546 | $(OBJDIR)/security_audit.o \ |
| 544 | 547 | $(OBJDIR)/setup.o \ |
| @@ -878,10 +881,11 @@ | ||
| 878 | 881 | $(OBJDIR)/purge_.c:$(OBJDIR)/purge.h \ |
| 879 | 882 | $(OBJDIR)/rebuild_.c:$(OBJDIR)/rebuild.h \ |
| 880 | 883 | $(OBJDIR)/regexp_.c:$(OBJDIR)/regexp.h \ |
| 881 | 884 | $(OBJDIR)/repolist_.c:$(OBJDIR)/repolist.h \ |
| 882 | 885 | $(OBJDIR)/report_.c:$(OBJDIR)/report.h \ |
| 886 | + $(OBJDIR)/robot_.c:$(OBJDIR)/robot.h \ | |
| 883 | 887 | $(OBJDIR)/rss_.c:$(OBJDIR)/rss.h \ |
| 884 | 888 | $(OBJDIR)/schema_.c:$(OBJDIR)/schema.h \ |
| 885 | 889 | $(OBJDIR)/search_.c:$(OBJDIR)/search.h \ |
| 886 | 890 | $(OBJDIR)/security_audit_.c:$(OBJDIR)/security_audit.h \ |
| 887 | 891 | $(OBJDIR)/setup_.c:$(OBJDIR)/setup.h \ |
| @@ -1768,10 +1772,18 @@ | ||
| 1768 | 1772 | |
| 1769 | 1773 | $(OBJDIR)/report.o: $(OBJDIR)/report_.c $(OBJDIR)/report.h $(SRCDIR)/config.h |
| 1770 | 1774 | $(XTCC) -o $(OBJDIR)/report.o -c $(OBJDIR)/report_.c |
| 1771 | 1775 | |
| 1772 | 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 | |
| 1773 | 1785 | |
| 1774 | 1786 | $(OBJDIR)/rss_.c: $(SRCDIR)/rss.c $(OBJDIR)/translate |
| 1775 | 1787 | $(OBJDIR)/translate $(SRCDIR)/rss.c >$@ |
| 1776 | 1788 | |
| 1777 | 1789 | $(OBJDIR)/rss.o: $(OBJDIR)/rss_.c $(OBJDIR)/rss.h $(SRCDIR)/config.h |
| 1778 | 1790 |
| --- 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 @@ | ||
| 481 | 481 | const char *zTagList = db_column_text(&q, 4); |
| 482 | 482 | char *zCom; |
| 483 | 483 | if( zTagList && zTagList[0] ){ |
| 484 | 484 | zCom = mprintf("%s (%s)", db_column_text(&q, 2), zTagList); |
| 485 | 485 | }else{ |
| 486 | - zCom = mprintf("%s", db_column_text(&q,2)); | |
| 486 | + zCom = fossil_strdup(db_column_text(&q,2)); | |
| 487 | 487 | } |
| 488 | 488 | fossil_print("%-*s [%S] by %s on %s\n%*s", |
| 489 | 489 | indent-1, zLabel, |
| 490 | 490 | db_column_text(&q, 3), |
| 491 | 491 | db_column_text(&q, 1), |
| 492 | 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 = 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 @@ | ||
| 581 | 581 | nTag = strlen(zTag); |
| 582 | 582 | for(i=0; i<nTag-8 && zTag[i]!=':'; i++){} |
| 583 | 583 | if( zTag[i]==':' |
| 584 | 584 | && (fossil_isdate(&zTag[i+1]) || fossil_expand_datetime(&zTag[i+1],0,0)!=0) |
| 585 | 585 | ){ |
| 586 | - char *zDate = mprintf("%s", &zTag[i+1]); | |
| 586 | + char *zDate = fossil_strdup(&zTag[i+1]); | |
| 587 | 587 | char *zTagBase = mprintf("%.*s", i, zTag); |
| 588 | 588 | char *zXDate; |
| 589 | 589 | int nDate = strlen(zDate); |
| 590 | 590 | if( sqlite3_strnicmp(&zDate[nDate-3],"utc",3)==0 ){ |
| 591 | 591 | zDate[nDate-3] = 'z'; |
| @@ -986,11 +986,11 @@ | ||
| 986 | 986 | } |
| 987 | 987 | style_header("Ambiguous Artifact ID"); |
| 988 | 988 | @ <p>The artifact hash prefix <b>%h(zName)</b> is ambiguous and might |
| 989 | 989 | @ mean any of the following: |
| 990 | 990 | @ <ol> |
| 991 | - z = mprintf("%s", zName); | |
| 991 | + z = fossil_strdup(zName); | |
| 992 | 992 | canonical16(z, strlen(z)); |
| 993 | 993 | db_prepare(&q, "SELECT uuid, rid FROM blob WHERE uuid GLOB '%q*'", z); |
| 994 | 994 | while( db_step(&q)==SQLITE_ROW ){ |
| 995 | 995 | const char *zUuid = db_column_text(&q, 0); |
| 996 | 996 | int rid = db_column_int(&q, 1); |
| 997 | 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 = 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 |
+2
-2
| --- src/pikchrshow.c | ||
| +++ src/pikchrshow.c | ||
| @@ -462,12 +462,12 @@ | ||
| 462 | 462 | } CX("</fieldset><!-- .zone-wrapper.input -->"); |
| 463 | 463 | CX("<fieldset class='zone-wrapper output'>"); { |
| 464 | 464 | CX("<legend><div class='button-bar'>"); |
| 465 | 465 | CX("<button id='btn-render-mode'>Render Mode</button> "); |
| 466 | 466 | 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>" | |
| 469 | 469 | "<label for='preview-copy-button' " |
| 470 | 470 | "title='Tap to copy to clipboard.'></label>" |
| 471 | 471 | "</span>"); |
| 472 | 472 | CX("</div></legend>"); |
| 473 | 473 | CX("<div id='pikchr-output-wrapper'>"); |
| 474 | 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 | "<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 @@ | ||
| 247 | 247 | if( zName[0]=='/' |
| 248 | 248 | #ifdef _WIN32 |
| 249 | 249 | || sqlite3_strglob("[a-zA-Z]:/*", zName)==0 |
| 250 | 250 | #endif |
| 251 | 251 | ){ |
| 252 | - zFull = mprintf("%s", zName); | |
| 252 | + zFull = fossil_strdup(zName); | |
| 253 | 253 | }else if ( allRepo ){ |
| 254 | 254 | zFull = mprintf("/%s", zName); |
| 255 | 255 | }else{ |
| 256 | 256 | zFull = mprintf("%s/%s", g.zRepositoryName, zName); |
| 257 | 257 | } |
| 258 | 258 | x.zRepoName = zFull; |
| 259 | 259 | remote_repo_info(&x); |
| 260 | 260 | if( x.isRepolistSkin ){ |
| 261 | 261 | 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); | |
| 264 | 264 | } |
| 265 | 265 | } |
| 266 | 266 | fossil_free(zFull); |
| 267 | 267 | if( !x.isValid |
| 268 | 268 | #if USE_SEE |
| 269 | 269 | |
| 270 | 270 | 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 |
+1
| --- 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 @@ | ||
| 130 | 130 | search_end(p); |
| 131 | 131 | }else{ |
| 132 | 132 | p = fossil_malloc(sizeof(*p)); |
| 133 | 133 | memset(p, 0, sizeof(*p)); |
| 134 | 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); | |
| 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 | 139 | p->fSrchFlg = fSrchFlg; |
| 140 | 140 | blob_init(&p->snip, 0, 0); |
| 141 | 141 | while( *z && p->nTerm<SEARCH_MAX_TERM ){ |
| 142 | 142 | while( *z && !ISALNUM(*z) ){ z++; } |
| 143 | 143 | if( *z==0 ) break; |
| @@ -1076,11 +1076,11 @@ | ||
| 1076 | 1076 | ** causing errors in FTS5 searches with inputs which contain AND, OR, |
| 1077 | 1077 | ** and symbols like #. The caller is responsible for passing the |
| 1078 | 1078 | ** result to fossil_free(). |
| 1079 | 1079 | */ |
| 1080 | 1080 | char *search_simplify_pattern(const char * zPattern){ |
| 1081 | - char *zPat = mprintf("%s",zPattern); | |
| 1081 | + char *zPat = fossil_strdup(zPattern); | |
| 1082 | 1082 | int i; |
| 1083 | 1083 | for(i=0; zPat[i]; i++){ |
| 1084 | 1084 | if( (zPat[i]&0x80)==0 && !fossil_isalnum(zPat[i]) ) zPat[i] = ' '; |
| 1085 | 1085 | if( fossil_isupper(zPat[i]) ) zPat[i] = fossil_tolower(zPat[i]); |
| 1086 | 1086 | } |
| 1087 | 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 = 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 @@ | ||
| 421 | 421 | }; |
| 422 | 422 | multiple_choice_attribute( |
| 423 | 423 | "Enable hyperlinks base on User-Agent and/or Javascript", |
| 424 | 424 | "auto-hyperlink", "autohyperlink", "1", |
| 425 | 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 <a> | |
| 433 | - @ elements. In either case, <a> 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> | |
| 455 | 427 | entry_attribute("Delay in milliseconds before enabling hyperlinks", 5, |
| 456 | 428 | "auto-hyperlink-delay", "ah-delay", "50", 0); |
| 457 | 429 | @ <br> |
| 458 | 430 | onoff_attribute("Also require a mouse event before enabling hyperlinks", |
| 459 | 431 | "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 <a> | |
| 440 | + @ elements. In either case, <a> tags are not generated if the | |
| 441 | + @ UserAgent string indicates that the client is a robot. | |
| 442 | + @ (Property: "auto-hyperlink")</p> | |
| 443 | + @ | |
| 461 | 444 | @ <p>For maximum robot defense, "Delay" should be at least 50 milliseconds |
| 462 | 445 | @ and "require a mouse event" should be turned on. These values only come |
| 463 | 446 | @ 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> | |
| 465 | 449 | @ |
| 466 | 450 | @ <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 | |
| 469 | 453 | @ that the "g.jsHref" value is "1".</p> |
| 470 | - @ <p>(Properties: "auto-hyperlink", "auto-hyperlink-delay", and | |
| 471 | - @ "auto-hyperlink-mouseover"")</p> | |
| 472 | 454 | } |
| 473 | 455 | |
| 474 | 456 | /* |
| 475 | 457 | ** WEBPAGE: setup_robot |
| 476 | 458 | ** |
| @@ -494,13 +476,43 @@ | ||
| 494 | 476 | @ defend the site against robots. |
| 495 | 477 | @ |
| 496 | 478 | @ <form action="%R/setup_robot" method="post"><div> |
| 497 | 479 | login_insert_csrf_secret(); |
| 498 | 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 | + @    <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 | + | |
| 499 | 504 | @ <hr> |
| 500 | 505 | addAutoHyperlinkSettings(); |
| 501 | 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 | + | |
| 502 | 514 | @ <hr> |
| 503 | 515 | entry_attribute("Server Load Average Limit", 11, "max-loadavg", "mxldavg", |
| 504 | 516 | "0.0", 0); |
| 505 | 517 | @ <p>Some expensive operations (such as computing tarballs, zip archives, |
| 506 | 518 | @ or annotation/blame pages) are prohibited if the load average on the host |
| @@ -508,37 +520,11 @@ | ||
| 508 | 520 | @ computations here. Set this to 0.0 to disable the load average limit. |
| 509 | 521 | @ This limit is only enforced on Unix servers. On Linux systems, |
| 510 | 522 | @ access to the /proc virtual filesystem is required, which means this limit |
| 511 | 523 | @ might not work inside a chroot() jail. |
| 512 | 524 | @ (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 | + @ | |
| 540 | 526 | @ <hr> |
| 541 | 527 | @ <p><input type="submit" name="submit" value="Apply Changes"></p> |
| 542 | 528 | @ </div></form> |
| 543 | 529 | db_end_transaction(0); |
| 544 | 530 | style_finish_page(); |
| @@ -774,10 +760,17 @@ | ||
| 774 | 760 | @ "anonymous" that will automatically fill in the CAPTCHA password. |
| 775 | 761 | @ This is less secure than forcing the user to do it manually, but is |
| 776 | 762 | @ probably secure enough and it is certainly more convenient for |
| 777 | 763 | @ anonymous users. (Property: "auto-captcha")</p> |
| 778 | 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 | + | |
| 779 | 772 | @ <hr> |
| 780 | 773 | @ <p><input type="submit" name="submit" value="Apply Changes"></p> |
| 781 | 774 | @ </div></form> |
| 782 | 775 | db_end_transaction(0); |
| 783 | 776 | style_finish_page(); |
| 784 | 777 |
| --- 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 <a> |
| 433 | @ elements. In either case, <a> 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 <a> |
| 440 | @ elements. In either case, <a> 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 | @    <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 @@ | ||
| 420 | 420 | |
| 421 | 421 | SHA1Init(&ctx); |
| 422 | 422 | SHA1Update(&ctx, (unsigned const char*)zIn, strlen(zIn)); |
| 423 | 423 | SHA1Final(zResult, &ctx); |
| 424 | 424 | DigestToBase16(zResult, zDigest); |
| 425 | - return mprintf("%s", zDigest); | |
| 425 | + return fossil_strdup(zDigest); | |
| 426 | 426 | } |
| 427 | 427 | |
| 428 | 428 | /* |
| 429 | 429 | ** Convert a cleartext password for a specific user into a SHA1 hash. |
| 430 | 430 | ** |
| @@ -459,11 +459,11 @@ | ||
| 459 | 459 | |
| 460 | 460 | /* On the first xfer request of a clone, the project-code is not yet |
| 461 | 461 | ** known. Use the cleartext password, since that is all we have. |
| 462 | 462 | */ |
| 463 | 463 | if( zProjectId==0 ){ |
| 464 | - return mprintf("%s", zPw); | |
| 464 | + return fossil_strdup(zPw); | |
| 465 | 465 | } |
| 466 | 466 | } |
| 467 | 467 | zProjCode = zProjectId; |
| 468 | 468 | } |
| 469 | 469 | SHA1Update(&ctx, (unsigned char*)zProjCode, strlen(zProjCode)); |
| @@ -471,11 +471,11 @@ | ||
| 471 | 471 | SHA1Update(&ctx, (unsigned char*)zLogin, strlen(zLogin)); |
| 472 | 472 | SHA1Update(&ctx, (unsigned char*)"/", 1); |
| 473 | 473 | SHA1Update(&ctx, (unsigned const char*)zPw, strlen(zPw)); |
| 474 | 474 | SHA1Final(zResult, &ctx); |
| 475 | 475 | DigestToBase16(zResult, zDigest); |
| 476 | - return mprintf("%s", zDigest); | |
| 476 | + return fossil_strdup(zDigest); | |
| 477 | 477 | } |
| 478 | 478 | |
| 479 | 479 | /* |
| 480 | 480 | ** Implement the shared_secret() SQL function. shared_secret() takes two or |
| 481 | 481 | ** three arguments; the third argument is optional. |
| 482 | 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 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 @@ | ||
| 612 | 612 | char zDigest[132]; |
| 613 | 613 | |
| 614 | 614 | SHA3Init(&ctx, iSize); |
| 615 | 615 | SHA3Update(&ctx, (unsigned const char*)zIn, strlen(zIn)); |
| 616 | 616 | DigestToBase16(SHA3Final(&ctx), zDigest, iSize/8); |
| 617 | - return mprintf("%s", zDigest); | |
| 617 | + return fossil_strdup(zDigest); | |
| 618 | 618 | } |
| 619 | 619 | #endif |
| 620 | 620 | |
| 621 | 621 | /* |
| 622 | 622 | ** COMMAND: sha3sum* |
| 623 | 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 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 @@ | ||
| 562 | 562 | ** > fossil stash gdiff ?STASHID? ?DIFF-OPTIONS? |
| 563 | 563 | ** |
| 564 | 564 | ** Show diffs of the current working directory and what that |
| 565 | 565 | ** directory would be if STASHID were applied. With gdiff, |
| 566 | 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. | |
| 567 | 571 | */ |
| 568 | 572 | void stash_cmd(void){ |
| 569 | 573 | const char *zCmd; |
| 570 | 574 | int nCmd; |
| 571 | 575 | int stashid = 0; |
| @@ -771,11 +775,17 @@ | ||
| 771 | 775 | } |
| 772 | 776 | diff_options(&DCfg, zCmd[0]=='g', 0); |
| 773 | 777 | stashid = stash_get_id(g.argc==4 ? g.argv[3] : 0); |
| 774 | 778 | stash_diff(stashid, fBaseline, &DCfg); |
| 775 | 779 | }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 ){ | |
| 777 | 787 | g.argv[1] = "help"; |
| 778 | 788 | g.argv[2] = "stash"; |
| 779 | 789 | g.argc = 3; |
| 780 | 790 | help_cmd(); |
| 781 | 791 | }else |
| 782 | 792 |
| --- 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 @@ | ||
| 478 | 478 | /* |
| 479 | 479 | ** Output TEXT with a click-to-copy button next to it. Loads the copybtn.js |
| 480 | 480 | ** Javascript module, and generates HTML elements with the following IDs: |
| 481 | 481 | ** |
| 482 | 482 | ** TARGETID: The <span> wrapper around TEXT. |
| 483 | -** copy-TARGETID: The <span> for the copy button. | |
| 483 | +** copy-TARGETID: The <button> for the copy button. | |
| 484 | 484 | ** |
| 485 | 485 | ** If the FLIPPED argument is non-zero, the copy button is displayed after TEXT. |
| 486 | 486 | ** |
| 487 | 487 | ** The COPYLENGTH argument defines the length of the substring of TEXT copied to |
| 488 | 488 | ** clipboard: |
| @@ -510,18 +510,20 @@ | ||
| 510 | 510 | if( cchLength==1 ) cchLength = hash_digits(0); |
| 511 | 511 | else if( cchLength==2 ) cchLength = hash_digits(1); |
| 512 | 512 | if( !bFlipped ){ |
| 513 | 513 | const char *zBtnFmt = |
| 514 | 514 | "<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>" | |
| 521 | 523 | "<span id=\"%h\">" |
| 522 | - "%s" | |
| 524 | + "%s" | |
| 523 | 525 | "</span>" |
| 524 | 526 | "</span>"; |
| 525 | 527 | if( bOutputCGI ){ |
| 526 | 528 | cgi_printf( |
| 527 | 529 | zBtnFmt/*works-like:"%h%h%d%h%s"*/, |
| @@ -533,18 +535,20 @@ | ||
| 533 | 535 | } |
| 534 | 536 | }else{ |
| 535 | 537 | const char *zBtnFmt = |
| 536 | 538 | "<span class=\"nobr\">" |
| 537 | 539 | "<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>" | |
| 546 | 550 | "</span>"; |
| 547 | 551 | if( bOutputCGI ){ |
| 548 | 552 | cgi_printf( |
| 549 | 553 | zBtnFmt/*works-like:"%h%s%h%h%d"*/, |
| 550 | 554 | zTargetId,zText,zTargetId,zTargetId,cchLength); |
| @@ -1386,55 +1390,10 @@ | ||
| 1386 | 1390 | */ |
| 1387 | 1391 | void page_test_env(void){ |
| 1388 | 1392 | webpage_error(""); |
| 1389 | 1393 | } |
| 1390 | 1394 | |
| 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 | 1395 | /* |
| 1437 | 1396 | ** Webpages that encounter an error due to missing or incorrect |
| 1438 | 1397 | ** query parameters can jump to this routine to render an error |
| 1439 | 1398 | ** message screen. |
| 1440 | 1399 | ** |
| @@ -1483,11 +1442,11 @@ | ||
| 1483 | 1442 | @ g.zHttpsURL = %h(g.zHttpsURL)<br> |
| 1484 | 1443 | @ g.zTop = %h(g.zTop)<br> |
| 1485 | 1444 | @ g.zPath = %h(g.zPath)<br> |
| 1486 | 1445 | @ g.userUid = %d(g.userUid)<br> |
| 1487 | 1446 | @ g.zLogin = %h(g.zLogin)<br> |
| 1488 | - @ g.isHuman = %d(g.isHuman)<br> | |
| 1447 | + @ g.isRobot = %d(g.isRobot)<br> | |
| 1489 | 1448 | @ g.jsHref = %d(g.jsHref)<br> |
| 1490 | 1449 | if( g.zLocalRoot ){ |
| 1491 | 1450 | @ g.zLocalRoot = %h(g.zLocalRoot)<br> |
| 1492 | 1451 | }else{ |
| 1493 | 1452 | @ g.zLocalRoot = <i>none</i><br> |
| 1494 | 1453 |
| --- 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 @@ | ||
| 218 | 218 | } |
| 219 | 219 | } |
| 220 | 220 | if( zCol ){ |
| 221 | 221 | db_multi_exec("UPDATE event SET \"%w\"=%Q WHERE objid=%d", |
| 222 | 222 | 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); | |
| 225 | 225 | backlink_extract(zCopy, MT_NONE, rid, BKLNK_COMMENT, mtime, 1); |
| 226 | 226 | free(zCopy); |
| 227 | 227 | } |
| 228 | 228 | } |
| 229 | 229 | if( tagid==TAG_DATE ){ |
| 230 | 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 ){ |
| 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 @@ | ||
| 760 | 760 | Blob tarball; /* Tarball accumulated here */ |
| 761 | 761 | const char *z; |
| 762 | 762 | |
| 763 | 763 | login_check_credentials(); |
| 764 | 764 | if( !g.perm.Zip ){ login_needed(g.anon.Zip); return; } |
| 765 | + if( robot_restrict("zip") ) return; | |
| 765 | 766 | fossil_nice_default(); |
| 766 | 767 | zName = fossil_strdup(PD("name","")); |
| 767 | 768 | z = P("r"); |
| 768 | 769 | if( z==0 ) z = P("uuid"); |
| 769 | 770 | if( z==0 ) z = tar_uuid_from_name(&zName); |
| 770 | 771 |
| --- 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 @@ | ||
| 956 | 956 | ** |
| 957 | 957 | */ |
| 958 | 958 | static int string_match_command( |
| 959 | 959 | Th_Interp *interp, void *ctx, int argc, const char **argv, int *argl |
| 960 | 960 | ){ |
| 961 | - extern char *fossil_strndup(const char*,int); | |
| 961 | + extern char *fossil_strndup(const char*,ssize_t); | |
| 962 | 962 | extern void fossil_free(void*); |
| 963 | 963 | char *zPat, *zStr; |
| 964 | 964 | int rc; |
| 965 | 965 | if( argc!=4 ){ |
| 966 | 966 | return Th_WrongNumArgs(interp, "string match pattern string"); |
| 967 | 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*,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 @@ | ||
| 1432 | 1432 | int *argl |
| 1433 | 1433 | ){ |
| 1434 | 1434 | if( argc!=3 ){ |
| 1435 | 1435 | return Th_WrongNumArgs(interp, "setParameter NAME VALUE"); |
| 1436 | 1436 | } |
| 1437 | - cgi_replace_parameter(mprintf("%s", argv[1]), mprintf("%s", argv[2])); | |
| 1437 | + cgi_replace_parameter(fossil_strdup(argv[1]), fossil_strdup(argv[2])); | |
| 1438 | 1438 | return TH_OK; |
| 1439 | 1439 | } |
| 1440 | 1440 | |
| 1441 | 1441 | /* |
| 1442 | 1442 | ** TH1 command: reinitialize ?FLAGS? |
| @@ -2965,11 +2965,11 @@ | ||
| 2965 | 2965 | if( rc!=TH_OK ) break; |
| 2966 | 2966 | z += i; |
| 2967 | 2967 | if( z[0] ){ z += 6; } |
| 2968 | 2968 | i = 0; |
| 2969 | 2969 | }else{ |
| 2970 | - i++; | |
| 2970 | + i += strcspn(&z[i+1], "<$") + 1; | |
| 2971 | 2971 | } |
| 2972 | 2972 | } |
| 2973 | 2973 | if( rc==TH_ERROR ){ |
| 2974 | 2974 | zResult = (char*)Th_GetResult(g.interp, &n); |
| 2975 | 2975 | sendError(pOut,zResult, n, 1); |
| 2976 | 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(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 @@ | ||
| 1830 | 1830 | || (bisectLocal && !g.perm.Setup) |
| 1831 | 1831 | ){ |
| 1832 | 1832 | login_needed(g.anon.Read && g.anon.RdTkt && g.anon.RdWiki); |
| 1833 | 1833 | return; |
| 1834 | 1834 | } |
| 1835 | + if( zBefore && robot_restrict("timelineX") ) return; | |
| 1835 | 1836 | if( !bisectLocal ){ |
| 1836 | 1837 | etag_check(ETAG_QUERY|ETAG_COOKIE|ETAG_DATA|ETAG_CONFIG, 0); |
| 1837 | 1838 | } |
| 1838 | 1839 | cookie_read_parameter("y","y"); |
| 1839 | 1840 | zType = P("y"); |
| @@ -1969,10 +1970,11 @@ | ||
| 1969 | 1970 | } |
| 1970 | 1971 | if( showSql ) db_append_dml_to_blob(&allSql); |
| 1971 | 1972 | if( zUses!=0 ){ |
| 1972 | 1973 | int ufid = db_int(0, "SELECT rid FROM blob WHERE uuid GLOB '%q*'", zUses); |
| 1973 | 1974 | if( ufid ){ |
| 1975 | + if( robot_restrict("timelineX") ) return; | |
| 1974 | 1976 | zUses = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", ufid); |
| 1975 | 1977 | db_multi_exec("CREATE TEMP TABLE usesfile(rid INTEGER PRIMARY KEY)"); |
| 1976 | 1978 | compute_uses_file("usesfile", ufid, 0); |
| 1977 | 1979 | zType = "ci"; |
| 1978 | 1980 | disableY = 1; |
| @@ -3039,11 +3041,11 @@ | ||
| 3039 | 3041 | "brlist", "List" |
| 3040 | 3042 | }; |
| 3041 | 3043 | double rDate; |
| 3042 | 3044 | zDate = db_text(0, "SELECT min(timestamp) FROM timeline /*scan*/"); |
| 3043 | 3045 | if( (!zDate || !zDate[0]) && ( zAfter || zBefore ) ){ |
| 3044 | - zDate = mprintf("%s", (zAfter ? zAfter : zBefore)); | |
| 3046 | + zDate = fossil_strdup((zAfter ? zAfter : zBefore)); | |
| 3045 | 3047 | } |
| 3046 | 3048 | if( zDate ){ |
| 3047 | 3049 | rDate = symbolic_name_to_mtime(zDate, 0, 0); |
| 3048 | 3050 | if( db_int(0, |
| 3049 | 3051 | "SELECT EXISTS (SELECT 1 FROM event CROSS JOIN blob" |
| @@ -3055,11 +3057,11 @@ | ||
| 3055 | 3057 | } |
| 3056 | 3058 | free(zDate); |
| 3057 | 3059 | } |
| 3058 | 3060 | zDate = db_text(0, "SELECT max(timestamp) FROM timeline /*scan*/"); |
| 3059 | 3061 | if( (!zDate || !zDate[0]) && ( zAfter || zBefore ) ){ |
| 3060 | - zDate = mprintf("%s", (zBefore ? zBefore : zAfter)); | |
| 3062 | + zDate = fossil_strdup((zBefore ? zBefore : zAfter)); | |
| 3061 | 3063 | } |
| 3062 | 3064 | if( zDate ){ |
| 3063 | 3065 | rDate = symbolic_name_to_mtime(zDate, 0, 0); |
| 3064 | 3066 | if( db_int(0, |
| 3065 | 3067 | "SELECT EXISTS (SELECT 1 FROM event CROSS JOIN blob" |
| 3066 | 3068 |
| --- 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 @@ | ||
| 102 | 102 | if( strchr(zFieldName,' ')!=0 ) continue; |
| 103 | 103 | if( nField%10==0 ){ |
| 104 | 104 | aField = fossil_realloc(aField, sizeof(aField[0])*(nField+10) ); |
| 105 | 105 | } |
| 106 | 106 | aField[nField].zBsln = 0; |
| 107 | - aField[nField].zName = mprintf("%s", zFieldName); | |
| 107 | + aField[nField].zName = fossil_strdup(zFieldName); | |
| 108 | 108 | aField[nField].mUsed = USEDBY_TICKET; |
| 109 | 109 | nField++; |
| 110 | 110 | } |
| 111 | 111 | db_finalize(&q); |
| 112 | 112 | if( nBaselines ){ |
| @@ -145,11 +145,11 @@ | ||
| 145 | 145 | } |
| 146 | 146 | if( nField%10==0 ){ |
| 147 | 147 | aField = fossil_realloc(aField, sizeof(aField[0])*(nField+10) ); |
| 148 | 148 | } |
| 149 | 149 | aField[nField].zBsln = 0; |
| 150 | - aField[nField].zName = mprintf("%s", zFieldName); | |
| 150 | + aField[nField].zName = fossil_strdup(zFieldName); | |
| 151 | 151 | aField[nField].mUsed = USEDBY_TICKETCHNG; |
| 152 | 152 | nField++; |
| 153 | 153 | } |
| 154 | 154 | db_finalize(&q); |
| 155 | 155 | qsort(aField, nField, sizeof(aField[0]), nameCmpr); |
| @@ -210,11 +210,11 @@ | ||
| 210 | 210 | zVal = ""; |
| 211 | 211 | }else if( strncmp(zName, "private_", 8)==0 ){ |
| 212 | 212 | zVal = zRevealed = db_reveal(zVal); |
| 213 | 213 | } |
| 214 | 214 | if( (j = fieldId(zName))>=0 ){ |
| 215 | - aField[j].zValue = mprintf("%s", zVal); | |
| 215 | + aField[j].zValue = fossil_strdup(zVal); | |
| 216 | 216 | }else if( memcmp(zName, "tkt_", 4)==0 && Th_Fetch(zName, &size)==0 ){ |
| 217 | 217 | /* TICKET table columns that begin with "tkt_" are always safe */ |
| 218 | 218 | Th_Store(zName, zVal); |
| 219 | 219 | } |
| 220 | 220 | free(zRevealed); |
| @@ -1787,11 +1787,11 @@ | ||
| 1787 | 1787 | if( i==g.argc ){ |
| 1788 | 1788 | fossil_fatal("missing value for '%s'!",zFName); |
| 1789 | 1789 | } |
| 1790 | 1790 | zFValue = g.argv[i++]; |
| 1791 | 1791 | if( tktEncoding == tktFossilize ){ |
| 1792 | - zFValue=mprintf("%s",zFValue); | |
| 1792 | + zFValue=fossil_strdup(zFValue); | |
| 1793 | 1793 | defossilize(zFValue); |
| 1794 | 1794 | } |
| 1795 | 1795 | append = (zFName[0] == '+'); |
| 1796 | 1796 | if( append ){ |
| 1797 | 1797 | zFName++; |
| 1798 | 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 = 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 @@ | ||
| 603 | 603 | @ } |
| 604 | 604 | @ } |
| 605 | 605 | @ } |
| 606 | 606 | @ set seenRow 0 |
| 607 | 607 | @ 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, | |
| 609 | 609 | @ mimetype as xmimetype, icomment AS xcomment, |
| 610 | 610 | @ username AS xusername |
| 611 | 611 | @ FROM ticketchng |
| 612 | 612 | @ WHERE tkt_id=$tkt_id AND length(icomment)>0} { |
| 613 | 613 | @ if {$seenRow} { |
| @@ -789,11 +789,11 @@ | ||
| 789 | 789 | @ </tr> |
| 790 | 790 | @ |
| 791 | 791 | @ <th1> |
| 792 | 792 | @ set seenRow 0 |
| 793 | 793 | @ 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, | |
| 795 | 795 | @ mimetype as xmimetype, icomment AS xcomment, |
| 796 | 796 | @ username AS xusername |
| 797 | 797 | @ FROM ticketchng |
| 798 | 798 | @ WHERE tkt_id=$tkt_id AND length(icomment)>0} { |
| 799 | 799 | @ if {$seenRow} { |
| @@ -926,12 +926,12 @@ | ||
| 926 | 926 | @ WHEN status='Fixed' THEN '#cfe8bd' |
| 927 | 927 | @ WHEN status='Tested' THEN '#bde5d6' |
| 928 | 928 | @ WHEN status='Deferred' THEN '#cacae5' |
| 929 | 929 | @ ELSE '#c8c8c8' END AS 'bgcolor', |
| 930 | 930 | @ 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', | |
| 933 | 933 | @ type, |
| 934 | 934 | @ status, |
| 935 | 935 | @ subsystem, |
| 936 | 936 | @ title, |
| 937 | 937 | @ comment AS '_comments' |
| 938 | 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) 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 |
+11
| --- src/unversioned.c | ||
| +++ src/unversioned.c | ||
| @@ -147,10 +147,12 @@ | ||
| 147 | 147 | }else{ |
| 148 | 148 | db_bind_int(&ins, ":encoding", 0); |
| 149 | 149 | db_bind_blob(&ins, ":content", pContent); |
| 150 | 150 | } |
| 151 | 151 | db_step(&ins); |
| 152 | + admin_log("Wrote unversioned file \"%w\" with hash %!S", | |
| 153 | + zUVFile, blob_str(&hash)); | |
| 152 | 154 | blob_reset(&compressed); |
| 153 | 155 | blob_reset(&hash); |
| 154 | 156 | db_finalize(&ins); |
| 155 | 157 | db_unset("uv-hash", 0); |
| 156 | 158 | } |
| @@ -318,10 +320,13 @@ | ||
| 318 | 320 | const char *zError = 0; |
| 319 | 321 | const char *zIn; |
| 320 | 322 | const char *zAs; |
| 321 | 323 | Blob file; |
| 322 | 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. */ | |
| 323 | 328 | |
| 324 | 329 | zAs = find_option("as",0,1); |
| 325 | 330 | verify_all_options(); |
| 326 | 331 | if( zAs && g.argc!=4 ) usage("add DISKFILE --as UVFILE"); |
| 327 | 332 | db_begin_transaction(); |
| @@ -334,13 +339,19 @@ | ||
| 334 | 339 | zError = "be absolute"; |
| 335 | 340 | }else if ( !file_is_simple_pathname(zIn,1) ){ |
| 336 | 341 | zError = "contain complex paths"; |
| 337 | 342 | }else if( contains_whitespace(zIn) ){ |
| 338 | 343 | zError = "contain whitespace"; |
| 344 | + }else if( strlen(zIn)>500 ){ | |
| 345 | + zError = "be more than 500 bytes long"; | |
| 339 | 346 | } |
| 340 | 347 | if( zError ){ |
| 341 | 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); | |
| 342 | 353 | } |
| 343 | 354 | blob_init(&file,0,0); |
| 344 | 355 | blob_read_from_file(&file, g.argv[i], ExtFILE); |
| 345 | 356 | unversioned_write(zIn, &file, mtime); |
| 346 | 357 | blob_reset(&file); |
| 347 | 358 |
| --- 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 @@ | ||
| 227 | 227 | }else{ |
| 228 | 228 | pUrlData->port = pUrlData->dfltPort; |
| 229 | 229 | pUrlData->hostname = pUrlData->name; |
| 230 | 230 | } |
| 231 | 231 | dehttpize(pUrlData->name); |
| 232 | - pUrlData->path = mprintf("%s", &zUrl[i]); | |
| 232 | + pUrlData->path = fossil_strdup(&zUrl[i]); | |
| 233 | 233 | for(i=0; pUrlData->path[i] && pUrlData->path[i]!='?'; i++){} |
| 234 | 234 | if( pUrlData->path[i] ){ |
| 235 | 235 | pUrlData->path[i] = 0; |
| 236 | 236 | i++; |
| 237 | 237 | } |
| 238 | - zExe = mprintf(""); | |
| 238 | + zExe = fossil_strdup(""); | |
| 239 | 239 | while( pUrlData->path[i]!=0 ){ |
| 240 | 240 | char *zName, *zValue; |
| 241 | 241 | zName = &pUrlData->path[i]; |
| 242 | 242 | zValue = zName; |
| 243 | 243 | while( pUrlData->path[i] && pUrlData->path[i]!='=' ){ i++; } |
| @@ -275,11 +275,11 @@ | ||
| 275 | 275 | pUrlData->path, zExe |
| 276 | 276 | ); |
| 277 | 277 | } |
| 278 | 278 | if( pUrlData->isSsh && pUrlData->path[1] ){ |
| 279 | 279 | char *zOld = pUrlData->path; |
| 280 | - pUrlData->path = mprintf("%s", zOld+1); | |
| 280 | + pUrlData->path = fossil_strdup(zOld+1); | |
| 281 | 281 | fossil_free(zOld); |
| 282 | 282 | } |
| 283 | 283 | free(zLogin); |
| 284 | 284 | }else if( strncmp(zUrl, "file:", 5)==0 ){ |
| 285 | 285 | pUrlData->isFile = 1; |
| @@ -286,14 +286,14 @@ | ||
| 286 | 286 | if( zUrl[5]=='/' && zUrl[6]=='/' ){ |
| 287 | 287 | i = 7; |
| 288 | 288 | }else{ |
| 289 | 289 | i = 5; |
| 290 | 290 | } |
| 291 | - zFile = mprintf("%s", &zUrl[i]); | |
| 291 | + zFile = fossil_strdup(&zUrl[i]); | |
| 292 | 292 | }else if( file_isfile(zUrl, ExtFILE) ){ |
| 293 | 293 | pUrlData->isFile = 1; |
| 294 | - zFile = mprintf("%s", zUrl); | |
| 294 | + zFile = fossil_strdup(zUrl); | |
| 295 | 295 | }else if( file_isdir(zUrl, ExtFILE)==1 ){ |
| 296 | 296 | zFile = mprintf("%s/FOSSIL", zUrl); |
| 297 | 297 | if( file_isfile(zFile, ExtFILE) ){ |
| 298 | 298 | pUrlData->isFile = 1; |
| 299 | 299 | }else{ |
| 300 | 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 = 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 @@ | ||
| 109 | 109 | } |
| 110 | 110 | void freepass(){ |
| 111 | 111 | if( !zPwdBuffer ) return; |
| 112 | 112 | assert( nPwdBuffer>0 ); |
| 113 | 113 | fossil_secure_free_page(zPwdBuffer, nPwdBuffer); |
| 114 | + zPwdBuffer = 0; | |
| 115 | + nPwdBuffer = 0; | |
| 114 | 116 | } |
| 115 | 117 | #endif |
| 116 | 118 | |
| 117 | 119 | /* |
| 118 | 120 | ** Scramble substitution matrix: |
| @@ -286,13 +288,12 @@ | ||
| 286 | 288 | char *zPrompt = mprintf("\rpassword for %s: ", zUser); |
| 287 | 289 | char *zPw; |
| 288 | 290 | Blob x; |
| 289 | 291 | fossil_force_newline(); |
| 290 | 292 | 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*/; | |
| 294 | 295 | return zPw; |
| 295 | 296 | } |
| 296 | 297 | |
| 297 | 298 | /* |
| 298 | 299 | ** Prompt the user to enter a single line of text. |
| @@ -465,11 +466,11 @@ | ||
| 465 | 466 | char *zSecret = sha1_shared_secret(blob_str(&pw), g.argv[3], 0); |
| 466 | 467 | db_unprotect(PROTECT_USER); |
| 467 | 468 | db_multi_exec("UPDATE user SET pw=%Q, mtime=now() WHERE uid=%d", |
| 468 | 469 | zSecret, uid); |
| 469 | 470 | db_protect_pop(); |
| 470 | - free(zSecret); | |
| 471 | + fossil_free(zSecret); | |
| 471 | 472 | } |
| 472 | 473 | }else if( n>=2 && strncmp(g.argv[2],"capabilities",2)==0 ){ |
| 473 | 474 | int uid; |
| 474 | 475 | if( g.argc!=4 && g.argc!=5 ){ |
| 475 | 476 | usage("capabilities USERNAME ?PERMISSIONS?"); |
| @@ -521,11 +522,11 @@ | ||
| 521 | 522 | return 0; |
| 522 | 523 | } |
| 523 | 524 | uid = db_int(0, "SELECT uid FROM user WHERE login=%Q", zLogin); |
| 524 | 525 | if( uid ){ |
| 525 | 526 | g.userUid = uid; |
| 526 | - g.zLogin = mprintf("%s", zLogin); | |
| 527 | + g.zLogin = fossil_strdup(zLogin); | |
| 527 | 528 | return 1; |
| 528 | 529 | } |
| 529 | 530 | return 0; |
| 530 | 531 | } |
| 531 | 532 | |
| 532 | 533 |
| --- 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 @@ | ||
| 899 | 899 | }else{ |
| 900 | 900 | fossil_print("%s\n", zPassword); |
| 901 | 901 | } |
| 902 | 902 | fossil_free(zPassword); |
| 903 | 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 | +} | |
| 904 | 946 | |
| 905 | 947 | /* |
| 906 | 948 | ** Return the number of decimal digits in a nonnegative integer. This is useful |
| 907 | 949 | ** when formatting text. |
| 908 | 950 | */ |
| 909 | 951 |
| --- 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 @@ | ||
| 643 | 643 | if( pEntry->d_name[0]=='.' ){ |
| 644 | 644 | if( (scanFlags & SCAN_ALL)==0 ) continue; |
| 645 | 645 | if( pEntry->d_name[1]==0 ) continue; |
| 646 | 646 | if( pEntry->d_name[1]=='.' && pEntry->d_name[2]==0 ) continue; |
| 647 | 647 | } |
| 648 | - zOrigPath = mprintf("%s", blob_str(pPath)); | |
| 648 | + zOrigPath = fossil_strdup(blob_str(pPath)); | |
| 649 | 649 | zUtf8 = fossil_path_to_utf8(pEntry->d_name); |
| 650 | 650 | blob_appendf(pPath, "/%s", zUtf8); |
| 651 | 651 | zPath = blob_str(pPath); |
| 652 | 652 | if( glob_match(pIgnore1, &zPath[nPrefix+1]) || |
| 653 | 653 | glob_match(pIgnore2, &zPath[nPrefix+1]) ){ |
| @@ -657,11 +657,11 @@ | ||
| 657 | 657 | ? (file_isdir(zPath, eFType)==1) : (pEntry->d_type==DT_DIR) ){ |
| 658 | 658 | #else |
| 659 | 659 | }else if( file_isdir(zPath, eFType)==1 ){ |
| 660 | 660 | #endif |
| 661 | 661 | if( (scanFlags & SCAN_NESTED) || !vfile_top_of_checkout(zPath) ){ |
| 662 | - char *zSavePath = mprintf("%s", zPath); | |
| 662 | + char *zSavePath = fossil_strdup(zPath); | |
| 663 | 663 | int count = vfile_dir_scan(pPath, nPrefix, scanFlags, pIgnore1, |
| 664 | 664 | pIgnore2, eFType); |
| 665 | 665 | db_bind_text(&ins, ":file", &zSavePath[nPrefix+1]); |
| 666 | 666 | db_bind_int(&ins, ":count", count); |
| 667 | 667 | db_step(&ins); |
| 668 | 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 = 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 @@ | ||
| 574 | 574 | Blob wiki; |
| 575 | 575 | Manifest *pWiki = 0; |
| 576 | 576 | const char *zPageName; |
| 577 | 577 | const char *zMimetype = 0; |
| 578 | 578 | int isPopup = P("popup")!=0; |
| 579 | - char *zBody = mprintf("%s","<i>Empty Page</i>"); | |
| 579 | + char *zBody = fossil_strdup("<i>Empty Page</i>"); | |
| 580 | 580 | int noSubmenu = P("nsm")!=0 || g.isHome; |
| 581 | 581 | |
| 582 | 582 | login_check_credentials(); |
| 583 | 583 | if( !g.perm.RdWiki ){ login_needed(g.anon.RdWiki); return; } |
| 584 | 584 | zPageName = P("name"); |
| 585 | 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 = 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 @@ | ||
| 1154 | 1154 | "port number must be in the range 1 - 65535."); |
| 1155 | 1155 | } |
| 1156 | 1156 | if( !zRepository ){ |
| 1157 | 1157 | db_must_be_within_tree(); |
| 1158 | 1158 | }else if( file_isdir(zRepository, ExtFILE)==1 ){ |
| 1159 | - g.zRepositoryName = mprintf("%s", zRepository); | |
| 1159 | + g.zRepositoryName = fossil_strdup(zRepository); | |
| 1160 | 1160 | file_simplify_name(g.zRepositoryName, -1, 0); |
| 1161 | 1161 | }else{ |
| 1162 | 1162 | db_open_repository(zRepository); |
| 1163 | 1163 | } |
| 1164 | 1164 | db_close(0); |
| 1165 | 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 = 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 @@ | ||
| 827 | 827 | int rc = -1; |
| 828 | 828 | char *zLogin = blob_terminate(pLogin); |
| 829 | 829 | defossilize(zLogin); |
| 830 | 830 | |
| 831 | 831 | if( fossil_strcmp(zLogin, "nobody")==0 |
| 832 | - || fossil_strcmp(zLogin,"anonymous")==0 | |
| 832 | + || fossil_strcmp(zLogin, "anonymous")==0 | |
| 833 | 833 | ){ |
| 834 | 834 | return 0; /* Anybody is allowed to sync as "nobody" or "anonymous" */ |
| 835 | 835 | } |
| 836 | 836 | if( fossil_strcmp(P("REMOTE_USER"), zLogin)==0 |
| 837 | 837 | && db_get_boolean("remote_user_ok",0) ){ |
| @@ -866,11 +866,11 @@ | ||
| 866 | 866 | const char *zPw = db_column_text(&q, 0); |
| 867 | 867 | char *zSecret = sha1_shared_secret(zPw, blob_str(pLogin), 0); |
| 868 | 868 | blob_zero(&combined); |
| 869 | 869 | blob_copy(&combined, pNonce); |
| 870 | 870 | blob_append(&combined, zSecret, -1); |
| 871 | - free(zSecret); | |
| 871 | + fossil_free(zSecret); | |
| 872 | 872 | sha1sum_blob(&combined, &hash); |
| 873 | 873 | rc = blob_constant_time_cmp(&hash, pSig); |
| 874 | 874 | blob_reset(&hash); |
| 875 | 875 | blob_reset(&combined); |
| 876 | 876 | } |
| @@ -1241,10 +1241,25 @@ | ||
| 1241 | 1241 | /* |
| 1242 | 1242 | ** If this variable is set, disable login checks. Used for debugging |
| 1243 | 1243 | ** only. |
| 1244 | 1244 | */ |
| 1245 | 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 | +} | |
| 1246 | 1261 | |
| 1247 | 1262 | /* |
| 1248 | 1263 | ** The CGI/HTTP preprocessor always redirects requests with a content-type |
| 1249 | 1264 | ** of application/x-fossil or application/x-fossil-debug to this page, |
| 1250 | 1265 | ** regardless of what path was specified in the HTTP header. This allows |
| @@ -1273,10 +1288,11 @@ | ||
| 1273 | 1288 | int nUuidList = 0; |
| 1274 | 1289 | char **pzUuidList = 0; |
| 1275 | 1290 | int *pnUuidList = 0; |
| 1276 | 1291 | int uvCatalogSent = 0; |
| 1277 | 1292 | int bSendLinks = 0; |
| 1293 | + int nLogin = 0; | |
| 1278 | 1294 | |
| 1279 | 1295 | if( fossil_strcmp(PD("REQUEST_METHOD","POST"),"POST") ){ |
| 1280 | 1296 | fossil_redirect_home(); |
| 1281 | 1297 | } |
| 1282 | 1298 | g.zLogin = "anonymous"; |
| @@ -1314,10 +1330,24 @@ | ||
| 1314 | 1330 | } |
| 1315 | 1331 | zScript = xfer_push_code(); |
| 1316 | 1332 | if( zScript ){ /* NOTE: Are TH1 transfer hooks enabled? */ |
| 1317 | 1333 | pzUuidList = &zUuidList; |
| 1318 | 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 | + } | |
| 1319 | 1349 | } |
| 1320 | 1350 | while( blob_line(xfer.pIn, &xfer.line) ){ |
| 1321 | 1351 | if( blob_buffer(&xfer.line)[0]=='#' ) continue; |
| 1322 | 1352 | if( blob_size(&xfer.line)==0 ) continue; |
| 1323 | 1353 | xfer.nToken = blob_tokenize(&xfer.line, xfer.aToken, count(xfer.aToken)); |
| @@ -1550,17 +1580,28 @@ | ||
| 1550 | 1580 | |
| 1551 | 1581 | /* login USER NONCE SIGNATURE |
| 1552 | 1582 | ** |
| 1553 | 1583 | ** The client has sent login credentials to the server. |
| 1554 | 1584 | ** 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. | |
| 1556 | 1590 | */ |
| 1557 | 1591 | if( blob_eq(&xfer.aToken[0], "login") |
| 1558 | 1592 | && xfer.nToken==4 |
| 1559 | 1593 | ){ |
| 1594 | + handle_login_card: | |
| 1595 | + nLogin++; | |
| 1560 | 1596 | if( disableLogin ){ |
| 1561 | 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; | |
| 1562 | 1603 | }else{ |
| 1563 | 1604 | if( check_tail_hash(&xfer.aToken[2], xfer.pIn) |
| 1564 | 1605 | || check_login(&xfer.aToken[1], &xfer.aToken[2], &xfer.aToken[3]) |
| 1565 | 1606 | ){ |
| 1566 | 1607 | cgi_reset_content(); |
| @@ -1651,11 +1692,10 @@ | ||
| 1651 | 1692 | }else{ |
| 1652 | 1693 | xfer.nextIsPrivate = 1; |
| 1653 | 1694 | } |
| 1654 | 1695 | }else |
| 1655 | 1696 | |
| 1656 | - | |
| 1657 | 1697 | /* pragma NAME VALUE... |
| 1658 | 1698 | ** |
| 1659 | 1699 | ** The client issues pragmas to try to influence the behavior of the |
| 1660 | 1700 | ** server. These are requests only. Unknown pragmas are silently |
| 1661 | 1701 | ** ignored. |
| @@ -1694,17 +1734,20 @@ | ||
| 1694 | 1734 | ** The client announces to the server what version of Fossil it |
| 1695 | 1735 | ** is running. The DATE and TIME are a pure numeric ISO8601 time |
| 1696 | 1736 | ** for the specific check-in of the client. |
| 1697 | 1737 | */ |
| 1698 | 1738 | 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])); | |
| 1700 | 1741 | if( xfer.nToken>=5 ){ |
| 1701 | 1742 | xfer.remoteDate = atoi(blob_str(&xfer.aToken[3])); |
| 1702 | 1743 | xfer.remoteTime = atoi(blob_str(&xfer.aToken[4])); |
| 1703 | 1744 | @ pragma server-version %d(RELEASE_VERSION_NUMBER) \ |
| 1704 | 1745 | @ %d(MANIFEST_NUMERIC_DATE) %d(MANIFEST_NUMERIC_TIME) |
| 1705 | 1746 | } |
| 1747 | + xfer_xflc_check( xfer.remoteVersion, xfer.remoteDate, | |
| 1748 | + xfer.remoteTime, 0x04 ); | |
| 1706 | 1749 | }else |
| 1707 | 1750 | |
| 1708 | 1751 | /* pragma uv-hash HASH |
| 1709 | 1752 | ** |
| 1710 | 1753 | ** The client wants to make sure that unversioned files are all synced. |
| @@ -2341,18 +2384,17 @@ | ||
| 2341 | 2384 | blob_appendf(&send, "pragma ci-lock %s %s\n", zCkinLock, zClientId); |
| 2342 | 2385 | zCkinLock = 0; |
| 2343 | 2386 | }else if( zClientId ){ |
| 2344 | 2387 | blob_appendf(&send, "pragma ci-unlock %s\n", zClientId); |
| 2345 | 2388 | } |
| 2346 | - | |
| 2347 | 2389 | /* Append randomness to the end of the uplink message. This makes all |
| 2348 | 2390 | ** messages unique so that that the login-card nonce will always |
| 2349 | 2391 | ** be unique. |
| 2350 | 2392 | */ |
| 2351 | 2393 | zRandomness = db_text(0, "SELECT hex(randomblob(20))"); |
| 2352 | 2394 | blob_appendf(&send, "# %s\n", zRandomness); |
| 2353 | - free(zRandomness); | |
| 2395 | + fossil_free(zRandomness); | |
| 2354 | 2396 | |
| 2355 | 2397 | if( (syncFlags & SYNC_VERBOSE)!=0 |
| 2356 | 2398 | && (syncFlags & SYNC_XVERBOSE)==0 |
| 2357 | 2399 | ){ |
| 2358 | 2400 | fossil_print("waiting for server..."); |
| @@ -2725,13 +2767,10 @@ | ||
| 2725 | 2767 | |
| 2726 | 2768 | /* message MESSAGE |
| 2727 | 2769 | ** |
| 2728 | 2770 | ** A message is received from the server. Print it. |
| 2729 | 2771 | ** 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 | 2772 | */ |
| 2734 | 2773 | if( blob_eq(&xfer.aToken[0],"message") && xfer.nToken==2 ){ |
| 2735 | 2774 | char *zMsg = blob_terminate(&xfer.aToken[1]); |
| 2736 | 2775 | defossilize(zMsg); |
| 2737 | 2776 | if( (syncFlags & SYNC_PUSH) && zMsg |
| @@ -2757,15 +2796,18 @@ | ||
| 2757 | 2796 | ** The server announces to the server what version of Fossil it |
| 2758 | 2797 | ** is running. The DATE and TIME are a pure numeric ISO8601 time |
| 2759 | 2798 | ** for the specific check-in of the client. |
| 2760 | 2799 | */ |
| 2761 | 2800 | 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])); | |
| 2763 | 2803 | if( xfer.nToken>=5 ){ |
| 2764 | 2804 | xfer.remoteDate = atoi(blob_str(&xfer.aToken[3])); |
| 2765 | 2805 | xfer.remoteTime = atoi(blob_str(&xfer.aToken[4])); |
| 2766 | 2806 | } |
| 2807 | + xfer_xflc_check( xfer.remoteVersion, xfer.remoteDate, | |
| 2808 | + xfer.remoteTime, 0x08 ); | |
| 2767 | 2809 | } |
| 2768 | 2810 | |
| 2769 | 2811 | /* pragma uv-pull-only |
| 2770 | 2812 | ** pragma uv-push-ok |
| 2771 | 2813 | ** |
| @@ -2896,11 +2938,11 @@ | ||
| 2896 | 2938 | &recv |
| 2897 | 2939 | ); |
| 2898 | 2940 | nErr++; |
| 2899 | 2941 | break; |
| 2900 | 2942 | } |
| 2901 | - blob_appendf(&xfer.err, "unknown command: [%b]\n", &xfer.aToken[0]); | |
| 2943 | + blob_appendf(&xfer.err, "unknown command: [%b]\n", &xfer.line); | |
| 2902 | 2944 | } |
| 2903 | 2945 | |
| 2904 | 2946 | if( blob_size(&xfer.err) ){ |
| 2905 | 2947 | fossil_force_newline(); |
| 2906 | 2948 | fossil_warning("%b", &xfer.err); |
| @@ -2963,11 +3005,11 @@ | ||
| 2963 | 3005 | }else{ |
| 2964 | 3006 | manifest_crosslink_end(MC_PERMIT_HOOKS); |
| 2965 | 3007 | content_enable_dephantomize(1); |
| 2966 | 3008 | } |
| 2967 | 3009 | db_end_transaction(0); |
| 2968 | - }; | |
| 3010 | + }; /* while(go) */ | |
| 2969 | 3011 | transport_stats(&nSent, &nRcvd, 1); |
| 2970 | 3012 | if( pnRcvd ) *pnRcvd = nArtifactRcvd; |
| 2971 | 3013 | if( (rSkew*24.0*3600.0) > 10.0 ){ |
| 2972 | 3014 | fossil_warning("*** time skew *** server is fast by %s", |
| 2973 | 3015 | db_timespan_name(rSkew)); |
| 2974 | 3016 |
| --- 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 @@ | ||
| 496 | 496 | if( fossil_strcmp(zName, azDir[j])==0 ) break; |
| 497 | 497 | } |
| 498 | 498 | if( j>=nDir ){ |
| 499 | 499 | nDir++; |
| 500 | 500 | azDir = fossil_realloc(azDir, sizeof(azDir[0])*nDir); |
| 501 | - azDir[j] = mprintf("%s", zName); | |
| 501 | + azDir[j] = fossil_strdup(zName); | |
| 502 | 502 | zip_add_file(p, zName, 0, 0); |
| 503 | 503 | } |
| 504 | 504 | zName[i+1] = c; |
| 505 | 505 | } |
| 506 | 506 | } |
| @@ -1012,10 +1012,11 @@ | ||
| 1012 | 1012 | int eType = ARCHIVE_ZIP; /* Type of archive to generate */ |
| 1013 | 1013 | char *zType; /* Human-readable archive type */ |
| 1014 | 1014 | |
| 1015 | 1015 | login_check_credentials(); |
| 1016 | 1016 | if( !g.perm.Zip ){ login_needed(g.anon.Zip); return; } |
| 1017 | + if( robot_restrict("zip") ) return; | |
| 1017 | 1018 | if( fossil_strcmp(g.zPath, "sqlar")==0 ){ |
| 1018 | 1019 | eType = ARCHIVE_SQLAR; |
| 1019 | 1020 | zType = "SQL"; |
| 1020 | 1021 | /* For some reason, SQL-archives are like catnip for robots. So |
| 1021 | 1022 | ** don't allow them to be downloaded by user "nobody" */ |
| 1022 | 1023 |
| --- 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 |
+1
| --- tools/makemake.tcl | ||
| +++ tools/makemake.tcl | ||
| @@ -152,10 +152,11 @@ | ||
| 152 | 152 | purge |
| 153 | 153 | rebuild |
| 154 | 154 | regexp |
| 155 | 155 | repolist |
| 156 | 156 | report |
| 157 | + robot | |
| 157 | 158 | rss |
| 158 | 159 | schema |
| 159 | 160 | search |
| 160 | 161 | security_audit |
| 161 | 162 | setup |
| 162 | 163 |
| --- 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 @@ | ||
| 32 | 32 | |
| 33 | 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 | 34 | |
| 35 | 35 | PIKCHR_OPTIONS = -DPIKCHR_TOKEN_LIMIT=10000 |
| 36 | 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 | |
| 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 | 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 | |
| 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 | 40 | |
| 41 | 41 | |
| 42 | 42 | RC=$(DMDIR)\bin\rcc |
| 43 | 43 | RCFLAGS=-32 -w1 -I$(SRCDIR) /D__DMC__ |
| 44 | 44 | |
| @@ -53,11 +53,11 @@ | ||
| 53 | 53 | |
| 54 | 54 | $(OBJDIR)\fossil.res: $B\win\fossil.rc |
| 55 | 55 | $(RC) $(RCFLAGS) -o$@ $** |
| 56 | 56 | |
| 57 | 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 > $@ | |
| 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 | 59 | +echo fossil >> $@ |
| 60 | 60 | +echo fossil >> $@ |
| 61 | 61 | +echo $(LIBS) >> $@ |
| 62 | 62 | +echo. >> $@ |
| 63 | 63 | +echo fossil >> $@ |
| @@ -755,10 +755,16 @@ | ||
| 755 | 755 | $(OBJDIR)\report$O : report_.c report.h |
| 756 | 756 | $(TCC) -o$@ -c report_.c |
| 757 | 757 | |
| 758 | 758 | report_.c : $(SRCDIR)\report.c |
| 759 | 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 $** > $@ | |
| 760 | 766 | |
| 761 | 767 | $(OBJDIR)\rss$O : rss_.c rss.h |
| 762 | 768 | $(TCC) -o$@ -c rss_.c |
| 763 | 769 | |
| 764 | 770 | rss_.c : $(SRCDIR)\rss.c |
| @@ -1015,7 +1021,7 @@ | ||
| 1015 | 1021 | |
| 1016 | 1022 | zip_.c : $(SRCDIR)\zip.c |
| 1017 | 1023 | +translate$E $** > $@ |
| 1018 | 1024 | |
| 1019 | 1025 | 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 | |
| 1021 | 1027 | @copy /Y nul: headers |
| 1022 | 1028 |
| --- 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 |
+12
| --- win/Makefile.mingw | ||
| +++ win/Makefile.mingw | ||
| @@ -505,10 +505,11 @@ | ||
| 505 | 505 | $(SRCDIR)/purge.c \ |
| 506 | 506 | $(SRCDIR)/rebuild.c \ |
| 507 | 507 | $(SRCDIR)/regexp.c \ |
| 508 | 508 | $(SRCDIR)/repolist.c \ |
| 509 | 509 | $(SRCDIR)/report.c \ |
| 510 | + $(SRCDIR)/robot.c \ | |
| 510 | 511 | $(SRCDIR)/rss.c \ |
| 511 | 512 | $(SRCDIR)/schema.c \ |
| 512 | 513 | $(SRCDIR)/search.c \ |
| 513 | 514 | $(SRCDIR)/security_audit.c \ |
| 514 | 515 | $(SRCDIR)/setup.c \ |
| @@ -771,10 +772,11 @@ | ||
| 771 | 772 | $(OBJDIR)/purge_.c \ |
| 772 | 773 | $(OBJDIR)/rebuild_.c \ |
| 773 | 774 | $(OBJDIR)/regexp_.c \ |
| 774 | 775 | $(OBJDIR)/repolist_.c \ |
| 775 | 776 | $(OBJDIR)/report_.c \ |
| 777 | + $(OBJDIR)/robot_.c \ | |
| 776 | 778 | $(OBJDIR)/rss_.c \ |
| 777 | 779 | $(OBJDIR)/schema_.c \ |
| 778 | 780 | $(OBJDIR)/search_.c \ |
| 779 | 781 | $(OBJDIR)/security_audit_.c \ |
| 780 | 782 | $(OBJDIR)/setup_.c \ |
| @@ -921,10 +923,11 @@ | ||
| 921 | 923 | $(OBJDIR)/purge.o \ |
| 922 | 924 | $(OBJDIR)/rebuild.o \ |
| 923 | 925 | $(OBJDIR)/regexp.o \ |
| 924 | 926 | $(OBJDIR)/repolist.o \ |
| 925 | 927 | $(OBJDIR)/report.o \ |
| 928 | + $(OBJDIR)/robot.o \ | |
| 926 | 929 | $(OBJDIR)/rss.o \ |
| 927 | 930 | $(OBJDIR)/schema.o \ |
| 928 | 931 | $(OBJDIR)/search.o \ |
| 929 | 932 | $(OBJDIR)/security_audit.o \ |
| 930 | 933 | $(OBJDIR)/setup.o \ |
| @@ -1275,10 +1278,11 @@ | ||
| 1275 | 1278 | $(OBJDIR)/purge_.c:$(OBJDIR)/purge.h \ |
| 1276 | 1279 | $(OBJDIR)/rebuild_.c:$(OBJDIR)/rebuild.h \ |
| 1277 | 1280 | $(OBJDIR)/regexp_.c:$(OBJDIR)/regexp.h \ |
| 1278 | 1281 | $(OBJDIR)/repolist_.c:$(OBJDIR)/repolist.h \ |
| 1279 | 1282 | $(OBJDIR)/report_.c:$(OBJDIR)/report.h \ |
| 1283 | + $(OBJDIR)/robot_.c:$(OBJDIR)/robot.h \ | |
| 1280 | 1284 | $(OBJDIR)/rss_.c:$(OBJDIR)/rss.h \ |
| 1281 | 1285 | $(OBJDIR)/schema_.c:$(OBJDIR)/schema.h \ |
| 1282 | 1286 | $(OBJDIR)/search_.c:$(OBJDIR)/search.h \ |
| 1283 | 1287 | $(OBJDIR)/security_audit_.c:$(OBJDIR)/security_audit.h \ |
| 1284 | 1288 | $(OBJDIR)/setup_.c:$(OBJDIR)/setup.h \ |
| @@ -2167,10 +2171,18 @@ | ||
| 2167 | 2171 | |
| 2168 | 2172 | $(OBJDIR)/report.o: $(OBJDIR)/report_.c $(OBJDIR)/report.h $(SRCDIR)/config.h |
| 2169 | 2173 | $(XTCC) -o $(OBJDIR)/report.o -c $(OBJDIR)/report_.c |
| 2170 | 2174 | |
| 2171 | 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 | |
| 2172 | 2184 | |
| 2173 | 2185 | $(OBJDIR)/rss_.c: $(SRCDIR)/rss.c $(TRANSLATE) |
| 2174 | 2186 | $(TRANSLATE) $(SRCDIR)/rss.c >$@ |
| 2175 | 2187 | |
| 2176 | 2188 | $(OBJDIR)/rss.o: $(OBJDIR)/rss_.c $(OBJDIR)/rss.h $(SRCDIR)/config.h |
| 2177 | 2189 |
| --- 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 |
+10
| --- win/Makefile.msc | ||
| +++ win/Makefile.msc | ||
| @@ -467,10 +467,11 @@ | ||
| 467 | 467 | "$(OX)\purge_.c" \ |
| 468 | 468 | "$(OX)\rebuild_.c" \ |
| 469 | 469 | "$(OX)\regexp_.c" \ |
| 470 | 470 | "$(OX)\repolist_.c" \ |
| 471 | 471 | "$(OX)\report_.c" \ |
| 472 | + "$(OX)\robot_.c" \ | |
| 472 | 473 | "$(OX)\rss_.c" \ |
| 473 | 474 | "$(OX)\schema_.c" \ |
| 474 | 475 | "$(OX)\search_.c" \ |
| 475 | 476 | "$(OX)\security_audit_.c" \ |
| 476 | 477 | "$(OX)\setup_.c" \ |
| @@ -734,10 +735,11 @@ | ||
| 734 | 735 | "$(OX)\purge$O" \ |
| 735 | 736 | "$(OX)\rebuild$O" \ |
| 736 | 737 | "$(OX)\regexp$O" \ |
| 737 | 738 | "$(OX)\repolist$O" \ |
| 738 | 739 | "$(OX)\report$O" \ |
| 740 | + "$(OX)\robot$O" \ | |
| 739 | 741 | "$(OX)\rss$O" \ |
| 740 | 742 | "$(OX)\schema$O" \ |
| 741 | 743 | "$(OX)\search$O" \ |
| 742 | 744 | "$(OX)\security_audit$O" \ |
| 743 | 745 | "$(OX)\setup$O" \ |
| @@ -984,10 +986,11 @@ | ||
| 984 | 986 | echo "$(OX)\purge.obj" >> $@ |
| 985 | 987 | echo "$(OX)\rebuild.obj" >> $@ |
| 986 | 988 | echo "$(OX)\regexp.obj" >> $@ |
| 987 | 989 | echo "$(OX)\repolist.obj" >> $@ |
| 988 | 990 | echo "$(OX)\report.obj" >> $@ |
| 991 | + echo "$(OX)\robot.obj" >> $@ | |
| 989 | 992 | echo "$(OX)\rss.obj" >> $@ |
| 990 | 993 | echo "$(OX)\schema.obj" >> $@ |
| 991 | 994 | echo "$(OX)\search.obj" >> $@ |
| 992 | 995 | echo "$(OX)\security_audit.obj" >> $@ |
| 993 | 996 | echo "$(OX)\setup.obj" >> $@ |
| @@ -1889,10 +1892,16 @@ | ||
| 1889 | 1892 | "$(OX)\report$O" : "$(OX)\report_.c" "$(OX)\report.h" |
| 1890 | 1893 | $(TCC) /Fo$@ /Fd$(@D)\ -c "$(OX)\report_.c" |
| 1891 | 1894 | |
| 1892 | 1895 | "$(OX)\report_.c" : "$(SRCDIR)\report.c" |
| 1893 | 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" $** > $@ | |
| 1894 | 1903 | |
| 1895 | 1904 | "$(OX)\rss$O" : "$(OX)\rss_.c" "$(OX)\rss.h" |
| 1896 | 1905 | $(TCC) /Fo$@ /Fd$(@D)\ -c "$(OX)\rss_.c" |
| 1897 | 1906 | |
| 1898 | 1907 | "$(OX)\rss_.c" : "$(SRCDIR)\rss.c" |
| @@ -2257,10 +2266,11 @@ | ||
| 2257 | 2266 | "$(OX)\purge_.c":"$(OX)\purge.h" \ |
| 2258 | 2267 | "$(OX)\rebuild_.c":"$(OX)\rebuild.h" \ |
| 2259 | 2268 | "$(OX)\regexp_.c":"$(OX)\regexp.h" \ |
| 2260 | 2269 | "$(OX)\repolist_.c":"$(OX)\repolist.h" \ |
| 2261 | 2270 | "$(OX)\report_.c":"$(OX)\report.h" \ |
| 2271 | + "$(OX)\robot_.c":"$(OX)\robot.h" \ | |
| 2262 | 2272 | "$(OX)\rss_.c":"$(OX)\rss.h" \ |
| 2263 | 2273 | "$(OX)\schema_.c":"$(OX)\schema.h" \ |
| 2264 | 2274 | "$(OX)\search_.c":"$(OX)\search.h" \ |
| 2265 | 2275 | "$(OX)\security_audit_.c":"$(OX)\security_audit.h" \ |
| 2266 | 2276 | "$(OX)\setup_.c":"$(OX)\setup.h" \ |
| 2267 | 2277 |
| --- 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 |
+3
| --- www/changes.wiki | ||
| +++ www/changes.wiki | ||
| @@ -13,10 +13,13 @@ | ||
| 13 | 13 | <li> Enable the --editor option on the [/help?cmd=amend|fossil amend] command. |
| 14 | 14 | <li> Require at least an anonymous login to access the /blame page and similar, |
| 15 | 15 | to help prevent robots from soaking up excess CPU time on such pages. |
| 16 | 16 | <li> When walking the filesystem looking for Fossil repositories, avoid descending |
| 17 | 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. | |
| 18 | 21 | </ol> |
| 19 | 22 | |
| 20 | 23 | <h2 id='v2_26'>Changes for version 2.26 (2025-04-30)</h2><ol> |
| 21 | 24 | <li>Enhancements to [/help?cmd=diff|fossil diff] and similar: |
| 22 | 25 | <ol type="a"> |
| 23 | 26 |
| --- 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 @@ | ||
| 220 | 220 | Every message from client to server begins with one or more login |
| 221 | 221 | cards. Each login card has the following format: |
| 222 | 222 | |
| 223 | 223 | <pre><b>login</b> <i>userid nonce signature</i></pre> |
| 224 | 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. | |
| 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 | + | |
| 241 | 253 | |
| 242 | 254 | <h3 id="file">3.3 File Cards</h3> |
| 243 | 255 | |
| 244 | 256 | Artifacts are transferred using either "file" cards, or "cfile" |
| 245 | 257 | or "uvfile" cards. |
| 246 | 258 |
| --- 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 |