Fossil SCM
Sync with trunk.
Commit
bab295db108edbd92ebdb58ce278734728ffd8466ab94dbd572df6c11a3c0425
Parent
a2a1e4a7dc5e6d7…
79 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
+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
-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.tcl
~
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/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; |
| @@ -1150,19 +1153,40 @@ | ||
| 1150 | 1153 | white-space: nowrap; |
| 1151 | 1154 | } |
| 1152 | 1155 | label[for] { |
| 1153 | 1156 | cursor: pointer; |
| 1154 | 1157 | } |
| 1155 | -.copy-button { | |
| 1156 | - display: inline-block; | |
| 1158 | +button.copy-button, | |
| 1159 | +button.copy-button:hover, | |
| 1160 | +button.copy-button:focus, | |
| 1161 | +button.copy-button:active { | |
| 1157 | 1162 | width: 14px; |
| 1158 | 1163 | height: 14px; |
| 1159 | 1164 | /*Note: .24em is slightly smaller than the average width of a normal space.*/ |
| 1160 | 1165 | margin: -2px .24em 0 0; |
| 1161 | 1166 | padding: 0; |
| 1162 | 1167 | border: 0; |
| 1168 | + outline: 0; | |
| 1169 | + background: none; | |
| 1170 | + font-size: inherit; /* Required for horizontal spacing. */ | |
| 1163 | 1171 | vertical-align: middle; |
| 1172 | + user-select: none; | |
| 1173 | + cursor: pointer; | |
| 1174 | +} | |
| 1175 | +button.copy-button-flipped, | |
| 1176 | +button.copy-button-flipped:hover, | |
| 1177 | +button.copy-button-flipped:focus, | |
| 1178 | +button.copy-button-flipped:active { | |
| 1179 | + margin: -2px 0 0 .24em; | |
| 1180 | +} | |
| 1181 | +button.copy-button span { | |
| 1182 | + display: block; | |
| 1183 | + width: 100%; | |
| 1184 | + height: 100%; | |
| 1185 | + margin: 0; | |
| 1186 | + padding: 0; | |
| 1187 | + border: 0; | |
| 1164 | 1188 | background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' \ |
| 1165 | 1189 | viewBox='0,0,14,14'%3E%3Cpath style='fill:black;opacity:0' \ |
| 1166 | 1190 | d='M14,14H0V0h14v14z'/%3E%3Cpath style='fill:rgb(240,240,240)' \ |
| 1167 | 1191 | d='M1,0h6.6l2,2h1l3.4,3.4v8.6h-10v-2h-3z'/%3E%3Cpath style='fill:rgb(64,64,64)' \ |
| 1168 | 1192 | d='M2,1h5l3,3v7h-8z'/%3E%3Cpath style='fill:rgb(248,248,248)' \ |
| @@ -1173,19 +1197,17 @@ | ||
| 1173 | 1197 | d='M7,8h4v1h-4zm0,2h4v1h-4z'/%3E%3C/svg%3E"); |
| 1174 | 1198 | background-repeat: no-repeat; |
| 1175 | 1199 | background-position: center; |
| 1176 | 1200 | cursor: pointer; |
| 1177 | 1201 | } |
| 1178 | -.copy-button.disabled { | |
| 1202 | +button.copy-button:enabled:active span { | |
| 1203 | + background-size: 90%; | |
| 1204 | +} | |
| 1205 | +button.copy-button:disabled span { | |
| 1179 | 1206 | filter: grayscale(1); |
| 1180 | 1207 | opacity: 0.4; |
| 1181 | 1208 | } |
| 1182 | -.copy-button-flipped { | |
| 1183 | -/*Note: .16em is suitable for element grouping.*/ | |
| 1184 | - margin-left: .16em; | |
| 1185 | - margin-right: 0; | |
| 1186 | -} | |
| 1187 | 1209 | .nobr { |
| 1188 | 1210 | white-space: nowrap; |
| 1189 | 1211 | } |
| 1190 | 1212 | .accordion { |
| 1191 | 1213 | cursor: pointer; |
| 1192 | 1214 |
| --- 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; |
| @@ -1150,19 +1153,40 @@ | |
| 1150 | white-space: nowrap; |
| 1151 | } |
| 1152 | label[for] { |
| 1153 | cursor: pointer; |
| 1154 | } |
| 1155 | .copy-button { |
| 1156 | display: inline-block; |
| 1157 | width: 14px; |
| 1158 | height: 14px; |
| 1159 | /*Note: .24em is slightly smaller than the average width of a normal space.*/ |
| 1160 | margin: -2px .24em 0 0; |
| 1161 | padding: 0; |
| 1162 | border: 0; |
| 1163 | vertical-align: middle; |
| 1164 | background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' \ |
| 1165 | viewBox='0,0,14,14'%3E%3Cpath style='fill:black;opacity:0' \ |
| 1166 | d='M14,14H0V0h14v14z'/%3E%3Cpath style='fill:rgb(240,240,240)' \ |
| 1167 | d='M1,0h6.6l2,2h1l3.4,3.4v8.6h-10v-2h-3z'/%3E%3Cpath style='fill:rgb(64,64,64)' \ |
| 1168 | d='M2,1h5l3,3v7h-8z'/%3E%3Cpath style='fill:rgb(248,248,248)' \ |
| @@ -1173,19 +1197,17 @@ | |
| 1173 | d='M7,8h4v1h-4zm0,2h4v1h-4z'/%3E%3C/svg%3E"); |
| 1174 | background-repeat: no-repeat; |
| 1175 | background-position: center; |
| 1176 | cursor: pointer; |
| 1177 | } |
| 1178 | .copy-button.disabled { |
| 1179 | filter: grayscale(1); |
| 1180 | opacity: 0.4; |
| 1181 | } |
| 1182 | .copy-button-flipped { |
| 1183 | /*Note: .16em is suitable for element grouping.*/ |
| 1184 | margin-left: .16em; |
| 1185 | margin-right: 0; |
| 1186 | } |
| 1187 | .nobr { |
| 1188 | white-space: nowrap; |
| 1189 | } |
| 1190 | .accordion { |
| 1191 | cursor: pointer; |
| 1192 |
| --- 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; |
| @@ -1150,19 +1153,40 @@ | |
| 1153 | white-space: nowrap; |
| 1154 | } |
| 1155 | label[for] { |
| 1156 | cursor: pointer; |
| 1157 | } |
| 1158 | button.copy-button, |
| 1159 | button.copy-button:hover, |
| 1160 | button.copy-button:focus, |
| 1161 | button.copy-button:active { |
| 1162 | width: 14px; |
| 1163 | height: 14px; |
| 1164 | /*Note: .24em is slightly smaller than the average width of a normal space.*/ |
| 1165 | margin: -2px .24em 0 0; |
| 1166 | padding: 0; |
| 1167 | border: 0; |
| 1168 | outline: 0; |
| 1169 | background: none; |
| 1170 | font-size: inherit; /* Required for horizontal spacing. */ |
| 1171 | vertical-align: middle; |
| 1172 | user-select: none; |
| 1173 | cursor: pointer; |
| 1174 | } |
| 1175 | button.copy-button-flipped, |
| 1176 | button.copy-button-flipped:hover, |
| 1177 | button.copy-button-flipped:focus, |
| 1178 | button.copy-button-flipped:active { |
| 1179 | margin: -2px 0 0 .24em; |
| 1180 | } |
| 1181 | button.copy-button span { |
| 1182 | display: block; |
| 1183 | width: 100%; |
| 1184 | height: 100%; |
| 1185 | margin: 0; |
| 1186 | padding: 0; |
| 1187 | border: 0; |
| 1188 | background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' \ |
| 1189 | viewBox='0,0,14,14'%3E%3Cpath style='fill:black;opacity:0' \ |
| 1190 | d='M14,14H0V0h14v14z'/%3E%3Cpath style='fill:rgb(240,240,240)' \ |
| 1191 | d='M1,0h6.6l2,2h1l3.4,3.4v8.6h-10v-2h-3z'/%3E%3Cpath style='fill:rgb(64,64,64)' \ |
| 1192 | d='M2,1h5l3,3v7h-8z'/%3E%3Cpath style='fill:rgb(248,248,248)' \ |
| @@ -1173,19 +1197,17 @@ | |
| 1197 | d='M7,8h4v1h-4zm0,2h4v1h-4z'/%3E%3C/svg%3E"); |
| 1198 | background-repeat: no-repeat; |
| 1199 | background-position: center; |
| 1200 | cursor: pointer; |
| 1201 | } |
| 1202 | button.copy-button:enabled:active span { |
| 1203 | background-size: 90%; |
| 1204 | } |
| 1205 | button.copy-button:disabled span { |
| 1206 | filter: grayscale(1); |
| 1207 | opacity: 0.4; |
| 1208 | } |
| 1209 | .nobr { |
| 1210 | white-space: nowrap; |
| 1211 | } |
| 1212 | .accordion { |
| 1213 | cursor: pointer; |
| 1214 |
+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; |
| @@ -1150,19 +1153,40 @@ | ||
| 1150 | 1153 | white-space: nowrap; |
| 1151 | 1154 | } |
| 1152 | 1155 | label[for] { |
| 1153 | 1156 | cursor: pointer; |
| 1154 | 1157 | } |
| 1155 | -.copy-button { | |
| 1156 | - display: inline-block; | |
| 1158 | +button.copy-button, | |
| 1159 | +button.copy-button:hover, | |
| 1160 | +button.copy-button:focus, | |
| 1161 | +button.copy-button:active { | |
| 1157 | 1162 | width: 14px; |
| 1158 | 1163 | height: 14px; |
| 1159 | 1164 | /*Note: .24em is slightly smaller than the average width of a normal space.*/ |
| 1160 | 1165 | margin: -2px .24em 0 0; |
| 1161 | 1166 | padding: 0; |
| 1162 | 1167 | border: 0; |
| 1168 | + outline: 0; | |
| 1169 | + background: none; | |
| 1170 | + font-size: inherit; /* Required for horizontal spacing. */ | |
| 1163 | 1171 | vertical-align: middle; |
| 1172 | + user-select: none; | |
| 1173 | + cursor: pointer; | |
| 1174 | +} | |
| 1175 | +button.copy-button-flipped, | |
| 1176 | +button.copy-button-flipped:hover, | |
| 1177 | +button.copy-button-flipped:focus, | |
| 1178 | +button.copy-button-flipped:active { | |
| 1179 | + margin: -2px 0 0 .24em; | |
| 1180 | +} | |
| 1181 | +button.copy-button span { | |
| 1182 | + display: block; | |
| 1183 | + width: 100%; | |
| 1184 | + height: 100%; | |
| 1185 | + margin: 0; | |
| 1186 | + padding: 0; | |
| 1187 | + border: 0; | |
| 1164 | 1188 | background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' \ |
| 1165 | 1189 | viewBox='0,0,14,14'%3E%3Cpath style='fill:black;opacity:0' \ |
| 1166 | 1190 | d='M14,14H0V0h14v14z'/%3E%3Cpath style='fill:rgb(240,240,240)' \ |
| 1167 | 1191 | d='M1,0h6.6l2,2h1l3.4,3.4v8.6h-10v-2h-3z'/%3E%3Cpath style='fill:rgb(64,64,64)' \ |
| 1168 | 1192 | d='M2,1h5l3,3v7h-8z'/%3E%3Cpath style='fill:rgb(248,248,248)' \ |
| @@ -1173,19 +1197,17 @@ | ||
| 1173 | 1197 | d='M7,8h4v1h-4zm0,2h4v1h-4z'/%3E%3C/svg%3E"); |
| 1174 | 1198 | background-repeat: no-repeat; |
| 1175 | 1199 | background-position: center; |
| 1176 | 1200 | cursor: pointer; |
| 1177 | 1201 | } |
| 1178 | -.copy-button.disabled { | |
| 1202 | +button.copy-button:enabled:active span { | |
| 1203 | + background-size: 90%; | |
| 1204 | +} | |
| 1205 | +button.copy-button:disabled span { | |
| 1179 | 1206 | filter: grayscale(1); |
| 1180 | 1207 | opacity: 0.4; |
| 1181 | 1208 | } |
| 1182 | -.copy-button-flipped { | |
| 1183 | -/*Note: .16em is suitable for element grouping.*/ | |
| 1184 | - margin-left: .16em; | |
| 1185 | - margin-right: 0; | |
| 1186 | -} | |
| 1187 | 1209 | .nobr { |
| 1188 | 1210 | white-space: nowrap; |
| 1189 | 1211 | } |
| 1190 | 1212 | .accordion { |
| 1191 | 1213 | cursor: pointer; |
| 1192 | 1214 |
| --- 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; |
| @@ -1150,19 +1153,40 @@ | |
| 1150 | white-space: nowrap; |
| 1151 | } |
| 1152 | label[for] { |
| 1153 | cursor: pointer; |
| 1154 | } |
| 1155 | .copy-button { |
| 1156 | display: inline-block; |
| 1157 | width: 14px; |
| 1158 | height: 14px; |
| 1159 | /*Note: .24em is slightly smaller than the average width of a normal space.*/ |
| 1160 | margin: -2px .24em 0 0; |
| 1161 | padding: 0; |
| 1162 | border: 0; |
| 1163 | vertical-align: middle; |
| 1164 | background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' \ |
| 1165 | viewBox='0,0,14,14'%3E%3Cpath style='fill:black;opacity:0' \ |
| 1166 | d='M14,14H0V0h14v14z'/%3E%3Cpath style='fill:rgb(240,240,240)' \ |
| 1167 | d='M1,0h6.6l2,2h1l3.4,3.4v8.6h-10v-2h-3z'/%3E%3Cpath style='fill:rgb(64,64,64)' \ |
| 1168 | d='M2,1h5l3,3v7h-8z'/%3E%3Cpath style='fill:rgb(248,248,248)' \ |
| @@ -1173,19 +1197,17 @@ | |
| 1173 | d='M7,8h4v1h-4zm0,2h4v1h-4z'/%3E%3C/svg%3E"); |
| 1174 | background-repeat: no-repeat; |
| 1175 | background-position: center; |
| 1176 | cursor: pointer; |
| 1177 | } |
| 1178 | .copy-button.disabled { |
| 1179 | filter: grayscale(1); |
| 1180 | opacity: 0.4; |
| 1181 | } |
| 1182 | .copy-button-flipped { |
| 1183 | /*Note: .16em is suitable for element grouping.*/ |
| 1184 | margin-left: .16em; |
| 1185 | margin-right: 0; |
| 1186 | } |
| 1187 | .nobr { |
| 1188 | white-space: nowrap; |
| 1189 | } |
| 1190 | .accordion { |
| 1191 | cursor: pointer; |
| 1192 |
| --- 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; |
| @@ -1150,19 +1153,40 @@ | |
| 1153 | white-space: nowrap; |
| 1154 | } |
| 1155 | label[for] { |
| 1156 | cursor: pointer; |
| 1157 | } |
| 1158 | button.copy-button, |
| 1159 | button.copy-button:hover, |
| 1160 | button.copy-button:focus, |
| 1161 | button.copy-button:active { |
| 1162 | width: 14px; |
| 1163 | height: 14px; |
| 1164 | /*Note: .24em is slightly smaller than the average width of a normal space.*/ |
| 1165 | margin: -2px .24em 0 0; |
| 1166 | padding: 0; |
| 1167 | border: 0; |
| 1168 | outline: 0; |
| 1169 | background: none; |
| 1170 | font-size: inherit; /* Required for horizontal spacing. */ |
| 1171 | vertical-align: middle; |
| 1172 | user-select: none; |
| 1173 | cursor: pointer; |
| 1174 | } |
| 1175 | button.copy-button-flipped, |
| 1176 | button.copy-button-flipped:hover, |
| 1177 | button.copy-button-flipped:focus, |
| 1178 | button.copy-button-flipped:active { |
| 1179 | margin: -2px 0 0 .24em; |
| 1180 | } |
| 1181 | button.copy-button span { |
| 1182 | display: block; |
| 1183 | width: 100%; |
| 1184 | height: 100%; |
| 1185 | margin: 0; |
| 1186 | padding: 0; |
| 1187 | border: 0; |
| 1188 | background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' \ |
| 1189 | viewBox='0,0,14,14'%3E%3Cpath style='fill:black;opacity:0' \ |
| 1190 | d='M14,14H0V0h14v14z'/%3E%3Cpath style='fill:rgb(240,240,240)' \ |
| 1191 | d='M1,0h6.6l2,2h1l3.4,3.4v8.6h-10v-2h-3z'/%3E%3Cpath style='fill:rgb(64,64,64)' \ |
| 1192 | d='M2,1h5l3,3v7h-8z'/%3E%3Cpath style='fill:rgb(248,248,248)' \ |
| @@ -1173,19 +1197,17 @@ | |
| 1197 | d='M7,8h4v1h-4zm0,2h4v1h-4z'/%3E%3C/svg%3E"); |
| 1198 | background-repeat: no-repeat; |
| 1199 | background-position: center; |
| 1200 | cursor: pointer; |
| 1201 | } |
| 1202 | button.copy-button:enabled:active span { |
| 1203 | background-size: 90%; |
| 1204 | } |
| 1205 | button.copy-button:disabled span { |
| 1206 | filter: grayscale(1); |
| 1207 | opacity: 0.4; |
| 1208 | } |
| 1209 | .nobr { |
| 1210 | white-space: nowrap; |
| 1211 | } |
| 1212 | .accordion { |
| 1213 | cursor: pointer; |
| 1214 |
+17
-11
| --- src/diff.c | ||
| +++ src/diff.c | ||
| @@ -3085,10 +3085,11 @@ | ||
| 3085 | 3085 | Blob *pOut, /* Write diff here if not NULL */ |
| 3086 | 3086 | DiffConfig *pCfg /* Configuration options */ |
| 3087 | 3087 | ){ |
| 3088 | 3088 | int ignoreWs; /* Ignore whitespace */ |
| 3089 | 3089 | DContext c; |
| 3090 | + int nDel = 0, nIns = 0; | |
| 3090 | 3091 | |
| 3091 | 3092 | if( pCfg->diffFlags & DIFF_INVERT ){ |
| 3092 | 3093 | Blob *pTemp = pA_Blob; |
| 3093 | 3094 | pA_Blob = pB_Blob; |
| 3094 | 3095 | pB_Blob = pTemp; |
| @@ -3163,22 +3164,27 @@ | ||
| 3163 | 3164 | c.aEdit[i+1] = sum; |
| 3164 | 3165 | for(k=0, sum=0; k<c.aEdit[i+2]; k++) sum += c.aTo[iB++].n; |
| 3165 | 3166 | c.aEdit[i+2] = sum; |
| 3166 | 3167 | } |
| 3167 | 3168 | } |
| 3169 | + | |
| 3170 | + if( pCfg->diffFlags & DIFF_NUMSTAT ){ | |
| 3171 | + int i; | |
| 3172 | + for(i=0; c.aEdit[i] || c.aEdit[i+1] || c.aEdit[i+2]; i+=3){ | |
| 3173 | + nDel += c.aEdit[i+1]; | |
| 3174 | + nIns += c.aEdit[i+2]; | |
| 3175 | + } | |
| 3176 | + g.diffCnt[1] += nIns; | |
| 3177 | + g.diffCnt[2] += nDel; | |
| 3178 | + if( nIns+nDel ){ | |
| 3179 | + g.diffCnt[0]++; | |
| 3180 | + } | |
| 3181 | + } | |
| 3168 | 3182 | |
| 3169 | 3183 | if( pOut ){ |
| 3170 | - if( pCfg->diffFlags & DIFF_NUMSTAT ){ | |
| 3171 | - int nDel = 0, nIns = 0, i; | |
| 3172 | - for(i=0; c.aEdit[i] || c.aEdit[i+1] || c.aEdit[i+2]; i+=3){ | |
| 3173 | - nDel += c.aEdit[i+1]; | |
| 3174 | - nIns += c.aEdit[i+2]; | |
| 3175 | - } | |
| 3176 | - g.diffCnt[1] += nIns; | |
| 3177 | - g.diffCnt[2] += nDel; | |
| 3184 | + if( pCfg->diffFlags & DIFF_NUMSTAT && !(pCfg->diffFlags & DIFF_HTML)){ | |
| 3178 | 3185 | if( nIns+nDel ){ |
| 3179 | - g.diffCnt[0]++; | |
| 3180 | 3186 | if( !(pCfg->diffFlags & DIFF_BRIEF) ){ |
| 3181 | 3187 | blob_appendf(pOut, "%10d %10d", nIns, nDel); |
| 3182 | 3188 | } |
| 3183 | 3189 | } |
| 3184 | 3190 | }else if( pCfg->diffFlags & (DIFF_RAW|DIFF_BY_TOKEN) ){ |
| @@ -3781,12 +3787,12 @@ | ||
| 3781 | 3787 | unsigned clr1, clr2, clr; |
| 3782 | 3788 | int bBlame = g.zPath[0]!='a';/* True for BLAME output. False for ANNOTATE. */ |
| 3783 | 3789 | |
| 3784 | 3790 | /* Gather query parameters */ |
| 3785 | 3791 | login_check_credentials(); |
| 3786 | - if( !g.perm.Read || g.zLogin==0 ){ login_needed(g.anon.Read); return; } | |
| 3787 | - if( exclude_spiders(0) ) return; | |
| 3792 | + if( !g.perm.Read ){ login_needed(g.anon.Read); return; } | |
| 3793 | + if( robot_restrict("annotate") ) return; | |
| 3788 | 3794 | fossil_nice_default(); |
| 3789 | 3795 | zFilename = P("filename"); |
| 3790 | 3796 | zRevision = PD("checkin",0); |
| 3791 | 3797 | zOrigin = P("origin"); |
| 3792 | 3798 | zLimit = P("limit"); |
| 3793 | 3799 |
| --- src/diff.c | |
| +++ src/diff.c | |
| @@ -3085,10 +3085,11 @@ | |
| 3085 | Blob *pOut, /* Write diff here if not NULL */ |
| 3086 | DiffConfig *pCfg /* Configuration options */ |
| 3087 | ){ |
| 3088 | int ignoreWs; /* Ignore whitespace */ |
| 3089 | DContext c; |
| 3090 | |
| 3091 | if( pCfg->diffFlags & DIFF_INVERT ){ |
| 3092 | Blob *pTemp = pA_Blob; |
| 3093 | pA_Blob = pB_Blob; |
| 3094 | pB_Blob = pTemp; |
| @@ -3163,22 +3164,27 @@ | |
| 3163 | c.aEdit[i+1] = sum; |
| 3164 | for(k=0, sum=0; k<c.aEdit[i+2]; k++) sum += c.aTo[iB++].n; |
| 3165 | c.aEdit[i+2] = sum; |
| 3166 | } |
| 3167 | } |
| 3168 | |
| 3169 | if( pOut ){ |
| 3170 | if( pCfg->diffFlags & DIFF_NUMSTAT ){ |
| 3171 | int nDel = 0, nIns = 0, i; |
| 3172 | for(i=0; c.aEdit[i] || c.aEdit[i+1] || c.aEdit[i+2]; i+=3){ |
| 3173 | nDel += c.aEdit[i+1]; |
| 3174 | nIns += c.aEdit[i+2]; |
| 3175 | } |
| 3176 | g.diffCnt[1] += nIns; |
| 3177 | g.diffCnt[2] += nDel; |
| 3178 | if( nIns+nDel ){ |
| 3179 | g.diffCnt[0]++; |
| 3180 | if( !(pCfg->diffFlags & DIFF_BRIEF) ){ |
| 3181 | blob_appendf(pOut, "%10d %10d", nIns, nDel); |
| 3182 | } |
| 3183 | } |
| 3184 | }else if( pCfg->diffFlags & (DIFF_RAW|DIFF_BY_TOKEN) ){ |
| @@ -3781,12 +3787,12 @@ | |
| 3781 | unsigned clr1, clr2, clr; |
| 3782 | int bBlame = g.zPath[0]!='a';/* True for BLAME output. False for ANNOTATE. */ |
| 3783 | |
| 3784 | /* Gather query parameters */ |
| 3785 | login_check_credentials(); |
| 3786 | if( !g.perm.Read || g.zLogin==0 ){ login_needed(g.anon.Read); return; } |
| 3787 | if( exclude_spiders(0) ) return; |
| 3788 | fossil_nice_default(); |
| 3789 | zFilename = P("filename"); |
| 3790 | zRevision = PD("checkin",0); |
| 3791 | zOrigin = P("origin"); |
| 3792 | zLimit = P("limit"); |
| 3793 |
| --- src/diff.c | |
| +++ src/diff.c | |
| @@ -3085,10 +3085,11 @@ | |
| 3085 | Blob *pOut, /* Write diff here if not NULL */ |
| 3086 | DiffConfig *pCfg /* Configuration options */ |
| 3087 | ){ |
| 3088 | int ignoreWs; /* Ignore whitespace */ |
| 3089 | DContext c; |
| 3090 | int nDel = 0, nIns = 0; |
| 3091 | |
| 3092 | if( pCfg->diffFlags & DIFF_INVERT ){ |
| 3093 | Blob *pTemp = pA_Blob; |
| 3094 | pA_Blob = pB_Blob; |
| 3095 | pB_Blob = pTemp; |
| @@ -3163,22 +3164,27 @@ | |
| 3164 | c.aEdit[i+1] = sum; |
| 3165 | for(k=0, sum=0; k<c.aEdit[i+2]; k++) sum += c.aTo[iB++].n; |
| 3166 | c.aEdit[i+2] = sum; |
| 3167 | } |
| 3168 | } |
| 3169 | |
| 3170 | if( pCfg->diffFlags & DIFF_NUMSTAT ){ |
| 3171 | int i; |
| 3172 | for(i=0; c.aEdit[i] || c.aEdit[i+1] || c.aEdit[i+2]; i+=3){ |
| 3173 | nDel += c.aEdit[i+1]; |
| 3174 | nIns += c.aEdit[i+2]; |
| 3175 | } |
| 3176 | g.diffCnt[1] += nIns; |
| 3177 | g.diffCnt[2] += nDel; |
| 3178 | if( nIns+nDel ){ |
| 3179 | g.diffCnt[0]++; |
| 3180 | } |
| 3181 | } |
| 3182 | |
| 3183 | if( pOut ){ |
| 3184 | if( pCfg->diffFlags & DIFF_NUMSTAT && !(pCfg->diffFlags & DIFF_HTML)){ |
| 3185 | if( nIns+nDel ){ |
| 3186 | if( !(pCfg->diffFlags & DIFF_BRIEF) ){ |
| 3187 | blob_appendf(pOut, "%10d %10d", nIns, nDel); |
| 3188 | } |
| 3189 | } |
| 3190 | }else if( pCfg->diffFlags & (DIFF_RAW|DIFF_BY_TOKEN) ){ |
| @@ -3781,12 +3787,12 @@ | |
| 3787 | unsigned clr1, clr2, clr; |
| 3788 | int bBlame = g.zPath[0]!='a';/* True for BLAME output. False for ANNOTATE. */ |
| 3789 | |
| 3790 | /* Gather query parameters */ |
| 3791 | login_check_credentials(); |
| 3792 | if( !g.perm.Read ){ login_needed(g.anon.Read); return; } |
| 3793 | if( robot_restrict("annotate") ) return; |
| 3794 | fossil_nice_default(); |
| 3795 | zFilename = P("filename"); |
| 3796 | zRevision = PD("checkin",0); |
| 3797 | zOrigin = P("origin"); |
| 3798 | zLimit = P("limit"); |
| 3799 |
+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 | ||
| @@ -1522,10 +1522,11 @@ | ||
| 1522 | 1522 | DiffConfig DCfg; |
| 1523 | 1523 | cgi_check_for_malice(); |
| 1524 | 1524 | login_check_credentials(); |
| 1525 | 1525 | if( !g.perm.Read ){ login_needed(g.anon.Read); return; } |
| 1526 | 1526 | if( zFrom==0 || zTo==0 ) fossil_redirect_home(); |
| 1527 | + if( robot_restrict("diff") ) return; | |
| 1527 | 1528 | |
| 1528 | 1529 | fossil_nice_default(); |
| 1529 | 1530 | cgi_set_content_type("text/plain"); |
| 1530 | 1531 | diff_config_init(&DCfg, DIFF_VERBOSE); |
| 1531 | 1532 | diff_two_versions(zFrom, zTo, &DCfg, 0); |
| 1532 | 1533 |
| --- src/diffcmd.c | |
| +++ src/diffcmd.c | |
| @@ -1522,10 +1522,11 @@ | |
| 1522 | DiffConfig DCfg; |
| 1523 | cgi_check_for_malice(); |
| 1524 | login_check_credentials(); |
| 1525 | if( !g.perm.Read ){ login_needed(g.anon.Read); return; } |
| 1526 | if( zFrom==0 || zTo==0 ) fossil_redirect_home(); |
| 1527 | |
| 1528 | fossil_nice_default(); |
| 1529 | cgi_set_content_type("text/plain"); |
| 1530 | diff_config_init(&DCfg, DIFF_VERBOSE); |
| 1531 | diff_two_versions(zFrom, zTo, &DCfg, 0); |
| 1532 |
| --- src/diffcmd.c | |
| +++ src/diffcmd.c | |
| @@ -1522,10 +1522,11 @@ | |
| 1522 | DiffConfig DCfg; |
| 1523 | cgi_check_for_malice(); |
| 1524 | login_check_credentials(); |
| 1525 | if( !g.perm.Read ){ login_needed(g.anon.Read); return; } |
| 1526 | if( zFrom==0 || zTo==0 ) fossil_redirect_home(); |
| 1527 | if( robot_restrict("diff") ) return; |
| 1528 | |
| 1529 | fossil_nice_default(); |
| 1530 | cgi_set_content_type("text/plain"); |
| 1531 | diff_config_init(&DCfg, DIFF_VERBOSE); |
| 1532 | diff_two_versions(zFrom, zTo, &DCfg, 0); |
| 1533 |
+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 | +The |
| --- a/src/robot.c | |
| +++ b/src/robot.c | |
| @@ -0,0 +1 @@ | |
| --- a/src/robot.c | |
| +++ b/src/robot.c | |
| @@ -0,0 +1 @@ | |
| 1 | The |
+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 | ||
| @@ -1852,10 +1852,11 @@ | ||
| 1852 | 1852 | || (bisectLocal && !g.perm.Setup) |
| 1853 | 1853 | ){ |
| 1854 | 1854 | login_needed(g.anon.Read && g.anon.RdTkt && g.anon.RdWiki); |
| 1855 | 1855 | return; |
| 1856 | 1856 | } |
| 1857 | + if( zBefore && robot_restrict("timelineX") ) return; | |
| 1857 | 1858 | if( !bisectLocal ){ |
| 1858 | 1859 | etag_check(ETAG_QUERY|ETAG_COOKIE|ETAG_DATA|ETAG_CONFIG, 0); |
| 1859 | 1860 | } |
| 1860 | 1861 | cookie_read_parameter("y","y"); |
| 1861 | 1862 | zType = P("y"); |
| @@ -1991,10 +1992,11 @@ | ||
| 1991 | 1992 | } |
| 1992 | 1993 | if( showSql ) db_append_dml_to_blob(&allSql); |
| 1993 | 1994 | if( zUses!=0 ){ |
| 1994 | 1995 | int ufid = db_int(0, "SELECT rid FROM blob WHERE uuid GLOB '%q*'", zUses); |
| 1995 | 1996 | if( ufid ){ |
| 1997 | + if( robot_restrict("timelineX") ) return; | |
| 1996 | 1998 | zUses = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", ufid); |
| 1997 | 1999 | db_multi_exec("CREATE TEMP TABLE usesfile(rid INTEGER PRIMARY KEY)"); |
| 1998 | 2000 | compute_uses_file("usesfile", ufid, 0); |
| 1999 | 2001 | zType = "ci"; |
| 2000 | 2002 | disableY = 1; |
| @@ -3061,11 +3063,11 @@ | ||
| 3061 | 3063 | "brlist", "List" |
| 3062 | 3064 | }; |
| 3063 | 3065 | double rDate; |
| 3064 | 3066 | zDate = db_text(0, "SELECT min(timestamp) FROM timeline /*scan*/"); |
| 3065 | 3067 | if( (!zDate || !zDate[0]) && ( zAfter || zBefore ) ){ |
| 3066 | - zDate = mprintf("%s", (zAfter ? zAfter : zBefore)); | |
| 3068 | + zDate = fossil_strdup((zAfter ? zAfter : zBefore)); | |
| 3067 | 3069 | } |
| 3068 | 3070 | if( zDate ){ |
| 3069 | 3071 | rDate = symbolic_name_to_mtime(zDate, 0, 0); |
| 3070 | 3072 | if( db_int(0, |
| 3071 | 3073 | "SELECT EXISTS (SELECT 1 FROM event CROSS JOIN blob" |
| @@ -3077,11 +3079,11 @@ | ||
| 3077 | 3079 | } |
| 3078 | 3080 | free(zDate); |
| 3079 | 3081 | } |
| 3080 | 3082 | zDate = db_text(0, "SELECT max(timestamp) FROM timeline /*scan*/"); |
| 3081 | 3083 | if( (!zDate || !zDate[0]) && ( zAfter || zBefore ) ){ |
| 3082 | - zDate = mprintf("%s", (zBefore ? zBefore : zAfter)); | |
| 3084 | + zDate = fossil_strdup((zBefore ? zBefore : zAfter)); | |
| 3083 | 3085 | } |
| 3084 | 3086 | if( zDate ){ |
| 3085 | 3087 | rDate = symbolic_name_to_mtime(zDate, 0, 0); |
| 3086 | 3088 | if( db_int(0, |
| 3087 | 3089 | "SELECT EXISTS (SELECT 1 FROM event CROSS JOIN blob" |
| 3088 | 3090 |
| --- src/timeline.c | |
| +++ src/timeline.c | |
| @@ -1852,10 +1852,11 @@ | |
| 1852 | || (bisectLocal && !g.perm.Setup) |
| 1853 | ){ |
| 1854 | login_needed(g.anon.Read && g.anon.RdTkt && g.anon.RdWiki); |
| 1855 | return; |
| 1856 | } |
| 1857 | if( !bisectLocal ){ |
| 1858 | etag_check(ETAG_QUERY|ETAG_COOKIE|ETAG_DATA|ETAG_CONFIG, 0); |
| 1859 | } |
| 1860 | cookie_read_parameter("y","y"); |
| 1861 | zType = P("y"); |
| @@ -1991,10 +1992,11 @@ | |
| 1991 | } |
| 1992 | if( showSql ) db_append_dml_to_blob(&allSql); |
| 1993 | if( zUses!=0 ){ |
| 1994 | int ufid = db_int(0, "SELECT rid FROM blob WHERE uuid GLOB '%q*'", zUses); |
| 1995 | if( ufid ){ |
| 1996 | zUses = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", ufid); |
| 1997 | db_multi_exec("CREATE TEMP TABLE usesfile(rid INTEGER PRIMARY KEY)"); |
| 1998 | compute_uses_file("usesfile", ufid, 0); |
| 1999 | zType = "ci"; |
| 2000 | disableY = 1; |
| @@ -3061,11 +3063,11 @@ | |
| 3061 | "brlist", "List" |
| 3062 | }; |
| 3063 | double rDate; |
| 3064 | zDate = db_text(0, "SELECT min(timestamp) FROM timeline /*scan*/"); |
| 3065 | if( (!zDate || !zDate[0]) && ( zAfter || zBefore ) ){ |
| 3066 | zDate = mprintf("%s", (zAfter ? zAfter : zBefore)); |
| 3067 | } |
| 3068 | if( zDate ){ |
| 3069 | rDate = symbolic_name_to_mtime(zDate, 0, 0); |
| 3070 | if( db_int(0, |
| 3071 | "SELECT EXISTS (SELECT 1 FROM event CROSS JOIN blob" |
| @@ -3077,11 +3079,11 @@ | |
| 3077 | } |
| 3078 | free(zDate); |
| 3079 | } |
| 3080 | zDate = db_text(0, "SELECT max(timestamp) FROM timeline /*scan*/"); |
| 3081 | if( (!zDate || !zDate[0]) && ( zAfter || zBefore ) ){ |
| 3082 | zDate = mprintf("%s", (zBefore ? zBefore : zAfter)); |
| 3083 | } |
| 3084 | if( zDate ){ |
| 3085 | rDate = symbolic_name_to_mtime(zDate, 0, 0); |
| 3086 | if( db_int(0, |
| 3087 | "SELECT EXISTS (SELECT 1 FROM event CROSS JOIN blob" |
| 3088 |
| --- src/timeline.c | |
| +++ src/timeline.c | |
| @@ -1852,10 +1852,11 @@ | |
| 1852 | || (bisectLocal && !g.perm.Setup) |
| 1853 | ){ |
| 1854 | login_needed(g.anon.Read && g.anon.RdTkt && g.anon.RdWiki); |
| 1855 | return; |
| 1856 | } |
| 1857 | if( zBefore && robot_restrict("timelineX") ) return; |
| 1858 | if( !bisectLocal ){ |
| 1859 | etag_check(ETAG_QUERY|ETAG_COOKIE|ETAG_DATA|ETAG_CONFIG, 0); |
| 1860 | } |
| 1861 | cookie_read_parameter("y","y"); |
| 1862 | zType = P("y"); |
| @@ -1991,10 +1992,11 @@ | |
| 1992 | } |
| 1993 | if( showSql ) db_append_dml_to_blob(&allSql); |
| 1994 | if( zUses!=0 ){ |
| 1995 | int ufid = db_int(0, "SELECT rid FROM blob WHERE uuid GLOB '%q*'", zUses); |
| 1996 | if( ufid ){ |
| 1997 | if( robot_restrict("timelineX") ) return; |
| 1998 | zUses = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", ufid); |
| 1999 | db_multi_exec("CREATE TEMP TABLE usesfile(rid INTEGER PRIMARY KEY)"); |
| 2000 | compute_uses_file("usesfile", ufid, 0); |
| 2001 | zType = "ci"; |
| 2002 | disableY = 1; |
| @@ -3061,11 +3063,11 @@ | |
| 3063 | "brlist", "List" |
| 3064 | }; |
| 3065 | double rDate; |
| 3066 | zDate = db_text(0, "SELECT min(timestamp) FROM timeline /*scan*/"); |
| 3067 | if( (!zDate || !zDate[0]) && ( zAfter || zBefore ) ){ |
| 3068 | zDate = fossil_strdup((zAfter ? zAfter : zBefore)); |
| 3069 | } |
| 3070 | if( zDate ){ |
| 3071 | rDate = symbolic_name_to_mtime(zDate, 0, 0); |
| 3072 | if( db_int(0, |
| 3073 | "SELECT EXISTS (SELECT 1 FROM event CROSS JOIN blob" |
| @@ -3077,11 +3079,11 @@ | |
| 3079 | } |
| 3080 | free(zDate); |
| 3081 | } |
| 3082 | zDate = db_text(0, "SELECT max(timestamp) FROM timeline /*scan*/"); |
| 3083 | if( (!zDate || !zDate[0]) && ( zAfter || zBefore ) ){ |
| 3084 | zDate = fossil_strdup((zBefore ? zBefore : zAfter)); |
| 3085 | } |
| 3086 | if( zDate ){ |
| 3087 | rDate = symbolic_name_to_mtime(zDate, 0, 0); |
| 3088 | if( db_int(0, |
| 3089 | "SELECT EXISTS (SELECT 1 FROM event CROSS JOIN blob" |
| 3090 |
+4
-2
| --- src/timeline.c | ||
| +++ src/timeline.c | ||
| @@ -1852,10 +1852,11 @@ | ||
| 1852 | 1852 | || (bisectLocal && !g.perm.Setup) |
| 1853 | 1853 | ){ |
| 1854 | 1854 | login_needed(g.anon.Read && g.anon.RdTkt && g.anon.RdWiki); |
| 1855 | 1855 | return; |
| 1856 | 1856 | } |
| 1857 | + if( zBefore && robot_restrict("timelineX") ) return; | |
| 1857 | 1858 | if( !bisectLocal ){ |
| 1858 | 1859 | etag_check(ETAG_QUERY|ETAG_COOKIE|ETAG_DATA|ETAG_CONFIG, 0); |
| 1859 | 1860 | } |
| 1860 | 1861 | cookie_read_parameter("y","y"); |
| 1861 | 1862 | zType = P("y"); |
| @@ -1991,10 +1992,11 @@ | ||
| 1991 | 1992 | } |
| 1992 | 1993 | if( showSql ) db_append_dml_to_blob(&allSql); |
| 1993 | 1994 | if( zUses!=0 ){ |
| 1994 | 1995 | int ufid = db_int(0, "SELECT rid FROM blob WHERE uuid GLOB '%q*'", zUses); |
| 1995 | 1996 | if( ufid ){ |
| 1997 | + if( robot_restrict("timelineX") ) return; | |
| 1996 | 1998 | zUses = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", ufid); |
| 1997 | 1999 | db_multi_exec("CREATE TEMP TABLE usesfile(rid INTEGER PRIMARY KEY)"); |
| 1998 | 2000 | compute_uses_file("usesfile", ufid, 0); |
| 1999 | 2001 | zType = "ci"; |
| 2000 | 2002 | disableY = 1; |
| @@ -3061,11 +3063,11 @@ | ||
| 3061 | 3063 | "brlist", "List" |
| 3062 | 3064 | }; |
| 3063 | 3065 | double rDate; |
| 3064 | 3066 | zDate = db_text(0, "SELECT min(timestamp) FROM timeline /*scan*/"); |
| 3065 | 3067 | if( (!zDate || !zDate[0]) && ( zAfter || zBefore ) ){ |
| 3066 | - zDate = mprintf("%s", (zAfter ? zAfter : zBefore)); | |
| 3068 | + zDate = fossil_strdup((zAfter ? zAfter : zBefore)); | |
| 3067 | 3069 | } |
| 3068 | 3070 | if( zDate ){ |
| 3069 | 3071 | rDate = symbolic_name_to_mtime(zDate, 0, 0); |
| 3070 | 3072 | if( db_int(0, |
| 3071 | 3073 | "SELECT EXISTS (SELECT 1 FROM event CROSS JOIN blob" |
| @@ -3077,11 +3079,11 @@ | ||
| 3077 | 3079 | } |
| 3078 | 3080 | free(zDate); |
| 3079 | 3081 | } |
| 3080 | 3082 | zDate = db_text(0, "SELECT max(timestamp) FROM timeline /*scan*/"); |
| 3081 | 3083 | if( (!zDate || !zDate[0]) && ( zAfter || zBefore ) ){ |
| 3082 | - zDate = mprintf("%s", (zBefore ? zBefore : zAfter)); | |
| 3084 | + zDate = fossil_strdup((zBefore ? zBefore : zAfter)); | |
| 3083 | 3085 | } |
| 3084 | 3086 | if( zDate ){ |
| 3085 | 3087 | rDate = symbolic_name_to_mtime(zDate, 0, 0); |
| 3086 | 3088 | if( db_int(0, |
| 3087 | 3089 | "SELECT EXISTS (SELECT 1 FROM event CROSS JOIN blob" |
| 3088 | 3090 |
| --- src/timeline.c | |
| +++ src/timeline.c | |
| @@ -1852,10 +1852,11 @@ | |
| 1852 | || (bisectLocal && !g.perm.Setup) |
| 1853 | ){ |
| 1854 | login_needed(g.anon.Read && g.anon.RdTkt && g.anon.RdWiki); |
| 1855 | return; |
| 1856 | } |
| 1857 | if( !bisectLocal ){ |
| 1858 | etag_check(ETAG_QUERY|ETAG_COOKIE|ETAG_DATA|ETAG_CONFIG, 0); |
| 1859 | } |
| 1860 | cookie_read_parameter("y","y"); |
| 1861 | zType = P("y"); |
| @@ -1991,10 +1992,11 @@ | |
| 1991 | } |
| 1992 | if( showSql ) db_append_dml_to_blob(&allSql); |
| 1993 | if( zUses!=0 ){ |
| 1994 | int ufid = db_int(0, "SELECT rid FROM blob WHERE uuid GLOB '%q*'", zUses); |
| 1995 | if( ufid ){ |
| 1996 | zUses = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", ufid); |
| 1997 | db_multi_exec("CREATE TEMP TABLE usesfile(rid INTEGER PRIMARY KEY)"); |
| 1998 | compute_uses_file("usesfile", ufid, 0); |
| 1999 | zType = "ci"; |
| 2000 | disableY = 1; |
| @@ -3061,11 +3063,11 @@ | |
| 3061 | "brlist", "List" |
| 3062 | }; |
| 3063 | double rDate; |
| 3064 | zDate = db_text(0, "SELECT min(timestamp) FROM timeline /*scan*/"); |
| 3065 | if( (!zDate || !zDate[0]) && ( zAfter || zBefore ) ){ |
| 3066 | zDate = mprintf("%s", (zAfter ? zAfter : zBefore)); |
| 3067 | } |
| 3068 | if( zDate ){ |
| 3069 | rDate = symbolic_name_to_mtime(zDate, 0, 0); |
| 3070 | if( db_int(0, |
| 3071 | "SELECT EXISTS (SELECT 1 FROM event CROSS JOIN blob" |
| @@ -3077,11 +3079,11 @@ | |
| 3077 | } |
| 3078 | free(zDate); |
| 3079 | } |
| 3080 | zDate = db_text(0, "SELECT max(timestamp) FROM timeline /*scan*/"); |
| 3081 | if( (!zDate || !zDate[0]) && ( zAfter || zBefore ) ){ |
| 3082 | zDate = mprintf("%s", (zBefore ? zBefore : zAfter)); |
| 3083 | } |
| 3084 | if( zDate ){ |
| 3085 | rDate = symbolic_name_to_mtime(zDate, 0, 0); |
| 3086 | if( db_int(0, |
| 3087 | "SELECT EXISTS (SELECT 1 FROM event CROSS JOIN blob" |
| 3088 |
| --- src/timeline.c | |
| +++ src/timeline.c | |
| @@ -1852,10 +1852,11 @@ | |
| 1852 | || (bisectLocal && !g.perm.Setup) |
| 1853 | ){ |
| 1854 | login_needed(g.anon.Read && g.anon.RdTkt && g.anon.RdWiki); |
| 1855 | return; |
| 1856 | } |
| 1857 | if( zBefore && robot_restrict("timelineX") ) return; |
| 1858 | if( !bisectLocal ){ |
| 1859 | etag_check(ETAG_QUERY|ETAG_COOKIE|ETAG_DATA|ETAG_CONFIG, 0); |
| 1860 | } |
| 1861 | cookie_read_parameter("y","y"); |
| 1862 | zType = P("y"); |
| @@ -1991,10 +1992,11 @@ | |
| 1992 | } |
| 1993 | if( showSql ) db_append_dml_to_blob(&allSql); |
| 1994 | if( zUses!=0 ){ |
| 1995 | int ufid = db_int(0, "SELECT rid FROM blob WHERE uuid GLOB '%q*'", zUses); |
| 1996 | if( ufid ){ |
| 1997 | if( robot_restrict("timelineX") ) return; |
| 1998 | zUses = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", ufid); |
| 1999 | db_multi_exec("CREATE TEMP TABLE usesfile(rid INTEGER PRIMARY KEY)"); |
| 2000 | compute_uses_file("usesfile", ufid, 0); |
| 2001 | zType = "ci"; |
| 2002 | disableY = 1; |
| @@ -3061,11 +3063,11 @@ | |
| 3063 | "brlist", "List" |
| 3064 | }; |
| 3065 | double rDate; |
| 3066 | zDate = db_text(0, "SELECT min(timestamp) FROM timeline /*scan*/"); |
| 3067 | if( (!zDate || !zDate[0]) && ( zAfter || zBefore ) ){ |
| 3068 | zDate = fossil_strdup((zAfter ? zAfter : zBefore)); |
| 3069 | } |
| 3070 | if( zDate ){ |
| 3071 | rDate = symbolic_name_to_mtime(zDate, 0, 0); |
| 3072 | if( db_int(0, |
| 3073 | "SELECT EXISTS (SELECT 1 FROM event CROSS JOIN blob" |
| @@ -3077,11 +3079,11 @@ | |
| 3079 | } |
| 3080 | free(zDate); |
| 3081 | } |
| 3082 | zDate = db_text(0, "SELECT max(timestamp) FROM timeline /*scan*/"); |
| 3083 | if( (!zDate || !zDate[0]) && ( zAfter || zBefore ) ){ |
| 3084 | zDate = fossil_strdup((zBefore ? zBefore : zAfter)); |
| 3085 | } |
| 3086 | if( zDate ){ |
| 3087 | rDate = symbolic_name_to_mtime(zDate, 0, 0); |
| 3088 | if( db_int(0, |
| 3089 | "SELECT EXISTS (SELECT 1 FROM event CROSS JOIN blob" |
| 3090 |
+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 |