Fossil SCM
Merge miscellanous auxiliary defenses and security enhancements. This check-in is not needed to fix any problems that are not already fixed in version 3.12.1. It merely provides additional defense in depth.
Commit
f741baa6be99434854b3ca8f20e95230a0e964cf03b2cb7765e685cf9e85b07d
Parent
1f4143ba285d490…
41 files changed
+30
-10
+7
-5
+6
+4
+2
+6
-8
+4
+8
-1
+367
-100
+176
-20
+2
+8
+10
-1
+2
+1
+10
-2
+10
-2
-1
+6
+21
-2
+2
+21
-3
+5
-1
+3
+14
+3
-3
+10
+22
+6
+14
+40
+2
+7
+71
+4
-2
+2
+3
-3
+5
+3
+6
+121
~
src/add.c
~
src/alerts.c
~
src/allrepo.c
~
src/backoffice.c
~
src/captcha.c
~
src/checkin.c
~
src/clone.c
~
src/configure.c
~
src/db.c
~
src/file.c
~
src/forum.c
~
src/hook.c
~
src/http.c
~
src/http_ssl.c
~
src/import.c
~
src/interwiki.c
~
src/interwiki.c
~
src/json_config.c
~
src/json_user.c
~
src/login.c
~
src/main.c
~
src/manifest.c
~
src/mkindex.c
~
src/printf.c
~
src/rebuild.c
~
src/report.c
~
src/security_audit.c
~
src/setup.c
~
src/setupuser.c
~
src/skins.c
~
src/sqlcmd.c
~
src/stash.c
~
src/sync.c
~
src/tkt.c
~
src/undo.c
~
src/update.c
~
src/url.c
~
src/user.c
~
src/vfile.c
~
src/xfer.c
~
test/reserved-names.test
+30
-10
| --- src/add.c | ||
| +++ src/add.c | ||
| @@ -156,10 +156,11 @@ | ||
| 156 | 156 | */ |
| 157 | 157 | static int add_one_file( |
| 158 | 158 | const char *zPath, /* Tree-name of file to add. */ |
| 159 | 159 | int vid /* Add to this VFILE */ |
| 160 | 160 | ){ |
| 161 | + int doSkip = 0; | |
| 161 | 162 | if( !file_is_simple_pathname(zPath, 1) ){ |
| 162 | 163 | fossil_warning("filename contains illegal characters: %s", zPath); |
| 163 | 164 | return 0; |
| 164 | 165 | } |
| 165 | 166 | if( db_exists("SELECT 1 FROM vfile" |
| @@ -168,17 +169,22 @@ | ||
| 168 | 169 | " WHERE pathname=%Q %s AND deleted", |
| 169 | 170 | zPath, filename_collation()); |
| 170 | 171 | }else{ |
| 171 | 172 | char *zFullname = mprintf("%s%s", g.zLocalRoot, zPath); |
| 172 | 173 | int isExe = file_isexe(zFullname, RepoFILE); |
| 173 | - db_multi_exec( | |
| 174 | - "INSERT INTO vfile(vid,deleted,rid,mrid,pathname,isexe,islink,mhash)" | |
| 175 | - "VALUES(%d,0,0,0,%Q,%d,%d,NULL)", | |
| 176 | - vid, zPath, isExe, file_islink(0)); | |
| 174 | + if( file_nondir_objects_on_path(g.zLocalRoot, zFullname) ){ | |
| 175 | + /* Do not add unsafe files to the vfile */ | |
| 176 | + doSkip = 1; | |
| 177 | + }else{ | |
| 178 | + db_multi_exec( | |
| 179 | + "INSERT INTO vfile(vid,deleted,rid,mrid,pathname,isexe,islink,mhash)" | |
| 180 | + "VALUES(%d,0,0,0,%Q,%d,%d,NULL)", | |
| 181 | + vid, zPath, isExe, file_islink(0)); | |
| 182 | + } | |
| 177 | 183 | fossil_free(zFullname); |
| 178 | 184 | } |
| 179 | - if( db_changes() ){ | |
| 185 | + if( db_changes() && !doSkip ){ | |
| 180 | 186 | fossil_print("ADDED %s\n", zPath); |
| 181 | 187 | return 1; |
| 182 | 188 | }else{ |
| 183 | 189 | fossil_print("SKIP %s\n", zPath); |
| 184 | 190 | return 0; |
| @@ -186,11 +192,13 @@ | ||
| 186 | 192 | } |
| 187 | 193 | |
| 188 | 194 | /* |
| 189 | 195 | ** Add all files in the sfile temp table. |
| 190 | 196 | ** |
| 191 | -** Automatically exclude the repository file. | |
| 197 | +** Automatically exclude the repository file and any other files | |
| 198 | +** with reserved names. Also exclude files that are beneath an | |
| 199 | +** existing symlink. | |
| 192 | 200 | */ |
| 193 | 201 | static int add_files_in_sfile(int vid){ |
| 194 | 202 | const char *zRepo; /* Name of the repository database file */ |
| 195 | 203 | int nAdd = 0; /* Number of files added */ |
| 196 | 204 | int i; /* Loop counter */ |
| @@ -208,18 +216,30 @@ | ||
| 208 | 216 | if( filenames_are_case_sensitive() ){ |
| 209 | 217 | xCmp = fossil_strcmp; |
| 210 | 218 | }else{ |
| 211 | 219 | xCmp = fossil_stricmp; |
| 212 | 220 | } |
| 213 | - db_prepare(&loop, "SELECT pathname FROM sfile ORDER BY pathname"); | |
| 221 | + db_prepare(&loop, | |
| 222 | + "SELECT pathname FROM sfile" | |
| 223 | + " WHERE pathname NOT IN (" | |
| 224 | + "SELECT sfile.pathname FROM vfile, sfile" | |
| 225 | + " WHERE vfile.islink" | |
| 226 | + " AND NOT vfile.deleted" | |
| 227 | + " AND sfile.pathname>(vfile.pathname||'/')" | |
| 228 | + " AND sfile.pathname<(vfile.pathname||'0'))" | |
| 229 | + " ORDER BY pathname"); | |
| 214 | 230 | while( db_step(&loop)==SQLITE_ROW ){ |
| 215 | 231 | const char *zToAdd = db_column_text(&loop, 0); |
| 216 | 232 | if( fossil_strcmp(zToAdd, zRepo)==0 ) continue; |
| 217 | - for(i=0; (zReserved = fossil_reserved_name(i, 0))!=0; i++){ | |
| 218 | - if( xCmp(zToAdd, zReserved)==0 ) break; | |
| 233 | + if( strchr(zToAdd,'/') ){ | |
| 234 | + if( file_is_reserved_name(zToAdd, -1) ) continue; | |
| 235 | + }else{ | |
| 236 | + for(i=0; (zReserved = fossil_reserved_name(i, 0))!=0; i++){ | |
| 237 | + if( xCmp(zToAdd, zReserved)==0 ) break; | |
| 238 | + } | |
| 239 | + if( zReserved ) continue; | |
| 219 | 240 | } |
| 220 | - if( zReserved ) continue; | |
| 221 | 241 | nAdd += add_one_file(zToAdd, vid); |
| 222 | 242 | } |
| 223 | 243 | db_finalize(&loop); |
| 224 | 244 | blob_reset(&repoName); |
| 225 | 245 | return nAdd; |
| 226 | 246 |
| --- src/add.c | |
| +++ src/add.c | |
| @@ -156,10 +156,11 @@ | |
| 156 | */ |
| 157 | static int add_one_file( |
| 158 | const char *zPath, /* Tree-name of file to add. */ |
| 159 | int vid /* Add to this VFILE */ |
| 160 | ){ |
| 161 | if( !file_is_simple_pathname(zPath, 1) ){ |
| 162 | fossil_warning("filename contains illegal characters: %s", zPath); |
| 163 | return 0; |
| 164 | } |
| 165 | if( db_exists("SELECT 1 FROM vfile" |
| @@ -168,17 +169,22 @@ | |
| 168 | " WHERE pathname=%Q %s AND deleted", |
| 169 | zPath, filename_collation()); |
| 170 | }else{ |
| 171 | char *zFullname = mprintf("%s%s", g.zLocalRoot, zPath); |
| 172 | int isExe = file_isexe(zFullname, RepoFILE); |
| 173 | db_multi_exec( |
| 174 | "INSERT INTO vfile(vid,deleted,rid,mrid,pathname,isexe,islink,mhash)" |
| 175 | "VALUES(%d,0,0,0,%Q,%d,%d,NULL)", |
| 176 | vid, zPath, isExe, file_islink(0)); |
| 177 | fossil_free(zFullname); |
| 178 | } |
| 179 | if( db_changes() ){ |
| 180 | fossil_print("ADDED %s\n", zPath); |
| 181 | return 1; |
| 182 | }else{ |
| 183 | fossil_print("SKIP %s\n", zPath); |
| 184 | return 0; |
| @@ -186,11 +192,13 @@ | |
| 186 | } |
| 187 | |
| 188 | /* |
| 189 | ** Add all files in the sfile temp table. |
| 190 | ** |
| 191 | ** Automatically exclude the repository file. |
| 192 | */ |
| 193 | static int add_files_in_sfile(int vid){ |
| 194 | const char *zRepo; /* Name of the repository database file */ |
| 195 | int nAdd = 0; /* Number of files added */ |
| 196 | int i; /* Loop counter */ |
| @@ -208,18 +216,30 @@ | |
| 208 | if( filenames_are_case_sensitive() ){ |
| 209 | xCmp = fossil_strcmp; |
| 210 | }else{ |
| 211 | xCmp = fossil_stricmp; |
| 212 | } |
| 213 | db_prepare(&loop, "SELECT pathname FROM sfile ORDER BY pathname"); |
| 214 | while( db_step(&loop)==SQLITE_ROW ){ |
| 215 | const char *zToAdd = db_column_text(&loop, 0); |
| 216 | if( fossil_strcmp(zToAdd, zRepo)==0 ) continue; |
| 217 | for(i=0; (zReserved = fossil_reserved_name(i, 0))!=0; i++){ |
| 218 | if( xCmp(zToAdd, zReserved)==0 ) break; |
| 219 | } |
| 220 | if( zReserved ) continue; |
| 221 | nAdd += add_one_file(zToAdd, vid); |
| 222 | } |
| 223 | db_finalize(&loop); |
| 224 | blob_reset(&repoName); |
| 225 | return nAdd; |
| 226 |
| --- src/add.c | |
| +++ src/add.c | |
| @@ -156,10 +156,11 @@ | |
| 156 | */ |
| 157 | static int add_one_file( |
| 158 | const char *zPath, /* Tree-name of file to add. */ |
| 159 | int vid /* Add to this VFILE */ |
| 160 | ){ |
| 161 | int doSkip = 0; |
| 162 | if( !file_is_simple_pathname(zPath, 1) ){ |
| 163 | fossil_warning("filename contains illegal characters: %s", zPath); |
| 164 | return 0; |
| 165 | } |
| 166 | if( db_exists("SELECT 1 FROM vfile" |
| @@ -168,17 +169,22 @@ | |
| 169 | " WHERE pathname=%Q %s AND deleted", |
| 170 | zPath, filename_collation()); |
| 171 | }else{ |
| 172 | char *zFullname = mprintf("%s%s", g.zLocalRoot, zPath); |
| 173 | int isExe = file_isexe(zFullname, RepoFILE); |
| 174 | if( file_nondir_objects_on_path(g.zLocalRoot, zFullname) ){ |
| 175 | /* Do not add unsafe files to the vfile */ |
| 176 | doSkip = 1; |
| 177 | }else{ |
| 178 | db_multi_exec( |
| 179 | "INSERT INTO vfile(vid,deleted,rid,mrid,pathname,isexe,islink,mhash)" |
| 180 | "VALUES(%d,0,0,0,%Q,%d,%d,NULL)", |
| 181 | vid, zPath, isExe, file_islink(0)); |
| 182 | } |
| 183 | fossil_free(zFullname); |
| 184 | } |
| 185 | if( db_changes() && !doSkip ){ |
| 186 | fossil_print("ADDED %s\n", zPath); |
| 187 | return 1; |
| 188 | }else{ |
| 189 | fossil_print("SKIP %s\n", zPath); |
| 190 | return 0; |
| @@ -186,11 +192,13 @@ | |
| 192 | } |
| 193 | |
| 194 | /* |
| 195 | ** Add all files in the sfile temp table. |
| 196 | ** |
| 197 | ** Automatically exclude the repository file and any other files |
| 198 | ** with reserved names. Also exclude files that are beneath an |
| 199 | ** existing symlink. |
| 200 | */ |
| 201 | static int add_files_in_sfile(int vid){ |
| 202 | const char *zRepo; /* Name of the repository database file */ |
| 203 | int nAdd = 0; /* Number of files added */ |
| 204 | int i; /* Loop counter */ |
| @@ -208,18 +216,30 @@ | |
| 216 | if( filenames_are_case_sensitive() ){ |
| 217 | xCmp = fossil_strcmp; |
| 218 | }else{ |
| 219 | xCmp = fossil_stricmp; |
| 220 | } |
| 221 | db_prepare(&loop, |
| 222 | "SELECT pathname FROM sfile" |
| 223 | " WHERE pathname NOT IN (" |
| 224 | "SELECT sfile.pathname FROM vfile, sfile" |
| 225 | " WHERE vfile.islink" |
| 226 | " AND NOT vfile.deleted" |
| 227 | " AND sfile.pathname>(vfile.pathname||'/')" |
| 228 | " AND sfile.pathname<(vfile.pathname||'0'))" |
| 229 | " ORDER BY pathname"); |
| 230 | while( db_step(&loop)==SQLITE_ROW ){ |
| 231 | const char *zToAdd = db_column_text(&loop, 0); |
| 232 | if( fossil_strcmp(zToAdd, zRepo)==0 ) continue; |
| 233 | if( strchr(zToAdd,'/') ){ |
| 234 | if( file_is_reserved_name(zToAdd, -1) ) continue; |
| 235 | }else{ |
| 236 | for(i=0; (zReserved = fossil_reserved_name(i, 0))!=0; i++){ |
| 237 | if( xCmp(zToAdd, zReserved)==0 ) break; |
| 238 | } |
| 239 | if( zReserved ) continue; |
| 240 | } |
| 241 | nAdd += add_one_file(zToAdd, vid); |
| 242 | } |
| 243 | db_finalize(&loop); |
| 244 | blob_reset(&repoName); |
| 245 | return nAdd; |
| 246 |
+7
-5
| --- src/alerts.c | ||
| +++ src/alerts.c | ||
| @@ -936,11 +936,11 @@ | ||
| 936 | 936 | ** This is a short name used to identifies the repository in the Subject: |
| 937 | 937 | ** line of email alerts. Traditionally this name is included in square |
| 938 | 938 | ** brackets. Examples: "[fossil-src]", "[sqlite-src]". |
| 939 | 939 | */ |
| 940 | 940 | /* |
| 941 | -** SETTING: email-send-method width=5 default=off | |
| 941 | +** SETTING: email-send-method width=5 default=off sensitive | |
| 942 | 942 | ** Determine the method used to send email. Allowed values are |
| 943 | 943 | ** "off", "relay", "pipe", "dir", "db", and "stdout". The "off" value |
| 944 | 944 | ** means no email is ever sent. The "relay" value means emails are sent |
| 945 | 945 | ** to an Mail Sending Agent using SMTP located at email-send-relayhost. |
| 946 | 946 | ** The "pipe" value means email messages are piped into a command |
| @@ -949,33 +949,33 @@ | ||
| 949 | 949 | ** by the email-send-dir setting. The "db" value means that emails |
| 950 | 950 | ** are added to an SQLite database named by the* email-send-db setting. |
| 951 | 951 | ** The "stdout" value writes email text to standard output, for debugging. |
| 952 | 952 | */ |
| 953 | 953 | /* |
| 954 | -** SETTING: email-send-command width=40 | |
| 954 | +** SETTING: email-send-command width=40 sensitive | |
| 955 | 955 | ** This is a command to which outbound email content is piped when the |
| 956 | 956 | ** email-send-method is set to "pipe". The command must extract |
| 957 | 957 | ** recipient, sender, subject, and all other relevant information |
| 958 | 958 | ** from the email header. |
| 959 | 959 | */ |
| 960 | 960 | /* |
| 961 | -** SETTING: email-send-dir width=40 | |
| 961 | +** SETTING: email-send-dir width=40 sensitive | |
| 962 | 962 | ** This is a directory into which outbound emails are written as individual |
| 963 | 963 | ** files if the email-send-method is set to "dir". |
| 964 | 964 | */ |
| 965 | 965 | /* |
| 966 | -** SETTING: email-send-db width=40 | |
| 966 | +** SETTING: email-send-db width=40 sensitive | |
| 967 | 967 | ** This is an SQLite database file into which outbound emails are written |
| 968 | 968 | ** if the email-send-method is set to "db". |
| 969 | 969 | */ |
| 970 | 970 | /* |
| 971 | 971 | ** SETTING: email-self width=40 |
| 972 | 972 | ** This is the email address for the repository. Outbound emails add |
| 973 | 973 | ** this email address as the "From:" field. |
| 974 | 974 | */ |
| 975 | 975 | /* |
| 976 | -** SETTING: email-send-relayhost width=40 | |
| 976 | +** SETTING: email-send-relayhost width=40 sensitive | |
| 977 | 977 | ** This is the hostname and TCP port to which output email messages |
| 978 | 978 | ** are sent when email-send-method is "relay". There should be an |
| 979 | 979 | ** SMTP server configured as a Mail Submission Agent listening on the |
| 980 | 980 | ** designated host and port and all times. |
| 981 | 981 | */ |
| @@ -1769,18 +1769,20 @@ | ||
| 1769 | 1769 | "UPDATE subscriber SET sverified=1" |
| 1770 | 1770 | " WHERE subscriberCode=hextoblob(%Q)", |
| 1771 | 1771 | zName); |
| 1772 | 1772 | if( db_get_boolean("selfreg-verify",0) ){ |
| 1773 | 1773 | char *zNewCap = db_get("default-perms","u"); |
| 1774 | + db_unprotect(PROTECT_USER); | |
| 1774 | 1775 | db_multi_exec( |
| 1775 | 1776 | "UPDATE user" |
| 1776 | 1777 | " SET cap=%Q" |
| 1777 | 1778 | " WHERE cap='7' AND login=(" |
| 1778 | 1779 | " SELECT suname FROM subscriber" |
| 1779 | 1780 | " WHERE subscriberCode=hextoblob(%Q))", |
| 1780 | 1781 | zNewCap, zName |
| 1781 | 1782 | ); |
| 1783 | + db_protect_pop(); | |
| 1782 | 1784 | login_set_capabilities(zNewCap, 0); |
| 1783 | 1785 | } |
| 1784 | 1786 | @ <h1>Your email alert subscription has been verified!</h1> |
| 1785 | 1787 | @ <p>Use the form below to update your subscription information.</p> |
| 1786 | 1788 | @ <p>Hint: Bookmark this page so that you can more easily update |
| 1787 | 1789 |
| --- src/alerts.c | |
| +++ src/alerts.c | |
| @@ -936,11 +936,11 @@ | |
| 936 | ** This is a short name used to identifies the repository in the Subject: |
| 937 | ** line of email alerts. Traditionally this name is included in square |
| 938 | ** brackets. Examples: "[fossil-src]", "[sqlite-src]". |
| 939 | */ |
| 940 | /* |
| 941 | ** SETTING: email-send-method width=5 default=off |
| 942 | ** Determine the method used to send email. Allowed values are |
| 943 | ** "off", "relay", "pipe", "dir", "db", and "stdout". The "off" value |
| 944 | ** means no email is ever sent. The "relay" value means emails are sent |
| 945 | ** to an Mail Sending Agent using SMTP located at email-send-relayhost. |
| 946 | ** The "pipe" value means email messages are piped into a command |
| @@ -949,33 +949,33 @@ | |
| 949 | ** by the email-send-dir setting. The "db" value means that emails |
| 950 | ** are added to an SQLite database named by the* email-send-db setting. |
| 951 | ** The "stdout" value writes email text to standard output, for debugging. |
| 952 | */ |
| 953 | /* |
| 954 | ** SETTING: email-send-command width=40 |
| 955 | ** This is a command to which outbound email content is piped when the |
| 956 | ** email-send-method is set to "pipe". The command must extract |
| 957 | ** recipient, sender, subject, and all other relevant information |
| 958 | ** from the email header. |
| 959 | */ |
| 960 | /* |
| 961 | ** SETTING: email-send-dir width=40 |
| 962 | ** This is a directory into which outbound emails are written as individual |
| 963 | ** files if the email-send-method is set to "dir". |
| 964 | */ |
| 965 | /* |
| 966 | ** SETTING: email-send-db width=40 |
| 967 | ** This is an SQLite database file into which outbound emails are written |
| 968 | ** if the email-send-method is set to "db". |
| 969 | */ |
| 970 | /* |
| 971 | ** SETTING: email-self width=40 |
| 972 | ** This is the email address for the repository. Outbound emails add |
| 973 | ** this email address as the "From:" field. |
| 974 | */ |
| 975 | /* |
| 976 | ** SETTING: email-send-relayhost width=40 |
| 977 | ** This is the hostname and TCP port to which output email messages |
| 978 | ** are sent when email-send-method is "relay". There should be an |
| 979 | ** SMTP server configured as a Mail Submission Agent listening on the |
| 980 | ** designated host and port and all times. |
| 981 | */ |
| @@ -1769,18 +1769,20 @@ | |
| 1769 | "UPDATE subscriber SET sverified=1" |
| 1770 | " WHERE subscriberCode=hextoblob(%Q)", |
| 1771 | zName); |
| 1772 | if( db_get_boolean("selfreg-verify",0) ){ |
| 1773 | char *zNewCap = db_get("default-perms","u"); |
| 1774 | db_multi_exec( |
| 1775 | "UPDATE user" |
| 1776 | " SET cap=%Q" |
| 1777 | " WHERE cap='7' AND login=(" |
| 1778 | " SELECT suname FROM subscriber" |
| 1779 | " WHERE subscriberCode=hextoblob(%Q))", |
| 1780 | zNewCap, zName |
| 1781 | ); |
| 1782 | login_set_capabilities(zNewCap, 0); |
| 1783 | } |
| 1784 | @ <h1>Your email alert subscription has been verified!</h1> |
| 1785 | @ <p>Use the form below to update your subscription information.</p> |
| 1786 | @ <p>Hint: Bookmark this page so that you can more easily update |
| 1787 |
| --- src/alerts.c | |
| +++ src/alerts.c | |
| @@ -936,11 +936,11 @@ | |
| 936 | ** This is a short name used to identifies the repository in the Subject: |
| 937 | ** line of email alerts. Traditionally this name is included in square |
| 938 | ** brackets. Examples: "[fossil-src]", "[sqlite-src]". |
| 939 | */ |
| 940 | /* |
| 941 | ** SETTING: email-send-method width=5 default=off sensitive |
| 942 | ** Determine the method used to send email. Allowed values are |
| 943 | ** "off", "relay", "pipe", "dir", "db", and "stdout". The "off" value |
| 944 | ** means no email is ever sent. The "relay" value means emails are sent |
| 945 | ** to an Mail Sending Agent using SMTP located at email-send-relayhost. |
| 946 | ** The "pipe" value means email messages are piped into a command |
| @@ -949,33 +949,33 @@ | |
| 949 | ** by the email-send-dir setting. The "db" value means that emails |
| 950 | ** are added to an SQLite database named by the* email-send-db setting. |
| 951 | ** The "stdout" value writes email text to standard output, for debugging. |
| 952 | */ |
| 953 | /* |
| 954 | ** SETTING: email-send-command width=40 sensitive |
| 955 | ** This is a command to which outbound email content is piped when the |
| 956 | ** email-send-method is set to "pipe". The command must extract |
| 957 | ** recipient, sender, subject, and all other relevant information |
| 958 | ** from the email header. |
| 959 | */ |
| 960 | /* |
| 961 | ** SETTING: email-send-dir width=40 sensitive |
| 962 | ** This is a directory into which outbound emails are written as individual |
| 963 | ** files if the email-send-method is set to "dir". |
| 964 | */ |
| 965 | /* |
| 966 | ** SETTING: email-send-db width=40 sensitive |
| 967 | ** This is an SQLite database file into which outbound emails are written |
| 968 | ** if the email-send-method is set to "db". |
| 969 | */ |
| 970 | /* |
| 971 | ** SETTING: email-self width=40 |
| 972 | ** This is the email address for the repository. Outbound emails add |
| 973 | ** this email address as the "From:" field. |
| 974 | */ |
| 975 | /* |
| 976 | ** SETTING: email-send-relayhost width=40 sensitive |
| 977 | ** This is the hostname and TCP port to which output email messages |
| 978 | ** are sent when email-send-method is "relay". There should be an |
| 979 | ** SMTP server configured as a Mail Submission Agent listening on the |
| 980 | ** designated host and port and all times. |
| 981 | */ |
| @@ -1769,18 +1769,20 @@ | |
| 1769 | "UPDATE subscriber SET sverified=1" |
| 1770 | " WHERE subscriberCode=hextoblob(%Q)", |
| 1771 | zName); |
| 1772 | if( db_get_boolean("selfreg-verify",0) ){ |
| 1773 | char *zNewCap = db_get("default-perms","u"); |
| 1774 | db_unprotect(PROTECT_USER); |
| 1775 | db_multi_exec( |
| 1776 | "UPDATE user" |
| 1777 | " SET cap=%Q" |
| 1778 | " WHERE cap='7' AND login=(" |
| 1779 | " SELECT suname FROM subscriber" |
| 1780 | " WHERE subscriberCode=hextoblob(%Q))", |
| 1781 | zNewCap, zName |
| 1782 | ); |
| 1783 | db_protect_pop(); |
| 1784 | login_set_capabilities(zNewCap, 0); |
| 1785 | } |
| 1786 | @ <h1>Your email alert subscription has been verified!</h1> |
| 1787 | @ <p>Use the form below to update your subscription information.</p> |
| 1788 | @ <p>Hint: Bookmark this page so that you can more easily update |
| 1789 |
+6
| --- src/allrepo.c | ||
| +++ src/allrepo.c | ||
| @@ -299,11 +299,13 @@ | ||
| 299 | 299 | useCheckouts?"ckout":"repo", blob_str(&fn) |
| 300 | 300 | ); |
| 301 | 301 | if( dryRunFlag ){ |
| 302 | 302 | fossil_print("%s\n", blob_sql_text(&sql)); |
| 303 | 303 | }else{ |
| 304 | + db_unprotect(PROTECT_CONFIG); | |
| 304 | 305 | db_multi_exec("%s", blob_sql_text(&sql)); |
| 306 | + db_protect_pop(); | |
| 305 | 307 | } |
| 306 | 308 | } |
| 307 | 309 | db_end_transaction(0); |
| 308 | 310 | blob_reset(&sql); |
| 309 | 311 | blob_reset(&fn); |
| @@ -334,11 +336,13 @@ | ||
| 334 | 336 | "VALUES('repo:%q',1)", z |
| 335 | 337 | ); |
| 336 | 338 | if( dryRunFlag ){ |
| 337 | 339 | fossil_print("%s\n", blob_sql_text(&sql)); |
| 338 | 340 | }else{ |
| 341 | + db_unprotect(PROTECT_CONFIG); | |
| 339 | 342 | db_multi_exec("%s", blob_sql_text(&sql)); |
| 343 | + db_protect_pop(); | |
| 340 | 344 | } |
| 341 | 345 | } |
| 342 | 346 | db_end_transaction(0); |
| 343 | 347 | blob_reset(&sql); |
| 344 | 348 | blob_reset(&fn); |
| @@ -428,9 +432,11 @@ | ||
| 428 | 432 | if( nToDel>0 ){ |
| 429 | 433 | const char *zSql = "DELETE FROM global_config WHERE name IN toDel"; |
| 430 | 434 | if( dryRunFlag ){ |
| 431 | 435 | fossil_print("%s\n", zSql); |
| 432 | 436 | }else{ |
| 437 | + db_unprotect(PROTECT_CONFIG); | |
| 433 | 438 | db_multi_exec("%s", zSql /*safe-for-%s*/ ); |
| 439 | + db_protect_pop(); | |
| 434 | 440 | } |
| 435 | 441 | } |
| 436 | 442 | } |
| 437 | 443 |
| --- src/allrepo.c | |
| +++ src/allrepo.c | |
| @@ -299,11 +299,13 @@ | |
| 299 | useCheckouts?"ckout":"repo", blob_str(&fn) |
| 300 | ); |
| 301 | if( dryRunFlag ){ |
| 302 | fossil_print("%s\n", blob_sql_text(&sql)); |
| 303 | }else{ |
| 304 | db_multi_exec("%s", blob_sql_text(&sql)); |
| 305 | } |
| 306 | } |
| 307 | db_end_transaction(0); |
| 308 | blob_reset(&sql); |
| 309 | blob_reset(&fn); |
| @@ -334,11 +336,13 @@ | |
| 334 | "VALUES('repo:%q',1)", z |
| 335 | ); |
| 336 | if( dryRunFlag ){ |
| 337 | fossil_print("%s\n", blob_sql_text(&sql)); |
| 338 | }else{ |
| 339 | db_multi_exec("%s", blob_sql_text(&sql)); |
| 340 | } |
| 341 | } |
| 342 | db_end_transaction(0); |
| 343 | blob_reset(&sql); |
| 344 | blob_reset(&fn); |
| @@ -428,9 +432,11 @@ | |
| 428 | if( nToDel>0 ){ |
| 429 | const char *zSql = "DELETE FROM global_config WHERE name IN toDel"; |
| 430 | if( dryRunFlag ){ |
| 431 | fossil_print("%s\n", zSql); |
| 432 | }else{ |
| 433 | db_multi_exec("%s", zSql /*safe-for-%s*/ ); |
| 434 | } |
| 435 | } |
| 436 | } |
| 437 |
| --- src/allrepo.c | |
| +++ src/allrepo.c | |
| @@ -299,11 +299,13 @@ | |
| 299 | useCheckouts?"ckout":"repo", blob_str(&fn) |
| 300 | ); |
| 301 | if( dryRunFlag ){ |
| 302 | fossil_print("%s\n", blob_sql_text(&sql)); |
| 303 | }else{ |
| 304 | db_unprotect(PROTECT_CONFIG); |
| 305 | db_multi_exec("%s", blob_sql_text(&sql)); |
| 306 | db_protect_pop(); |
| 307 | } |
| 308 | } |
| 309 | db_end_transaction(0); |
| 310 | blob_reset(&sql); |
| 311 | blob_reset(&fn); |
| @@ -334,11 +336,13 @@ | |
| 336 | "VALUES('repo:%q',1)", z |
| 337 | ); |
| 338 | if( dryRunFlag ){ |
| 339 | fossil_print("%s\n", blob_sql_text(&sql)); |
| 340 | }else{ |
| 341 | db_unprotect(PROTECT_CONFIG); |
| 342 | db_multi_exec("%s", blob_sql_text(&sql)); |
| 343 | db_protect_pop(); |
| 344 | } |
| 345 | } |
| 346 | db_end_transaction(0); |
| 347 | blob_reset(&sql); |
| 348 | blob_reset(&fn); |
| @@ -428,9 +432,11 @@ | |
| 432 | if( nToDel>0 ){ |
| 433 | const char *zSql = "DELETE FROM global_config WHERE name IN toDel"; |
| 434 | if( dryRunFlag ){ |
| 435 | fossil_print("%s\n", zSql); |
| 436 | }else{ |
| 437 | db_unprotect(PROTECT_CONFIG); |
| 438 | db_multi_exec("%s", zSql /*safe-for-%s*/ ); |
| 439 | db_protect_pop(); |
| 440 | } |
| 441 | } |
| 442 | } |
| 443 |
+4
| --- src/backoffice.c | ||
| +++ src/backoffice.c | ||
| @@ -241,10 +241,11 @@ | ||
| 241 | 241 | ** process (1) no longer exists and the current time exceeds (2). |
| 242 | 242 | */ |
| 243 | 243 | static void backofficeReadLease(Lease *pLease){ |
| 244 | 244 | Stmt q; |
| 245 | 245 | memset(pLease, 0, sizeof(*pLease)); |
| 246 | + db_unprotect(PROTECT_CONFIG); | |
| 246 | 247 | db_prepare(&q, "SELECT value FROM repository.config" |
| 247 | 248 | " WHERE name='backoffice'"); |
| 248 | 249 | if( db_step(&q)==SQLITE_ROW ){ |
| 249 | 250 | const char *z = db_column_text(&q,0); |
| 250 | 251 | z = backofficeParseInt(z, &pLease->idCurrent); |
| @@ -251,10 +252,11 @@ | ||
| 251 | 252 | z = backofficeParseInt(z, &pLease->tmCurrent); |
| 252 | 253 | z = backofficeParseInt(z, &pLease->idNext); |
| 253 | 254 | backofficeParseInt(z, &pLease->tmNext); |
| 254 | 255 | } |
| 255 | 256 | db_finalize(&q); |
| 257 | + db_protect_pop(); | |
| 256 | 258 | } |
| 257 | 259 | |
| 258 | 260 | /* |
| 259 | 261 | ** Return a string that describes how long it has been since the |
| 260 | 262 | ** last backoffice run. The string is obtained from fossil_malloc(). |
| @@ -277,15 +279,17 @@ | ||
| 277 | 279 | |
| 278 | 280 | /* |
| 279 | 281 | ** Write a lease to the backoffice property |
| 280 | 282 | */ |
| 281 | 283 | static void backofficeWriteLease(Lease *pLease){ |
| 284 | + db_unprotect(PROTECT_CONFIG); | |
| 282 | 285 | db_multi_exec( |
| 283 | 286 | "REPLACE INTO repository.config(name,value,mtime)" |
| 284 | 287 | " VALUES('backoffice','%lld %lld %lld %lld',now())", |
| 285 | 288 | pLease->idCurrent, pLease->tmCurrent, |
| 286 | 289 | pLease->idNext, pLease->tmNext); |
| 290 | + db_protect_pop(); | |
| 287 | 291 | } |
| 288 | 292 | |
| 289 | 293 | /* |
| 290 | 294 | ** Check to see if the specified Win32 process is still alive. It |
| 291 | 295 | ** should be noted that even if this function returns non-zero, the |
| 292 | 296 |
| --- src/backoffice.c | |
| +++ src/backoffice.c | |
| @@ -241,10 +241,11 @@ | |
| 241 | ** process (1) no longer exists and the current time exceeds (2). |
| 242 | */ |
| 243 | static void backofficeReadLease(Lease *pLease){ |
| 244 | Stmt q; |
| 245 | memset(pLease, 0, sizeof(*pLease)); |
| 246 | db_prepare(&q, "SELECT value FROM repository.config" |
| 247 | " WHERE name='backoffice'"); |
| 248 | if( db_step(&q)==SQLITE_ROW ){ |
| 249 | const char *z = db_column_text(&q,0); |
| 250 | z = backofficeParseInt(z, &pLease->idCurrent); |
| @@ -251,10 +252,11 @@ | |
| 251 | z = backofficeParseInt(z, &pLease->tmCurrent); |
| 252 | z = backofficeParseInt(z, &pLease->idNext); |
| 253 | backofficeParseInt(z, &pLease->tmNext); |
| 254 | } |
| 255 | db_finalize(&q); |
| 256 | } |
| 257 | |
| 258 | /* |
| 259 | ** Return a string that describes how long it has been since the |
| 260 | ** last backoffice run. The string is obtained from fossil_malloc(). |
| @@ -277,15 +279,17 @@ | |
| 277 | |
| 278 | /* |
| 279 | ** Write a lease to the backoffice property |
| 280 | */ |
| 281 | static void backofficeWriteLease(Lease *pLease){ |
| 282 | db_multi_exec( |
| 283 | "REPLACE INTO repository.config(name,value,mtime)" |
| 284 | " VALUES('backoffice','%lld %lld %lld %lld',now())", |
| 285 | pLease->idCurrent, pLease->tmCurrent, |
| 286 | pLease->idNext, pLease->tmNext); |
| 287 | } |
| 288 | |
| 289 | /* |
| 290 | ** Check to see if the specified Win32 process is still alive. It |
| 291 | ** should be noted that even if this function returns non-zero, the |
| 292 |
| --- src/backoffice.c | |
| +++ src/backoffice.c | |
| @@ -241,10 +241,11 @@ | |
| 241 | ** process (1) no longer exists and the current time exceeds (2). |
| 242 | */ |
| 243 | static void backofficeReadLease(Lease *pLease){ |
| 244 | Stmt q; |
| 245 | memset(pLease, 0, sizeof(*pLease)); |
| 246 | db_unprotect(PROTECT_CONFIG); |
| 247 | db_prepare(&q, "SELECT value FROM repository.config" |
| 248 | " WHERE name='backoffice'"); |
| 249 | if( db_step(&q)==SQLITE_ROW ){ |
| 250 | const char *z = db_column_text(&q,0); |
| 251 | z = backofficeParseInt(z, &pLease->idCurrent); |
| @@ -251,10 +252,11 @@ | |
| 252 | z = backofficeParseInt(z, &pLease->tmCurrent); |
| 253 | z = backofficeParseInt(z, &pLease->idNext); |
| 254 | backofficeParseInt(z, &pLease->tmNext); |
| 255 | } |
| 256 | db_finalize(&q); |
| 257 | db_protect_pop(); |
| 258 | } |
| 259 | |
| 260 | /* |
| 261 | ** Return a string that describes how long it has been since the |
| 262 | ** last backoffice run. The string is obtained from fossil_malloc(). |
| @@ -277,15 +279,17 @@ | |
| 279 | |
| 280 | /* |
| 281 | ** Write a lease to the backoffice property |
| 282 | */ |
| 283 | static void backofficeWriteLease(Lease *pLease){ |
| 284 | db_unprotect(PROTECT_CONFIG); |
| 285 | db_multi_exec( |
| 286 | "REPLACE INTO repository.config(name,value,mtime)" |
| 287 | " VALUES('backoffice','%lld %lld %lld %lld',now())", |
| 288 | pLease->idCurrent, pLease->tmCurrent, |
| 289 | pLease->idNext, pLease->tmNext); |
| 290 | db_protect_pop(); |
| 291 | } |
| 292 | |
| 293 | /* |
| 294 | ** Check to see if the specified Win32 process is still alive. It |
| 295 | ** should be noted that even if this function returns non-zero, the |
| 296 |
+2
| --- src/captcha.c | ||
| +++ src/captcha.c | ||
| @@ -458,14 +458,16 @@ | ||
| 458 | 458 | Blob b; |
| 459 | 459 | static char zRes[20]; |
| 460 | 460 | |
| 461 | 461 | zSecret = db_get("captcha-secret", 0); |
| 462 | 462 | if( zSecret==0 ){ |
| 463 | + db_unprotect(PROTECT_CONFIG); | |
| 463 | 464 | db_multi_exec( |
| 464 | 465 | "REPLACE INTO config(name,value)" |
| 465 | 466 | " VALUES('captcha-secret', lower(hex(randomblob(20))));" |
| 466 | 467 | ); |
| 468 | + db_protect_pop(); | |
| 467 | 469 | zSecret = db_get("captcha-secret", 0); |
| 468 | 470 | assert( zSecret!=0 ); |
| 469 | 471 | } |
| 470 | 472 | blob_init(&b, 0, 0); |
| 471 | 473 | blob_appendf(&b, "%s-%x", zSecret, seed); |
| 472 | 474 |
| --- src/captcha.c | |
| +++ src/captcha.c | |
| @@ -458,14 +458,16 @@ | |
| 458 | Blob b; |
| 459 | static char zRes[20]; |
| 460 | |
| 461 | zSecret = db_get("captcha-secret", 0); |
| 462 | if( zSecret==0 ){ |
| 463 | db_multi_exec( |
| 464 | "REPLACE INTO config(name,value)" |
| 465 | " VALUES('captcha-secret', lower(hex(randomblob(20))));" |
| 466 | ); |
| 467 | zSecret = db_get("captcha-secret", 0); |
| 468 | assert( zSecret!=0 ); |
| 469 | } |
| 470 | blob_init(&b, 0, 0); |
| 471 | blob_appendf(&b, "%s-%x", zSecret, seed); |
| 472 |
| --- src/captcha.c | |
| +++ src/captcha.c | |
| @@ -458,14 +458,16 @@ | |
| 458 | Blob b; |
| 459 | static char zRes[20]; |
| 460 | |
| 461 | zSecret = db_get("captcha-secret", 0); |
| 462 | if( zSecret==0 ){ |
| 463 | db_unprotect(PROTECT_CONFIG); |
| 464 | db_multi_exec( |
| 465 | "REPLACE INTO config(name,value)" |
| 466 | " VALUES('captcha-secret', lower(hex(randomblob(20))));" |
| 467 | ); |
| 468 | db_protect_pop(); |
| 469 | zSecret = db_get("captcha-secret", 0); |
| 470 | assert( zSecret!=0 ); |
| 471 | } |
| 472 | blob_init(&b, 0, 0); |
| 473 | blob_appendf(&b, "%s-%x", zSecret, seed); |
| 474 |
+6
-8
| --- src/checkin.c | ||
| +++ src/checkin.c | ||
| @@ -62,10 +62,13 @@ | ||
| 62 | 62 | ** Create a TEMP table named SFILE and add all unmanaged files named on |
| 63 | 63 | ** the command-line to that table. If directories are named, then add |
| 64 | 64 | ** all unmanaged files contained underneath those directories. If there |
| 65 | 65 | ** are no files or directories named on the command-line, then add all |
| 66 | 66 | ** unmanaged files anywhere in the checkout. |
| 67 | +** | |
| 68 | +** This routine never follows symlinks. It always treats symlinks as | |
| 69 | +** object unto themselves. | |
| 67 | 70 | */ |
| 68 | 71 | static void locate_unmanaged_files( |
| 69 | 72 | int argc, /* Number of command-line arguments to examine */ |
| 70 | 73 | char **argv, /* values of command-line arguments */ |
| 71 | 74 | unsigned scanFlags, /* Zero or more SCAN_xxx flags */ |
| @@ -80,19 +83,19 @@ | ||
| 80 | 83 | db_multi_exec("CREATE TEMP TABLE sfile(pathname TEXT PRIMARY KEY %s," |
| 81 | 84 | " mtime INTEGER, size INTEGER)", filename_collation()); |
| 82 | 85 | nRoot = (int)strlen(g.zLocalRoot); |
| 83 | 86 | if( argc==0 ){ |
| 84 | 87 | blob_init(&name, g.zLocalRoot, nRoot - 1); |
| 85 | - vfile_scan(&name, blob_size(&name), scanFlags, pIgnore, 0, RepoFILE); | |
| 88 | + vfile_scan(&name, blob_size(&name), scanFlags, pIgnore, 0, SymFILE); | |
| 86 | 89 | blob_reset(&name); |
| 87 | 90 | }else{ |
| 88 | 91 | for(i=0; i<argc; i++){ |
| 89 | 92 | file_canonical_name(argv[i], &name, 0); |
| 90 | 93 | zName = blob_str(&name); |
| 91 | - isDir = file_isdir(zName, RepoFILE); | |
| 94 | + isDir = file_isdir(zName, SymFILE); | |
| 92 | 95 | if( isDir==1 ){ |
| 93 | - vfile_scan(&name, nRoot-1, scanFlags, pIgnore, 0, RepoFILE); | |
| 96 | + vfile_scan(&name, nRoot-1, scanFlags, pIgnore, 0, SymFILE); | |
| 94 | 97 | }else if( isDir==0 ){ |
| 95 | 98 | fossil_warning("not found: %s", &zName[nRoot]); |
| 96 | 99 | }else if( file_access(zName, R_OK) ){ |
| 97 | 100 | fossil_fatal("cannot open %s", &zName[nRoot]); |
| 98 | 101 | }else{ |
| @@ -856,12 +859,10 @@ | ||
| 856 | 859 | |
| 857 | 860 | if( zIgnoreFlag==0 ){ |
| 858 | 861 | zIgnoreFlag = db_get("ignore-glob", 0); |
| 859 | 862 | } |
| 860 | 863 | pIgnore = glob_create(zIgnoreFlag); |
| 861 | - /* Always consider symlinks. */ | |
| 862 | - g.allowSymlinks = db_allow_symlinks_by_default(); | |
| 863 | 864 | locate_unmanaged_files(g.argc-2, g.argv+2, scanFlags, pIgnore); |
| 864 | 865 | glob_free(pIgnore); |
| 865 | 866 | |
| 866 | 867 | blob_zero(&report); |
| 867 | 868 | status_report(&report, flags); |
| @@ -1015,12 +1016,10 @@ | ||
| 1015 | 1016 | verify_all_options(); |
| 1016 | 1017 | pIgnore = glob_create(zIgnoreFlag); |
| 1017 | 1018 | pKeep = glob_create(zKeepFlag); |
| 1018 | 1019 | pClean = glob_create(zCleanFlag); |
| 1019 | 1020 | nRoot = (int)strlen(g.zLocalRoot); |
| 1020 | - /* Always consider symlinks. */ | |
| 1021 | - g.allowSymlinks = db_allow_symlinks_by_default(); | |
| 1022 | 1021 | if( !dirsOnlyFlag ){ |
| 1023 | 1022 | Stmt q; |
| 1024 | 1023 | Blob repo; |
| 1025 | 1024 | if( !dryRunFlag && !disableUndo ) undo_begin(); |
| 1026 | 1025 | locate_unmanaged_files(g.argc-2, g.argv+2, scanFlags, pIgnore); |
| @@ -2734,11 +2733,10 @@ | ||
| 2734 | 2733 | /* Commit */ |
| 2735 | 2734 | db_multi_exec("DELETE FROM vvar WHERE name='ci-comment'"); |
| 2736 | 2735 | db_multi_exec("PRAGMA repository.application_id=252006673;"); |
| 2737 | 2736 | db_multi_exec("PRAGMA localdb.application_id=252006674;"); |
| 2738 | 2737 | if( dryRunFlag ){ |
| 2739 | - leaf_ambiguity_warning(nvid,nvid); | |
| 2740 | 2738 | db_end_transaction(1); |
| 2741 | 2739 | exit(1); |
| 2742 | 2740 | } |
| 2743 | 2741 | db_end_transaction(0); |
| 2744 | 2742 | |
| 2745 | 2743 |
| --- src/checkin.c | |
| +++ src/checkin.c | |
| @@ -62,10 +62,13 @@ | |
| 62 | ** Create a TEMP table named SFILE and add all unmanaged files named on |
| 63 | ** the command-line to that table. If directories are named, then add |
| 64 | ** all unmanaged files contained underneath those directories. If there |
| 65 | ** are no files or directories named on the command-line, then add all |
| 66 | ** unmanaged files anywhere in the checkout. |
| 67 | */ |
| 68 | static void locate_unmanaged_files( |
| 69 | int argc, /* Number of command-line arguments to examine */ |
| 70 | char **argv, /* values of command-line arguments */ |
| 71 | unsigned scanFlags, /* Zero or more SCAN_xxx flags */ |
| @@ -80,19 +83,19 @@ | |
| 80 | db_multi_exec("CREATE TEMP TABLE sfile(pathname TEXT PRIMARY KEY %s," |
| 81 | " mtime INTEGER, size INTEGER)", filename_collation()); |
| 82 | nRoot = (int)strlen(g.zLocalRoot); |
| 83 | if( argc==0 ){ |
| 84 | blob_init(&name, g.zLocalRoot, nRoot - 1); |
| 85 | vfile_scan(&name, blob_size(&name), scanFlags, pIgnore, 0, RepoFILE); |
| 86 | blob_reset(&name); |
| 87 | }else{ |
| 88 | for(i=0; i<argc; i++){ |
| 89 | file_canonical_name(argv[i], &name, 0); |
| 90 | zName = blob_str(&name); |
| 91 | isDir = file_isdir(zName, RepoFILE); |
| 92 | if( isDir==1 ){ |
| 93 | vfile_scan(&name, nRoot-1, scanFlags, pIgnore, 0, RepoFILE); |
| 94 | }else if( isDir==0 ){ |
| 95 | fossil_warning("not found: %s", &zName[nRoot]); |
| 96 | }else if( file_access(zName, R_OK) ){ |
| 97 | fossil_fatal("cannot open %s", &zName[nRoot]); |
| 98 | }else{ |
| @@ -856,12 +859,10 @@ | |
| 856 | |
| 857 | if( zIgnoreFlag==0 ){ |
| 858 | zIgnoreFlag = db_get("ignore-glob", 0); |
| 859 | } |
| 860 | pIgnore = glob_create(zIgnoreFlag); |
| 861 | /* Always consider symlinks. */ |
| 862 | g.allowSymlinks = db_allow_symlinks_by_default(); |
| 863 | locate_unmanaged_files(g.argc-2, g.argv+2, scanFlags, pIgnore); |
| 864 | glob_free(pIgnore); |
| 865 | |
| 866 | blob_zero(&report); |
| 867 | status_report(&report, flags); |
| @@ -1015,12 +1016,10 @@ | |
| 1015 | verify_all_options(); |
| 1016 | pIgnore = glob_create(zIgnoreFlag); |
| 1017 | pKeep = glob_create(zKeepFlag); |
| 1018 | pClean = glob_create(zCleanFlag); |
| 1019 | nRoot = (int)strlen(g.zLocalRoot); |
| 1020 | /* Always consider symlinks. */ |
| 1021 | g.allowSymlinks = db_allow_symlinks_by_default(); |
| 1022 | if( !dirsOnlyFlag ){ |
| 1023 | Stmt q; |
| 1024 | Blob repo; |
| 1025 | if( !dryRunFlag && !disableUndo ) undo_begin(); |
| 1026 | locate_unmanaged_files(g.argc-2, g.argv+2, scanFlags, pIgnore); |
| @@ -2734,11 +2733,10 @@ | |
| 2734 | /* Commit */ |
| 2735 | db_multi_exec("DELETE FROM vvar WHERE name='ci-comment'"); |
| 2736 | db_multi_exec("PRAGMA repository.application_id=252006673;"); |
| 2737 | db_multi_exec("PRAGMA localdb.application_id=252006674;"); |
| 2738 | if( dryRunFlag ){ |
| 2739 | leaf_ambiguity_warning(nvid,nvid); |
| 2740 | db_end_transaction(1); |
| 2741 | exit(1); |
| 2742 | } |
| 2743 | db_end_transaction(0); |
| 2744 | |
| 2745 |
| --- src/checkin.c | |
| +++ src/checkin.c | |
| @@ -62,10 +62,13 @@ | |
| 62 | ** Create a TEMP table named SFILE and add all unmanaged files named on |
| 63 | ** the command-line to that table. If directories are named, then add |
| 64 | ** all unmanaged files contained underneath those directories. If there |
| 65 | ** are no files or directories named on the command-line, then add all |
| 66 | ** unmanaged files anywhere in the checkout. |
| 67 | ** |
| 68 | ** This routine never follows symlinks. It always treats symlinks as |
| 69 | ** object unto themselves. |
| 70 | */ |
| 71 | static void locate_unmanaged_files( |
| 72 | int argc, /* Number of command-line arguments to examine */ |
| 73 | char **argv, /* values of command-line arguments */ |
| 74 | unsigned scanFlags, /* Zero or more SCAN_xxx flags */ |
| @@ -80,19 +83,19 @@ | |
| 83 | db_multi_exec("CREATE TEMP TABLE sfile(pathname TEXT PRIMARY KEY %s," |
| 84 | " mtime INTEGER, size INTEGER)", filename_collation()); |
| 85 | nRoot = (int)strlen(g.zLocalRoot); |
| 86 | if( argc==0 ){ |
| 87 | blob_init(&name, g.zLocalRoot, nRoot - 1); |
| 88 | vfile_scan(&name, blob_size(&name), scanFlags, pIgnore, 0, SymFILE); |
| 89 | blob_reset(&name); |
| 90 | }else{ |
| 91 | for(i=0; i<argc; i++){ |
| 92 | file_canonical_name(argv[i], &name, 0); |
| 93 | zName = blob_str(&name); |
| 94 | isDir = file_isdir(zName, SymFILE); |
| 95 | if( isDir==1 ){ |
| 96 | vfile_scan(&name, nRoot-1, scanFlags, pIgnore, 0, SymFILE); |
| 97 | }else if( isDir==0 ){ |
| 98 | fossil_warning("not found: %s", &zName[nRoot]); |
| 99 | }else if( file_access(zName, R_OK) ){ |
| 100 | fossil_fatal("cannot open %s", &zName[nRoot]); |
| 101 | }else{ |
| @@ -856,12 +859,10 @@ | |
| 859 | |
| 860 | if( zIgnoreFlag==0 ){ |
| 861 | zIgnoreFlag = db_get("ignore-glob", 0); |
| 862 | } |
| 863 | pIgnore = glob_create(zIgnoreFlag); |
| 864 | locate_unmanaged_files(g.argc-2, g.argv+2, scanFlags, pIgnore); |
| 865 | glob_free(pIgnore); |
| 866 | |
| 867 | blob_zero(&report); |
| 868 | status_report(&report, flags); |
| @@ -1015,12 +1016,10 @@ | |
| 1016 | verify_all_options(); |
| 1017 | pIgnore = glob_create(zIgnoreFlag); |
| 1018 | pKeep = glob_create(zKeepFlag); |
| 1019 | pClean = glob_create(zCleanFlag); |
| 1020 | nRoot = (int)strlen(g.zLocalRoot); |
| 1021 | if( !dirsOnlyFlag ){ |
| 1022 | Stmt q; |
| 1023 | Blob repo; |
| 1024 | if( !dryRunFlag && !disableUndo ) undo_begin(); |
| 1025 | locate_unmanaged_files(g.argc-2, g.argv+2, scanFlags, pIgnore); |
| @@ -2734,11 +2733,10 @@ | |
| 2733 | /* Commit */ |
| 2734 | db_multi_exec("DELETE FROM vvar WHERE name='ci-comment'"); |
| 2735 | db_multi_exec("PRAGMA repository.application_id=252006673;"); |
| 2736 | db_multi_exec("PRAGMA localdb.application_id=252006674;"); |
| 2737 | if( dryRunFlag ){ |
| 2738 | db_end_transaction(1); |
| 2739 | exit(1); |
| 2740 | } |
| 2741 | db_end_transaction(0); |
| 2742 | |
| 2743 |
+4
| --- src/clone.c | ||
| +++ src/clone.c | ||
| @@ -202,15 +202,17 @@ | ||
| 202 | 202 | blob_zero(&fn); |
| 203 | 203 | file_canonical_name(g.zSSLIdentity, &fn, 0); |
| 204 | 204 | db_set("ssl-identity", blob_str(&fn), 0); |
| 205 | 205 | blob_reset(&fn); |
| 206 | 206 | } |
| 207 | + db_unprotect(PROTECT_CONFIG); | |
| 207 | 208 | db_multi_exec( |
| 208 | 209 | "REPLACE INTO config(name,value,mtime)" |
| 209 | 210 | " VALUES('server-code', lower(hex(randomblob(20))), now());" |
| 210 | 211 | "DELETE FROM config WHERE name='project-code';" |
| 211 | 212 | ); |
| 213 | + db_protect_pop(); | |
| 212 | 214 | url_enable_proxy(0); |
| 213 | 215 | clone_ssh_db_set_options(); |
| 214 | 216 | url_get_password_if_needed(); |
| 215 | 217 | g.xlinkClusterOnly = 1; |
| 216 | 218 | nErr = client_sync(syncFlags,CONFIGSET_ALL,0,0); |
| @@ -236,11 +238,13 @@ | ||
| 236 | 238 | fossil_print("Vacuuming the database... "); fflush(stdout); |
| 237 | 239 | if( db_int(0, "PRAGMA page_count")>1000 |
| 238 | 240 | && db_int(0, "PRAGMA page_size")<8192 ){ |
| 239 | 241 | db_multi_exec("PRAGMA page_size=8192;"); |
| 240 | 242 | } |
| 243 | + db_unprotect(PROTECT_ALL); | |
| 241 | 244 | db_multi_exec("VACUUM"); |
| 245 | + db_protect_pop(); | |
| 242 | 246 | fossil_print("\nproject-id: %s\n", db_get("project-code", 0)); |
| 243 | 247 | fossil_print("server-id: %s\n", db_get("server-code", 0)); |
| 244 | 248 | zPassword = db_text(0, "SELECT pw FROM user WHERE login=%Q", g.zLogin); |
| 245 | 249 | fossil_print("admin-user: %s (password is \"%s\")\n", g.zLogin, zPassword); |
| 246 | 250 | } |
| 247 | 251 |
| --- src/clone.c | |
| +++ src/clone.c | |
| @@ -202,15 +202,17 @@ | |
| 202 | blob_zero(&fn); |
| 203 | file_canonical_name(g.zSSLIdentity, &fn, 0); |
| 204 | db_set("ssl-identity", blob_str(&fn), 0); |
| 205 | blob_reset(&fn); |
| 206 | } |
| 207 | db_multi_exec( |
| 208 | "REPLACE INTO config(name,value,mtime)" |
| 209 | " VALUES('server-code', lower(hex(randomblob(20))), now());" |
| 210 | "DELETE FROM config WHERE name='project-code';" |
| 211 | ); |
| 212 | url_enable_proxy(0); |
| 213 | clone_ssh_db_set_options(); |
| 214 | url_get_password_if_needed(); |
| 215 | g.xlinkClusterOnly = 1; |
| 216 | nErr = client_sync(syncFlags,CONFIGSET_ALL,0,0); |
| @@ -236,11 +238,13 @@ | |
| 236 | fossil_print("Vacuuming the database... "); fflush(stdout); |
| 237 | if( db_int(0, "PRAGMA page_count")>1000 |
| 238 | && db_int(0, "PRAGMA page_size")<8192 ){ |
| 239 | db_multi_exec("PRAGMA page_size=8192;"); |
| 240 | } |
| 241 | db_multi_exec("VACUUM"); |
| 242 | fossil_print("\nproject-id: %s\n", db_get("project-code", 0)); |
| 243 | fossil_print("server-id: %s\n", db_get("server-code", 0)); |
| 244 | zPassword = db_text(0, "SELECT pw FROM user WHERE login=%Q", g.zLogin); |
| 245 | fossil_print("admin-user: %s (password is \"%s\")\n", g.zLogin, zPassword); |
| 246 | } |
| 247 |
| --- src/clone.c | |
| +++ src/clone.c | |
| @@ -202,15 +202,17 @@ | |
| 202 | blob_zero(&fn); |
| 203 | file_canonical_name(g.zSSLIdentity, &fn, 0); |
| 204 | db_set("ssl-identity", blob_str(&fn), 0); |
| 205 | blob_reset(&fn); |
| 206 | } |
| 207 | db_unprotect(PROTECT_CONFIG); |
| 208 | db_multi_exec( |
| 209 | "REPLACE INTO config(name,value,mtime)" |
| 210 | " VALUES('server-code', lower(hex(randomblob(20))), now());" |
| 211 | "DELETE FROM config WHERE name='project-code';" |
| 212 | ); |
| 213 | db_protect_pop(); |
| 214 | url_enable_proxy(0); |
| 215 | clone_ssh_db_set_options(); |
| 216 | url_get_password_if_needed(); |
| 217 | g.xlinkClusterOnly = 1; |
| 218 | nErr = client_sync(syncFlags,CONFIGSET_ALL,0,0); |
| @@ -236,11 +238,13 @@ | |
| 238 | fossil_print("Vacuuming the database... "); fflush(stdout); |
| 239 | if( db_int(0, "PRAGMA page_count")>1000 |
| 240 | && db_int(0, "PRAGMA page_size")<8192 ){ |
| 241 | db_multi_exec("PRAGMA page_size=8192;"); |
| 242 | } |
| 243 | db_unprotect(PROTECT_ALL); |
| 244 | db_multi_exec("VACUUM"); |
| 245 | db_protect_pop(); |
| 246 | fossil_print("\nproject-id: %s\n", db_get("project-code", 0)); |
| 247 | fossil_print("server-id: %s\n", db_get("server-code", 0)); |
| 248 | zPassword = db_text(0, "SELECT pw FROM user WHERE login=%Q", g.zLogin); |
| 249 | fossil_print("admin-user: %s (password is \"%s\")\n", g.zLogin, zPassword); |
| 250 | } |
| 251 |
+8
-1
| --- src/configure.c | ||
| +++ src/configure.c | ||
| @@ -145,11 +145,10 @@ | ||
| 145 | 145 | { "keep-glob", CONFIGSET_PROJ }, |
| 146 | 146 | { "crlf-glob", CONFIGSET_PROJ }, |
| 147 | 147 | { "crnl-glob", CONFIGSET_PROJ }, |
| 148 | 148 | { "encoding-glob", CONFIGSET_PROJ }, |
| 149 | 149 | { "empty-dirs", CONFIGSET_PROJ }, |
| 150 | - { "allow-symlinks", CONFIGSET_PROJ }, | |
| 151 | 150 | { "dotfiles", CONFIGSET_PROJ }, |
| 152 | 151 | { "parent-project-code", CONFIGSET_PROJ }, |
| 153 | 152 | { "parent-project-name", CONFIGSET_PROJ }, |
| 154 | 153 | { "hash-policy", CONFIGSET_PROJ }, |
| 155 | 154 | { "comment-format", CONFIGSET_PROJ }, |
| @@ -446,10 +445,11 @@ | ||
| 446 | 445 | blob_append_sql(&sql,") VALUES(%s,%s", |
| 447 | 446 | azToken[1] /*safe-for-%s*/, azToken[0]/*safe-for-%s*/); |
| 448 | 447 | for(jj=2; jj<nToken; jj+=2){ |
| 449 | 448 | blob_append_sql(&sql, ",%s", azToken[jj+1] /*safe-for-%s*/); |
| 450 | 449 | } |
| 450 | + db_protect_only(PROTECT_SENSITIVE); | |
| 451 | 451 | db_multi_exec("%s)", blob_sql_text(&sql)); |
| 452 | 452 | if( db_changes()==0 ){ |
| 453 | 453 | blob_reset(&sql); |
| 454 | 454 | blob_append_sql(&sql, "UPDATE \"%w\" SET mtime=%s", |
| 455 | 455 | &zName[1], azToken[0]/*safe-for-%s*/); |
| @@ -460,10 +460,11 @@ | ||
| 460 | 460 | blob_append_sql(&sql, " WHERE \"%w\"=%s AND mtime<%s", |
| 461 | 461 | aType[ii].zPrimKey, azToken[1]/*safe-for-%s*/, |
| 462 | 462 | azToken[0]/*safe-for-%s*/); |
| 463 | 463 | db_multi_exec("%s", blob_sql_text(&sql)); |
| 464 | 464 | } |
| 465 | + db_protect_pop(); | |
| 465 | 466 | blob_reset(&sql); |
| 466 | 467 | rebuildMask |= thisMask; |
| 467 | 468 | } |
| 468 | 469 | } |
| 469 | 470 | |
| @@ -861,13 +862,17 @@ | ||
| 861 | 862 | export_config(mask, g.argv[3], 0, zBackup); |
| 862 | 863 | for(i=0; i<count(aConfig); i++){ |
| 863 | 864 | const char *zName = aConfig[i].zName; |
| 864 | 865 | if( (aConfig[i].groupMask & mask)==0 ) continue; |
| 865 | 866 | if( zName[0]!='@' ){ |
| 867 | + db_unprotect(PROTECT_CONFIG); | |
| 866 | 868 | db_multi_exec("DELETE FROM config WHERE name=%Q", zName); |
| 869 | + db_protect_pop(); | |
| 867 | 870 | }else if( fossil_strcmp(zName,"@user")==0 ){ |
| 871 | + db_unprotect(PROTECT_USER); | |
| 868 | 872 | db_multi_exec("DELETE FROM user"); |
| 873 | + db_protect_pop(); | |
| 869 | 874 | db_create_default_users(0, 0); |
| 870 | 875 | }else if( fossil_strcmp(zName,"@concealed")==0 ){ |
| 871 | 876 | db_multi_exec("DELETE FROM concealed"); |
| 872 | 877 | }else if( fossil_strcmp(zName,"@shun")==0 ){ |
| 873 | 878 | db_multi_exec("DELETE FROM shun"); |
| @@ -1075,10 +1080,11 @@ | ||
| 1075 | 1080 | }else if( zBlob ){ |
| 1076 | 1081 | blob_read_from_file(&x, zBlob, ExtFILE); |
| 1077 | 1082 | }else{ |
| 1078 | 1083 | blob_init(&x,g.argv[3],-1); |
| 1079 | 1084 | } |
| 1085 | + db_unprotect(PROTECT_CONFIG); | |
| 1080 | 1086 | db_prepare(&ins, |
| 1081 | 1087 | "REPLACE INTO config(name,value,mtime)" |
| 1082 | 1088 | "VALUES(%Q,:val,now())", zVar); |
| 1083 | 1089 | if( zBlob ){ |
| 1084 | 1090 | db_bind_blob(&ins, ":val", &x); |
| @@ -1085,7 +1091,8 @@ | ||
| 1085 | 1091 | }else{ |
| 1086 | 1092 | db_bind_text(&ins, ":val", blob_str(&x)); |
| 1087 | 1093 | } |
| 1088 | 1094 | db_step(&ins); |
| 1089 | 1095 | db_finalize(&ins); |
| 1096 | + db_protect_pop(); | |
| 1090 | 1097 | blob_reset(&x); |
| 1091 | 1098 | } |
| 1092 | 1099 |
| --- src/configure.c | |
| +++ src/configure.c | |
| @@ -145,11 +145,10 @@ | |
| 145 | { "keep-glob", CONFIGSET_PROJ }, |
| 146 | { "crlf-glob", CONFIGSET_PROJ }, |
| 147 | { "crnl-glob", CONFIGSET_PROJ }, |
| 148 | { "encoding-glob", CONFIGSET_PROJ }, |
| 149 | { "empty-dirs", CONFIGSET_PROJ }, |
| 150 | { "allow-symlinks", CONFIGSET_PROJ }, |
| 151 | { "dotfiles", CONFIGSET_PROJ }, |
| 152 | { "parent-project-code", CONFIGSET_PROJ }, |
| 153 | { "parent-project-name", CONFIGSET_PROJ }, |
| 154 | { "hash-policy", CONFIGSET_PROJ }, |
| 155 | { "comment-format", CONFIGSET_PROJ }, |
| @@ -446,10 +445,11 @@ | |
| 446 | blob_append_sql(&sql,") VALUES(%s,%s", |
| 447 | azToken[1] /*safe-for-%s*/, azToken[0]/*safe-for-%s*/); |
| 448 | for(jj=2; jj<nToken; jj+=2){ |
| 449 | blob_append_sql(&sql, ",%s", azToken[jj+1] /*safe-for-%s*/); |
| 450 | } |
| 451 | db_multi_exec("%s)", blob_sql_text(&sql)); |
| 452 | if( db_changes()==0 ){ |
| 453 | blob_reset(&sql); |
| 454 | blob_append_sql(&sql, "UPDATE \"%w\" SET mtime=%s", |
| 455 | &zName[1], azToken[0]/*safe-for-%s*/); |
| @@ -460,10 +460,11 @@ | |
| 460 | blob_append_sql(&sql, " WHERE \"%w\"=%s AND mtime<%s", |
| 461 | aType[ii].zPrimKey, azToken[1]/*safe-for-%s*/, |
| 462 | azToken[0]/*safe-for-%s*/); |
| 463 | db_multi_exec("%s", blob_sql_text(&sql)); |
| 464 | } |
| 465 | blob_reset(&sql); |
| 466 | rebuildMask |= thisMask; |
| 467 | } |
| 468 | } |
| 469 | |
| @@ -861,13 +862,17 @@ | |
| 861 | export_config(mask, g.argv[3], 0, zBackup); |
| 862 | for(i=0; i<count(aConfig); i++){ |
| 863 | const char *zName = aConfig[i].zName; |
| 864 | if( (aConfig[i].groupMask & mask)==0 ) continue; |
| 865 | if( zName[0]!='@' ){ |
| 866 | db_multi_exec("DELETE FROM config WHERE name=%Q", zName); |
| 867 | }else if( fossil_strcmp(zName,"@user")==0 ){ |
| 868 | db_multi_exec("DELETE FROM user"); |
| 869 | db_create_default_users(0, 0); |
| 870 | }else if( fossil_strcmp(zName,"@concealed")==0 ){ |
| 871 | db_multi_exec("DELETE FROM concealed"); |
| 872 | }else if( fossil_strcmp(zName,"@shun")==0 ){ |
| 873 | db_multi_exec("DELETE FROM shun"); |
| @@ -1075,10 +1080,11 @@ | |
| 1075 | }else if( zBlob ){ |
| 1076 | blob_read_from_file(&x, zBlob, ExtFILE); |
| 1077 | }else{ |
| 1078 | blob_init(&x,g.argv[3],-1); |
| 1079 | } |
| 1080 | db_prepare(&ins, |
| 1081 | "REPLACE INTO config(name,value,mtime)" |
| 1082 | "VALUES(%Q,:val,now())", zVar); |
| 1083 | if( zBlob ){ |
| 1084 | db_bind_blob(&ins, ":val", &x); |
| @@ -1085,7 +1091,8 @@ | |
| 1085 | }else{ |
| 1086 | db_bind_text(&ins, ":val", blob_str(&x)); |
| 1087 | } |
| 1088 | db_step(&ins); |
| 1089 | db_finalize(&ins); |
| 1090 | blob_reset(&x); |
| 1091 | } |
| 1092 |
| --- src/configure.c | |
| +++ src/configure.c | |
| @@ -145,11 +145,10 @@ | |
| 145 | { "keep-glob", CONFIGSET_PROJ }, |
| 146 | { "crlf-glob", CONFIGSET_PROJ }, |
| 147 | { "crnl-glob", CONFIGSET_PROJ }, |
| 148 | { "encoding-glob", CONFIGSET_PROJ }, |
| 149 | { "empty-dirs", CONFIGSET_PROJ }, |
| 150 | { "dotfiles", CONFIGSET_PROJ }, |
| 151 | { "parent-project-code", CONFIGSET_PROJ }, |
| 152 | { "parent-project-name", CONFIGSET_PROJ }, |
| 153 | { "hash-policy", CONFIGSET_PROJ }, |
| 154 | { "comment-format", CONFIGSET_PROJ }, |
| @@ -446,10 +445,11 @@ | |
| 445 | blob_append_sql(&sql,") VALUES(%s,%s", |
| 446 | azToken[1] /*safe-for-%s*/, azToken[0]/*safe-for-%s*/); |
| 447 | for(jj=2; jj<nToken; jj+=2){ |
| 448 | blob_append_sql(&sql, ",%s", azToken[jj+1] /*safe-for-%s*/); |
| 449 | } |
| 450 | db_protect_only(PROTECT_SENSITIVE); |
| 451 | db_multi_exec("%s)", blob_sql_text(&sql)); |
| 452 | if( db_changes()==0 ){ |
| 453 | blob_reset(&sql); |
| 454 | blob_append_sql(&sql, "UPDATE \"%w\" SET mtime=%s", |
| 455 | &zName[1], azToken[0]/*safe-for-%s*/); |
| @@ -460,10 +460,11 @@ | |
| 460 | blob_append_sql(&sql, " WHERE \"%w\"=%s AND mtime<%s", |
| 461 | aType[ii].zPrimKey, azToken[1]/*safe-for-%s*/, |
| 462 | azToken[0]/*safe-for-%s*/); |
| 463 | db_multi_exec("%s", blob_sql_text(&sql)); |
| 464 | } |
| 465 | db_protect_pop(); |
| 466 | blob_reset(&sql); |
| 467 | rebuildMask |= thisMask; |
| 468 | } |
| 469 | } |
| 470 | |
| @@ -861,13 +862,17 @@ | |
| 862 | export_config(mask, g.argv[3], 0, zBackup); |
| 863 | for(i=0; i<count(aConfig); i++){ |
| 864 | const char *zName = aConfig[i].zName; |
| 865 | if( (aConfig[i].groupMask & mask)==0 ) continue; |
| 866 | if( zName[0]!='@' ){ |
| 867 | db_unprotect(PROTECT_CONFIG); |
| 868 | db_multi_exec("DELETE FROM config WHERE name=%Q", zName); |
| 869 | db_protect_pop(); |
| 870 | }else if( fossil_strcmp(zName,"@user")==0 ){ |
| 871 | db_unprotect(PROTECT_USER); |
| 872 | db_multi_exec("DELETE FROM user"); |
| 873 | db_protect_pop(); |
| 874 | db_create_default_users(0, 0); |
| 875 | }else if( fossil_strcmp(zName,"@concealed")==0 ){ |
| 876 | db_multi_exec("DELETE FROM concealed"); |
| 877 | }else if( fossil_strcmp(zName,"@shun")==0 ){ |
| 878 | db_multi_exec("DELETE FROM shun"); |
| @@ -1075,10 +1080,11 @@ | |
| 1080 | }else if( zBlob ){ |
| 1081 | blob_read_from_file(&x, zBlob, ExtFILE); |
| 1082 | }else{ |
| 1083 | blob_init(&x,g.argv[3],-1); |
| 1084 | } |
| 1085 | db_unprotect(PROTECT_CONFIG); |
| 1086 | db_prepare(&ins, |
| 1087 | "REPLACE INTO config(name,value,mtime)" |
| 1088 | "VALUES(%Q,:val,now())", zVar); |
| 1089 | if( zBlob ){ |
| 1090 | db_bind_blob(&ins, ":val", &x); |
| @@ -1085,7 +1091,8 @@ | |
| 1091 | }else{ |
| 1092 | db_bind_text(&ins, ":val", blob_str(&x)); |
| 1093 | } |
| 1094 | db_step(&ins); |
| 1095 | db_finalize(&ins); |
| 1096 | db_protect_pop(); |
| 1097 | blob_reset(&x); |
| 1098 | } |
| 1099 |
M
src/db.c
+367
-100
| --- src/db.c | ||
| +++ src/db.c | ||
| @@ -69,10 +69,11 @@ | ||
| 69 | 69 | #endif /* INTERFACE */ |
| 70 | 70 | const struct Stmt empty_Stmt = empty_Stmt_m; |
| 71 | 71 | |
| 72 | 72 | /* |
| 73 | 73 | ** Call this routine when a database error occurs. |
| 74 | +** This routine throws a fatal error. It does not return. | |
| 74 | 75 | */ |
| 75 | 76 | static void db_err(const char *zFormat, ...){ |
| 76 | 77 | va_list ap; |
| 77 | 78 | char *z; |
| 78 | 79 | va_start(ap, zFormat); |
| @@ -113,10 +114,11 @@ | ||
| 113 | 114 | /* |
| 114 | 115 | ** All static variable that a used by only this file are gathered into |
| 115 | 116 | ** the following structure. |
| 116 | 117 | */ |
| 117 | 118 | static struct DbLocalData { |
| 119 | + unsigned protectMask; /* Prevent changes to database */ | |
| 118 | 120 | int nBegin; /* Nesting depth of BEGIN */ |
| 119 | 121 | int doRollback; /* True to force a rollback */ |
| 120 | 122 | int nCommitHook; /* Number of commit hooks */ |
| 121 | 123 | int wrTxn; /* Outer-most TNX is a write */ |
| 122 | 124 | Stmt *pAllStmt; /* List of all unfinalized statements */ |
| @@ -130,11 +132,19 @@ | ||
| 130 | 132 | char *azBeforeCommit[5]; /* Commands to run prior to COMMIT */ |
| 131 | 133 | int nBeforeCommit; /* Number of entries in azBeforeCommit */ |
| 132 | 134 | int nPriorChanges; /* sqlite3_total_changes() at transaction start */ |
| 133 | 135 | const char *zStartFile; /* File in which transaction was started */ |
| 134 | 136 | int iStartLine; /* Line of zStartFile where transaction started */ |
| 135 | -} db = {0, 0, 0, 0, 0, 0, }; | |
| 137 | + int (*xAuth)(void*,int,const char*,const char*,const char*,const char*); | |
| 138 | + void *pAuthArg; /* Argument to the authorizer */ | |
| 139 | + const char *zAuthName; /* Name of the authorizer */ | |
| 140 | + int bProtectTriggers; /* True if protection triggers already exist */ | |
| 141 | + int nProtect; /* Slots of aProtect used */ | |
| 142 | + unsigned aProtect[10]; /* Saved values of protectMask */ | |
| 143 | +} db = { | |
| 144 | + PROTECT_USER|PROTECT_CONFIG|PROTECT_BASELINE, /* protectMask */ | |
| 145 | + 0, 0, 0, 0, 0, 0, }; | |
| 136 | 146 | |
| 137 | 147 | /* |
| 138 | 148 | ** Arrange for the given file to be deleted on a failure. |
| 139 | 149 | */ |
| 140 | 150 | void db_delete_on_failure(const char *zFilename){ |
| @@ -238,17 +248,19 @@ | ||
| 238 | 248 | db.nBegin--; |
| 239 | 249 | if( db.nBegin==0 ){ |
| 240 | 250 | int i; |
| 241 | 251 | if( db.doRollback==0 && db.nPriorChanges<sqlite3_total_changes(g.db) ){ |
| 242 | 252 | i = 0; |
| 253 | + db_protect_only(PROTECT_SENSITIVE); | |
| 243 | 254 | while( db.nBeforeCommit ){ |
| 244 | 255 | db.nBeforeCommit--; |
| 245 | 256 | sqlite3_exec(g.db, db.azBeforeCommit[i], 0, 0, 0); |
| 246 | 257 | sqlite3_free(db.azBeforeCommit[i]); |
| 247 | 258 | i++; |
| 248 | 259 | } |
| 249 | 260 | leaf_do_pending_checks(); |
| 261 | + db_protect_pop(); | |
| 250 | 262 | } |
| 251 | 263 | for(i=0; db.doRollback==0 && i<db.nCommitHook; i++){ |
| 252 | 264 | int rc = db.aHook[i].xHook(); |
| 253 | 265 | if( rc ){ |
| 254 | 266 | db.doRollback = 1; |
| @@ -316,10 +328,230 @@ | ||
| 316 | 328 | } |
| 317 | 329 | db.aHook[db.nCommitHook].sequence = sequence; |
| 318 | 330 | db.aHook[db.nCommitHook].xHook = x; |
| 319 | 331 | db.nCommitHook++; |
| 320 | 332 | } |
| 333 | + | |
| 334 | +#if INTERFACE | |
| 335 | +/* | |
| 336 | +** Flag bits for db_protect() and db_unprotect() indicating which parts | |
| 337 | +** of the databases should be write protected or write enabled, respectively. | |
| 338 | +*/ | |
| 339 | +#define PROTECT_USER 0x01 /* USER table */ | |
| 340 | +#define PROTECT_CONFIG 0x02 /* CONFIG and GLOBAL_CONFIG tables */ | |
| 341 | +#define PROTECT_SENSITIVE 0x04 /* Sensitive and/or global settings */ | |
| 342 | +#define PROTECT_READONLY 0x08 /* everything except TEMP tables */ | |
| 343 | +#define PROTECT_BASELINE 0x10 /* protection system is working */ | |
| 344 | +#define PROTECT_ALL 0x1f /* All of the above */ | |
| 345 | +#define PROTECT_NONE 0x00 /* Nothing. Everything is open */ | |
| 346 | +#endif /* INTERFACE */ | |
| 347 | + | |
| 348 | +/* | |
| 349 | +** Enable or disable database write protections. | |
| 350 | +** | |
| 351 | +** db_protext(X) Add protects on X | |
| 352 | +** db_unprotect(X) Remove protections on X | |
| 353 | +** db_protect_only(X) Remove all prior protections then set | |
| 354 | +** protections to only X. | |
| 355 | +** | |
| 356 | +** Each of these routines pushes the previous protection mask onto | |
| 357 | +** a finite-size stack. Each should be followed by a call to | |
| 358 | +** db_protect_pop() to pop the stack and restore the protections that | |
| 359 | +** existed prior to the call. The protection mask stack has a limited | |
| 360 | +** depth, so take care not to next calls too deeply. | |
| 361 | +** | |
| 362 | +** About Database Write Protection | |
| 363 | +** ------------------------------- | |
| 364 | +** | |
| 365 | +** This is *not* a primary means of defending the application from | |
| 366 | +** attack. Fossil should be secure even if this mechanism is disabled. | |
| 367 | +** The purpose of database write protection is to provide an additional | |
| 368 | +** layer of defense in case SQL injection bugs somehow slip into other | |
| 369 | +** parts of the system. In other words, database write protection is | |
| 370 | +** not primary defense but rather defense in depth. | |
| 371 | +** | |
| 372 | +** This mechanism mostly focuses on the USER table, to prevent an | |
| 373 | +** attacker from giving themselves Admin privilegs, and on the | |
| 374 | +** CONFIG table and specially "sensitive" settings such as | |
| 375 | +** "diff-command" or "editor" that if compromised by an attacker | |
| 376 | +** could lead to an RCE. | |
| 377 | +** | |
| 378 | +** By default, the USER and CONFIG tables are read-only. Various | |
| 379 | +** subsystems that legitimately need to change those tables can | |
| 380 | +** temporarily do so using: | |
| 381 | +** | |
| 382 | +** db_unprotect(PROTECT_xxx); | |
| 383 | +** // make the legitmate changes here | |
| 384 | +** db_protect_pop(); | |
| 385 | +** | |
| 386 | +** Code that runs inside of reduced protections should be carefully | |
| 387 | +** reviewed to ensure that it is harmless and not subject to SQL | |
| 388 | +** injection. | |
| 389 | +** | |
| 390 | +** Read-only operations (such as many web pages like /timeline) | |
| 391 | +** can invoke db_protect(PROTECT_ALL) to effectively make the database | |
| 392 | +** read-only. TEMP tables (which are often used for these kinds of | |
| 393 | +** pages) are still writable, however. | |
| 394 | +** | |
| 395 | +** The PROTECT_SENSITIVE protection is a subset of PROTECT_CONFIG | |
| 396 | +** that blocks changes to all of the global_config table, but only | |
| 397 | +** "sensitive" settings in the config table. PROTECT_SENSITIVE | |
| 398 | +** relies on triggers and the protected_setting() SQL function to | |
| 399 | +** prevent changes to sensitive settings. | |
| 400 | +** | |
| 401 | +** Additional Notes | |
| 402 | +** ---------------- | |
| 403 | +** | |
| 404 | +** Calls to routines like db_set() and db_unset() temporarily disable | |
| 405 | +** the PROTECT_CONFIG protection. The assumption is that these calls | |
| 406 | +** cannot be invoked by an SQL injection and are thus safe. Make sure | |
| 407 | +** this is the case by always using a string literal as the name argument | |
| 408 | +** to db_set() and db_unset() and friend, not a variable that might | |
| 409 | +** be compromised by an attack. | |
| 410 | +*/ | |
| 411 | +void db_protect_only(unsigned flags){ | |
| 412 | + if( db.nProtect>=count(db.aProtect)-2 ){ | |
| 413 | + fossil_panic("too many db_protect() calls"); | |
| 414 | + } | |
| 415 | + db.aProtect[db.nProtect++] = db.protectMask; | |
| 416 | + if( (flags & PROTECT_SENSITIVE)!=0 | |
| 417 | + && db.bProtectTriggers==0 | |
| 418 | + && g.repositoryOpen | |
| 419 | + ){ | |
| 420 | + /* Create the triggers needed to protect sensitive settings from | |
| 421 | + ** being created or modified the first time that PROTECT_SENSITIVE | |
| 422 | + ** is enabled. Deleting a sensitive setting is harmless, so there | |
| 423 | + ** is not trigger to block deletes. After being created once, the | |
| 424 | + ** triggers persist for the life of the database connection. */ | |
| 425 | + db_multi_exec( | |
| 426 | + "CREATE TEMP TRIGGER protect_1 BEFORE INSERT ON config" | |
| 427 | + " WHEN protected_setting(new.name) BEGIN" | |
| 428 | + " SELECT raise(abort,'not authorized');" | |
| 429 | + "END;\n" | |
| 430 | + "CREATE TEMP TRIGGER protect_2 BEFORE UPDATE ON config" | |
| 431 | + " WHEN protected_setting(new.name) BEGIN" | |
| 432 | + " SELECT raise(abort,'not authorized');" | |
| 433 | + "END;\n" | |
| 434 | + ); | |
| 435 | + db.bProtectTriggers = 1; | |
| 436 | + } | |
| 437 | + db.protectMask = flags; | |
| 438 | +} | |
| 439 | +void db_protect(unsigned flags){ | |
| 440 | + db_protect_only(db.protectMask | flags); | |
| 441 | +} | |
| 442 | +void db_unprotect(unsigned flags){ | |
| 443 | + if( db.nProtect>=count(db.aProtect)-2 ){ | |
| 444 | + fossil_panic("too many db_unprotect() calls"); | |
| 445 | + } | |
| 446 | + db.aProtect[db.nProtect++] = db.protectMask; | |
| 447 | + db.protectMask &= ~flags; | |
| 448 | +} | |
| 449 | +void db_protect_pop(void){ | |
| 450 | + if( db.nProtect<1 ){ | |
| 451 | + fossil_panic("too many db_protect_pop() calls"); | |
| 452 | + } | |
| 453 | + db.protectMask = db.aProtect[--db.nProtect]; | |
| 454 | +} | |
| 455 | + | |
| 456 | +/* | |
| 457 | +** Verify that the desired database write pertections are in place. | |
| 458 | +** Throw a fatal error if not. | |
| 459 | +*/ | |
| 460 | +void db_assert_protected(unsigned flags){ | |
| 461 | + if( (flags & db.protectMask)!=flags ){ | |
| 462 | + fossil_panic("missing database write protection bits: %02x", | |
| 463 | + flags & ~db.protectMask); | |
| 464 | + } | |
| 465 | +} | |
| 466 | + | |
| 467 | +/* | |
| 468 | +** Assert that either all protections are off (including PROTECT_BASELINE | |
| 469 | +** which is usually always enabled), or the setting named in the argument | |
| 470 | +** is no a sensitive setting. | |
| 471 | +** | |
| 472 | +** This assert() is used to verify that the db_set() and db_set_int() | |
| 473 | +** interfaces do not modify a sensitive setting. | |
| 474 | +*/ | |
| 475 | +void db_assert_protection_off_or_not_sensitive(const char *zName){ | |
| 476 | + if( db.protectMask!=0 && db_setting_is_protected(zName) ){ | |
| 477 | + fossil_panic("unauthorized change to protected setting \"%s\"", zName); | |
| 478 | + } | |
| 479 | +} | |
| 480 | + | |
| 481 | +/* | |
| 482 | +** Every Fossil database connection automatically registers the following | |
| 483 | +** overarching authenticator callback, and leaves it registered for the | |
| 484 | +** duration of the connection. This authenticator will call any | |
| 485 | +** sub-authenticators that are registered using db_set_authorizer(). | |
| 486 | +*/ | |
| 487 | +int db_top_authorizer( | |
| 488 | + void *pNotUsed, | |
| 489 | + int eCode, | |
| 490 | + const char *z0, | |
| 491 | + const char *z1, | |
| 492 | + const char *z2, | |
| 493 | + const char *z3 | |
| 494 | +){ | |
| 495 | + int rc = SQLITE_OK; | |
| 496 | + switch( eCode ){ | |
| 497 | + case SQLITE_INSERT: | |
| 498 | + case SQLITE_UPDATE: | |
| 499 | + case SQLITE_DELETE: { | |
| 500 | + if( (db.protectMask & PROTECT_USER)!=0 | |
| 501 | + && sqlite3_stricmp(z0,"user")==0 ){ | |
| 502 | + rc = SQLITE_DENY; | |
| 503 | + }else if( (db.protectMask & PROTECT_CONFIG)!=0 && | |
| 504 | + (sqlite3_stricmp(z0,"config")==0 || | |
| 505 | + sqlite3_stricmp(z0,"global_config")==0) ){ | |
| 506 | + rc = SQLITE_DENY; | |
| 507 | + }else if( (db.protectMask & PROTECT_SENSITIVE)!=0 && | |
| 508 | + sqlite3_stricmp(z0,"global_config")==0 ){ | |
| 509 | + rc = SQLITE_DENY; | |
| 510 | + }else if( (db.protectMask & PROTECT_READONLY)!=0 | |
| 511 | + && sqlite3_stricmp(z2,"temp")!=0 ){ | |
| 512 | + rc = SQLITE_DENY; | |
| 513 | + } | |
| 514 | + break; | |
| 515 | + } | |
| 516 | + case SQLITE_DROP_TEMP_TRIGGER: { | |
| 517 | + /* Do not allow the triggers that enforce PROTECT_SENSITIVE | |
| 518 | + ** to be dropped */ | |
| 519 | + rc = SQLITE_DENY; | |
| 520 | + break; | |
| 521 | + } | |
| 522 | + } | |
| 523 | + if( db.xAuth && rc==SQLITE_OK ){ | |
| 524 | + rc = db.xAuth(db.pAuthArg, eCode, z0, z1, z2, z3); | |
| 525 | + } | |
| 526 | + return rc; | |
| 527 | +} | |
| 528 | + | |
| 529 | +/* | |
| 530 | +** Set or unset the query authorizer callback function | |
| 531 | +*/ | |
| 532 | +void db_set_authorizer( | |
| 533 | + int(*xAuth)(void*,int,const char*,const char*,const char*,const char*), | |
| 534 | + void *pArg, | |
| 535 | + const char *zName /* for tracing */ | |
| 536 | +){ | |
| 537 | + if( db.xAuth ){ | |
| 538 | + fossil_panic("multiple active db_set_authorizer() calls"); | |
| 539 | + } | |
| 540 | + db.xAuth = xAuth; | |
| 541 | + db.pAuthArg = pArg; | |
| 542 | + db.zAuthName = zName; | |
| 543 | + if( g.fSqlTrace ) fossil_trace("-- set authorizer %s\n", zName); | |
| 544 | +} | |
| 545 | +void db_clear_authorizer(void){ | |
| 546 | + if( db.zAuthName && g.fSqlTrace ){ | |
| 547 | + fossil_trace("-- discontinue authorizer %s\n", db.zAuthName); | |
| 548 | + } | |
| 549 | + db.xAuth = 0; | |
| 550 | + db.pAuthArg = 0; | |
| 551 | + db.zAuthName = 0; | |
| 552 | +} | |
| 321 | 553 | |
| 322 | 554 | #if INTERFACE |
| 323 | 555 | /* |
| 324 | 556 | ** Possible flags to db_vprepare |
| 325 | 557 | */ |
| @@ -334,21 +566,24 @@ | ||
| 334 | 566 | */ |
| 335 | 567 | int db_vprepare(Stmt *pStmt, int flags, const char *zFormat, va_list ap){ |
| 336 | 568 | int rc; |
| 337 | 569 | int prepFlags = 0; |
| 338 | 570 | char *zSql; |
| 571 | + const char *zExtra = 0; | |
| 339 | 572 | blob_zero(&pStmt->sql); |
| 340 | 573 | blob_vappendf(&pStmt->sql, zFormat, ap); |
| 341 | 574 | va_end(ap); |
| 342 | 575 | zSql = blob_str(&pStmt->sql); |
| 343 | 576 | db.nPrepare++; |
| 344 | 577 | if( flags & DB_PREPARE_PERSISTENT ){ |
| 345 | 578 | prepFlags = SQLITE_PREPARE_PERSISTENT; |
| 346 | 579 | } |
| 347 | - rc = sqlite3_prepare_v3(g.db, zSql, -1, prepFlags, &pStmt->pStmt, 0); | |
| 580 | + rc = sqlite3_prepare_v3(g.db, zSql, -1, prepFlags, &pStmt->pStmt, &zExtra); | |
| 348 | 581 | if( rc!=0 && (flags & DB_PREPARE_IGNORE_ERROR)==0 ){ |
| 349 | 582 | db_err("%s\n%s", sqlite3_errmsg(g.db), zSql); |
| 583 | + }else if( zExtra && !fossil_all_whitespace(zExtra) ){ | |
| 584 | + db_err("surplus text follows SQL: \"%s\"", zExtra); | |
| 350 | 585 | } |
| 351 | 586 | pStmt->pNext = db.pAllStmt; |
| 352 | 587 | pStmt->pPrev = 0; |
| 353 | 588 | if( db.pAllStmt ) db.pAllStmt->pPrev = pStmt; |
| 354 | 589 | db.pAllStmt = pStmt; |
| @@ -611,10 +846,11 @@ | ||
| 611 | 846 | return rc; |
| 612 | 847 | } |
| 613 | 848 | |
| 614 | 849 | /* |
| 615 | 850 | ** COMMAND: test-db-exec-error |
| 851 | +** Usage: %fossil test-db-exec-error | |
| 616 | 852 | ** |
| 617 | 853 | ** Invoke the db_exec() interface with an erroneous SQL statement |
| 618 | 854 | ** in order to verify the error handling logic. |
| 619 | 855 | */ |
| 620 | 856 | void db_test_db_exec_cmd(void){ |
| @@ -621,10 +857,27 @@ | ||
| 621 | 857 | Stmt err; |
| 622 | 858 | db_find_and_open_repository(0,0); |
| 623 | 859 | db_prepare(&err, "INSERT INTO repository.config(name) VALUES(NULL);"); |
| 624 | 860 | db_exec(&err); |
| 625 | 861 | } |
| 862 | + | |
| 863 | +/* | |
| 864 | +** COMMAND: test-db-prepare | |
| 865 | +** Usage: %fossil test-db-prepare ?OPTIONS? SQL | |
| 866 | +** | |
| 867 | +** Invoke db_prepare() on the SQL input. Report any errors encountered. | |
| 868 | +** This command is used to verify error detection logic in the db_prepare() | |
| 869 | +** utility routine. | |
| 870 | +*/ | |
| 871 | +void db_test_db_prepare(void){ | |
| 872 | + Stmt err; | |
| 873 | + db_find_and_open_repository(0,0); | |
| 874 | + verify_all_options(); | |
| 875 | + if( g.argc!=3 ) usage("?OPTIONS? SQL"); | |
| 876 | + db_prepare(&err, "%s", g.argv[2]/*safe-for-%s*/); | |
| 877 | + db_finalize(&err); | |
| 878 | +} | |
| 626 | 879 | |
| 627 | 880 | /* |
| 628 | 881 | ** Print the output of one or more SQL queries on standard output. |
| 629 | 882 | ** This routine is used for debugging purposes only. |
| 630 | 883 | */ |
| @@ -844,34 +1097,34 @@ | ||
| 844 | 1097 | void db_init_database( |
| 845 | 1098 | const char *zFileName, /* Name of database file to create */ |
| 846 | 1099 | const char *zSchema, /* First part of schema */ |
| 847 | 1100 | ... /* Additional SQL to run. Terminate with NULL. */ |
| 848 | 1101 | ){ |
| 849 | - sqlite3 *db; | |
| 1102 | + sqlite3 *xdb; | |
| 850 | 1103 | int rc; |
| 851 | 1104 | const char *zSql; |
| 852 | 1105 | va_list ap; |
| 853 | 1106 | |
| 854 | - db = db_open(zFileName ? zFileName : ":memory:"); | |
| 855 | - sqlite3_exec(db, "BEGIN EXCLUSIVE", 0, 0, 0); | |
| 856 | - rc = sqlite3_exec(db, zSchema, 0, 0, 0); | |
| 1107 | + xdb = db_open(zFileName ? zFileName : ":memory:"); | |
| 1108 | + sqlite3_exec(xdb, "BEGIN EXCLUSIVE", 0, 0, 0); | |
| 1109 | + rc = sqlite3_exec(xdb, zSchema, 0, 0, 0); | |
| 857 | 1110 | if( rc!=SQLITE_OK ){ |
| 858 | - db_err("%s", sqlite3_errmsg(db)); | |
| 1111 | + db_err("%s", sqlite3_errmsg(xdb)); | |
| 859 | 1112 | } |
| 860 | 1113 | va_start(ap, zSchema); |
| 861 | 1114 | while( (zSql = va_arg(ap, const char*))!=0 ){ |
| 862 | - rc = sqlite3_exec(db, zSql, 0, 0, 0); | |
| 1115 | + rc = sqlite3_exec(xdb, zSql, 0, 0, 0); | |
| 863 | 1116 | if( rc!=SQLITE_OK ){ |
| 864 | - db_err("%s", sqlite3_errmsg(db)); | |
| 1117 | + db_err("%s", sqlite3_errmsg(xdb)); | |
| 865 | 1118 | } |
| 866 | 1119 | } |
| 867 | 1120 | va_end(ap); |
| 868 | - sqlite3_exec(db, "COMMIT", 0, 0, 0); | |
| 1121 | + sqlite3_exec(xdb, "COMMIT", 0, 0, 0); | |
| 869 | 1122 | if( zFileName || g.db!=0 ){ |
| 870 | - sqlite3_close(db); | |
| 1123 | + sqlite3_close(xdb); | |
| 871 | 1124 | }else{ |
| 872 | - g.db = db; | |
| 1125 | + g.db = xdb; | |
| 873 | 1126 | } |
| 874 | 1127 | } |
| 875 | 1128 | |
| 876 | 1129 | /* |
| 877 | 1130 | ** Function to return the number of seconds since 1970. This is |
| @@ -1060,10 +1313,37 @@ | ||
| 1060 | 1313 | } |
| 1061 | 1314 | strcpy(zOut, zTemp = obscure((char*)zIn)); |
| 1062 | 1315 | fossil_free(zTemp); |
| 1063 | 1316 | sqlite3_result_text(context, zOut, strlen(zOut), sqlite3_free); |
| 1064 | 1317 | } |
| 1318 | + | |
| 1319 | +/* | |
| 1320 | +** Return True if zName is a protected (a.k.a. "sensitive") setting. | |
| 1321 | +*/ | |
| 1322 | +int db_setting_is_protected(const char *zName){ | |
| 1323 | + const Setting *pSetting = zName ? db_find_setting(zName,0) : 0; | |
| 1324 | + return pSetting!=0 && pSetting->sensitive!=0; | |
| 1325 | +} | |
| 1326 | + | |
| 1327 | +/* | |
| 1328 | +** Implement the protected_setting(X) SQL function. This function returns | |
| 1329 | +** true if X is the name of a protected (security-sensitive) setting and | |
| 1330 | +** the db.protectSensitive flag is enabled. It returns false otherwise. | |
| 1331 | +*/ | |
| 1332 | +LOCAL void db_protected_setting_func( | |
| 1333 | + sqlite3_context *context, | |
| 1334 | + int argc, | |
| 1335 | + sqlite3_value **argv | |
| 1336 | +){ | |
| 1337 | + const char *zSetting; | |
| 1338 | + if( (db.protectMask & PROTECT_SENSITIVE)==0 ){ | |
| 1339 | + sqlite3_result_int(context, 0); | |
| 1340 | + return; | |
| 1341 | + } | |
| 1342 | + zSetting = (const char*)sqlite3_value_text(argv[0]); | |
| 1343 | + sqlite3_result_int(context, db_setting_is_protected(zSetting)); | |
| 1344 | +} | |
| 1065 | 1345 | |
| 1066 | 1346 | /* |
| 1067 | 1347 | ** Register the SQL functions that are useful both to the internal |
| 1068 | 1348 | ** representation and to the "fossil sql" command. |
| 1069 | 1349 | */ |
| @@ -1090,10 +1370,12 @@ | ||
| 1090 | 1370 | alert_find_emailaddr_func, 0, 0); |
| 1091 | 1371 | sqlite3_create_function(db, "display_name", 1, SQLITE_UTF8, 0, |
| 1092 | 1372 | alert_display_name_func, 0, 0); |
| 1093 | 1373 | sqlite3_create_function(db, "obscure", 1, SQLITE_UTF8, 0, |
| 1094 | 1374 | db_obscure, 0, 0); |
| 1375 | + sqlite3_create_function(db, "protected_setting", 1, SQLITE_UTF8, 0, | |
| 1376 | + db_protected_setting_func, 0, 0); | |
| 1095 | 1377 | } |
| 1096 | 1378 | |
| 1097 | 1379 | #if USE_SEE |
| 1098 | 1380 | /* |
| 1099 | 1381 | ** This is a pointer to the saved database encryption key string. |
| @@ -1348,10 +1630,11 @@ | ||
| 1348 | 1630 | if( g.fSqlTrace ) sqlite3_trace_v2(db, SQLITE_TRACE_PROFILE, db_sql_trace, 0); |
| 1349 | 1631 | db_add_aux_functions(db); |
| 1350 | 1632 | re_add_sql_func(db); /* The REGEXP operator */ |
| 1351 | 1633 | foci_register(db); /* The "files_of_checkin" virtual table */ |
| 1352 | 1634 | sqlite3_db_config(db, SQLITE_DBCONFIG_ENABLE_FKEY, 0, &rc); |
| 1635 | + sqlite3_set_authorizer(db, db_top_authorizer, db); | |
| 1353 | 1636 | return db; |
| 1354 | 1637 | } |
| 1355 | 1638 | |
| 1356 | 1639 | |
| 1357 | 1640 | /* |
| @@ -1791,22 +2074,10 @@ | ||
| 1791 | 2074 | } |
| 1792 | 2075 | } |
| 1793 | 2076 | return zRepo; |
| 1794 | 2077 | } |
| 1795 | 2078 | |
| 1796 | -/* | |
| 1797 | -** Returns non-zero if the default value for the "allow-symlinks" setting | |
| 1798 | -** is "on". When on Windows, this always returns false. | |
| 1799 | -*/ | |
| 1800 | -int db_allow_symlinks_by_default(void){ | |
| 1801 | -#if defined(_WIN32) | |
| 1802 | - return 0; | |
| 1803 | -#else | |
| 1804 | - return 1; | |
| 1805 | -#endif | |
| 1806 | -} | |
| 1807 | - | |
| 1808 | 2079 | /* |
| 1809 | 2080 | ** Returns non-zero if support for symlinks is currently enabled. |
| 1810 | 2081 | */ |
| 1811 | 2082 | int db_allow_symlinks(void){ |
| 1812 | 2083 | return g.allowSymlinks; |
| @@ -1848,13 +2119,14 @@ | ||
| 1848 | 2119 | g.zRepositoryName = mprintf("%s", zDbName); |
| 1849 | 2120 | db_open_or_attach(g.zRepositoryName, "repository"); |
| 1850 | 2121 | g.repositoryOpen = 1; |
| 1851 | 2122 | sqlite3_file_control(g.db, "repository", SQLITE_FCNTL_DATA_VERSION, |
| 1852 | 2123 | &g.iRepoDataVers); |
| 2124 | + | |
| 1853 | 2125 | /* Cache "allow-symlinks" option, because we'll need it on every stat call */ |
| 1854 | - g.allowSymlinks = db_get_boolean("allow-symlinks", | |
| 1855 | - db_allow_symlinks_by_default()); | |
| 2126 | + g.allowSymlinks = db_get_boolean("allow-symlinks",0); | |
| 2127 | + | |
| 1856 | 2128 | g.zAuxSchema = db_get("aux-schema",""); |
| 1857 | 2129 | g.eHashPolicy = db_get_int("hash-policy",-1); |
| 1858 | 2130 | if( g.eHashPolicy<0 ){ |
| 1859 | 2131 | g.eHashPolicy = hname_default_policy(); |
| 1860 | 2132 | db_set_int("hash-policy", g.eHashPolicy, 0); |
| @@ -2089,10 +2361,11 @@ | ||
| 2089 | 2361 | ** argument is true. Ignore unfinalized statements when false. |
| 2090 | 2362 | */ |
| 2091 | 2363 | void db_close(int reportErrors){ |
| 2092 | 2364 | sqlite3_stmt *pStmt; |
| 2093 | 2365 | if( g.db==0 ) return; |
| 2366 | + sqlite3_set_authorizer(g.db, 0, 0); | |
| 2094 | 2367 | if( g.fSqlStats ){ |
| 2095 | 2368 | int cur, hiwtr; |
| 2096 | 2369 | sqlite3_db_status(g.db, SQLITE_DBSTATUS_LOOKASIDE_USED, &cur, &hiwtr, 0); |
| 2097 | 2370 | fprintf(stderr, "-- LOOKASIDE_USED %10d %10d\n", cur, hiwtr); |
| 2098 | 2371 | sqlite3_db_status(g.db, SQLITE_DBSTATUS_LOOKASIDE_HIT, &cur, &hiwtr, 0); |
| @@ -2118,17 +2391,20 @@ | ||
| 2118 | 2391 | fprintf(stderr, "-- prepared statements %10d\n", db.nPrepare); |
| 2119 | 2392 | } |
| 2120 | 2393 | while( db.pAllStmt ){ |
| 2121 | 2394 | db_finalize(db.pAllStmt); |
| 2122 | 2395 | } |
| 2123 | - if( db.nBegin && reportErrors ){ | |
| 2124 | - fossil_warning("Transaction started at %s:%d never commits", | |
| 2125 | - db.zStartFile, db.iStartLine); | |
| 2396 | + if( db.nBegin ){ | |
| 2397 | + if( reportErrors ){ | |
| 2398 | + fossil_warning("Transaction started at %s:%d never commits", | |
| 2399 | + db.zStartFile, db.iStartLine); | |
| 2400 | + } | |
| 2126 | 2401 | db_end_transaction(1); |
| 2127 | 2402 | } |
| 2128 | 2403 | pStmt = 0; |
| 2129 | - g.dbIgnoreErrors++; /* Stop "database locked" warnings from PRAGMA optimize */ | |
| 2404 | + sqlite3_busy_timeout(g.db, 0); | |
| 2405 | + g.dbIgnoreErrors++; /* Stop "database locked" warnings */ | |
| 2130 | 2406 | sqlite3_exec(g.db, "PRAGMA optimize", 0, 0, 0); |
| 2131 | 2407 | g.dbIgnoreErrors--; |
| 2132 | 2408 | db_close_config(); |
| 2133 | 2409 | |
| 2134 | 2410 | /* If the localdb has a lot of unused free space, |
| @@ -2136,11 +2412,13 @@ | ||
| 2136 | 2412 | */ |
| 2137 | 2413 | if( db_database_slot("localdb")>=0 ){ |
| 2138 | 2414 | int nFree = db_int(0, "PRAGMA localdb.freelist_count"); |
| 2139 | 2415 | int nTotal = db_int(0, "PRAGMA localdb.page_count"); |
| 2140 | 2416 | if( nFree>nTotal/4 ){ |
| 2417 | + db_unprotect(PROTECT_ALL); | |
| 2141 | 2418 | db_multi_exec("VACUUM localdb;"); |
| 2419 | + db_protect_pop(); | |
| 2142 | 2420 | } |
| 2143 | 2421 | } |
| 2144 | 2422 | |
| 2145 | 2423 | if( g.db ){ |
| 2146 | 2424 | int rc; |
| @@ -2154,10 +2432,11 @@ | ||
| 2154 | 2432 | } |
| 2155 | 2433 | g.db = 0; |
| 2156 | 2434 | } |
| 2157 | 2435 | g.repositoryOpen = 0; |
| 2158 | 2436 | g.localOpen = 0; |
| 2437 | + db.bProtectTriggers = 0; | |
| 2159 | 2438 | assert( g.dbConfig==0 ); |
| 2160 | 2439 | assert( g.zConfigDbName==0 ); |
| 2161 | 2440 | backoffice_run_if_needed(); |
| 2162 | 2441 | } |
| 2163 | 2442 | |
| @@ -2168,10 +2447,11 @@ | ||
| 2168 | 2447 | if( g.db ){ |
| 2169 | 2448 | int rc; |
| 2170 | 2449 | sqlite3_wal_checkpoint(g.db, 0); |
| 2171 | 2450 | rc = sqlite3_close(g.db); |
| 2172 | 2451 | if( g.fSqlTrace ) fossil_trace("-- sqlite3_close(%d)\n", rc); |
| 2452 | + db_clear_authorizer(); | |
| 2173 | 2453 | } |
| 2174 | 2454 | g.db = 0; |
| 2175 | 2455 | g.repositoryOpen = 0; |
| 2176 | 2456 | g.localOpen = 0; |
| 2177 | 2457 | } |
| @@ -2215,10 +2495,11 @@ | ||
| 2215 | 2495 | zUser = fossil_getenv("USERNAME"); |
| 2216 | 2496 | } |
| 2217 | 2497 | if( zUser==0 ){ |
| 2218 | 2498 | zUser = "root"; |
| 2219 | 2499 | } |
| 2500 | + db_unprotect(PROTECT_USER); | |
| 2220 | 2501 | db_multi_exec( |
| 2221 | 2502 | "INSERT OR IGNORE INTO user(login, info) VALUES(%Q,'')", zUser |
| 2222 | 2503 | ); |
| 2223 | 2504 | db_multi_exec( |
| 2224 | 2505 | "UPDATE user SET cap='s', pw=%Q" |
| @@ -2234,10 +2515,11 @@ | ||
| 2234 | 2515 | " VALUES('developer','','ei','Dev');" |
| 2235 | 2516 | "INSERT OR IGNORE INTO user(login,pw,cap,info)" |
| 2236 | 2517 | " VALUES('reader','','kptw','Reader');" |
| 2237 | 2518 | ); |
| 2238 | 2519 | } |
| 2520 | + db_protect_pop(); | |
| 2239 | 2521 | } |
| 2240 | 2522 | |
| 2241 | 2523 | /* |
| 2242 | 2524 | ** Return a pointer to a string that contains the RHS of an IN operator |
| 2243 | 2525 | ** that will select CONFIG table names that are in the list of control |
| @@ -2285,10 +2567,11 @@ | ||
| 2285 | 2567 | ){ |
| 2286 | 2568 | char *zDate; |
| 2287 | 2569 | Blob hash; |
| 2288 | 2570 | Blob manifest; |
| 2289 | 2571 | |
| 2572 | + db_unprotect(PROTECT_ALL); | |
| 2290 | 2573 | db_set("content-schema", CONTENT_SCHEMA, 0); |
| 2291 | 2574 | db_set("aux-schema", AUX_SCHEMA_MAX, 0); |
| 2292 | 2575 | db_set("rebuilt", get_version(), 0); |
| 2293 | 2576 | db_set("admin-log", "1", 0); |
| 2294 | 2577 | db_set("access-log", "1", 0); |
| @@ -2343,10 +2626,11 @@ | ||
| 2343 | 2626 | " photo = (SELECT u2.photo FROM settingSrc.user u2" |
| 2344 | 2627 | " WHERE u2.login = user.login)" |
| 2345 | 2628 | " WHERE user.login IN ('anonymous','nobody','developer','reader');" |
| 2346 | 2629 | ); |
| 2347 | 2630 | } |
| 2631 | + db_protect_pop(); | |
| 2348 | 2632 | |
| 2349 | 2633 | if( zInitialDate ){ |
| 2350 | 2634 | int rid; |
| 2351 | 2635 | blob_zero(&manifest); |
| 2352 | 2636 | blob_appendf(&manifest, "C initial\\sempty\\scheck-in\n"); |
| @@ -2839,10 +3123,12 @@ | ||
| 2839 | 3123 | z = db_text(0, "SELECT strftime(%Q,%Q,'unixepoch');", zFormat, z); |
| 2840 | 3124 | } |
| 2841 | 3125 | return z; |
| 2842 | 3126 | } |
| 2843 | 3127 | void db_set(const char *zName, const char *zValue, int globalFlag){ |
| 3128 | + db_assert_protection_off_or_not_sensitive(zName); | |
| 3129 | + db_unprotect(PROTECT_CONFIG); | |
| 2844 | 3130 | db_begin_transaction(); |
| 2845 | 3131 | if( globalFlag ){ |
| 2846 | 3132 | db_swap_connections(); |
| 2847 | 3133 | db_multi_exec("REPLACE INTO global_config(name,value) VALUES(%Q,%Q)", |
| 2848 | 3134 | zName, zValue); |
| @@ -2853,13 +3139,15 @@ | ||
| 2853 | 3139 | } |
| 2854 | 3140 | if( globalFlag && g.repositoryOpen ){ |
| 2855 | 3141 | db_multi_exec("DELETE FROM config WHERE name=%Q", zName); |
| 2856 | 3142 | } |
| 2857 | 3143 | db_end_transaction(0); |
| 3144 | + db_protect_pop(); | |
| 2858 | 3145 | } |
| 2859 | 3146 | void db_unset(const char *zName, int globalFlag){ |
| 2860 | 3147 | db_begin_transaction(); |
| 3148 | + db_unprotect(PROTECT_CONFIG); | |
| 2861 | 3149 | if( globalFlag ){ |
| 2862 | 3150 | db_swap_connections(); |
| 2863 | 3151 | db_multi_exec("DELETE FROM global_config WHERE name=%Q", zName); |
| 2864 | 3152 | db_swap_connections(); |
| 2865 | 3153 | }else{ |
| @@ -2866,10 +3154,11 @@ | ||
| 2866 | 3154 | db_multi_exec("DELETE FROM config WHERE name=%Q", zName); |
| 2867 | 3155 | } |
| 2868 | 3156 | if( globalFlag && g.repositoryOpen ){ |
| 2869 | 3157 | db_multi_exec("DELETE FROM config WHERE name=%Q", zName); |
| 2870 | 3158 | } |
| 3159 | + db_protect_pop(); | |
| 2871 | 3160 | db_end_transaction(0); |
| 2872 | 3161 | } |
| 2873 | 3162 | int db_is_global(const char *zName){ |
| 2874 | 3163 | int rc = 0; |
| 2875 | 3164 | if( g.zConfigDbName ){ |
| @@ -2899,10 +3188,12 @@ | ||
| 2899 | 3188 | db_swap_connections(); |
| 2900 | 3189 | } |
| 2901 | 3190 | return v; |
| 2902 | 3191 | } |
| 2903 | 3192 | void db_set_int(const char *zName, int value, int globalFlag){ |
| 3193 | + db_assert_protection_off_or_not_sensitive(zName); | |
| 3194 | + db_unprotect(PROTECT_CONFIG); | |
| 2904 | 3195 | if( globalFlag ){ |
| 2905 | 3196 | db_swap_connections(); |
| 2906 | 3197 | db_multi_exec("REPLACE INTO global_config(name,value) VALUES(%Q,%d)", |
| 2907 | 3198 | zName, value); |
| 2908 | 3199 | db_swap_connections(); |
| @@ -2911,10 +3202,11 @@ | ||
| 2911 | 3202 | zName, value); |
| 2912 | 3203 | } |
| 2913 | 3204 | if( globalFlag && g.repositoryOpen ){ |
| 2914 | 3205 | db_multi_exec("DELETE FROM config WHERE name=%Q", zName); |
| 2915 | 3206 | } |
| 3207 | + db_protect_pop(); | |
| 2916 | 3208 | } |
| 2917 | 3209 | int db_get_boolean(const char *zName, int dflt){ |
| 2918 | 3210 | char *zVal = db_get(zName, dflt ? "on" : "off"); |
| 2919 | 3211 | if( is_truth(zVal) ){ |
| 2920 | 3212 | dflt = 1; |
| @@ -3040,24 +3332,28 @@ | ||
| 3040 | 3332 | } |
| 3041 | 3333 | file_canonical_name(zName, &full, 0); |
| 3042 | 3334 | (void)filename_collation(); /* Initialize before connection swap */ |
| 3043 | 3335 | db_swap_connections(); |
| 3044 | 3336 | zRepoSetting = mprintf("repo:%q", blob_str(&full)); |
| 3337 | + | |
| 3338 | + db_unprotect(PROTECT_CONFIG); | |
| 3045 | 3339 | db_multi_exec( |
| 3046 | 3340 | "DELETE FROM global_config WHERE name %s = %Q;", |
| 3047 | 3341 | filename_collation(), zRepoSetting |
| 3048 | 3342 | ); |
| 3049 | 3343 | db_multi_exec( |
| 3050 | 3344 | "INSERT OR IGNORE INTO global_config(name,value)" |
| 3051 | 3345 | "VALUES(%Q,1);", |
| 3052 | 3346 | zRepoSetting |
| 3053 | 3347 | ); |
| 3348 | + db_protect_pop(); | |
| 3054 | 3349 | fossil_free(zRepoSetting); |
| 3055 | 3350 | if( g.localOpen && g.zLocalRoot && g.zLocalRoot[0] ){ |
| 3056 | 3351 | Blob localRoot; |
| 3057 | 3352 | file_canonical_name(g.zLocalRoot, &localRoot, 1); |
| 3058 | 3353 | zCkoutSetting = mprintf("ckout:%q", blob_str(&localRoot)); |
| 3354 | + db_unprotect(PROTECT_CONFIG); | |
| 3059 | 3355 | db_multi_exec( |
| 3060 | 3356 | "DELETE FROM global_config WHERE name %s = %Q;", |
| 3061 | 3357 | filename_collation(), zCkoutSetting |
| 3062 | 3358 | ); |
| 3063 | 3359 | db_multi_exec( |
| @@ -3073,10 +3369,11 @@ | ||
| 3073 | 3369 | db_optional_sql("repository", |
| 3074 | 3370 | "REPLACE INTO config(name,value,mtime)" |
| 3075 | 3371 | "VALUES(%Q,1,now());", |
| 3076 | 3372 | zCkoutSetting |
| 3077 | 3373 | ); |
| 3374 | + db_protect_pop(); | |
| 3078 | 3375 | fossil_free(zCkoutSetting); |
| 3079 | 3376 | blob_reset(&localRoot); |
| 3080 | 3377 | }else{ |
| 3081 | 3378 | db_swap_connections(); |
| 3082 | 3379 | } |
| @@ -3131,11 +3428,10 @@ | ||
| 3131 | 3428 | void cmd_open(void){ |
| 3132 | 3429 | int emptyFlag; |
| 3133 | 3430 | int keepFlag; |
| 3134 | 3431 | int forceMissingFlag; |
| 3135 | 3432 | int allowNested; |
| 3136 | - int allowSymlinks; | |
| 3137 | 3433 | int setmtimeFlag; /* --setmtime. Set mtimes on files */ |
| 3138 | 3434 | int bForce = 0; /* --force. Open even if non-empty dir */ |
| 3139 | 3435 | static char *azNewArgv[] = { 0, "checkout", "--prompt", 0, 0, 0, 0 }; |
| 3140 | 3436 | const char *zWorkDir; /* --workdir value */ |
| 3141 | 3437 | const char *zRepo = 0; /* Name of the repository file */ |
| @@ -3242,23 +3538,10 @@ | ||
| 3242 | 3538 | }else if( db_exists("SELECT 1 FROM event WHERE type='ci'") ){ |
| 3243 | 3539 | g.zOpenRevision = db_get("main-branch", 0); |
| 3244 | 3540 | } |
| 3245 | 3541 | } |
| 3246 | 3542 | |
| 3247 | - if( g.zOpenRevision ){ | |
| 3248 | - /* Since the repository is open and we know the revision now, | |
| 3249 | - ** refresh the allow-symlinks flag. Since neither the local | |
| 3250 | - ** checkout nor the configuration database are open at this | |
| 3251 | - ** point, this should always return the versioned setting, | |
| 3252 | - ** if any, or the default value, which is negative one. The | |
| 3253 | - ** value negative one, in this context, means that the code | |
| 3254 | - ** below should fallback to using the setting value from the | |
| 3255 | - ** repository or global configuration databases only. */ | |
| 3256 | - allowSymlinks = db_get_versioned_boolean("allow-symlinks", -1); | |
| 3257 | - }else{ | |
| 3258 | - allowSymlinks = -1; /* Use non-versioned settings only. */ | |
| 3259 | - } | |
| 3260 | 3543 | |
| 3261 | 3544 | #if defined(_WIN32) || defined(__CYGWIN__) |
| 3262 | 3545 | # define LOCALDB_NAME "./_FOSSIL_" |
| 3263 | 3546 | #else |
| 3264 | 3547 | # define LOCALDB_NAME "./.fslckout" |
| @@ -3268,26 +3551,10 @@ | ||
| 3268 | 3551 | "COMMIT; PRAGMA journal_mode=WAL; BEGIN;", |
| 3269 | 3552 | #endif |
| 3270 | 3553 | (char*)0); |
| 3271 | 3554 | db_delete_on_failure(LOCALDB_NAME); |
| 3272 | 3555 | db_open_local(0); |
| 3273 | - if( allowSymlinks>=0 ){ | |
| 3274 | - /* Use the value from the versioned setting, which was read | |
| 3275 | - ** prior to opening the local checkout (i.e. which is most | |
| 3276 | - ** likely empty and does not actually contain any versioned | |
| 3277 | - ** setting files yet). Normally, this value would be given | |
| 3278 | - ** first priority within db_get_boolean(); however, this is | |
| 3279 | - ** a special case because we know the on-disk files may not | |
| 3280 | - ** exist yet. */ | |
| 3281 | - g.allowSymlinks = allowSymlinks; | |
| 3282 | - }else{ | |
| 3283 | - /* Since the local checkout may not have any files at this | |
| 3284 | - ** point, this will probably be the setting value from the | |
| 3285 | - ** repository or global configuration databases. */ | |
| 3286 | - g.allowSymlinks = db_get_boolean("allow-symlinks", | |
| 3287 | - db_allow_symlinks_by_default()); | |
| 3288 | - } | |
| 3289 | 3556 | db_lset("repository", zRepo); |
| 3290 | 3557 | db_record_repository_filename(zRepo); |
| 3291 | 3558 | db_set_checkout(0); |
| 3292 | 3559 | azNewArgv[0] = g.argv[0]; |
| 3293 | 3560 | g.argv = azNewArgv; |
| @@ -3376,12 +3643,13 @@ | ||
| 3376 | 3643 | const char *name; /* Name of the setting */ |
| 3377 | 3644 | const char *var; /* Internal variable name used by db_set() */ |
| 3378 | 3645 | int width; /* Width of display. 0 for boolean values and |
| 3379 | 3646 | ** negative for values which should not appear |
| 3380 | 3647 | ** on the /setup_settings page. */ |
| 3381 | - int versionable; /* Is this setting versionable? */ | |
| 3382 | - int forceTextArea; /* Force using a text area for display? */ | |
| 3648 | + char versionable; /* Is this setting versionable? */ | |
| 3649 | + char forceTextArea; /* Force using a text area for display? */ | |
| 3650 | + char sensitive; /* True if this a security-sensitive setting */ | |
| 3383 | 3651 | const char *def; /* Default value */ |
| 3384 | 3652 | }; |
| 3385 | 3653 | #endif /* INTERFACE */ |
| 3386 | 3654 | |
| 3387 | 3655 | /* |
| @@ -3395,32 +3663,29 @@ | ||
| 3395 | 3663 | ** SETTING: admin-log boolean default=off |
| 3396 | 3664 | ** |
| 3397 | 3665 | ** When the admin-log setting is enabled, configuration changes are recorded |
| 3398 | 3666 | ** in the "admin_log" table of the repository. |
| 3399 | 3667 | */ |
| 3400 | -#if defined(_WIN32) | |
| 3401 | -/* | |
| 3402 | -** SETTING: allow-symlinks boolean default=off versionable | |
| 3403 | -** | |
| 3404 | -** When allow-symlinks is OFF, symbolic links in the repository are followed | |
| 3405 | -** and treated no differently from real files. When allow-symlinks is ON, | |
| 3406 | -** the object to which the symbolic link points is ignored, and the content | |
| 3407 | -** of the symbolic link that is stored in the repository is the name of the | |
| 3408 | -** object to which the symbolic link points. | |
| 3409 | -*/ | |
| 3410 | -#endif | |
| 3411 | -#if !defined(_WIN32) | |
| 3412 | -/* | |
| 3413 | -** SETTING: allow-symlinks boolean default=on versionable | |
| 3414 | -** | |
| 3415 | -** When allow-symlinks is OFF, symbolic links in the repository are followed | |
| 3416 | -** and treated no differently from real files. When allow-symlinks is ON, | |
| 3417 | -** the object to which the symbolic link points is ignored, and the content | |
| 3418 | -** of the symbolic link that is stored in the repository is the name of the | |
| 3419 | -** object to which the symbolic link points. | |
| 3420 | -*/ | |
| 3421 | -#endif | |
| 3668 | +/* | |
| 3669 | +** SETTING: allow-symlinks boolean default=off sensitive | |
| 3670 | +** | |
| 3671 | +** When allow-symlinks is OFF, Fossil does not see symbolic links | |
| 3672 | +** (a.k.a "symlinks") on disk as a separate class of object. Instead Fossil | |
| 3673 | +** sees the object that the symlink points to. Fossil will only manage files | |
| 3674 | +** and directories, not symlinks. When a symlink is added to a repository, | |
| 3675 | +** the object that the symlink points to is added, not the symlink itself. | |
| 3676 | +** | |
| 3677 | +** When allow-symlinks is ON, Fossil sees symlinks on disk as a separate | |
| 3678 | +** object class that is distinct from files and directories. When a symlink | |
| 3679 | +** is added to a repository, Fossil stores the target filename. In other | |
| 3680 | +** words, Fossil stores the symlink itself, not the object that the symlink | |
| 3681 | +** points to. | |
| 3682 | +** | |
| 3683 | +** Symlinks are not cross-platform. They are not available on all | |
| 3684 | +** operating systems and file systems. Hence the allow-symlinks setting is | |
| 3685 | +** OFF by default, for portability. | |
| 3686 | +*/ | |
| 3422 | 3687 | /* |
| 3423 | 3688 | ** SETTING: auto-captcha boolean default=on variable=autocaptcha |
| 3424 | 3689 | ** If enabled, the /login page provides a button that will automatically |
| 3425 | 3690 | ** fill in the captcha password. This makes things easier for human users, |
| 3426 | 3691 | ** at the expense of also making logins easier for malicious robots. |
| @@ -3470,11 +3735,11 @@ | ||
| 3470 | 3735 | ** there is no cron job periodically running "fossil backoffice", |
| 3471 | 3736 | ** email notifications and other work normally done by the |
| 3472 | 3737 | ** backoffice will not occur. |
| 3473 | 3738 | */ |
| 3474 | 3739 | /* |
| 3475 | -** SETTING: backoffice-logfile width=40 | |
| 3740 | +** SETTING: backoffice-logfile width=40 sensitive | |
| 3476 | 3741 | ** If backoffice-logfile is not an empty string and is a valid |
| 3477 | 3742 | ** filename, then a one-line message is appended to that file |
| 3478 | 3743 | ** every time the backoffice runs. This can be used for debugging, |
| 3479 | 3744 | ** to ensure that backoffice is running appropriately. |
| 3480 | 3745 | */ |
| @@ -3547,11 +3812,11 @@ | ||
| 3547 | 3812 | /* |
| 3548 | 3813 | ** SETTING: crnl-glob width=40 versionable block-text |
| 3549 | 3814 | ** This is an alias for the crlf-glob setting. |
| 3550 | 3815 | */ |
| 3551 | 3816 | /* |
| 3552 | -** SETTING: default-perms width=16 default=u | |
| 3817 | +** SETTING: default-perms width=16 default=u sensitive | |
| 3553 | 3818 | ** Permissions given automatically to new users. For more |
| 3554 | 3819 | ** information on permissions see the Users page in Server |
| 3555 | 3820 | ** Administration of the HTTP UI. |
| 3556 | 3821 | */ |
| 3557 | 3822 | /* |
| @@ -3559,11 +3824,11 @@ | ||
| 3559 | 3824 | ** If enabled, permit files that may be binary |
| 3560 | 3825 | ** or that match the "binary-glob" setting to be used with |
| 3561 | 3826 | ** external diff programs. If disabled, skip these files. |
| 3562 | 3827 | */ |
| 3563 | 3828 | /* |
| 3564 | -** SETTING: diff-command width=40 | |
| 3829 | +** SETTING: diff-command width=40 sensitive | |
| 3565 | 3830 | ** The value is an external command to run when performing a diff. |
| 3566 | 3831 | ** If undefined, the internal text diff will be used. |
| 3567 | 3832 | */ |
| 3568 | 3833 | /* |
| 3569 | 3834 | ** SETTING: dont-push boolean default=off |
| @@ -3574,11 +3839,11 @@ | ||
| 3574 | 3839 | /* |
| 3575 | 3840 | ** SETTING: dotfiles boolean versionable default=off |
| 3576 | 3841 | ** If enabled, include --dotfiles option for all compatible commands. |
| 3577 | 3842 | */ |
| 3578 | 3843 | /* |
| 3579 | -** SETTING: editor width=32 | |
| 3844 | +** SETTING: editor width=32 sensitive | |
| 3580 | 3845 | ** The value is an external command that will launch the |
| 3581 | 3846 | ** text editor command used for check-in comments. |
| 3582 | 3847 | */ |
| 3583 | 3848 | /* |
| 3584 | 3849 | ** SETTING: empty-dirs width=40 versionable block-text |
| @@ -3617,16 +3882,16 @@ | ||
| 3617 | 3882 | ** An empty list prohibits editing via that page. Note that |
| 3618 | 3883 | ** it cannot edit binary files, so the list should not |
| 3619 | 3884 | ** contain any globs for, e.g., images or PDFs. |
| 3620 | 3885 | */ |
| 3621 | 3886 | /* |
| 3622 | -** SETTING: gdiff-command width=40 default=gdiff | |
| 3887 | +** SETTING: gdiff-command width=40 default=gdiff sensitive | |
| 3623 | 3888 | ** The value is an external command to run when performing a graphical |
| 3624 | 3889 | ** diff. If undefined, text diff will be used. |
| 3625 | 3890 | */ |
| 3626 | 3891 | /* |
| 3627 | -** SETTING: gmerge-command width=40 | |
| 3892 | +** SETTING: gmerge-command width=40 sensitive | |
| 3628 | 3893 | ** The value is a graphical merge conflict resolver command operating |
| 3629 | 3894 | ** on four files. Examples: |
| 3630 | 3895 | ** |
| 3631 | 3896 | ** kdiff3 "%baseline" "%original" "%merge" -o "%output" |
| 3632 | 3897 | ** xxdiff "%original" "%baseline" "%merge" -M "%output" |
| @@ -3757,11 +4022,11 @@ | ||
| 3757 | 4022 | ** the associated files within the checkout -AND- the "rm" |
| 3758 | 4023 | ** and "delete" commands will also remove the associated |
| 3759 | 4024 | ** files from within the checkout. |
| 3760 | 4025 | */ |
| 3761 | 4026 | /* |
| 3762 | -** SETTING: pgp-command width=40 | |
| 4027 | +** SETTING: pgp-command width=40 sensitive | |
| 3763 | 4028 | ** Command used to clear-sign manifests at check-in. |
| 3764 | 4029 | ** Default value is "gpg --clearsign -o" |
| 3765 | 4030 | */ |
| 3766 | 4031 | /* |
| 3767 | 4032 | ** SETTING: forbid-delta-manifests boolean default=off |
| @@ -3817,22 +4082,22 @@ | ||
| 3817 | 4082 | ** |
| 3818 | 4083 | ** If repolist-skin has a value of 2, then the repository is omitted from |
| 3819 | 4084 | ** the list in use cases 1 through 4, but not for 5 and 6. |
| 3820 | 4085 | */ |
| 3821 | 4086 | /* |
| 3822 | -** SETTING: self-register boolean default=off | |
| 4087 | +** SETTING: self-register boolean default=off sensitive | |
| 3823 | 4088 | ** Allow users to register themselves through the HTTP UI. |
| 3824 | 4089 | ** This is useful if you want to see other names than |
| 3825 | 4090 | ** "Anonymous" in e.g. ticketing system. On the other hand |
| 3826 | 4091 | ** users can not be deleted. |
| 3827 | 4092 | */ |
| 3828 | 4093 | /* |
| 3829 | -** SETTING: ssh-command width=40 | |
| 4094 | +** SETTING: ssh-command width=40 sensitive | |
| 3830 | 4095 | ** The command used to talk to a remote machine with the "ssh://" protocol. |
| 3831 | 4096 | */ |
| 3832 | 4097 | /* |
| 3833 | -** SETTING: ssl-ca-location width=40 | |
| 4098 | +** SETTING: ssl-ca-location width=40 sensitive | |
| 3834 | 4099 | ** The full pathname to a file containing PEM encoded |
| 3835 | 4100 | ** CA root certificates, or a directory of certificates |
| 3836 | 4101 | ** with filenames formed from the certificate hashes as |
| 3837 | 4102 | ** required by OpenSSL. |
| 3838 | 4103 | ** |
| @@ -3842,11 +4107,11 @@ | ||
| 3842 | 4107 | ** Checking your platform behaviour is required if the |
| 3843 | 4108 | ** exact contents of the CA root is critical for your |
| 3844 | 4109 | ** application. |
| 3845 | 4110 | */ |
| 3846 | 4111 | /* |
| 3847 | -** SETTING: ssl-identity width=40 | |
| 4112 | +** SETTING: ssl-identity width=40 sensitive | |
| 3848 | 4113 | ** The full pathname to a file containing a certificate |
| 3849 | 4114 | ** and private key in PEM format. Create by concatenating |
| 3850 | 4115 | ** the certificate and private key files. |
| 3851 | 4116 | ** |
| 3852 | 4117 | ** This identity will be presented to SSL servers to |
| @@ -3853,33 +4118,33 @@ | ||
| 3853 | 4118 | ** authenticate this client, in addition to the normal |
| 3854 | 4119 | ** password authentication. |
| 3855 | 4120 | */ |
| 3856 | 4121 | #ifdef FOSSIL_ENABLE_TCL |
| 3857 | 4122 | /* |
| 3858 | -** SETTING: tcl boolean default=off | |
| 4123 | +** SETTING: tcl boolean default=off sensitive | |
| 3859 | 4124 | ** If enabled Tcl integration commands will be added to the TH1 |
| 3860 | 4125 | ** interpreter, allowing arbitrary Tcl expressions and |
| 3861 | 4126 | ** scripts to be evaluated from TH1. Additionally, the Tcl |
| 3862 | 4127 | ** interpreter will be able to evaluate arbitrary TH1 |
| 3863 | 4128 | ** expressions and scripts. |
| 3864 | 4129 | */ |
| 3865 | 4130 | /* |
| 3866 | -** SETTING: tcl-setup width=40 block-text | |
| 4131 | +** SETTING: tcl-setup width=40 block-text sensitive | |
| 3867 | 4132 | ** This is the setup script to be evaluated after creating |
| 3868 | 4133 | ** and initializing the Tcl interpreter. By default, this |
| 3869 | 4134 | ** is empty and no extra setup is performed. |
| 3870 | 4135 | */ |
| 3871 | 4136 | #endif /* FOSSIL_ENABLE_TCL */ |
| 3872 | 4137 | /* |
| 3873 | -** SETTING: tclsh width=80 default=tclsh | |
| 4138 | +** SETTING: tclsh width=80 default=tclsh sensitive | |
| 3874 | 4139 | ** Name of the external TCL interpreter used for such things |
| 3875 | 4140 | ** as running the GUI diff viewer launched by the --tk option |
| 3876 | 4141 | ** of the various "diff" commands. |
| 3877 | 4142 | */ |
| 3878 | 4143 | #ifdef FOSSIL_ENABLE_TH1_DOCS |
| 3879 | 4144 | /* |
| 3880 | -** SETTING: th1-docs boolean default=off | |
| 4145 | +** SETTING: th1-docs boolean default=off sensitive | |
| 3881 | 4146 | ** If enabled, this allows embedded documentation files to contain |
| 3882 | 4147 | ** arbitrary TH1 scripts that are evaluated on the server. If native |
| 3883 | 4148 | ** Tcl integration is also enabled, this setting has the |
| 3884 | 4149 | ** potential to allow anybody with check-in privileges to |
| 3885 | 4150 | ** do almost anything that the associated operating system |
| @@ -3932,11 +4197,11 @@ | ||
| 3932 | 4197 | ** of a "fossil clone" or "fossil sync" command. The |
| 3933 | 4198 | ** default is false, in which case the -u option is |
| 3934 | 4199 | ** needed to clone or sync unversioned files. |
| 3935 | 4200 | */ |
| 3936 | 4201 | /* |
| 3937 | -** SETTING: web-browser width=30 | |
| 4202 | +** SETTING: web-browser width=30 sensitive | |
| 3938 | 4203 | ** A shell command used to launch your preferred |
| 3939 | 4204 | ** web browser when given a URL as an argument. |
| 3940 | 4205 | ** Defaults to "start" on windows, "open" on Mac, |
| 3941 | 4206 | ** and "firefox" on Unix. |
| 3942 | 4207 | */ |
| @@ -4058,11 +4323,13 @@ | ||
| 4058 | 4323 | fossil_fatal("cannot set 'manifest' globally"); |
| 4059 | 4324 | } |
| 4060 | 4325 | if( unsetFlag ){ |
| 4061 | 4326 | db_unset(pSetting->name, globalFlag); |
| 4062 | 4327 | }else{ |
| 4328 | + db_protect_only(PROTECT_NONE); | |
| 4063 | 4329 | db_set(pSetting->name, g.argv[3], globalFlag); |
| 4330 | + db_protect_pop(); | |
| 4064 | 4331 | } |
| 4065 | 4332 | if( isManifest && g.localOpen ){ |
| 4066 | 4333 | manifest_to_disk(db_lget_int("checkout", 0)); |
| 4067 | 4334 | } |
| 4068 | 4335 | }else{ |
| 4069 | 4336 |
| --- src/db.c | |
| +++ src/db.c | |
| @@ -69,10 +69,11 @@ | |
| 69 | #endif /* INTERFACE */ |
| 70 | const struct Stmt empty_Stmt = empty_Stmt_m; |
| 71 | |
| 72 | /* |
| 73 | ** Call this routine when a database error occurs. |
| 74 | */ |
| 75 | static void db_err(const char *zFormat, ...){ |
| 76 | va_list ap; |
| 77 | char *z; |
| 78 | va_start(ap, zFormat); |
| @@ -113,10 +114,11 @@ | |
| 113 | /* |
| 114 | ** All static variable that a used by only this file are gathered into |
| 115 | ** the following structure. |
| 116 | */ |
| 117 | static struct DbLocalData { |
| 118 | int nBegin; /* Nesting depth of BEGIN */ |
| 119 | int doRollback; /* True to force a rollback */ |
| 120 | int nCommitHook; /* Number of commit hooks */ |
| 121 | int wrTxn; /* Outer-most TNX is a write */ |
| 122 | Stmt *pAllStmt; /* List of all unfinalized statements */ |
| @@ -130,11 +132,19 @@ | |
| 130 | char *azBeforeCommit[5]; /* Commands to run prior to COMMIT */ |
| 131 | int nBeforeCommit; /* Number of entries in azBeforeCommit */ |
| 132 | int nPriorChanges; /* sqlite3_total_changes() at transaction start */ |
| 133 | const char *zStartFile; /* File in which transaction was started */ |
| 134 | int iStartLine; /* Line of zStartFile where transaction started */ |
| 135 | } db = {0, 0, 0, 0, 0, 0, }; |
| 136 | |
| 137 | /* |
| 138 | ** Arrange for the given file to be deleted on a failure. |
| 139 | */ |
| 140 | void db_delete_on_failure(const char *zFilename){ |
| @@ -238,17 +248,19 @@ | |
| 238 | db.nBegin--; |
| 239 | if( db.nBegin==0 ){ |
| 240 | int i; |
| 241 | if( db.doRollback==0 && db.nPriorChanges<sqlite3_total_changes(g.db) ){ |
| 242 | i = 0; |
| 243 | while( db.nBeforeCommit ){ |
| 244 | db.nBeforeCommit--; |
| 245 | sqlite3_exec(g.db, db.azBeforeCommit[i], 0, 0, 0); |
| 246 | sqlite3_free(db.azBeforeCommit[i]); |
| 247 | i++; |
| 248 | } |
| 249 | leaf_do_pending_checks(); |
| 250 | } |
| 251 | for(i=0; db.doRollback==0 && i<db.nCommitHook; i++){ |
| 252 | int rc = db.aHook[i].xHook(); |
| 253 | if( rc ){ |
| 254 | db.doRollback = 1; |
| @@ -316,10 +328,230 @@ | |
| 316 | } |
| 317 | db.aHook[db.nCommitHook].sequence = sequence; |
| 318 | db.aHook[db.nCommitHook].xHook = x; |
| 319 | db.nCommitHook++; |
| 320 | } |
| 321 | |
| 322 | #if INTERFACE |
| 323 | /* |
| 324 | ** Possible flags to db_vprepare |
| 325 | */ |
| @@ -334,21 +566,24 @@ | |
| 334 | */ |
| 335 | int db_vprepare(Stmt *pStmt, int flags, const char *zFormat, va_list ap){ |
| 336 | int rc; |
| 337 | int prepFlags = 0; |
| 338 | char *zSql; |
| 339 | blob_zero(&pStmt->sql); |
| 340 | blob_vappendf(&pStmt->sql, zFormat, ap); |
| 341 | va_end(ap); |
| 342 | zSql = blob_str(&pStmt->sql); |
| 343 | db.nPrepare++; |
| 344 | if( flags & DB_PREPARE_PERSISTENT ){ |
| 345 | prepFlags = SQLITE_PREPARE_PERSISTENT; |
| 346 | } |
| 347 | rc = sqlite3_prepare_v3(g.db, zSql, -1, prepFlags, &pStmt->pStmt, 0); |
| 348 | if( rc!=0 && (flags & DB_PREPARE_IGNORE_ERROR)==0 ){ |
| 349 | db_err("%s\n%s", sqlite3_errmsg(g.db), zSql); |
| 350 | } |
| 351 | pStmt->pNext = db.pAllStmt; |
| 352 | pStmt->pPrev = 0; |
| 353 | if( db.pAllStmt ) db.pAllStmt->pPrev = pStmt; |
| 354 | db.pAllStmt = pStmt; |
| @@ -611,10 +846,11 @@ | |
| 611 | return rc; |
| 612 | } |
| 613 | |
| 614 | /* |
| 615 | ** COMMAND: test-db-exec-error |
| 616 | ** |
| 617 | ** Invoke the db_exec() interface with an erroneous SQL statement |
| 618 | ** in order to verify the error handling logic. |
| 619 | */ |
| 620 | void db_test_db_exec_cmd(void){ |
| @@ -621,10 +857,27 @@ | |
| 621 | Stmt err; |
| 622 | db_find_and_open_repository(0,0); |
| 623 | db_prepare(&err, "INSERT INTO repository.config(name) VALUES(NULL);"); |
| 624 | db_exec(&err); |
| 625 | } |
| 626 | |
| 627 | /* |
| 628 | ** Print the output of one or more SQL queries on standard output. |
| 629 | ** This routine is used for debugging purposes only. |
| 630 | */ |
| @@ -844,34 +1097,34 @@ | |
| 844 | void db_init_database( |
| 845 | const char *zFileName, /* Name of database file to create */ |
| 846 | const char *zSchema, /* First part of schema */ |
| 847 | ... /* Additional SQL to run. Terminate with NULL. */ |
| 848 | ){ |
| 849 | sqlite3 *db; |
| 850 | int rc; |
| 851 | const char *zSql; |
| 852 | va_list ap; |
| 853 | |
| 854 | db = db_open(zFileName ? zFileName : ":memory:"); |
| 855 | sqlite3_exec(db, "BEGIN EXCLUSIVE", 0, 0, 0); |
| 856 | rc = sqlite3_exec(db, zSchema, 0, 0, 0); |
| 857 | if( rc!=SQLITE_OK ){ |
| 858 | db_err("%s", sqlite3_errmsg(db)); |
| 859 | } |
| 860 | va_start(ap, zSchema); |
| 861 | while( (zSql = va_arg(ap, const char*))!=0 ){ |
| 862 | rc = sqlite3_exec(db, zSql, 0, 0, 0); |
| 863 | if( rc!=SQLITE_OK ){ |
| 864 | db_err("%s", sqlite3_errmsg(db)); |
| 865 | } |
| 866 | } |
| 867 | va_end(ap); |
| 868 | sqlite3_exec(db, "COMMIT", 0, 0, 0); |
| 869 | if( zFileName || g.db!=0 ){ |
| 870 | sqlite3_close(db); |
| 871 | }else{ |
| 872 | g.db = db; |
| 873 | } |
| 874 | } |
| 875 | |
| 876 | /* |
| 877 | ** Function to return the number of seconds since 1970. This is |
| @@ -1060,10 +1313,37 @@ | |
| 1060 | } |
| 1061 | strcpy(zOut, zTemp = obscure((char*)zIn)); |
| 1062 | fossil_free(zTemp); |
| 1063 | sqlite3_result_text(context, zOut, strlen(zOut), sqlite3_free); |
| 1064 | } |
| 1065 | |
| 1066 | /* |
| 1067 | ** Register the SQL functions that are useful both to the internal |
| 1068 | ** representation and to the "fossil sql" command. |
| 1069 | */ |
| @@ -1090,10 +1370,12 @@ | |
| 1090 | alert_find_emailaddr_func, 0, 0); |
| 1091 | sqlite3_create_function(db, "display_name", 1, SQLITE_UTF8, 0, |
| 1092 | alert_display_name_func, 0, 0); |
| 1093 | sqlite3_create_function(db, "obscure", 1, SQLITE_UTF8, 0, |
| 1094 | db_obscure, 0, 0); |
| 1095 | } |
| 1096 | |
| 1097 | #if USE_SEE |
| 1098 | /* |
| 1099 | ** This is a pointer to the saved database encryption key string. |
| @@ -1348,10 +1630,11 @@ | |
| 1348 | if( g.fSqlTrace ) sqlite3_trace_v2(db, SQLITE_TRACE_PROFILE, db_sql_trace, 0); |
| 1349 | db_add_aux_functions(db); |
| 1350 | re_add_sql_func(db); /* The REGEXP operator */ |
| 1351 | foci_register(db); /* The "files_of_checkin" virtual table */ |
| 1352 | sqlite3_db_config(db, SQLITE_DBCONFIG_ENABLE_FKEY, 0, &rc); |
| 1353 | return db; |
| 1354 | } |
| 1355 | |
| 1356 | |
| 1357 | /* |
| @@ -1791,22 +2074,10 @@ | |
| 1791 | } |
| 1792 | } |
| 1793 | return zRepo; |
| 1794 | } |
| 1795 | |
| 1796 | /* |
| 1797 | ** Returns non-zero if the default value for the "allow-symlinks" setting |
| 1798 | ** is "on". When on Windows, this always returns false. |
| 1799 | */ |
| 1800 | int db_allow_symlinks_by_default(void){ |
| 1801 | #if defined(_WIN32) |
| 1802 | return 0; |
| 1803 | #else |
| 1804 | return 1; |
| 1805 | #endif |
| 1806 | } |
| 1807 | |
| 1808 | /* |
| 1809 | ** Returns non-zero if support for symlinks is currently enabled. |
| 1810 | */ |
| 1811 | int db_allow_symlinks(void){ |
| 1812 | return g.allowSymlinks; |
| @@ -1848,13 +2119,14 @@ | |
| 1848 | g.zRepositoryName = mprintf("%s", zDbName); |
| 1849 | db_open_or_attach(g.zRepositoryName, "repository"); |
| 1850 | g.repositoryOpen = 1; |
| 1851 | sqlite3_file_control(g.db, "repository", SQLITE_FCNTL_DATA_VERSION, |
| 1852 | &g.iRepoDataVers); |
| 1853 | /* Cache "allow-symlinks" option, because we'll need it on every stat call */ |
| 1854 | g.allowSymlinks = db_get_boolean("allow-symlinks", |
| 1855 | db_allow_symlinks_by_default()); |
| 1856 | g.zAuxSchema = db_get("aux-schema",""); |
| 1857 | g.eHashPolicy = db_get_int("hash-policy",-1); |
| 1858 | if( g.eHashPolicy<0 ){ |
| 1859 | g.eHashPolicy = hname_default_policy(); |
| 1860 | db_set_int("hash-policy", g.eHashPolicy, 0); |
| @@ -2089,10 +2361,11 @@ | |
| 2089 | ** argument is true. Ignore unfinalized statements when false. |
| 2090 | */ |
| 2091 | void db_close(int reportErrors){ |
| 2092 | sqlite3_stmt *pStmt; |
| 2093 | if( g.db==0 ) return; |
| 2094 | if( g.fSqlStats ){ |
| 2095 | int cur, hiwtr; |
| 2096 | sqlite3_db_status(g.db, SQLITE_DBSTATUS_LOOKASIDE_USED, &cur, &hiwtr, 0); |
| 2097 | fprintf(stderr, "-- LOOKASIDE_USED %10d %10d\n", cur, hiwtr); |
| 2098 | sqlite3_db_status(g.db, SQLITE_DBSTATUS_LOOKASIDE_HIT, &cur, &hiwtr, 0); |
| @@ -2118,17 +2391,20 @@ | |
| 2118 | fprintf(stderr, "-- prepared statements %10d\n", db.nPrepare); |
| 2119 | } |
| 2120 | while( db.pAllStmt ){ |
| 2121 | db_finalize(db.pAllStmt); |
| 2122 | } |
| 2123 | if( db.nBegin && reportErrors ){ |
| 2124 | fossil_warning("Transaction started at %s:%d never commits", |
| 2125 | db.zStartFile, db.iStartLine); |
| 2126 | db_end_transaction(1); |
| 2127 | } |
| 2128 | pStmt = 0; |
| 2129 | g.dbIgnoreErrors++; /* Stop "database locked" warnings from PRAGMA optimize */ |
| 2130 | sqlite3_exec(g.db, "PRAGMA optimize", 0, 0, 0); |
| 2131 | g.dbIgnoreErrors--; |
| 2132 | db_close_config(); |
| 2133 | |
| 2134 | /* If the localdb has a lot of unused free space, |
| @@ -2136,11 +2412,13 @@ | |
| 2136 | */ |
| 2137 | if( db_database_slot("localdb")>=0 ){ |
| 2138 | int nFree = db_int(0, "PRAGMA localdb.freelist_count"); |
| 2139 | int nTotal = db_int(0, "PRAGMA localdb.page_count"); |
| 2140 | if( nFree>nTotal/4 ){ |
| 2141 | db_multi_exec("VACUUM localdb;"); |
| 2142 | } |
| 2143 | } |
| 2144 | |
| 2145 | if( g.db ){ |
| 2146 | int rc; |
| @@ -2154,10 +2432,11 @@ | |
| 2154 | } |
| 2155 | g.db = 0; |
| 2156 | } |
| 2157 | g.repositoryOpen = 0; |
| 2158 | g.localOpen = 0; |
| 2159 | assert( g.dbConfig==0 ); |
| 2160 | assert( g.zConfigDbName==0 ); |
| 2161 | backoffice_run_if_needed(); |
| 2162 | } |
| 2163 | |
| @@ -2168,10 +2447,11 @@ | |
| 2168 | if( g.db ){ |
| 2169 | int rc; |
| 2170 | sqlite3_wal_checkpoint(g.db, 0); |
| 2171 | rc = sqlite3_close(g.db); |
| 2172 | if( g.fSqlTrace ) fossil_trace("-- sqlite3_close(%d)\n", rc); |
| 2173 | } |
| 2174 | g.db = 0; |
| 2175 | g.repositoryOpen = 0; |
| 2176 | g.localOpen = 0; |
| 2177 | } |
| @@ -2215,10 +2495,11 @@ | |
| 2215 | zUser = fossil_getenv("USERNAME"); |
| 2216 | } |
| 2217 | if( zUser==0 ){ |
| 2218 | zUser = "root"; |
| 2219 | } |
| 2220 | db_multi_exec( |
| 2221 | "INSERT OR IGNORE INTO user(login, info) VALUES(%Q,'')", zUser |
| 2222 | ); |
| 2223 | db_multi_exec( |
| 2224 | "UPDATE user SET cap='s', pw=%Q" |
| @@ -2234,10 +2515,11 @@ | |
| 2234 | " VALUES('developer','','ei','Dev');" |
| 2235 | "INSERT OR IGNORE INTO user(login,pw,cap,info)" |
| 2236 | " VALUES('reader','','kptw','Reader');" |
| 2237 | ); |
| 2238 | } |
| 2239 | } |
| 2240 | |
| 2241 | /* |
| 2242 | ** Return a pointer to a string that contains the RHS of an IN operator |
| 2243 | ** that will select CONFIG table names that are in the list of control |
| @@ -2285,10 +2567,11 @@ | |
| 2285 | ){ |
| 2286 | char *zDate; |
| 2287 | Blob hash; |
| 2288 | Blob manifest; |
| 2289 | |
| 2290 | db_set("content-schema", CONTENT_SCHEMA, 0); |
| 2291 | db_set("aux-schema", AUX_SCHEMA_MAX, 0); |
| 2292 | db_set("rebuilt", get_version(), 0); |
| 2293 | db_set("admin-log", "1", 0); |
| 2294 | db_set("access-log", "1", 0); |
| @@ -2343,10 +2626,11 @@ | |
| 2343 | " photo = (SELECT u2.photo FROM settingSrc.user u2" |
| 2344 | " WHERE u2.login = user.login)" |
| 2345 | " WHERE user.login IN ('anonymous','nobody','developer','reader');" |
| 2346 | ); |
| 2347 | } |
| 2348 | |
| 2349 | if( zInitialDate ){ |
| 2350 | int rid; |
| 2351 | blob_zero(&manifest); |
| 2352 | blob_appendf(&manifest, "C initial\\sempty\\scheck-in\n"); |
| @@ -2839,10 +3123,12 @@ | |
| 2839 | z = db_text(0, "SELECT strftime(%Q,%Q,'unixepoch');", zFormat, z); |
| 2840 | } |
| 2841 | return z; |
| 2842 | } |
| 2843 | void db_set(const char *zName, const char *zValue, int globalFlag){ |
| 2844 | db_begin_transaction(); |
| 2845 | if( globalFlag ){ |
| 2846 | db_swap_connections(); |
| 2847 | db_multi_exec("REPLACE INTO global_config(name,value) VALUES(%Q,%Q)", |
| 2848 | zName, zValue); |
| @@ -2853,13 +3139,15 @@ | |
| 2853 | } |
| 2854 | if( globalFlag && g.repositoryOpen ){ |
| 2855 | db_multi_exec("DELETE FROM config WHERE name=%Q", zName); |
| 2856 | } |
| 2857 | db_end_transaction(0); |
| 2858 | } |
| 2859 | void db_unset(const char *zName, int globalFlag){ |
| 2860 | db_begin_transaction(); |
| 2861 | if( globalFlag ){ |
| 2862 | db_swap_connections(); |
| 2863 | db_multi_exec("DELETE FROM global_config WHERE name=%Q", zName); |
| 2864 | db_swap_connections(); |
| 2865 | }else{ |
| @@ -2866,10 +3154,11 @@ | |
| 2866 | db_multi_exec("DELETE FROM config WHERE name=%Q", zName); |
| 2867 | } |
| 2868 | if( globalFlag && g.repositoryOpen ){ |
| 2869 | db_multi_exec("DELETE FROM config WHERE name=%Q", zName); |
| 2870 | } |
| 2871 | db_end_transaction(0); |
| 2872 | } |
| 2873 | int db_is_global(const char *zName){ |
| 2874 | int rc = 0; |
| 2875 | if( g.zConfigDbName ){ |
| @@ -2899,10 +3188,12 @@ | |
| 2899 | db_swap_connections(); |
| 2900 | } |
| 2901 | return v; |
| 2902 | } |
| 2903 | void db_set_int(const char *zName, int value, int globalFlag){ |
| 2904 | if( globalFlag ){ |
| 2905 | db_swap_connections(); |
| 2906 | db_multi_exec("REPLACE INTO global_config(name,value) VALUES(%Q,%d)", |
| 2907 | zName, value); |
| 2908 | db_swap_connections(); |
| @@ -2911,10 +3202,11 @@ | |
| 2911 | zName, value); |
| 2912 | } |
| 2913 | if( globalFlag && g.repositoryOpen ){ |
| 2914 | db_multi_exec("DELETE FROM config WHERE name=%Q", zName); |
| 2915 | } |
| 2916 | } |
| 2917 | int db_get_boolean(const char *zName, int dflt){ |
| 2918 | char *zVal = db_get(zName, dflt ? "on" : "off"); |
| 2919 | if( is_truth(zVal) ){ |
| 2920 | dflt = 1; |
| @@ -3040,24 +3332,28 @@ | |
| 3040 | } |
| 3041 | file_canonical_name(zName, &full, 0); |
| 3042 | (void)filename_collation(); /* Initialize before connection swap */ |
| 3043 | db_swap_connections(); |
| 3044 | zRepoSetting = mprintf("repo:%q", blob_str(&full)); |
| 3045 | db_multi_exec( |
| 3046 | "DELETE FROM global_config WHERE name %s = %Q;", |
| 3047 | filename_collation(), zRepoSetting |
| 3048 | ); |
| 3049 | db_multi_exec( |
| 3050 | "INSERT OR IGNORE INTO global_config(name,value)" |
| 3051 | "VALUES(%Q,1);", |
| 3052 | zRepoSetting |
| 3053 | ); |
| 3054 | fossil_free(zRepoSetting); |
| 3055 | if( g.localOpen && g.zLocalRoot && g.zLocalRoot[0] ){ |
| 3056 | Blob localRoot; |
| 3057 | file_canonical_name(g.zLocalRoot, &localRoot, 1); |
| 3058 | zCkoutSetting = mprintf("ckout:%q", blob_str(&localRoot)); |
| 3059 | db_multi_exec( |
| 3060 | "DELETE FROM global_config WHERE name %s = %Q;", |
| 3061 | filename_collation(), zCkoutSetting |
| 3062 | ); |
| 3063 | db_multi_exec( |
| @@ -3073,10 +3369,11 @@ | |
| 3073 | db_optional_sql("repository", |
| 3074 | "REPLACE INTO config(name,value,mtime)" |
| 3075 | "VALUES(%Q,1,now());", |
| 3076 | zCkoutSetting |
| 3077 | ); |
| 3078 | fossil_free(zCkoutSetting); |
| 3079 | blob_reset(&localRoot); |
| 3080 | }else{ |
| 3081 | db_swap_connections(); |
| 3082 | } |
| @@ -3131,11 +3428,10 @@ | |
| 3131 | void cmd_open(void){ |
| 3132 | int emptyFlag; |
| 3133 | int keepFlag; |
| 3134 | int forceMissingFlag; |
| 3135 | int allowNested; |
| 3136 | int allowSymlinks; |
| 3137 | int setmtimeFlag; /* --setmtime. Set mtimes on files */ |
| 3138 | int bForce = 0; /* --force. Open even if non-empty dir */ |
| 3139 | static char *azNewArgv[] = { 0, "checkout", "--prompt", 0, 0, 0, 0 }; |
| 3140 | const char *zWorkDir; /* --workdir value */ |
| 3141 | const char *zRepo = 0; /* Name of the repository file */ |
| @@ -3242,23 +3538,10 @@ | |
| 3242 | }else if( db_exists("SELECT 1 FROM event WHERE type='ci'") ){ |
| 3243 | g.zOpenRevision = db_get("main-branch", 0); |
| 3244 | } |
| 3245 | } |
| 3246 | |
| 3247 | if( g.zOpenRevision ){ |
| 3248 | /* Since the repository is open and we know the revision now, |
| 3249 | ** refresh the allow-symlinks flag. Since neither the local |
| 3250 | ** checkout nor the configuration database are open at this |
| 3251 | ** point, this should always return the versioned setting, |
| 3252 | ** if any, or the default value, which is negative one. The |
| 3253 | ** value negative one, in this context, means that the code |
| 3254 | ** below should fallback to using the setting value from the |
| 3255 | ** repository or global configuration databases only. */ |
| 3256 | allowSymlinks = db_get_versioned_boolean("allow-symlinks", -1); |
| 3257 | }else{ |
| 3258 | allowSymlinks = -1; /* Use non-versioned settings only. */ |
| 3259 | } |
| 3260 | |
| 3261 | #if defined(_WIN32) || defined(__CYGWIN__) |
| 3262 | # define LOCALDB_NAME "./_FOSSIL_" |
| 3263 | #else |
| 3264 | # define LOCALDB_NAME "./.fslckout" |
| @@ -3268,26 +3551,10 @@ | |
| 3268 | "COMMIT; PRAGMA journal_mode=WAL; BEGIN;", |
| 3269 | #endif |
| 3270 | (char*)0); |
| 3271 | db_delete_on_failure(LOCALDB_NAME); |
| 3272 | db_open_local(0); |
| 3273 | if( allowSymlinks>=0 ){ |
| 3274 | /* Use the value from the versioned setting, which was read |
| 3275 | ** prior to opening the local checkout (i.e. which is most |
| 3276 | ** likely empty and does not actually contain any versioned |
| 3277 | ** setting files yet). Normally, this value would be given |
| 3278 | ** first priority within db_get_boolean(); however, this is |
| 3279 | ** a special case because we know the on-disk files may not |
| 3280 | ** exist yet. */ |
| 3281 | g.allowSymlinks = allowSymlinks; |
| 3282 | }else{ |
| 3283 | /* Since the local checkout may not have any files at this |
| 3284 | ** point, this will probably be the setting value from the |
| 3285 | ** repository or global configuration databases. */ |
| 3286 | g.allowSymlinks = db_get_boolean("allow-symlinks", |
| 3287 | db_allow_symlinks_by_default()); |
| 3288 | } |
| 3289 | db_lset("repository", zRepo); |
| 3290 | db_record_repository_filename(zRepo); |
| 3291 | db_set_checkout(0); |
| 3292 | azNewArgv[0] = g.argv[0]; |
| 3293 | g.argv = azNewArgv; |
| @@ -3376,12 +3643,13 @@ | |
| 3376 | const char *name; /* Name of the setting */ |
| 3377 | const char *var; /* Internal variable name used by db_set() */ |
| 3378 | int width; /* Width of display. 0 for boolean values and |
| 3379 | ** negative for values which should not appear |
| 3380 | ** on the /setup_settings page. */ |
| 3381 | int versionable; /* Is this setting versionable? */ |
| 3382 | int forceTextArea; /* Force using a text area for display? */ |
| 3383 | const char *def; /* Default value */ |
| 3384 | }; |
| 3385 | #endif /* INTERFACE */ |
| 3386 | |
| 3387 | /* |
| @@ -3395,32 +3663,29 @@ | |
| 3395 | ** SETTING: admin-log boolean default=off |
| 3396 | ** |
| 3397 | ** When the admin-log setting is enabled, configuration changes are recorded |
| 3398 | ** in the "admin_log" table of the repository. |
| 3399 | */ |
| 3400 | #if defined(_WIN32) |
| 3401 | /* |
| 3402 | ** SETTING: allow-symlinks boolean default=off versionable |
| 3403 | ** |
| 3404 | ** When allow-symlinks is OFF, symbolic links in the repository are followed |
| 3405 | ** and treated no differently from real files. When allow-symlinks is ON, |
| 3406 | ** the object to which the symbolic link points is ignored, and the content |
| 3407 | ** of the symbolic link that is stored in the repository is the name of the |
| 3408 | ** object to which the symbolic link points. |
| 3409 | */ |
| 3410 | #endif |
| 3411 | #if !defined(_WIN32) |
| 3412 | /* |
| 3413 | ** SETTING: allow-symlinks boolean default=on versionable |
| 3414 | ** |
| 3415 | ** When allow-symlinks is OFF, symbolic links in the repository are followed |
| 3416 | ** and treated no differently from real files. When allow-symlinks is ON, |
| 3417 | ** the object to which the symbolic link points is ignored, and the content |
| 3418 | ** of the symbolic link that is stored in the repository is the name of the |
| 3419 | ** object to which the symbolic link points. |
| 3420 | */ |
| 3421 | #endif |
| 3422 | /* |
| 3423 | ** SETTING: auto-captcha boolean default=on variable=autocaptcha |
| 3424 | ** If enabled, the /login page provides a button that will automatically |
| 3425 | ** fill in the captcha password. This makes things easier for human users, |
| 3426 | ** at the expense of also making logins easier for malicious robots. |
| @@ -3470,11 +3735,11 @@ | |
| 3470 | ** there is no cron job periodically running "fossil backoffice", |
| 3471 | ** email notifications and other work normally done by the |
| 3472 | ** backoffice will not occur. |
| 3473 | */ |
| 3474 | /* |
| 3475 | ** SETTING: backoffice-logfile width=40 |
| 3476 | ** If backoffice-logfile is not an empty string and is a valid |
| 3477 | ** filename, then a one-line message is appended to that file |
| 3478 | ** every time the backoffice runs. This can be used for debugging, |
| 3479 | ** to ensure that backoffice is running appropriately. |
| 3480 | */ |
| @@ -3547,11 +3812,11 @@ | |
| 3547 | /* |
| 3548 | ** SETTING: crnl-glob width=40 versionable block-text |
| 3549 | ** This is an alias for the crlf-glob setting. |
| 3550 | */ |
| 3551 | /* |
| 3552 | ** SETTING: default-perms width=16 default=u |
| 3553 | ** Permissions given automatically to new users. For more |
| 3554 | ** information on permissions see the Users page in Server |
| 3555 | ** Administration of the HTTP UI. |
| 3556 | */ |
| 3557 | /* |
| @@ -3559,11 +3824,11 @@ | |
| 3559 | ** If enabled, permit files that may be binary |
| 3560 | ** or that match the "binary-glob" setting to be used with |
| 3561 | ** external diff programs. If disabled, skip these files. |
| 3562 | */ |
| 3563 | /* |
| 3564 | ** SETTING: diff-command width=40 |
| 3565 | ** The value is an external command to run when performing a diff. |
| 3566 | ** If undefined, the internal text diff will be used. |
| 3567 | */ |
| 3568 | /* |
| 3569 | ** SETTING: dont-push boolean default=off |
| @@ -3574,11 +3839,11 @@ | |
| 3574 | /* |
| 3575 | ** SETTING: dotfiles boolean versionable default=off |
| 3576 | ** If enabled, include --dotfiles option for all compatible commands. |
| 3577 | */ |
| 3578 | /* |
| 3579 | ** SETTING: editor width=32 |
| 3580 | ** The value is an external command that will launch the |
| 3581 | ** text editor command used for check-in comments. |
| 3582 | */ |
| 3583 | /* |
| 3584 | ** SETTING: empty-dirs width=40 versionable block-text |
| @@ -3617,16 +3882,16 @@ | |
| 3617 | ** An empty list prohibits editing via that page. Note that |
| 3618 | ** it cannot edit binary files, so the list should not |
| 3619 | ** contain any globs for, e.g., images or PDFs. |
| 3620 | */ |
| 3621 | /* |
| 3622 | ** SETTING: gdiff-command width=40 default=gdiff |
| 3623 | ** The value is an external command to run when performing a graphical |
| 3624 | ** diff. If undefined, text diff will be used. |
| 3625 | */ |
| 3626 | /* |
| 3627 | ** SETTING: gmerge-command width=40 |
| 3628 | ** The value is a graphical merge conflict resolver command operating |
| 3629 | ** on four files. Examples: |
| 3630 | ** |
| 3631 | ** kdiff3 "%baseline" "%original" "%merge" -o "%output" |
| 3632 | ** xxdiff "%original" "%baseline" "%merge" -M "%output" |
| @@ -3757,11 +4022,11 @@ | |
| 3757 | ** the associated files within the checkout -AND- the "rm" |
| 3758 | ** and "delete" commands will also remove the associated |
| 3759 | ** files from within the checkout. |
| 3760 | */ |
| 3761 | /* |
| 3762 | ** SETTING: pgp-command width=40 |
| 3763 | ** Command used to clear-sign manifests at check-in. |
| 3764 | ** Default value is "gpg --clearsign -o" |
| 3765 | */ |
| 3766 | /* |
| 3767 | ** SETTING: forbid-delta-manifests boolean default=off |
| @@ -3817,22 +4082,22 @@ | |
| 3817 | ** |
| 3818 | ** If repolist-skin has a value of 2, then the repository is omitted from |
| 3819 | ** the list in use cases 1 through 4, but not for 5 and 6. |
| 3820 | */ |
| 3821 | /* |
| 3822 | ** SETTING: self-register boolean default=off |
| 3823 | ** Allow users to register themselves through the HTTP UI. |
| 3824 | ** This is useful if you want to see other names than |
| 3825 | ** "Anonymous" in e.g. ticketing system. On the other hand |
| 3826 | ** users can not be deleted. |
| 3827 | */ |
| 3828 | /* |
| 3829 | ** SETTING: ssh-command width=40 |
| 3830 | ** The command used to talk to a remote machine with the "ssh://" protocol. |
| 3831 | */ |
| 3832 | /* |
| 3833 | ** SETTING: ssl-ca-location width=40 |
| 3834 | ** The full pathname to a file containing PEM encoded |
| 3835 | ** CA root certificates, or a directory of certificates |
| 3836 | ** with filenames formed from the certificate hashes as |
| 3837 | ** required by OpenSSL. |
| 3838 | ** |
| @@ -3842,11 +4107,11 @@ | |
| 3842 | ** Checking your platform behaviour is required if the |
| 3843 | ** exact contents of the CA root is critical for your |
| 3844 | ** application. |
| 3845 | */ |
| 3846 | /* |
| 3847 | ** SETTING: ssl-identity width=40 |
| 3848 | ** The full pathname to a file containing a certificate |
| 3849 | ** and private key in PEM format. Create by concatenating |
| 3850 | ** the certificate and private key files. |
| 3851 | ** |
| 3852 | ** This identity will be presented to SSL servers to |
| @@ -3853,33 +4118,33 @@ | |
| 3853 | ** authenticate this client, in addition to the normal |
| 3854 | ** password authentication. |
| 3855 | */ |
| 3856 | #ifdef FOSSIL_ENABLE_TCL |
| 3857 | /* |
| 3858 | ** SETTING: tcl boolean default=off |
| 3859 | ** If enabled Tcl integration commands will be added to the TH1 |
| 3860 | ** interpreter, allowing arbitrary Tcl expressions and |
| 3861 | ** scripts to be evaluated from TH1. Additionally, the Tcl |
| 3862 | ** interpreter will be able to evaluate arbitrary TH1 |
| 3863 | ** expressions and scripts. |
| 3864 | */ |
| 3865 | /* |
| 3866 | ** SETTING: tcl-setup width=40 block-text |
| 3867 | ** This is the setup script to be evaluated after creating |
| 3868 | ** and initializing the Tcl interpreter. By default, this |
| 3869 | ** is empty and no extra setup is performed. |
| 3870 | */ |
| 3871 | #endif /* FOSSIL_ENABLE_TCL */ |
| 3872 | /* |
| 3873 | ** SETTING: tclsh width=80 default=tclsh |
| 3874 | ** Name of the external TCL interpreter used for such things |
| 3875 | ** as running the GUI diff viewer launched by the --tk option |
| 3876 | ** of the various "diff" commands. |
| 3877 | */ |
| 3878 | #ifdef FOSSIL_ENABLE_TH1_DOCS |
| 3879 | /* |
| 3880 | ** SETTING: th1-docs boolean default=off |
| 3881 | ** If enabled, this allows embedded documentation files to contain |
| 3882 | ** arbitrary TH1 scripts that are evaluated on the server. If native |
| 3883 | ** Tcl integration is also enabled, this setting has the |
| 3884 | ** potential to allow anybody with check-in privileges to |
| 3885 | ** do almost anything that the associated operating system |
| @@ -3932,11 +4197,11 @@ | |
| 3932 | ** of a "fossil clone" or "fossil sync" command. The |
| 3933 | ** default is false, in which case the -u option is |
| 3934 | ** needed to clone or sync unversioned files. |
| 3935 | */ |
| 3936 | /* |
| 3937 | ** SETTING: web-browser width=30 |
| 3938 | ** A shell command used to launch your preferred |
| 3939 | ** web browser when given a URL as an argument. |
| 3940 | ** Defaults to "start" on windows, "open" on Mac, |
| 3941 | ** and "firefox" on Unix. |
| 3942 | */ |
| @@ -4058,11 +4323,13 @@ | |
| 4058 | fossil_fatal("cannot set 'manifest' globally"); |
| 4059 | } |
| 4060 | if( unsetFlag ){ |
| 4061 | db_unset(pSetting->name, globalFlag); |
| 4062 | }else{ |
| 4063 | db_set(pSetting->name, g.argv[3], globalFlag); |
| 4064 | } |
| 4065 | if( isManifest && g.localOpen ){ |
| 4066 | manifest_to_disk(db_lget_int("checkout", 0)); |
| 4067 | } |
| 4068 | }else{ |
| 4069 |
| --- src/db.c | |
| +++ src/db.c | |
| @@ -69,10 +69,11 @@ | |
| 69 | #endif /* INTERFACE */ |
| 70 | const struct Stmt empty_Stmt = empty_Stmt_m; |
| 71 | |
| 72 | /* |
| 73 | ** Call this routine when a database error occurs. |
| 74 | ** This routine throws a fatal error. It does not return. |
| 75 | */ |
| 76 | static void db_err(const char *zFormat, ...){ |
| 77 | va_list ap; |
| 78 | char *z; |
| 79 | va_start(ap, zFormat); |
| @@ -113,10 +114,11 @@ | |
| 114 | /* |
| 115 | ** All static variable that a used by only this file are gathered into |
| 116 | ** the following structure. |
| 117 | */ |
| 118 | static struct DbLocalData { |
| 119 | unsigned protectMask; /* Prevent changes to database */ |
| 120 | int nBegin; /* Nesting depth of BEGIN */ |
| 121 | int doRollback; /* True to force a rollback */ |
| 122 | int nCommitHook; /* Number of commit hooks */ |
| 123 | int wrTxn; /* Outer-most TNX is a write */ |
| 124 | Stmt *pAllStmt; /* List of all unfinalized statements */ |
| @@ -130,11 +132,19 @@ | |
| 132 | char *azBeforeCommit[5]; /* Commands to run prior to COMMIT */ |
| 133 | int nBeforeCommit; /* Number of entries in azBeforeCommit */ |
| 134 | int nPriorChanges; /* sqlite3_total_changes() at transaction start */ |
| 135 | const char *zStartFile; /* File in which transaction was started */ |
| 136 | int iStartLine; /* Line of zStartFile where transaction started */ |
| 137 | int (*xAuth)(void*,int,const char*,const char*,const char*,const char*); |
| 138 | void *pAuthArg; /* Argument to the authorizer */ |
| 139 | const char *zAuthName; /* Name of the authorizer */ |
| 140 | int bProtectTriggers; /* True if protection triggers already exist */ |
| 141 | int nProtect; /* Slots of aProtect used */ |
| 142 | unsigned aProtect[10]; /* Saved values of protectMask */ |
| 143 | } db = { |
| 144 | PROTECT_USER|PROTECT_CONFIG|PROTECT_BASELINE, /* protectMask */ |
| 145 | 0, 0, 0, 0, 0, 0, }; |
| 146 | |
| 147 | /* |
| 148 | ** Arrange for the given file to be deleted on a failure. |
| 149 | */ |
| 150 | void db_delete_on_failure(const char *zFilename){ |
| @@ -238,17 +248,19 @@ | |
| 248 | db.nBegin--; |
| 249 | if( db.nBegin==0 ){ |
| 250 | int i; |
| 251 | if( db.doRollback==0 && db.nPriorChanges<sqlite3_total_changes(g.db) ){ |
| 252 | i = 0; |
| 253 | db_protect_only(PROTECT_SENSITIVE); |
| 254 | while( db.nBeforeCommit ){ |
| 255 | db.nBeforeCommit--; |
| 256 | sqlite3_exec(g.db, db.azBeforeCommit[i], 0, 0, 0); |
| 257 | sqlite3_free(db.azBeforeCommit[i]); |
| 258 | i++; |
| 259 | } |
| 260 | leaf_do_pending_checks(); |
| 261 | db_protect_pop(); |
| 262 | } |
| 263 | for(i=0; db.doRollback==0 && i<db.nCommitHook; i++){ |
| 264 | int rc = db.aHook[i].xHook(); |
| 265 | if( rc ){ |
| 266 | db.doRollback = 1; |
| @@ -316,10 +328,230 @@ | |
| 328 | } |
| 329 | db.aHook[db.nCommitHook].sequence = sequence; |
| 330 | db.aHook[db.nCommitHook].xHook = x; |
| 331 | db.nCommitHook++; |
| 332 | } |
| 333 | |
| 334 | #if INTERFACE |
| 335 | /* |
| 336 | ** Flag bits for db_protect() and db_unprotect() indicating which parts |
| 337 | ** of the databases should be write protected or write enabled, respectively. |
| 338 | */ |
| 339 | #define PROTECT_USER 0x01 /* USER table */ |
| 340 | #define PROTECT_CONFIG 0x02 /* CONFIG and GLOBAL_CONFIG tables */ |
| 341 | #define PROTECT_SENSITIVE 0x04 /* Sensitive and/or global settings */ |
| 342 | #define PROTECT_READONLY 0x08 /* everything except TEMP tables */ |
| 343 | #define PROTECT_BASELINE 0x10 /* protection system is working */ |
| 344 | #define PROTECT_ALL 0x1f /* All of the above */ |
| 345 | #define PROTECT_NONE 0x00 /* Nothing. Everything is open */ |
| 346 | #endif /* INTERFACE */ |
| 347 | |
| 348 | /* |
| 349 | ** Enable or disable database write protections. |
| 350 | ** |
| 351 | ** db_protext(X) Add protects on X |
| 352 | ** db_unprotect(X) Remove protections on X |
| 353 | ** db_protect_only(X) Remove all prior protections then set |
| 354 | ** protections to only X. |
| 355 | ** |
| 356 | ** Each of these routines pushes the previous protection mask onto |
| 357 | ** a finite-size stack. Each should be followed by a call to |
| 358 | ** db_protect_pop() to pop the stack and restore the protections that |
| 359 | ** existed prior to the call. The protection mask stack has a limited |
| 360 | ** depth, so take care not to next calls too deeply. |
| 361 | ** |
| 362 | ** About Database Write Protection |
| 363 | ** ------------------------------- |
| 364 | ** |
| 365 | ** This is *not* a primary means of defending the application from |
| 366 | ** attack. Fossil should be secure even if this mechanism is disabled. |
| 367 | ** The purpose of database write protection is to provide an additional |
| 368 | ** layer of defense in case SQL injection bugs somehow slip into other |
| 369 | ** parts of the system. In other words, database write protection is |
| 370 | ** not primary defense but rather defense in depth. |
| 371 | ** |
| 372 | ** This mechanism mostly focuses on the USER table, to prevent an |
| 373 | ** attacker from giving themselves Admin privilegs, and on the |
| 374 | ** CONFIG table and specially "sensitive" settings such as |
| 375 | ** "diff-command" or "editor" that if compromised by an attacker |
| 376 | ** could lead to an RCE. |
| 377 | ** |
| 378 | ** By default, the USER and CONFIG tables are read-only. Various |
| 379 | ** subsystems that legitimately need to change those tables can |
| 380 | ** temporarily do so using: |
| 381 | ** |
| 382 | ** db_unprotect(PROTECT_xxx); |
| 383 | ** // make the legitmate changes here |
| 384 | ** db_protect_pop(); |
| 385 | ** |
| 386 | ** Code that runs inside of reduced protections should be carefully |
| 387 | ** reviewed to ensure that it is harmless and not subject to SQL |
| 388 | ** injection. |
| 389 | ** |
| 390 | ** Read-only operations (such as many web pages like /timeline) |
| 391 | ** can invoke db_protect(PROTECT_ALL) to effectively make the database |
| 392 | ** read-only. TEMP tables (which are often used for these kinds of |
| 393 | ** pages) are still writable, however. |
| 394 | ** |
| 395 | ** The PROTECT_SENSITIVE protection is a subset of PROTECT_CONFIG |
| 396 | ** that blocks changes to all of the global_config table, but only |
| 397 | ** "sensitive" settings in the config table. PROTECT_SENSITIVE |
| 398 | ** relies on triggers and the protected_setting() SQL function to |
| 399 | ** prevent changes to sensitive settings. |
| 400 | ** |
| 401 | ** Additional Notes |
| 402 | ** ---------------- |
| 403 | ** |
| 404 | ** Calls to routines like db_set() and db_unset() temporarily disable |
| 405 | ** the PROTECT_CONFIG protection. The assumption is that these calls |
| 406 | ** cannot be invoked by an SQL injection and are thus safe. Make sure |
| 407 | ** this is the case by always using a string literal as the name argument |
| 408 | ** to db_set() and db_unset() and friend, not a variable that might |
| 409 | ** be compromised by an attack. |
| 410 | */ |
| 411 | void db_protect_only(unsigned flags){ |
| 412 | if( db.nProtect>=count(db.aProtect)-2 ){ |
| 413 | fossil_panic("too many db_protect() calls"); |
| 414 | } |
| 415 | db.aProtect[db.nProtect++] = db.protectMask; |
| 416 | if( (flags & PROTECT_SENSITIVE)!=0 |
| 417 | && db.bProtectTriggers==0 |
| 418 | && g.repositoryOpen |
| 419 | ){ |
| 420 | /* Create the triggers needed to protect sensitive settings from |
| 421 | ** being created or modified the first time that PROTECT_SENSITIVE |
| 422 | ** is enabled. Deleting a sensitive setting is harmless, so there |
| 423 | ** is not trigger to block deletes. After being created once, the |
| 424 | ** triggers persist for the life of the database connection. */ |
| 425 | db_multi_exec( |
| 426 | "CREATE TEMP TRIGGER protect_1 BEFORE INSERT ON config" |
| 427 | " WHEN protected_setting(new.name) BEGIN" |
| 428 | " SELECT raise(abort,'not authorized');" |
| 429 | "END;\n" |
| 430 | "CREATE TEMP TRIGGER protect_2 BEFORE UPDATE ON config" |
| 431 | " WHEN protected_setting(new.name) BEGIN" |
| 432 | " SELECT raise(abort,'not authorized');" |
| 433 | "END;\n" |
| 434 | ); |
| 435 | db.bProtectTriggers = 1; |
| 436 | } |
| 437 | db.protectMask = flags; |
| 438 | } |
| 439 | void db_protect(unsigned flags){ |
| 440 | db_protect_only(db.protectMask | flags); |
| 441 | } |
| 442 | void db_unprotect(unsigned flags){ |
| 443 | if( db.nProtect>=count(db.aProtect)-2 ){ |
| 444 | fossil_panic("too many db_unprotect() calls"); |
| 445 | } |
| 446 | db.aProtect[db.nProtect++] = db.protectMask; |
| 447 | db.protectMask &= ~flags; |
| 448 | } |
| 449 | void db_protect_pop(void){ |
| 450 | if( db.nProtect<1 ){ |
| 451 | fossil_panic("too many db_protect_pop() calls"); |
| 452 | } |
| 453 | db.protectMask = db.aProtect[--db.nProtect]; |
| 454 | } |
| 455 | |
| 456 | /* |
| 457 | ** Verify that the desired database write pertections are in place. |
| 458 | ** Throw a fatal error if not. |
| 459 | */ |
| 460 | void db_assert_protected(unsigned flags){ |
| 461 | if( (flags & db.protectMask)!=flags ){ |
| 462 | fossil_panic("missing database write protection bits: %02x", |
| 463 | flags & ~db.protectMask); |
| 464 | } |
| 465 | } |
| 466 | |
| 467 | /* |
| 468 | ** Assert that either all protections are off (including PROTECT_BASELINE |
| 469 | ** which is usually always enabled), or the setting named in the argument |
| 470 | ** is no a sensitive setting. |
| 471 | ** |
| 472 | ** This assert() is used to verify that the db_set() and db_set_int() |
| 473 | ** interfaces do not modify a sensitive setting. |
| 474 | */ |
| 475 | void db_assert_protection_off_or_not_sensitive(const char *zName){ |
| 476 | if( db.protectMask!=0 && db_setting_is_protected(zName) ){ |
| 477 | fossil_panic("unauthorized change to protected setting \"%s\"", zName); |
| 478 | } |
| 479 | } |
| 480 | |
| 481 | /* |
| 482 | ** Every Fossil database connection automatically registers the following |
| 483 | ** overarching authenticator callback, and leaves it registered for the |
| 484 | ** duration of the connection. This authenticator will call any |
| 485 | ** sub-authenticators that are registered using db_set_authorizer(). |
| 486 | */ |
| 487 | int db_top_authorizer( |
| 488 | void *pNotUsed, |
| 489 | int eCode, |
| 490 | const char *z0, |
| 491 | const char *z1, |
| 492 | const char *z2, |
| 493 | const char *z3 |
| 494 | ){ |
| 495 | int rc = SQLITE_OK; |
| 496 | switch( eCode ){ |
| 497 | case SQLITE_INSERT: |
| 498 | case SQLITE_UPDATE: |
| 499 | case SQLITE_DELETE: { |
| 500 | if( (db.protectMask & PROTECT_USER)!=0 |
| 501 | && sqlite3_stricmp(z0,"user")==0 ){ |
| 502 | rc = SQLITE_DENY; |
| 503 | }else if( (db.protectMask & PROTECT_CONFIG)!=0 && |
| 504 | (sqlite3_stricmp(z0,"config")==0 || |
| 505 | sqlite3_stricmp(z0,"global_config")==0) ){ |
| 506 | rc = SQLITE_DENY; |
| 507 | }else if( (db.protectMask & PROTECT_SENSITIVE)!=0 && |
| 508 | sqlite3_stricmp(z0,"global_config")==0 ){ |
| 509 | rc = SQLITE_DENY; |
| 510 | }else if( (db.protectMask & PROTECT_READONLY)!=0 |
| 511 | && sqlite3_stricmp(z2,"temp")!=0 ){ |
| 512 | rc = SQLITE_DENY; |
| 513 | } |
| 514 | break; |
| 515 | } |
| 516 | case SQLITE_DROP_TEMP_TRIGGER: { |
| 517 | /* Do not allow the triggers that enforce PROTECT_SENSITIVE |
| 518 | ** to be dropped */ |
| 519 | rc = SQLITE_DENY; |
| 520 | break; |
| 521 | } |
| 522 | } |
| 523 | if( db.xAuth && rc==SQLITE_OK ){ |
| 524 | rc = db.xAuth(db.pAuthArg, eCode, z0, z1, z2, z3); |
| 525 | } |
| 526 | return rc; |
| 527 | } |
| 528 | |
| 529 | /* |
| 530 | ** Set or unset the query authorizer callback function |
| 531 | */ |
| 532 | void db_set_authorizer( |
| 533 | int(*xAuth)(void*,int,const char*,const char*,const char*,const char*), |
| 534 | void *pArg, |
| 535 | const char *zName /* for tracing */ |
| 536 | ){ |
| 537 | if( db.xAuth ){ |
| 538 | fossil_panic("multiple active db_set_authorizer() calls"); |
| 539 | } |
| 540 | db.xAuth = xAuth; |
| 541 | db.pAuthArg = pArg; |
| 542 | db.zAuthName = zName; |
| 543 | if( g.fSqlTrace ) fossil_trace("-- set authorizer %s\n", zName); |
| 544 | } |
| 545 | void db_clear_authorizer(void){ |
| 546 | if( db.zAuthName && g.fSqlTrace ){ |
| 547 | fossil_trace("-- discontinue authorizer %s\n", db.zAuthName); |
| 548 | } |
| 549 | db.xAuth = 0; |
| 550 | db.pAuthArg = 0; |
| 551 | db.zAuthName = 0; |
| 552 | } |
| 553 | |
| 554 | #if INTERFACE |
| 555 | /* |
| 556 | ** Possible flags to db_vprepare |
| 557 | */ |
| @@ -334,21 +566,24 @@ | |
| 566 | */ |
| 567 | int db_vprepare(Stmt *pStmt, int flags, const char *zFormat, va_list ap){ |
| 568 | int rc; |
| 569 | int prepFlags = 0; |
| 570 | char *zSql; |
| 571 | const char *zExtra = 0; |
| 572 | blob_zero(&pStmt->sql); |
| 573 | blob_vappendf(&pStmt->sql, zFormat, ap); |
| 574 | va_end(ap); |
| 575 | zSql = blob_str(&pStmt->sql); |
| 576 | db.nPrepare++; |
| 577 | if( flags & DB_PREPARE_PERSISTENT ){ |
| 578 | prepFlags = SQLITE_PREPARE_PERSISTENT; |
| 579 | } |
| 580 | rc = sqlite3_prepare_v3(g.db, zSql, -1, prepFlags, &pStmt->pStmt, &zExtra); |
| 581 | if( rc!=0 && (flags & DB_PREPARE_IGNORE_ERROR)==0 ){ |
| 582 | db_err("%s\n%s", sqlite3_errmsg(g.db), zSql); |
| 583 | }else if( zExtra && !fossil_all_whitespace(zExtra) ){ |
| 584 | db_err("surplus text follows SQL: \"%s\"", zExtra); |
| 585 | } |
| 586 | pStmt->pNext = db.pAllStmt; |
| 587 | pStmt->pPrev = 0; |
| 588 | if( db.pAllStmt ) db.pAllStmt->pPrev = pStmt; |
| 589 | db.pAllStmt = pStmt; |
| @@ -611,10 +846,11 @@ | |
| 846 | return rc; |
| 847 | } |
| 848 | |
| 849 | /* |
| 850 | ** COMMAND: test-db-exec-error |
| 851 | ** Usage: %fossil test-db-exec-error |
| 852 | ** |
| 853 | ** Invoke the db_exec() interface with an erroneous SQL statement |
| 854 | ** in order to verify the error handling logic. |
| 855 | */ |
| 856 | void db_test_db_exec_cmd(void){ |
| @@ -621,10 +857,27 @@ | |
| 857 | Stmt err; |
| 858 | db_find_and_open_repository(0,0); |
| 859 | db_prepare(&err, "INSERT INTO repository.config(name) VALUES(NULL);"); |
| 860 | db_exec(&err); |
| 861 | } |
| 862 | |
| 863 | /* |
| 864 | ** COMMAND: test-db-prepare |
| 865 | ** Usage: %fossil test-db-prepare ?OPTIONS? SQL |
| 866 | ** |
| 867 | ** Invoke db_prepare() on the SQL input. Report any errors encountered. |
| 868 | ** This command is used to verify error detection logic in the db_prepare() |
| 869 | ** utility routine. |
| 870 | */ |
| 871 | void db_test_db_prepare(void){ |
| 872 | Stmt err; |
| 873 | db_find_and_open_repository(0,0); |
| 874 | verify_all_options(); |
| 875 | if( g.argc!=3 ) usage("?OPTIONS? SQL"); |
| 876 | db_prepare(&err, "%s", g.argv[2]/*safe-for-%s*/); |
| 877 | db_finalize(&err); |
| 878 | } |
| 879 | |
| 880 | /* |
| 881 | ** Print the output of one or more SQL queries on standard output. |
| 882 | ** This routine is used for debugging purposes only. |
| 883 | */ |
| @@ -844,34 +1097,34 @@ | |
| 1097 | void db_init_database( |
| 1098 | const char *zFileName, /* Name of database file to create */ |
| 1099 | const char *zSchema, /* First part of schema */ |
| 1100 | ... /* Additional SQL to run. Terminate with NULL. */ |
| 1101 | ){ |
| 1102 | sqlite3 *xdb; |
| 1103 | int rc; |
| 1104 | const char *zSql; |
| 1105 | va_list ap; |
| 1106 | |
| 1107 | xdb = db_open(zFileName ? zFileName : ":memory:"); |
| 1108 | sqlite3_exec(xdb, "BEGIN EXCLUSIVE", 0, 0, 0); |
| 1109 | rc = sqlite3_exec(xdb, zSchema, 0, 0, 0); |
| 1110 | if( rc!=SQLITE_OK ){ |
| 1111 | db_err("%s", sqlite3_errmsg(xdb)); |
| 1112 | } |
| 1113 | va_start(ap, zSchema); |
| 1114 | while( (zSql = va_arg(ap, const char*))!=0 ){ |
| 1115 | rc = sqlite3_exec(xdb, zSql, 0, 0, 0); |
| 1116 | if( rc!=SQLITE_OK ){ |
| 1117 | db_err("%s", sqlite3_errmsg(xdb)); |
| 1118 | } |
| 1119 | } |
| 1120 | va_end(ap); |
| 1121 | sqlite3_exec(xdb, "COMMIT", 0, 0, 0); |
| 1122 | if( zFileName || g.db!=0 ){ |
| 1123 | sqlite3_close(xdb); |
| 1124 | }else{ |
| 1125 | g.db = xdb; |
| 1126 | } |
| 1127 | } |
| 1128 | |
| 1129 | /* |
| 1130 | ** Function to return the number of seconds since 1970. This is |
| @@ -1060,10 +1313,37 @@ | |
| 1313 | } |
| 1314 | strcpy(zOut, zTemp = obscure((char*)zIn)); |
| 1315 | fossil_free(zTemp); |
| 1316 | sqlite3_result_text(context, zOut, strlen(zOut), sqlite3_free); |
| 1317 | } |
| 1318 | |
| 1319 | /* |
| 1320 | ** Return True if zName is a protected (a.k.a. "sensitive") setting. |
| 1321 | */ |
| 1322 | int db_setting_is_protected(const char *zName){ |
| 1323 | const Setting *pSetting = zName ? db_find_setting(zName,0) : 0; |
| 1324 | return pSetting!=0 && pSetting->sensitive!=0; |
| 1325 | } |
| 1326 | |
| 1327 | /* |
| 1328 | ** Implement the protected_setting(X) SQL function. This function returns |
| 1329 | ** true if X is the name of a protected (security-sensitive) setting and |
| 1330 | ** the db.protectSensitive flag is enabled. It returns false otherwise. |
| 1331 | */ |
| 1332 | LOCAL void db_protected_setting_func( |
| 1333 | sqlite3_context *context, |
| 1334 | int argc, |
| 1335 | sqlite3_value **argv |
| 1336 | ){ |
| 1337 | const char *zSetting; |
| 1338 | if( (db.protectMask & PROTECT_SENSITIVE)==0 ){ |
| 1339 | sqlite3_result_int(context, 0); |
| 1340 | return; |
| 1341 | } |
| 1342 | zSetting = (const char*)sqlite3_value_text(argv[0]); |
| 1343 | sqlite3_result_int(context, db_setting_is_protected(zSetting)); |
| 1344 | } |
| 1345 | |
| 1346 | /* |
| 1347 | ** Register the SQL functions that are useful both to the internal |
| 1348 | ** representation and to the "fossil sql" command. |
| 1349 | */ |
| @@ -1090,10 +1370,12 @@ | |
| 1370 | alert_find_emailaddr_func, 0, 0); |
| 1371 | sqlite3_create_function(db, "display_name", 1, SQLITE_UTF8, 0, |
| 1372 | alert_display_name_func, 0, 0); |
| 1373 | sqlite3_create_function(db, "obscure", 1, SQLITE_UTF8, 0, |
| 1374 | db_obscure, 0, 0); |
| 1375 | sqlite3_create_function(db, "protected_setting", 1, SQLITE_UTF8, 0, |
| 1376 | db_protected_setting_func, 0, 0); |
| 1377 | } |
| 1378 | |
| 1379 | #if USE_SEE |
| 1380 | /* |
| 1381 | ** This is a pointer to the saved database encryption key string. |
| @@ -1348,10 +1630,11 @@ | |
| 1630 | if( g.fSqlTrace ) sqlite3_trace_v2(db, SQLITE_TRACE_PROFILE, db_sql_trace, 0); |
| 1631 | db_add_aux_functions(db); |
| 1632 | re_add_sql_func(db); /* The REGEXP operator */ |
| 1633 | foci_register(db); /* The "files_of_checkin" virtual table */ |
| 1634 | sqlite3_db_config(db, SQLITE_DBCONFIG_ENABLE_FKEY, 0, &rc); |
| 1635 | sqlite3_set_authorizer(db, db_top_authorizer, db); |
| 1636 | return db; |
| 1637 | } |
| 1638 | |
| 1639 | |
| 1640 | /* |
| @@ -1791,22 +2074,10 @@ | |
| 2074 | } |
| 2075 | } |
| 2076 | return zRepo; |
| 2077 | } |
| 2078 | |
| 2079 | /* |
| 2080 | ** Returns non-zero if support for symlinks is currently enabled. |
| 2081 | */ |
| 2082 | int db_allow_symlinks(void){ |
| 2083 | return g.allowSymlinks; |
| @@ -1848,13 +2119,14 @@ | |
| 2119 | g.zRepositoryName = mprintf("%s", zDbName); |
| 2120 | db_open_or_attach(g.zRepositoryName, "repository"); |
| 2121 | g.repositoryOpen = 1; |
| 2122 | sqlite3_file_control(g.db, "repository", SQLITE_FCNTL_DATA_VERSION, |
| 2123 | &g.iRepoDataVers); |
| 2124 | |
| 2125 | /* Cache "allow-symlinks" option, because we'll need it on every stat call */ |
| 2126 | g.allowSymlinks = db_get_boolean("allow-symlinks",0); |
| 2127 | |
| 2128 | g.zAuxSchema = db_get("aux-schema",""); |
| 2129 | g.eHashPolicy = db_get_int("hash-policy",-1); |
| 2130 | if( g.eHashPolicy<0 ){ |
| 2131 | g.eHashPolicy = hname_default_policy(); |
| 2132 | db_set_int("hash-policy", g.eHashPolicy, 0); |
| @@ -2089,10 +2361,11 @@ | |
| 2361 | ** argument is true. Ignore unfinalized statements when false. |
| 2362 | */ |
| 2363 | void db_close(int reportErrors){ |
| 2364 | sqlite3_stmt *pStmt; |
| 2365 | if( g.db==0 ) return; |
| 2366 | sqlite3_set_authorizer(g.db, 0, 0); |
| 2367 | if( g.fSqlStats ){ |
| 2368 | int cur, hiwtr; |
| 2369 | sqlite3_db_status(g.db, SQLITE_DBSTATUS_LOOKASIDE_USED, &cur, &hiwtr, 0); |
| 2370 | fprintf(stderr, "-- LOOKASIDE_USED %10d %10d\n", cur, hiwtr); |
| 2371 | sqlite3_db_status(g.db, SQLITE_DBSTATUS_LOOKASIDE_HIT, &cur, &hiwtr, 0); |
| @@ -2118,17 +2391,20 @@ | |
| 2391 | fprintf(stderr, "-- prepared statements %10d\n", db.nPrepare); |
| 2392 | } |
| 2393 | while( db.pAllStmt ){ |
| 2394 | db_finalize(db.pAllStmt); |
| 2395 | } |
| 2396 | if( db.nBegin ){ |
| 2397 | if( reportErrors ){ |
| 2398 | fossil_warning("Transaction started at %s:%d never commits", |
| 2399 | db.zStartFile, db.iStartLine); |
| 2400 | } |
| 2401 | db_end_transaction(1); |
| 2402 | } |
| 2403 | pStmt = 0; |
| 2404 | sqlite3_busy_timeout(g.db, 0); |
| 2405 | g.dbIgnoreErrors++; /* Stop "database locked" warnings */ |
| 2406 | sqlite3_exec(g.db, "PRAGMA optimize", 0, 0, 0); |
| 2407 | g.dbIgnoreErrors--; |
| 2408 | db_close_config(); |
| 2409 | |
| 2410 | /* If the localdb has a lot of unused free space, |
| @@ -2136,11 +2412,13 @@ | |
| 2412 | */ |
| 2413 | if( db_database_slot("localdb")>=0 ){ |
| 2414 | int nFree = db_int(0, "PRAGMA localdb.freelist_count"); |
| 2415 | int nTotal = db_int(0, "PRAGMA localdb.page_count"); |
| 2416 | if( nFree>nTotal/4 ){ |
| 2417 | db_unprotect(PROTECT_ALL); |
| 2418 | db_multi_exec("VACUUM localdb;"); |
| 2419 | db_protect_pop(); |
| 2420 | } |
| 2421 | } |
| 2422 | |
| 2423 | if( g.db ){ |
| 2424 | int rc; |
| @@ -2154,10 +2432,11 @@ | |
| 2432 | } |
| 2433 | g.db = 0; |
| 2434 | } |
| 2435 | g.repositoryOpen = 0; |
| 2436 | g.localOpen = 0; |
| 2437 | db.bProtectTriggers = 0; |
| 2438 | assert( g.dbConfig==0 ); |
| 2439 | assert( g.zConfigDbName==0 ); |
| 2440 | backoffice_run_if_needed(); |
| 2441 | } |
| 2442 | |
| @@ -2168,10 +2447,11 @@ | |
| 2447 | if( g.db ){ |
| 2448 | int rc; |
| 2449 | sqlite3_wal_checkpoint(g.db, 0); |
| 2450 | rc = sqlite3_close(g.db); |
| 2451 | if( g.fSqlTrace ) fossil_trace("-- sqlite3_close(%d)\n", rc); |
| 2452 | db_clear_authorizer(); |
| 2453 | } |
| 2454 | g.db = 0; |
| 2455 | g.repositoryOpen = 0; |
| 2456 | g.localOpen = 0; |
| 2457 | } |
| @@ -2215,10 +2495,11 @@ | |
| 2495 | zUser = fossil_getenv("USERNAME"); |
| 2496 | } |
| 2497 | if( zUser==0 ){ |
| 2498 | zUser = "root"; |
| 2499 | } |
| 2500 | db_unprotect(PROTECT_USER); |
| 2501 | db_multi_exec( |
| 2502 | "INSERT OR IGNORE INTO user(login, info) VALUES(%Q,'')", zUser |
| 2503 | ); |
| 2504 | db_multi_exec( |
| 2505 | "UPDATE user SET cap='s', pw=%Q" |
| @@ -2234,10 +2515,11 @@ | |
| 2515 | " VALUES('developer','','ei','Dev');" |
| 2516 | "INSERT OR IGNORE INTO user(login,pw,cap,info)" |
| 2517 | " VALUES('reader','','kptw','Reader');" |
| 2518 | ); |
| 2519 | } |
| 2520 | db_protect_pop(); |
| 2521 | } |
| 2522 | |
| 2523 | /* |
| 2524 | ** Return a pointer to a string that contains the RHS of an IN operator |
| 2525 | ** that will select CONFIG table names that are in the list of control |
| @@ -2285,10 +2567,11 @@ | |
| 2567 | ){ |
| 2568 | char *zDate; |
| 2569 | Blob hash; |
| 2570 | Blob manifest; |
| 2571 | |
| 2572 | db_unprotect(PROTECT_ALL); |
| 2573 | db_set("content-schema", CONTENT_SCHEMA, 0); |
| 2574 | db_set("aux-schema", AUX_SCHEMA_MAX, 0); |
| 2575 | db_set("rebuilt", get_version(), 0); |
| 2576 | db_set("admin-log", "1", 0); |
| 2577 | db_set("access-log", "1", 0); |
| @@ -2343,10 +2626,11 @@ | |
| 2626 | " photo = (SELECT u2.photo FROM settingSrc.user u2" |
| 2627 | " WHERE u2.login = user.login)" |
| 2628 | " WHERE user.login IN ('anonymous','nobody','developer','reader');" |
| 2629 | ); |
| 2630 | } |
| 2631 | db_protect_pop(); |
| 2632 | |
| 2633 | if( zInitialDate ){ |
| 2634 | int rid; |
| 2635 | blob_zero(&manifest); |
| 2636 | blob_appendf(&manifest, "C initial\\sempty\\scheck-in\n"); |
| @@ -2839,10 +3123,12 @@ | |
| 3123 | z = db_text(0, "SELECT strftime(%Q,%Q,'unixepoch');", zFormat, z); |
| 3124 | } |
| 3125 | return z; |
| 3126 | } |
| 3127 | void db_set(const char *zName, const char *zValue, int globalFlag){ |
| 3128 | db_assert_protection_off_or_not_sensitive(zName); |
| 3129 | db_unprotect(PROTECT_CONFIG); |
| 3130 | db_begin_transaction(); |
| 3131 | if( globalFlag ){ |
| 3132 | db_swap_connections(); |
| 3133 | db_multi_exec("REPLACE INTO global_config(name,value) VALUES(%Q,%Q)", |
| 3134 | zName, zValue); |
| @@ -2853,13 +3139,15 @@ | |
| 3139 | } |
| 3140 | if( globalFlag && g.repositoryOpen ){ |
| 3141 | db_multi_exec("DELETE FROM config WHERE name=%Q", zName); |
| 3142 | } |
| 3143 | db_end_transaction(0); |
| 3144 | db_protect_pop(); |
| 3145 | } |
| 3146 | void db_unset(const char *zName, int globalFlag){ |
| 3147 | db_begin_transaction(); |
| 3148 | db_unprotect(PROTECT_CONFIG); |
| 3149 | if( globalFlag ){ |
| 3150 | db_swap_connections(); |
| 3151 | db_multi_exec("DELETE FROM global_config WHERE name=%Q", zName); |
| 3152 | db_swap_connections(); |
| 3153 | }else{ |
| @@ -2866,10 +3154,11 @@ | |
| 3154 | db_multi_exec("DELETE FROM config WHERE name=%Q", zName); |
| 3155 | } |
| 3156 | if( globalFlag && g.repositoryOpen ){ |
| 3157 | db_multi_exec("DELETE FROM config WHERE name=%Q", zName); |
| 3158 | } |
| 3159 | db_protect_pop(); |
| 3160 | db_end_transaction(0); |
| 3161 | } |
| 3162 | int db_is_global(const char *zName){ |
| 3163 | int rc = 0; |
| 3164 | if( g.zConfigDbName ){ |
| @@ -2899,10 +3188,12 @@ | |
| 3188 | db_swap_connections(); |
| 3189 | } |
| 3190 | return v; |
| 3191 | } |
| 3192 | void db_set_int(const char *zName, int value, int globalFlag){ |
| 3193 | db_assert_protection_off_or_not_sensitive(zName); |
| 3194 | db_unprotect(PROTECT_CONFIG); |
| 3195 | if( globalFlag ){ |
| 3196 | db_swap_connections(); |
| 3197 | db_multi_exec("REPLACE INTO global_config(name,value) VALUES(%Q,%d)", |
| 3198 | zName, value); |
| 3199 | db_swap_connections(); |
| @@ -2911,10 +3202,11 @@ | |
| 3202 | zName, value); |
| 3203 | } |
| 3204 | if( globalFlag && g.repositoryOpen ){ |
| 3205 | db_multi_exec("DELETE FROM config WHERE name=%Q", zName); |
| 3206 | } |
| 3207 | db_protect_pop(); |
| 3208 | } |
| 3209 | int db_get_boolean(const char *zName, int dflt){ |
| 3210 | char *zVal = db_get(zName, dflt ? "on" : "off"); |
| 3211 | if( is_truth(zVal) ){ |
| 3212 | dflt = 1; |
| @@ -3040,24 +3332,28 @@ | |
| 3332 | } |
| 3333 | file_canonical_name(zName, &full, 0); |
| 3334 | (void)filename_collation(); /* Initialize before connection swap */ |
| 3335 | db_swap_connections(); |
| 3336 | zRepoSetting = mprintf("repo:%q", blob_str(&full)); |
| 3337 | |
| 3338 | db_unprotect(PROTECT_CONFIG); |
| 3339 | db_multi_exec( |
| 3340 | "DELETE FROM global_config WHERE name %s = %Q;", |
| 3341 | filename_collation(), zRepoSetting |
| 3342 | ); |
| 3343 | db_multi_exec( |
| 3344 | "INSERT OR IGNORE INTO global_config(name,value)" |
| 3345 | "VALUES(%Q,1);", |
| 3346 | zRepoSetting |
| 3347 | ); |
| 3348 | db_protect_pop(); |
| 3349 | fossil_free(zRepoSetting); |
| 3350 | if( g.localOpen && g.zLocalRoot && g.zLocalRoot[0] ){ |
| 3351 | Blob localRoot; |
| 3352 | file_canonical_name(g.zLocalRoot, &localRoot, 1); |
| 3353 | zCkoutSetting = mprintf("ckout:%q", blob_str(&localRoot)); |
| 3354 | db_unprotect(PROTECT_CONFIG); |
| 3355 | db_multi_exec( |
| 3356 | "DELETE FROM global_config WHERE name %s = %Q;", |
| 3357 | filename_collation(), zCkoutSetting |
| 3358 | ); |
| 3359 | db_multi_exec( |
| @@ -3073,10 +3369,11 @@ | |
| 3369 | db_optional_sql("repository", |
| 3370 | "REPLACE INTO config(name,value,mtime)" |
| 3371 | "VALUES(%Q,1,now());", |
| 3372 | zCkoutSetting |
| 3373 | ); |
| 3374 | db_protect_pop(); |
| 3375 | fossil_free(zCkoutSetting); |
| 3376 | blob_reset(&localRoot); |
| 3377 | }else{ |
| 3378 | db_swap_connections(); |
| 3379 | } |
| @@ -3131,11 +3428,10 @@ | |
| 3428 | void cmd_open(void){ |
| 3429 | int emptyFlag; |
| 3430 | int keepFlag; |
| 3431 | int forceMissingFlag; |
| 3432 | int allowNested; |
| 3433 | int setmtimeFlag; /* --setmtime. Set mtimes on files */ |
| 3434 | int bForce = 0; /* --force. Open even if non-empty dir */ |
| 3435 | static char *azNewArgv[] = { 0, "checkout", "--prompt", 0, 0, 0, 0 }; |
| 3436 | const char *zWorkDir; /* --workdir value */ |
| 3437 | const char *zRepo = 0; /* Name of the repository file */ |
| @@ -3242,23 +3538,10 @@ | |
| 3538 | }else if( db_exists("SELECT 1 FROM event WHERE type='ci'") ){ |
| 3539 | g.zOpenRevision = db_get("main-branch", 0); |
| 3540 | } |
| 3541 | } |
| 3542 | |
| 3543 | |
| 3544 | #if defined(_WIN32) || defined(__CYGWIN__) |
| 3545 | # define LOCALDB_NAME "./_FOSSIL_" |
| 3546 | #else |
| 3547 | # define LOCALDB_NAME "./.fslckout" |
| @@ -3268,26 +3551,10 @@ | |
| 3551 | "COMMIT; PRAGMA journal_mode=WAL; BEGIN;", |
| 3552 | #endif |
| 3553 | (char*)0); |
| 3554 | db_delete_on_failure(LOCALDB_NAME); |
| 3555 | db_open_local(0); |
| 3556 | db_lset("repository", zRepo); |
| 3557 | db_record_repository_filename(zRepo); |
| 3558 | db_set_checkout(0); |
| 3559 | azNewArgv[0] = g.argv[0]; |
| 3560 | g.argv = azNewArgv; |
| @@ -3376,12 +3643,13 @@ | |
| 3643 | const char *name; /* Name of the setting */ |
| 3644 | const char *var; /* Internal variable name used by db_set() */ |
| 3645 | int width; /* Width of display. 0 for boolean values and |
| 3646 | ** negative for values which should not appear |
| 3647 | ** on the /setup_settings page. */ |
| 3648 | char versionable; /* Is this setting versionable? */ |
| 3649 | char forceTextArea; /* Force using a text area for display? */ |
| 3650 | char sensitive; /* True if this a security-sensitive setting */ |
| 3651 | const char *def; /* Default value */ |
| 3652 | }; |
| 3653 | #endif /* INTERFACE */ |
| 3654 | |
| 3655 | /* |
| @@ -3395,32 +3663,29 @@ | |
| 3663 | ** SETTING: admin-log boolean default=off |
| 3664 | ** |
| 3665 | ** When the admin-log setting is enabled, configuration changes are recorded |
| 3666 | ** in the "admin_log" table of the repository. |
| 3667 | */ |
| 3668 | /* |
| 3669 | ** SETTING: allow-symlinks boolean default=off sensitive |
| 3670 | ** |
| 3671 | ** When allow-symlinks is OFF, Fossil does not see symbolic links |
| 3672 | ** (a.k.a "symlinks") on disk as a separate class of object. Instead Fossil |
| 3673 | ** sees the object that the symlink points to. Fossil will only manage files |
| 3674 | ** and directories, not symlinks. When a symlink is added to a repository, |
| 3675 | ** the object that the symlink points to is added, not the symlink itself. |
| 3676 | ** |
| 3677 | ** When allow-symlinks is ON, Fossil sees symlinks on disk as a separate |
| 3678 | ** object class that is distinct from files and directories. When a symlink |
| 3679 | ** is added to a repository, Fossil stores the target filename. In other |
| 3680 | ** words, Fossil stores the symlink itself, not the object that the symlink |
| 3681 | ** points to. |
| 3682 | ** |
| 3683 | ** Symlinks are not cross-platform. They are not available on all |
| 3684 | ** operating systems and file systems. Hence the allow-symlinks setting is |
| 3685 | ** OFF by default, for portability. |
| 3686 | */ |
| 3687 | /* |
| 3688 | ** SETTING: auto-captcha boolean default=on variable=autocaptcha |
| 3689 | ** If enabled, the /login page provides a button that will automatically |
| 3690 | ** fill in the captcha password. This makes things easier for human users, |
| 3691 | ** at the expense of also making logins easier for malicious robots. |
| @@ -3470,11 +3735,11 @@ | |
| 3735 | ** there is no cron job periodically running "fossil backoffice", |
| 3736 | ** email notifications and other work normally done by the |
| 3737 | ** backoffice will not occur. |
| 3738 | */ |
| 3739 | /* |
| 3740 | ** SETTING: backoffice-logfile width=40 sensitive |
| 3741 | ** If backoffice-logfile is not an empty string and is a valid |
| 3742 | ** filename, then a one-line message is appended to that file |
| 3743 | ** every time the backoffice runs. This can be used for debugging, |
| 3744 | ** to ensure that backoffice is running appropriately. |
| 3745 | */ |
| @@ -3547,11 +3812,11 @@ | |
| 3812 | /* |
| 3813 | ** SETTING: crnl-glob width=40 versionable block-text |
| 3814 | ** This is an alias for the crlf-glob setting. |
| 3815 | */ |
| 3816 | /* |
| 3817 | ** SETTING: default-perms width=16 default=u sensitive |
| 3818 | ** Permissions given automatically to new users. For more |
| 3819 | ** information on permissions see the Users page in Server |
| 3820 | ** Administration of the HTTP UI. |
| 3821 | */ |
| 3822 | /* |
| @@ -3559,11 +3824,11 @@ | |
| 3824 | ** If enabled, permit files that may be binary |
| 3825 | ** or that match the "binary-glob" setting to be used with |
| 3826 | ** external diff programs. If disabled, skip these files. |
| 3827 | */ |
| 3828 | /* |
| 3829 | ** SETTING: diff-command width=40 sensitive |
| 3830 | ** The value is an external command to run when performing a diff. |
| 3831 | ** If undefined, the internal text diff will be used. |
| 3832 | */ |
| 3833 | /* |
| 3834 | ** SETTING: dont-push boolean default=off |
| @@ -3574,11 +3839,11 @@ | |
| 3839 | /* |
| 3840 | ** SETTING: dotfiles boolean versionable default=off |
| 3841 | ** If enabled, include --dotfiles option for all compatible commands. |
| 3842 | */ |
| 3843 | /* |
| 3844 | ** SETTING: editor width=32 sensitive |
| 3845 | ** The value is an external command that will launch the |
| 3846 | ** text editor command used for check-in comments. |
| 3847 | */ |
| 3848 | /* |
| 3849 | ** SETTING: empty-dirs width=40 versionable block-text |
| @@ -3617,16 +3882,16 @@ | |
| 3882 | ** An empty list prohibits editing via that page. Note that |
| 3883 | ** it cannot edit binary files, so the list should not |
| 3884 | ** contain any globs for, e.g., images or PDFs. |
| 3885 | */ |
| 3886 | /* |
| 3887 | ** SETTING: gdiff-command width=40 default=gdiff sensitive |
| 3888 | ** The value is an external command to run when performing a graphical |
| 3889 | ** diff. If undefined, text diff will be used. |
| 3890 | */ |
| 3891 | /* |
| 3892 | ** SETTING: gmerge-command width=40 sensitive |
| 3893 | ** The value is a graphical merge conflict resolver command operating |
| 3894 | ** on four files. Examples: |
| 3895 | ** |
| 3896 | ** kdiff3 "%baseline" "%original" "%merge" -o "%output" |
| 3897 | ** xxdiff "%original" "%baseline" "%merge" -M "%output" |
| @@ -3757,11 +4022,11 @@ | |
| 4022 | ** the associated files within the checkout -AND- the "rm" |
| 4023 | ** and "delete" commands will also remove the associated |
| 4024 | ** files from within the checkout. |
| 4025 | */ |
| 4026 | /* |
| 4027 | ** SETTING: pgp-command width=40 sensitive |
| 4028 | ** Command used to clear-sign manifests at check-in. |
| 4029 | ** Default value is "gpg --clearsign -o" |
| 4030 | */ |
| 4031 | /* |
| 4032 | ** SETTING: forbid-delta-manifests boolean default=off |
| @@ -3817,22 +4082,22 @@ | |
| 4082 | ** |
| 4083 | ** If repolist-skin has a value of 2, then the repository is omitted from |
| 4084 | ** the list in use cases 1 through 4, but not for 5 and 6. |
| 4085 | */ |
| 4086 | /* |
| 4087 | ** SETTING: self-register boolean default=off sensitive |
| 4088 | ** Allow users to register themselves through the HTTP UI. |
| 4089 | ** This is useful if you want to see other names than |
| 4090 | ** "Anonymous" in e.g. ticketing system. On the other hand |
| 4091 | ** users can not be deleted. |
| 4092 | */ |
| 4093 | /* |
| 4094 | ** SETTING: ssh-command width=40 sensitive |
| 4095 | ** The command used to talk to a remote machine with the "ssh://" protocol. |
| 4096 | */ |
| 4097 | /* |
| 4098 | ** SETTING: ssl-ca-location width=40 sensitive |
| 4099 | ** The full pathname to a file containing PEM encoded |
| 4100 | ** CA root certificates, or a directory of certificates |
| 4101 | ** with filenames formed from the certificate hashes as |
| 4102 | ** required by OpenSSL. |
| 4103 | ** |
| @@ -3842,11 +4107,11 @@ | |
| 4107 | ** Checking your platform behaviour is required if the |
| 4108 | ** exact contents of the CA root is critical for your |
| 4109 | ** application. |
| 4110 | */ |
| 4111 | /* |
| 4112 | ** SETTING: ssl-identity width=40 sensitive |
| 4113 | ** The full pathname to a file containing a certificate |
| 4114 | ** and private key in PEM format. Create by concatenating |
| 4115 | ** the certificate and private key files. |
| 4116 | ** |
| 4117 | ** This identity will be presented to SSL servers to |
| @@ -3853,33 +4118,33 @@ | |
| 4118 | ** authenticate this client, in addition to the normal |
| 4119 | ** password authentication. |
| 4120 | */ |
| 4121 | #ifdef FOSSIL_ENABLE_TCL |
| 4122 | /* |
| 4123 | ** SETTING: tcl boolean default=off sensitive |
| 4124 | ** If enabled Tcl integration commands will be added to the TH1 |
| 4125 | ** interpreter, allowing arbitrary Tcl expressions and |
| 4126 | ** scripts to be evaluated from TH1. Additionally, the Tcl |
| 4127 | ** interpreter will be able to evaluate arbitrary TH1 |
| 4128 | ** expressions and scripts. |
| 4129 | */ |
| 4130 | /* |
| 4131 | ** SETTING: tcl-setup width=40 block-text sensitive |
| 4132 | ** This is the setup script to be evaluated after creating |
| 4133 | ** and initializing the Tcl interpreter. By default, this |
| 4134 | ** is empty and no extra setup is performed. |
| 4135 | */ |
| 4136 | #endif /* FOSSIL_ENABLE_TCL */ |
| 4137 | /* |
| 4138 | ** SETTING: tclsh width=80 default=tclsh sensitive |
| 4139 | ** Name of the external TCL interpreter used for such things |
| 4140 | ** as running the GUI diff viewer launched by the --tk option |
| 4141 | ** of the various "diff" commands. |
| 4142 | */ |
| 4143 | #ifdef FOSSIL_ENABLE_TH1_DOCS |
| 4144 | /* |
| 4145 | ** SETTING: th1-docs boolean default=off sensitive |
| 4146 | ** If enabled, this allows embedded documentation files to contain |
| 4147 | ** arbitrary TH1 scripts that are evaluated on the server. If native |
| 4148 | ** Tcl integration is also enabled, this setting has the |
| 4149 | ** potential to allow anybody with check-in privileges to |
| 4150 | ** do almost anything that the associated operating system |
| @@ -3932,11 +4197,11 @@ | |
| 4197 | ** of a "fossil clone" or "fossil sync" command. The |
| 4198 | ** default is false, in which case the -u option is |
| 4199 | ** needed to clone or sync unversioned files. |
| 4200 | */ |
| 4201 | /* |
| 4202 | ** SETTING: web-browser width=30 sensitive |
| 4203 | ** A shell command used to launch your preferred |
| 4204 | ** web browser when given a URL as an argument. |
| 4205 | ** Defaults to "start" on windows, "open" on Mac, |
| 4206 | ** and "firefox" on Unix. |
| 4207 | */ |
| @@ -4058,11 +4323,13 @@ | |
| 4323 | fossil_fatal("cannot set 'manifest' globally"); |
| 4324 | } |
| 4325 | if( unsetFlag ){ |
| 4326 | db_unset(pSetting->name, globalFlag); |
| 4327 | }else{ |
| 4328 | db_protect_only(PROTECT_NONE); |
| 4329 | db_set(pSetting->name, g.argv[3], globalFlag); |
| 4330 | db_protect_pop(); |
| 4331 | } |
| 4332 | if( isManifest && g.localOpen ){ |
| 4333 | manifest_to_disk(db_lget_int("checkout", 0)); |
| 4334 | } |
| 4335 | }else{ |
| 4336 |
+176
-20
| --- src/file.c | ||
| +++ src/file.c | ||
| @@ -47,22 +47,21 @@ | ||
| 47 | 47 | ** used for files that are under management by a Fossil repository. ExtFILE |
| 48 | 48 | ** should be used for files that are not under management. SymFILE is for |
| 49 | 49 | ** a few special cases such as the "fossil test-tarball" command when we never |
| 50 | 50 | ** want to follow symlinks. |
| 51 | 51 | ** |
| 52 | -** If RepoFILE is used and if the allow-symlinks setting is true and if | |
| 53 | -** the object is a symbolic link, then the object is treated like an ordinary | |
| 54 | -** file whose content is name of the object to which the symbolic link | |
| 55 | -** points. | |
| 56 | -** | |
| 57 | -** If ExtFILE is used or allow-symlinks is false, then operations on a | |
| 58 | -** symbolic link are the same as operations on the object to which the | |
| 59 | -** symbolic link points. | |
| 60 | -** | |
| 61 | -** SymFILE is like RepoFILE except that it always uses the target filename of | |
| 62 | -** a symbolic link as the content, instead of the content of the object | |
| 63 | -** that the symlink points to. SymFILE acts as if allow-symlinks is always ON. | |
| 52 | +** ExtFILE Symbolic links always refer to the object to which the | |
| 53 | +** link points. Symlinks are never recognized as symlinks but | |
| 54 | +** instead always appear to the the target object. | |
| 55 | +** | |
| 56 | +** SymFILE Symbolic links always appear to be files whose name is | |
| 57 | +** the target pathname of the symbolic link. | |
| 58 | +** | |
| 59 | +** RepoFILE Like symfile is allow-symlinks is true, or like | |
| 60 | +** ExtFile if allow-symlinks is false. In other words, | |
| 61 | +** symbolic links are only recognized as something different | |
| 62 | +** from files or directories if allow-symlinks is true. | |
| 64 | 63 | */ |
| 65 | 64 | #define ExtFILE 0 /* Always follow symlinks */ |
| 66 | 65 | #define RepoFILE 1 /* Follow symlinks if and only if allow-symlinks is OFF */ |
| 67 | 66 | #define SymFILE 2 /* Never follow symlinks */ |
| 68 | 67 | |
| @@ -134,13 +133,16 @@ | ||
| 134 | 133 | int eFType /* Look at symlink itself if RepoFILE and enabled. */ |
| 135 | 134 | ){ |
| 136 | 135 | int rc; |
| 137 | 136 | void *zMbcs = fossil_utf8_to_path(zFilename, 0); |
| 138 | 137 | #if !defined(_WIN32) |
| 139 | - if( eFType>=RepoFILE && (eFType==SymFILE || db_allow_symlinks()) ){ | |
| 138 | + if( (eFType=RepoFILE && db_allow_symlinks()) | |
| 139 | + || eFType==SymFILE ){ | |
| 140 | + /* Symlinks look like files whose content is the name of the target */ | |
| 140 | 141 | rc = lstat(zMbcs, buf); |
| 141 | 142 | }else{ |
| 143 | + /* Symlinks look like the object to which they point */ | |
| 142 | 144 | rc = stat(zMbcs, buf); |
| 143 | 145 | } |
| 144 | 146 | #else |
| 145 | 147 | rc = win32_stat(zMbcs, buf, eFType); |
| 146 | 148 | #endif |
| @@ -316,17 +318,90 @@ | ||
| 316 | 318 | |
| 317 | 319 | /* |
| 318 | 320 | ** Return TRUE if the named file is a symlink and symlinks are allowed. |
| 319 | 321 | ** Return false for all other cases. |
| 320 | 322 | ** |
| 321 | -** This routines RepoFILE - that zFilename is always a file under management. | |
| 323 | +** This routines assumes RepoFILE - that zFilename is always a file | |
| 324 | +** under management. | |
| 322 | 325 | ** |
| 323 | 326 | ** On Windows, always return False. |
| 324 | 327 | */ |
| 325 | 328 | int file_islink(const char *zFilename){ |
| 326 | 329 | return file_perm(zFilename, RepoFILE)==PERM_LNK; |
| 327 | 330 | } |
| 331 | + | |
| 332 | +/* | |
| 333 | +** Check every sub-directory of zRoot along the path to zFile. | |
| 334 | +** If any sub-directory is really an ordinary file or a symbolic link, | |
| 335 | +** return an integer which is the length of the prefix of zFile which | |
| 336 | +** is the name of that object. Return 0 if all no non-directory | |
| 337 | +** objects are found along the path. | |
| 338 | +** | |
| 339 | +** Example: Given inputs | |
| 340 | +** | |
| 341 | +** zRoot = /home/alice/project1 | |
| 342 | +** zFile = /home/alice/project1/main/src/js/fileA.js | |
| 343 | +** | |
| 344 | +** Look for objects in the following order: | |
| 345 | +** | |
| 346 | +** /home/alice/project/main | |
| 347 | +** /home/alice/project/main/src | |
| 348 | +** /home/alice/project/main/src/js | |
| 349 | +** | |
| 350 | +** If any of those objects exist and are something other than a directory | |
| 351 | +** then return the length of the name of the first non-directory object | |
| 352 | +** seen. | |
| 353 | +*/ | |
| 354 | +int file_nondir_objects_on_path(const char *zRoot, const char *zFile){ | |
| 355 | + int i = (int)strlen(zRoot); | |
| 356 | + char *z = fossil_strdup(zFile); | |
| 357 | + assert( fossil_strnicmp(zRoot, z, i)==0 ); | |
| 358 | + if( i && zRoot[i-1]=='/' ) i--; | |
| 359 | + while( z[i]=='/' ){ | |
| 360 | + int j, rc; | |
| 361 | + for(j=i+1; z[j] && z[j]!='/'; j++){} | |
| 362 | + if( z[j]!='/' ) break; | |
| 363 | + z[j] = 0; | |
| 364 | + rc = file_isdir(z, SymFILE); | |
| 365 | + if( rc!=1 ){ | |
| 366 | + if( rc==2 ){ | |
| 367 | + fossil_free(z); | |
| 368 | + return j; | |
| 369 | + } | |
| 370 | + break; | |
| 371 | + } | |
| 372 | + z[j] = '/'; | |
| 373 | + i = j; | |
| 374 | + } | |
| 375 | + fossil_free(z); | |
| 376 | + return 0; | |
| 377 | +} | |
| 378 | + | |
| 379 | +/* | |
| 380 | +** The file named zFile is suppose to be an in-tree file. Check to | |
| 381 | +** ensure that it will be safe to write to this file by verifying that | |
| 382 | +** there are no symlinks or other non-directory objects in between the | |
| 383 | +** root of the checkout and zFile. | |
| 384 | +** | |
| 385 | +** If a problem is found, print a warning message (using fossil_warning()) | |
| 386 | +** and return non-zero. If everything is ok, return zero. | |
| 387 | +*/ | |
| 388 | +int file_unsafe_in_tree_path(const char *zFile){ | |
| 389 | + int n; | |
| 390 | + if( !file_is_absolute_path(zFile) ){ | |
| 391 | + fossil_panic("%s is not an absolute pathname",zFile); | |
| 392 | + } | |
| 393 | + if( fossil_strnicmp(g.zLocalRoot, zFile, (int)strlen(g.zLocalRoot)) ){ | |
| 394 | + fossil_panic("%s is not a prefix of %s", g.zLocalRoot, zFile); | |
| 395 | + } | |
| 396 | + n = file_nondir_objects_on_path(g.zLocalRoot, zFile); | |
| 397 | + if( n ){ | |
| 398 | + fossil_warning("cannot write to %s because non-directory object %.*s" | |
| 399 | + " is in the way", zFile, n, zFile); | |
| 400 | + } | |
| 401 | + return n; | |
| 402 | +} | |
| 328 | 403 | |
| 329 | 404 | /* |
| 330 | 405 | ** Return 1 if zFilename is a directory. Return 0 if zFilename |
| 331 | 406 | ** does not exist. Return 2 if zFilename exists but is something |
| 332 | 407 | ** other than a directory. |
| @@ -570,11 +645,14 @@ | ||
| 570 | 645 | */ |
| 571 | 646 | int file_setexe(const char *zFilename, int onoff){ |
| 572 | 647 | int rc = 0; |
| 573 | 648 | #if !defined(_WIN32) |
| 574 | 649 | struct stat buf; |
| 575 | - if( fossil_stat(zFilename, &buf, RepoFILE)!=0 || S_ISLNK(buf.st_mode) ){ | |
| 650 | + if( fossil_stat(zFilename, &buf, RepoFILE)!=0 | |
| 651 | + || S_ISLNK(buf.st_mode) | |
| 652 | + || S_ISDIR(buf.st_mode) | |
| 653 | + ){ | |
| 576 | 654 | return 0; |
| 577 | 655 | } |
| 578 | 656 | if( onoff ){ |
| 579 | 657 | int targetMode = (buf.st_mode & 0444)>>2; |
| 580 | 658 | if( (buf.st_mode & 0100)==0 ){ |
| @@ -1236,12 +1314,12 @@ | ||
| 1236 | 1314 | sqlite3_int64 iMtime; |
| 1237 | 1315 | struct fossilStat testFileStat; |
| 1238 | 1316 | memset(zBuf, 0, sizeof(zBuf)); |
| 1239 | 1317 | blob_zero(&x); |
| 1240 | 1318 | file_canonical_name(zPath, &x, slash); |
| 1241 | - fossil_print("[%s] -> [%s]\n", zPath, blob_buffer(&x)); | |
| 1242 | - blob_reset(&x); | |
| 1319 | + char *zFull = blob_str(&x); | |
| 1320 | + fossil_print("[%s] -> [%s]\n", zPath, zFull); | |
| 1243 | 1321 | memset(&testFileStat, 0, sizeof(struct fossilStat)); |
| 1244 | 1322 | rc = fossil_stat(zPath, &testFileStat, 0); |
| 1245 | 1323 | fossil_print(" stat_rc = %d\n", rc); |
| 1246 | 1324 | sqlite3_snprintf(sizeof(zBuf), zBuf, "%lld", testFileStat.st_size); |
| 1247 | 1325 | fossil_print(" stat_size = %s\n", zBuf); |
| @@ -1285,10 +1363,13 @@ | ||
| 1285 | 1363 | fossil_print(" file_isfile_or_link = %d\n", file_isfile_or_link(zPath)); |
| 1286 | 1364 | fossil_print(" file_islink = %d\n", file_islink(zPath)); |
| 1287 | 1365 | fossil_print(" file_isexe(RepoFILE) = %d\n", file_isexe(zPath,RepoFILE)); |
| 1288 | 1366 | fossil_print(" file_isdir(RepoFILE) = %d\n", file_isdir(zPath,RepoFILE)); |
| 1289 | 1367 | fossil_print(" file_is_repository = %d\n", file_is_repository(zPath)); |
| 1368 | + fossil_print(" file_is_reserved_name = %d\n", | |
| 1369 | + file_is_reserved_name(zFull,-1)); | |
| 1370 | + blob_reset(&x); | |
| 1290 | 1371 | if( reset ) resetStat(); |
| 1291 | 1372 | } |
| 1292 | 1373 | |
| 1293 | 1374 | /* |
| 1294 | 1375 | ** COMMAND: test-file-environment |
| @@ -1300,32 +1381,45 @@ | ||
| 1300 | 1381 | ** |
| 1301 | 1382 | ** Options: |
| 1302 | 1383 | ** |
| 1303 | 1384 | ** --allow-symlinks BOOLEAN Temporarily turn allow-symlinks on/off |
| 1304 | 1385 | ** --open-config Open the configuration database first. |
| 1305 | -** --slash Trailing slashes, if any, are retained. | |
| 1306 | 1386 | ** --reset Reset cached stat() info for each file. |
| 1387 | +** --root ROOT Use ROOT as the root of the checkout | |
| 1388 | +** --slash Trailing slashes, if any, are retained. | |
| 1307 | 1389 | */ |
| 1308 | 1390 | void cmd_test_file_environment(void){ |
| 1309 | 1391 | int i; |
| 1310 | 1392 | int slashFlag = find_option("slash",0,0)!=0; |
| 1311 | 1393 | int resetFlag = find_option("reset",0,0)!=0; |
| 1394 | + const char *zRoot = find_option("root",0,1); | |
| 1312 | 1395 | const char *zAllow = find_option("allow-symlinks",0,1); |
| 1313 | 1396 | if( find_option("open-config", 0, 0)!=0 ){ |
| 1314 | 1397 | Th_OpenConfig(1); |
| 1315 | 1398 | } |
| 1316 | 1399 | db_find_and_open_repository(OPEN_ANY_SCHEMA|OPEN_OK_NOT_FOUND, 0); |
| 1317 | 1400 | fossil_print("filenames_are_case_sensitive() = %d\n", |
| 1318 | 1401 | filenames_are_case_sensitive()); |
| 1319 | - fossil_print("db_allow_symlinks_by_default() = %d\n", | |
| 1320 | - db_allow_symlinks_by_default()); | |
| 1321 | 1402 | if( zAllow ){ |
| 1322 | 1403 | g.allowSymlinks = !is_false(zAllow); |
| 1323 | 1404 | } |
| 1405 | + if( zRoot==0 ) zRoot = g.zLocalRoot; | |
| 1324 | 1406 | fossil_print("db_allow_symlinks() = %d\n", db_allow_symlinks()); |
| 1407 | + fossil_print("local-root = [%s]\n", zRoot); | |
| 1325 | 1408 | for(i=2; i<g.argc; i++){ |
| 1409 | + char *z; | |
| 1326 | 1410 | emitFileStat(g.argv[i], slashFlag, resetFlag); |
| 1411 | + z = file_canonical_name_dup(g.argv[i]); | |
| 1412 | + fossil_print(" file_canonical_name = %s\n", z); | |
| 1413 | + fossil_print(" file_nondir_path = "); | |
| 1414 | + if( fossil_strnicmp(zRoot,z,(int)strlen(zRoot))!=0 ){ | |
| 1415 | + fossil_print("(--root is not a prefix of this file)\n"); | |
| 1416 | + }else{ | |
| 1417 | + int n = file_nondir_objects_on_path(zRoot, z); | |
| 1418 | + fossil_print("%.*s\n", n, z); | |
| 1419 | + } | |
| 1420 | + fossil_free(z); | |
| 1327 | 1421 | } |
| 1328 | 1422 | } |
| 1329 | 1423 | |
| 1330 | 1424 | /* |
| 1331 | 1425 | ** COMMAND: test-canonical-name |
| @@ -2408,5 +2502,67 @@ | ||
| 2408 | 2502 | */ |
| 2409 | 2503 | const char * file_extension(const char *zFileName){ |
| 2410 | 2504 | const char * zExt = zFileName ? strrchr(zFileName, '.') : 0; |
| 2411 | 2505 | return zExt ? &zExt[1] : 0; |
| 2412 | 2506 | } |
| 2507 | + | |
| 2508 | +/* | |
| 2509 | +** Returns non-zero if the specified file name ends with any reserved name, | |
| 2510 | +** e.g.: _FOSSIL_ or .fslckout. Specifically, it returns 1 for exact match | |
| 2511 | +** or 2 for a tail match on a longer file name. | |
| 2512 | +** | |
| 2513 | +** For the sake of efficiency, zFilename must be a canonical name, e.g. an | |
| 2514 | +** absolute path using only forward slash ('/') as a directory separator. | |
| 2515 | +** | |
| 2516 | +** nFilename must be the length of zFilename. When negative, strlen() will | |
| 2517 | +** be used to calculate it. | |
| 2518 | +*/ | |
| 2519 | +int file_is_reserved_name(const char *zFilename, int nFilename){ | |
| 2520 | + const char *zEnd; /* one-after-the-end of zFilename */ | |
| 2521 | + int gotSuffix = 0; /* length of suffix (-wal, -shm, -journal) */ | |
| 2522 | + | |
| 2523 | + assert( zFilename && "API misuse" ); | |
| 2524 | + if( nFilename<0 ) nFilename = (int)strlen(zFilename); | |
| 2525 | + if( nFilename<8 ) return 0; /* strlen("_FOSSIL_") */ | |
| 2526 | + zEnd = zFilename + nFilename; | |
| 2527 | + if( nFilename>=12 ){ /* strlen("_FOSSIL_-(shm|wal)") */ | |
| 2528 | + /* Check for (-wal, -shm, -journal) suffixes, with an eye towards | |
| 2529 | + ** runtime speed. */ | |
| 2530 | + if( zEnd[-4]=='-' ){ | |
| 2531 | + if( fossil_strnicmp("wal", &zEnd[-3], 3) | |
| 2532 | + && fossil_strnicmp("shm", &zEnd[-3], 3) ){ | |
| 2533 | + return 0; | |
| 2534 | + } | |
| 2535 | + gotSuffix = 4; | |
| 2536 | + }else if( nFilename>=16 && zEnd[-8]=='-' ){ /*strlen(_FOSSIL_-journal) */ | |
| 2537 | + if( fossil_strnicmp("journal", &zEnd[-7], 7) ) return 0; | |
| 2538 | + gotSuffix = 8; | |
| 2539 | + } | |
| 2540 | + if( gotSuffix ){ | |
| 2541 | + assert( 4==gotSuffix || 8==gotSuffix ); | |
| 2542 | + zEnd -= gotSuffix; | |
| 2543 | + nFilename -= gotSuffix; | |
| 2544 | + gotSuffix = 1; | |
| 2545 | + } | |
| 2546 | + assert( nFilename>=8 && "strlen(_FOSSIL_)" ); | |
| 2547 | + assert( gotSuffix==0 || gotSuffix==1 ); | |
| 2548 | + } | |
| 2549 | + switch( zEnd[-1] ){ | |
| 2550 | + case '_':{ | |
| 2551 | + if( fossil_strnicmp("_FOSSIL_", &zEnd[-8], 8) ) return 0; | |
| 2552 | + if( 8==nFilename ) return 1; | |
| 2553 | + return zEnd[-9]=='/' ? 2 : gotSuffix; | |
| 2554 | + } | |
| 2555 | + case 'T': | |
| 2556 | + case 't':{ | |
| 2557 | + if( nFilename<9 || zEnd[-9]!='.' | |
| 2558 | + || fossil_strnicmp(".fslckout", &zEnd[-9], 9) ){ | |
| 2559 | + return 0; | |
| 2560 | + } | |
| 2561 | + if( 9==nFilename ) return 1; | |
| 2562 | + return zEnd[-10]=='/' ? 2 : gotSuffix; | |
| 2563 | + } | |
| 2564 | + default:{ | |
| 2565 | + return 0; | |
| 2566 | + } | |
| 2567 | + } | |
| 2568 | +} | |
| 2413 | 2569 |
| --- src/file.c | |
| +++ src/file.c | |
| @@ -47,22 +47,21 @@ | |
| 47 | ** used for files that are under management by a Fossil repository. ExtFILE |
| 48 | ** should be used for files that are not under management. SymFILE is for |
| 49 | ** a few special cases such as the "fossil test-tarball" command when we never |
| 50 | ** want to follow symlinks. |
| 51 | ** |
| 52 | ** If RepoFILE is used and if the allow-symlinks setting is true and if |
| 53 | ** the object is a symbolic link, then the object is treated like an ordinary |
| 54 | ** file whose content is name of the object to which the symbolic link |
| 55 | ** points. |
| 56 | ** |
| 57 | ** If ExtFILE is used or allow-symlinks is false, then operations on a |
| 58 | ** symbolic link are the same as operations on the object to which the |
| 59 | ** symbolic link points. |
| 60 | ** |
| 61 | ** SymFILE is like RepoFILE except that it always uses the target filename of |
| 62 | ** a symbolic link as the content, instead of the content of the object |
| 63 | ** that the symlink points to. SymFILE acts as if allow-symlinks is always ON. |
| 64 | */ |
| 65 | #define ExtFILE 0 /* Always follow symlinks */ |
| 66 | #define RepoFILE 1 /* Follow symlinks if and only if allow-symlinks is OFF */ |
| 67 | #define SymFILE 2 /* Never follow symlinks */ |
| 68 | |
| @@ -134,13 +133,16 @@ | |
| 134 | int eFType /* Look at symlink itself if RepoFILE and enabled. */ |
| 135 | ){ |
| 136 | int rc; |
| 137 | void *zMbcs = fossil_utf8_to_path(zFilename, 0); |
| 138 | #if !defined(_WIN32) |
| 139 | if( eFType>=RepoFILE && (eFType==SymFILE || db_allow_symlinks()) ){ |
| 140 | rc = lstat(zMbcs, buf); |
| 141 | }else{ |
| 142 | rc = stat(zMbcs, buf); |
| 143 | } |
| 144 | #else |
| 145 | rc = win32_stat(zMbcs, buf, eFType); |
| 146 | #endif |
| @@ -316,17 +318,90 @@ | |
| 316 | |
| 317 | /* |
| 318 | ** Return TRUE if the named file is a symlink and symlinks are allowed. |
| 319 | ** Return false for all other cases. |
| 320 | ** |
| 321 | ** This routines RepoFILE - that zFilename is always a file under management. |
| 322 | ** |
| 323 | ** On Windows, always return False. |
| 324 | */ |
| 325 | int file_islink(const char *zFilename){ |
| 326 | return file_perm(zFilename, RepoFILE)==PERM_LNK; |
| 327 | } |
| 328 | |
| 329 | /* |
| 330 | ** Return 1 if zFilename is a directory. Return 0 if zFilename |
| 331 | ** does not exist. Return 2 if zFilename exists but is something |
| 332 | ** other than a directory. |
| @@ -570,11 +645,14 @@ | |
| 570 | */ |
| 571 | int file_setexe(const char *zFilename, int onoff){ |
| 572 | int rc = 0; |
| 573 | #if !defined(_WIN32) |
| 574 | struct stat buf; |
| 575 | if( fossil_stat(zFilename, &buf, RepoFILE)!=0 || S_ISLNK(buf.st_mode) ){ |
| 576 | return 0; |
| 577 | } |
| 578 | if( onoff ){ |
| 579 | int targetMode = (buf.st_mode & 0444)>>2; |
| 580 | if( (buf.st_mode & 0100)==0 ){ |
| @@ -1236,12 +1314,12 @@ | |
| 1236 | sqlite3_int64 iMtime; |
| 1237 | struct fossilStat testFileStat; |
| 1238 | memset(zBuf, 0, sizeof(zBuf)); |
| 1239 | blob_zero(&x); |
| 1240 | file_canonical_name(zPath, &x, slash); |
| 1241 | fossil_print("[%s] -> [%s]\n", zPath, blob_buffer(&x)); |
| 1242 | blob_reset(&x); |
| 1243 | memset(&testFileStat, 0, sizeof(struct fossilStat)); |
| 1244 | rc = fossil_stat(zPath, &testFileStat, 0); |
| 1245 | fossil_print(" stat_rc = %d\n", rc); |
| 1246 | sqlite3_snprintf(sizeof(zBuf), zBuf, "%lld", testFileStat.st_size); |
| 1247 | fossil_print(" stat_size = %s\n", zBuf); |
| @@ -1285,10 +1363,13 @@ | |
| 1285 | fossil_print(" file_isfile_or_link = %d\n", file_isfile_or_link(zPath)); |
| 1286 | fossil_print(" file_islink = %d\n", file_islink(zPath)); |
| 1287 | fossil_print(" file_isexe(RepoFILE) = %d\n", file_isexe(zPath,RepoFILE)); |
| 1288 | fossil_print(" file_isdir(RepoFILE) = %d\n", file_isdir(zPath,RepoFILE)); |
| 1289 | fossil_print(" file_is_repository = %d\n", file_is_repository(zPath)); |
| 1290 | if( reset ) resetStat(); |
| 1291 | } |
| 1292 | |
| 1293 | /* |
| 1294 | ** COMMAND: test-file-environment |
| @@ -1300,32 +1381,45 @@ | |
| 1300 | ** |
| 1301 | ** Options: |
| 1302 | ** |
| 1303 | ** --allow-symlinks BOOLEAN Temporarily turn allow-symlinks on/off |
| 1304 | ** --open-config Open the configuration database first. |
| 1305 | ** --slash Trailing slashes, if any, are retained. |
| 1306 | ** --reset Reset cached stat() info for each file. |
| 1307 | */ |
| 1308 | void cmd_test_file_environment(void){ |
| 1309 | int i; |
| 1310 | int slashFlag = find_option("slash",0,0)!=0; |
| 1311 | int resetFlag = find_option("reset",0,0)!=0; |
| 1312 | const char *zAllow = find_option("allow-symlinks",0,1); |
| 1313 | if( find_option("open-config", 0, 0)!=0 ){ |
| 1314 | Th_OpenConfig(1); |
| 1315 | } |
| 1316 | db_find_and_open_repository(OPEN_ANY_SCHEMA|OPEN_OK_NOT_FOUND, 0); |
| 1317 | fossil_print("filenames_are_case_sensitive() = %d\n", |
| 1318 | filenames_are_case_sensitive()); |
| 1319 | fossil_print("db_allow_symlinks_by_default() = %d\n", |
| 1320 | db_allow_symlinks_by_default()); |
| 1321 | if( zAllow ){ |
| 1322 | g.allowSymlinks = !is_false(zAllow); |
| 1323 | } |
| 1324 | fossil_print("db_allow_symlinks() = %d\n", db_allow_symlinks()); |
| 1325 | for(i=2; i<g.argc; i++){ |
| 1326 | emitFileStat(g.argv[i], slashFlag, resetFlag); |
| 1327 | } |
| 1328 | } |
| 1329 | |
| 1330 | /* |
| 1331 | ** COMMAND: test-canonical-name |
| @@ -2408,5 +2502,67 @@ | |
| 2408 | */ |
| 2409 | const char * file_extension(const char *zFileName){ |
| 2410 | const char * zExt = zFileName ? strrchr(zFileName, '.') : 0; |
| 2411 | return zExt ? &zExt[1] : 0; |
| 2412 | } |
| 2413 |
| --- src/file.c | |
| +++ src/file.c | |
| @@ -47,22 +47,21 @@ | |
| 47 | ** used for files that are under management by a Fossil repository. ExtFILE |
| 48 | ** should be used for files that are not under management. SymFILE is for |
| 49 | ** a few special cases such as the "fossil test-tarball" command when we never |
| 50 | ** want to follow symlinks. |
| 51 | ** |
| 52 | ** ExtFILE Symbolic links always refer to the object to which the |
| 53 | ** link points. Symlinks are never recognized as symlinks but |
| 54 | ** instead always appear to the the target object. |
| 55 | ** |
| 56 | ** SymFILE Symbolic links always appear to be files whose name is |
| 57 | ** the target pathname of the symbolic link. |
| 58 | ** |
| 59 | ** RepoFILE Like symfile is allow-symlinks is true, or like |
| 60 | ** ExtFile if allow-symlinks is false. In other words, |
| 61 | ** symbolic links are only recognized as something different |
| 62 | ** from files or directories if allow-symlinks is true. |
| 63 | */ |
| 64 | #define ExtFILE 0 /* Always follow symlinks */ |
| 65 | #define RepoFILE 1 /* Follow symlinks if and only if allow-symlinks is OFF */ |
| 66 | #define SymFILE 2 /* Never follow symlinks */ |
| 67 | |
| @@ -134,13 +133,16 @@ | |
| 133 | int eFType /* Look at symlink itself if RepoFILE and enabled. */ |
| 134 | ){ |
| 135 | int rc; |
| 136 | void *zMbcs = fossil_utf8_to_path(zFilename, 0); |
| 137 | #if !defined(_WIN32) |
| 138 | if( (eFType=RepoFILE && db_allow_symlinks()) |
| 139 | || eFType==SymFILE ){ |
| 140 | /* Symlinks look like files whose content is the name of the target */ |
| 141 | rc = lstat(zMbcs, buf); |
| 142 | }else{ |
| 143 | /* Symlinks look like the object to which they point */ |
| 144 | rc = stat(zMbcs, buf); |
| 145 | } |
| 146 | #else |
| 147 | rc = win32_stat(zMbcs, buf, eFType); |
| 148 | #endif |
| @@ -316,17 +318,90 @@ | |
| 318 | |
| 319 | /* |
| 320 | ** Return TRUE if the named file is a symlink and symlinks are allowed. |
| 321 | ** Return false for all other cases. |
| 322 | ** |
| 323 | ** This routines assumes RepoFILE - that zFilename is always a file |
| 324 | ** under management. |
| 325 | ** |
| 326 | ** On Windows, always return False. |
| 327 | */ |
| 328 | int file_islink(const char *zFilename){ |
| 329 | return file_perm(zFilename, RepoFILE)==PERM_LNK; |
| 330 | } |
| 331 | |
| 332 | /* |
| 333 | ** Check every sub-directory of zRoot along the path to zFile. |
| 334 | ** If any sub-directory is really an ordinary file or a symbolic link, |
| 335 | ** return an integer which is the length of the prefix of zFile which |
| 336 | ** is the name of that object. Return 0 if all no non-directory |
| 337 | ** objects are found along the path. |
| 338 | ** |
| 339 | ** Example: Given inputs |
| 340 | ** |
| 341 | ** zRoot = /home/alice/project1 |
| 342 | ** zFile = /home/alice/project1/main/src/js/fileA.js |
| 343 | ** |
| 344 | ** Look for objects in the following order: |
| 345 | ** |
| 346 | ** /home/alice/project/main |
| 347 | ** /home/alice/project/main/src |
| 348 | ** /home/alice/project/main/src/js |
| 349 | ** |
| 350 | ** If any of those objects exist and are something other than a directory |
| 351 | ** then return the length of the name of the first non-directory object |
| 352 | ** seen. |
| 353 | */ |
| 354 | int file_nondir_objects_on_path(const char *zRoot, const char *zFile){ |
| 355 | int i = (int)strlen(zRoot); |
| 356 | char *z = fossil_strdup(zFile); |
| 357 | assert( fossil_strnicmp(zRoot, z, i)==0 ); |
| 358 | if( i && zRoot[i-1]=='/' ) i--; |
| 359 | while( z[i]=='/' ){ |
| 360 | int j, rc; |
| 361 | for(j=i+1; z[j] && z[j]!='/'; j++){} |
| 362 | if( z[j]!='/' ) break; |
| 363 | z[j] = 0; |
| 364 | rc = file_isdir(z, SymFILE); |
| 365 | if( rc!=1 ){ |
| 366 | if( rc==2 ){ |
| 367 | fossil_free(z); |
| 368 | return j; |
| 369 | } |
| 370 | break; |
| 371 | } |
| 372 | z[j] = '/'; |
| 373 | i = j; |
| 374 | } |
| 375 | fossil_free(z); |
| 376 | return 0; |
| 377 | } |
| 378 | |
| 379 | /* |
| 380 | ** The file named zFile is suppose to be an in-tree file. Check to |
| 381 | ** ensure that it will be safe to write to this file by verifying that |
| 382 | ** there are no symlinks or other non-directory objects in between the |
| 383 | ** root of the checkout and zFile. |
| 384 | ** |
| 385 | ** If a problem is found, print a warning message (using fossil_warning()) |
| 386 | ** and return non-zero. If everything is ok, return zero. |
| 387 | */ |
| 388 | int file_unsafe_in_tree_path(const char *zFile){ |
| 389 | int n; |
| 390 | if( !file_is_absolute_path(zFile) ){ |
| 391 | fossil_panic("%s is not an absolute pathname",zFile); |
| 392 | } |
| 393 | if( fossil_strnicmp(g.zLocalRoot, zFile, (int)strlen(g.zLocalRoot)) ){ |
| 394 | fossil_panic("%s is not a prefix of %s", g.zLocalRoot, zFile); |
| 395 | } |
| 396 | n = file_nondir_objects_on_path(g.zLocalRoot, zFile); |
| 397 | if( n ){ |
| 398 | fossil_warning("cannot write to %s because non-directory object %.*s" |
| 399 | " is in the way", zFile, n, zFile); |
| 400 | } |
| 401 | return n; |
| 402 | } |
| 403 | |
| 404 | /* |
| 405 | ** Return 1 if zFilename is a directory. Return 0 if zFilename |
| 406 | ** does not exist. Return 2 if zFilename exists but is something |
| 407 | ** other than a directory. |
| @@ -570,11 +645,14 @@ | |
| 645 | */ |
| 646 | int file_setexe(const char *zFilename, int onoff){ |
| 647 | int rc = 0; |
| 648 | #if !defined(_WIN32) |
| 649 | struct stat buf; |
| 650 | if( fossil_stat(zFilename, &buf, RepoFILE)!=0 |
| 651 | || S_ISLNK(buf.st_mode) |
| 652 | || S_ISDIR(buf.st_mode) |
| 653 | ){ |
| 654 | return 0; |
| 655 | } |
| 656 | if( onoff ){ |
| 657 | int targetMode = (buf.st_mode & 0444)>>2; |
| 658 | if( (buf.st_mode & 0100)==0 ){ |
| @@ -1236,12 +1314,12 @@ | |
| 1314 | sqlite3_int64 iMtime; |
| 1315 | struct fossilStat testFileStat; |
| 1316 | memset(zBuf, 0, sizeof(zBuf)); |
| 1317 | blob_zero(&x); |
| 1318 | file_canonical_name(zPath, &x, slash); |
| 1319 | char *zFull = blob_str(&x); |
| 1320 | fossil_print("[%s] -> [%s]\n", zPath, zFull); |
| 1321 | memset(&testFileStat, 0, sizeof(struct fossilStat)); |
| 1322 | rc = fossil_stat(zPath, &testFileStat, 0); |
| 1323 | fossil_print(" stat_rc = %d\n", rc); |
| 1324 | sqlite3_snprintf(sizeof(zBuf), zBuf, "%lld", testFileStat.st_size); |
| 1325 | fossil_print(" stat_size = %s\n", zBuf); |
| @@ -1285,10 +1363,13 @@ | |
| 1363 | fossil_print(" file_isfile_or_link = %d\n", file_isfile_or_link(zPath)); |
| 1364 | fossil_print(" file_islink = %d\n", file_islink(zPath)); |
| 1365 | fossil_print(" file_isexe(RepoFILE) = %d\n", file_isexe(zPath,RepoFILE)); |
| 1366 | fossil_print(" file_isdir(RepoFILE) = %d\n", file_isdir(zPath,RepoFILE)); |
| 1367 | fossil_print(" file_is_repository = %d\n", file_is_repository(zPath)); |
| 1368 | fossil_print(" file_is_reserved_name = %d\n", |
| 1369 | file_is_reserved_name(zFull,-1)); |
| 1370 | blob_reset(&x); |
| 1371 | if( reset ) resetStat(); |
| 1372 | } |
| 1373 | |
| 1374 | /* |
| 1375 | ** COMMAND: test-file-environment |
| @@ -1300,32 +1381,45 @@ | |
| 1381 | ** |
| 1382 | ** Options: |
| 1383 | ** |
| 1384 | ** --allow-symlinks BOOLEAN Temporarily turn allow-symlinks on/off |
| 1385 | ** --open-config Open the configuration database first. |
| 1386 | ** --reset Reset cached stat() info for each file. |
| 1387 | ** --root ROOT Use ROOT as the root of the checkout |
| 1388 | ** --slash Trailing slashes, if any, are retained. |
| 1389 | */ |
| 1390 | void cmd_test_file_environment(void){ |
| 1391 | int i; |
| 1392 | int slashFlag = find_option("slash",0,0)!=0; |
| 1393 | int resetFlag = find_option("reset",0,0)!=0; |
| 1394 | const char *zRoot = find_option("root",0,1); |
| 1395 | const char *zAllow = find_option("allow-symlinks",0,1); |
| 1396 | if( find_option("open-config", 0, 0)!=0 ){ |
| 1397 | Th_OpenConfig(1); |
| 1398 | } |
| 1399 | db_find_and_open_repository(OPEN_ANY_SCHEMA|OPEN_OK_NOT_FOUND, 0); |
| 1400 | fossil_print("filenames_are_case_sensitive() = %d\n", |
| 1401 | filenames_are_case_sensitive()); |
| 1402 | if( zAllow ){ |
| 1403 | g.allowSymlinks = !is_false(zAllow); |
| 1404 | } |
| 1405 | if( zRoot==0 ) zRoot = g.zLocalRoot; |
| 1406 | fossil_print("db_allow_symlinks() = %d\n", db_allow_symlinks()); |
| 1407 | fossil_print("local-root = [%s]\n", zRoot); |
| 1408 | for(i=2; i<g.argc; i++){ |
| 1409 | char *z; |
| 1410 | emitFileStat(g.argv[i], slashFlag, resetFlag); |
| 1411 | z = file_canonical_name_dup(g.argv[i]); |
| 1412 | fossil_print(" file_canonical_name = %s\n", z); |
| 1413 | fossil_print(" file_nondir_path = "); |
| 1414 | if( fossil_strnicmp(zRoot,z,(int)strlen(zRoot))!=0 ){ |
| 1415 | fossil_print("(--root is not a prefix of this file)\n"); |
| 1416 | }else{ |
| 1417 | int n = file_nondir_objects_on_path(zRoot, z); |
| 1418 | fossil_print("%.*s\n", n, z); |
| 1419 | } |
| 1420 | fossil_free(z); |
| 1421 | } |
| 1422 | } |
| 1423 | |
| 1424 | /* |
| 1425 | ** COMMAND: test-canonical-name |
| @@ -2408,5 +2502,67 @@ | |
| 2502 | */ |
| 2503 | const char * file_extension(const char *zFileName){ |
| 2504 | const char * zExt = zFileName ? strrchr(zFileName, '.') : 0; |
| 2505 | return zExt ? &zExt[1] : 0; |
| 2506 | } |
| 2507 | |
| 2508 | /* |
| 2509 | ** Returns non-zero if the specified file name ends with any reserved name, |
| 2510 | ** e.g.: _FOSSIL_ or .fslckout. Specifically, it returns 1 for exact match |
| 2511 | ** or 2 for a tail match on a longer file name. |
| 2512 | ** |
| 2513 | ** For the sake of efficiency, zFilename must be a canonical name, e.g. an |
| 2514 | ** absolute path using only forward slash ('/') as a directory separator. |
| 2515 | ** |
| 2516 | ** nFilename must be the length of zFilename. When negative, strlen() will |
| 2517 | ** be used to calculate it. |
| 2518 | */ |
| 2519 | int file_is_reserved_name(const char *zFilename, int nFilename){ |
| 2520 | const char *zEnd; /* one-after-the-end of zFilename */ |
| 2521 | int gotSuffix = 0; /* length of suffix (-wal, -shm, -journal) */ |
| 2522 | |
| 2523 | assert( zFilename && "API misuse" ); |
| 2524 | if( nFilename<0 ) nFilename = (int)strlen(zFilename); |
| 2525 | if( nFilename<8 ) return 0; /* strlen("_FOSSIL_") */ |
| 2526 | zEnd = zFilename + nFilename; |
| 2527 | if( nFilename>=12 ){ /* strlen("_FOSSIL_-(shm|wal)") */ |
| 2528 | /* Check for (-wal, -shm, -journal) suffixes, with an eye towards |
| 2529 | ** runtime speed. */ |
| 2530 | if( zEnd[-4]=='-' ){ |
| 2531 | if( fossil_strnicmp("wal", &zEnd[-3], 3) |
| 2532 | && fossil_strnicmp("shm", &zEnd[-3], 3) ){ |
| 2533 | return 0; |
| 2534 | } |
| 2535 | gotSuffix = 4; |
| 2536 | }else if( nFilename>=16 && zEnd[-8]=='-' ){ /*strlen(_FOSSIL_-journal) */ |
| 2537 | if( fossil_strnicmp("journal", &zEnd[-7], 7) ) return 0; |
| 2538 | gotSuffix = 8; |
| 2539 | } |
| 2540 | if( gotSuffix ){ |
| 2541 | assert( 4==gotSuffix || 8==gotSuffix ); |
| 2542 | zEnd -= gotSuffix; |
| 2543 | nFilename -= gotSuffix; |
| 2544 | gotSuffix = 1; |
| 2545 | } |
| 2546 | assert( nFilename>=8 && "strlen(_FOSSIL_)" ); |
| 2547 | assert( gotSuffix==0 || gotSuffix==1 ); |
| 2548 | } |
| 2549 | switch( zEnd[-1] ){ |
| 2550 | case '_':{ |
| 2551 | if( fossil_strnicmp("_FOSSIL_", &zEnd[-8], 8) ) return 0; |
| 2552 | if( 8==nFilename ) return 1; |
| 2553 | return zEnd[-9]=='/' ? 2 : gotSuffix; |
| 2554 | } |
| 2555 | case 'T': |
| 2556 | case 't':{ |
| 2557 | if( nFilename<9 || zEnd[-9]!='.' |
| 2558 | || fossil_strnicmp(".fslckout", &zEnd[-9], 9) ){ |
| 2559 | return 0; |
| 2560 | } |
| 2561 | if( 9==nFilename ) return 1; |
| 2562 | return zEnd[-10]=='/' ? 2 : gotSuffix; |
| 2563 | } |
| 2564 | default:{ |
| 2565 | return 0; |
| 2566 | } |
| 2567 | } |
| 2568 | } |
| 2569 |
+2
| --- src/forum.c | ||
| +++ src/forum.c | ||
| @@ -1114,13 +1114,15 @@ | ||
| 1114 | 1114 | moderation_approve('f', fpid); |
| 1115 | 1115 | if( g.perm.AdminForum |
| 1116 | 1116 | && PB("trust") |
| 1117 | 1117 | && (zUserToTrust = P("trustuser"))!=0 |
| 1118 | 1118 | ){ |
| 1119 | + db_unprotect(PROTECT_USER); | |
| 1119 | 1120 | db_multi_exec("UPDATE user SET cap=cap||'4' " |
| 1120 | 1121 | "WHERE login=%Q AND cap NOT GLOB '*4*'", |
| 1121 | 1122 | zUserToTrust); |
| 1123 | + db_protect_pop(); | |
| 1122 | 1124 | } |
| 1123 | 1125 | cgi_redirectf("%R/forumpost/%S",P("fpid")); |
| 1124 | 1126 | return; |
| 1125 | 1127 | } |
| 1126 | 1128 | if( P("reject") ){ |
| 1127 | 1129 |
| --- src/forum.c | |
| +++ src/forum.c | |
| @@ -1114,13 +1114,15 @@ | |
| 1114 | moderation_approve('f', fpid); |
| 1115 | if( g.perm.AdminForum |
| 1116 | && PB("trust") |
| 1117 | && (zUserToTrust = P("trustuser"))!=0 |
| 1118 | ){ |
| 1119 | db_multi_exec("UPDATE user SET cap=cap||'4' " |
| 1120 | "WHERE login=%Q AND cap NOT GLOB '*4*'", |
| 1121 | zUserToTrust); |
| 1122 | } |
| 1123 | cgi_redirectf("%R/forumpost/%S",P("fpid")); |
| 1124 | return; |
| 1125 | } |
| 1126 | if( P("reject") ){ |
| 1127 |
| --- src/forum.c | |
| +++ src/forum.c | |
| @@ -1114,13 +1114,15 @@ | |
| 1114 | moderation_approve('f', fpid); |
| 1115 | if( g.perm.AdminForum |
| 1116 | && PB("trust") |
| 1117 | && (zUserToTrust = P("trustuser"))!=0 |
| 1118 | ){ |
| 1119 | db_unprotect(PROTECT_USER); |
| 1120 | db_multi_exec("UPDATE user SET cap=cap||'4' " |
| 1121 | "WHERE login=%Q AND cap NOT GLOB '*4*'", |
| 1122 | zUserToTrust); |
| 1123 | db_protect_pop(); |
| 1124 | } |
| 1125 | cgi_redirectf("%R/forumpost/%S",P("fpid")); |
| 1126 | return; |
| 1127 | } |
| 1128 | if( P("reject") ){ |
| 1129 |
+8
| --- src/hook.c | ||
| +++ src/hook.c | ||
| @@ -123,15 +123,17 @@ | ||
| 123 | 123 | ** If N==0, then there is no expectation of new artifacts arriving |
| 124 | 124 | ** soon and so post-receive hooks can be run without delay. |
| 125 | 125 | */ |
| 126 | 126 | void hook_expecting_more_artifacts(int N){ |
| 127 | 127 | if( N>0 ){ |
| 128 | + db_unprotect(PROTECT_CONFIG); | |
| 128 | 129 | db_multi_exec( |
| 129 | 130 | "REPLACE INTO config(name,value,mtime)" |
| 130 | 131 | "VALUES('hook-embargo',now()+%d,now())", |
| 131 | 132 | N |
| 132 | 133 | ); |
| 134 | + db_protect_pop(); | |
| 133 | 135 | }else{ |
| 134 | 136 | db_unset("hook-embargo",0); |
| 135 | 137 | } |
| 136 | 138 | } |
| 137 | 139 | |
| @@ -243,10 +245,11 @@ | ||
| 243 | 245 | fossil_fatal("the --command and --type options are required"); |
| 244 | 246 | } |
| 245 | 247 | validate_type(zType); |
| 246 | 248 | nSeq = zSeq ? atoi(zSeq) : 10; |
| 247 | 249 | db_begin_write(); |
| 250 | + db_unprotect(PROTECT_CONFIG); | |
| 248 | 251 | db_multi_exec( |
| 249 | 252 | "INSERT OR IGNORE INTO config(name,value) VALUES('hooks','[]');\n" |
| 250 | 253 | "UPDATE config" |
| 251 | 254 | " SET value=json_insert(" |
| 252 | 255 | " CASE WHEN json_valid(value) THEN value ELSE '[]' END,'$[#]'," |
| @@ -253,10 +256,11 @@ | ||
| 253 | 256 | " json_object('cmd',%Q,'type',%Q,'seq',%d))," |
| 254 | 257 | " mtime=now()" |
| 255 | 258 | " WHERE name='hooks';", |
| 256 | 259 | zCmd, zType, nSeq |
| 257 | 260 | ); |
| 261 | + db_protect_pop(); | |
| 258 | 262 | db_commit_transaction(); |
| 259 | 263 | }else |
| 260 | 264 | if( strncmp(zCmd, "edit", nCmd)==0 ){ |
| 261 | 265 | const char *zCmd = find_option("command",0,1); |
| 262 | 266 | const char *zType = find_option("type",0,1); |
| @@ -290,20 +294,23 @@ | ||
| 290 | 294 | } |
| 291 | 295 | if( zSeq ){ |
| 292 | 296 | blob_append_sql(&sql, ",'$[%d].seq',%d", id, nSeq); |
| 293 | 297 | } |
| 294 | 298 | blob_append_sql(&sql,") WHERE name='hooks';"); |
| 299 | + db_unprotect(PROTECT_CONFIG); | |
| 295 | 300 | db_multi_exec("%s", blob_sql_text(&sql)); |
| 301 | + db_protect_pop(); | |
| 296 | 302 | blob_reset(&sql); |
| 297 | 303 | } |
| 298 | 304 | db_commit_transaction(); |
| 299 | 305 | }else |
| 300 | 306 | if( strncmp(zCmd, "delete", nCmd)==0 ){ |
| 301 | 307 | int i; |
| 302 | 308 | verify_all_options(); |
| 303 | 309 | if( g.argc<4 ) usage("delete ID ..."); |
| 304 | 310 | db_begin_write(); |
| 311 | + db_unprotect(PROTECT_CONFIG); | |
| 305 | 312 | db_multi_exec( |
| 306 | 313 | "INSERT OR IGNORE INTO config(name,value) VALUES('hooks','[]');\n" |
| 307 | 314 | ); |
| 308 | 315 | for(i=3; i<g.argc; i++){ |
| 309 | 316 | const char *zId = g.argv[i]; |
| @@ -321,10 +328,11 @@ | ||
| 321 | 328 | " mtime=now()" |
| 322 | 329 | " WHERE name='hooks';", |
| 323 | 330 | atoi(zId) |
| 324 | 331 | ); |
| 325 | 332 | } |
| 333 | + db_protect_pop(); | |
| 326 | 334 | db_commit_transaction(); |
| 327 | 335 | }else |
| 328 | 336 | if( strncmp(zCmd, "list", nCmd)==0 ){ |
| 329 | 337 | Stmt q; |
| 330 | 338 | int n = 0; |
| 331 | 339 |
| --- src/hook.c | |
| +++ src/hook.c | |
| @@ -123,15 +123,17 @@ | |
| 123 | ** If N==0, then there is no expectation of new artifacts arriving |
| 124 | ** soon and so post-receive hooks can be run without delay. |
| 125 | */ |
| 126 | void hook_expecting_more_artifacts(int N){ |
| 127 | if( N>0 ){ |
| 128 | db_multi_exec( |
| 129 | "REPLACE INTO config(name,value,mtime)" |
| 130 | "VALUES('hook-embargo',now()+%d,now())", |
| 131 | N |
| 132 | ); |
| 133 | }else{ |
| 134 | db_unset("hook-embargo",0); |
| 135 | } |
| 136 | } |
| 137 | |
| @@ -243,10 +245,11 @@ | |
| 243 | fossil_fatal("the --command and --type options are required"); |
| 244 | } |
| 245 | validate_type(zType); |
| 246 | nSeq = zSeq ? atoi(zSeq) : 10; |
| 247 | db_begin_write(); |
| 248 | db_multi_exec( |
| 249 | "INSERT OR IGNORE INTO config(name,value) VALUES('hooks','[]');\n" |
| 250 | "UPDATE config" |
| 251 | " SET value=json_insert(" |
| 252 | " CASE WHEN json_valid(value) THEN value ELSE '[]' END,'$[#]'," |
| @@ -253,10 +256,11 @@ | |
| 253 | " json_object('cmd',%Q,'type',%Q,'seq',%d))," |
| 254 | " mtime=now()" |
| 255 | " WHERE name='hooks';", |
| 256 | zCmd, zType, nSeq |
| 257 | ); |
| 258 | db_commit_transaction(); |
| 259 | }else |
| 260 | if( strncmp(zCmd, "edit", nCmd)==0 ){ |
| 261 | const char *zCmd = find_option("command",0,1); |
| 262 | const char *zType = find_option("type",0,1); |
| @@ -290,20 +294,23 @@ | |
| 290 | } |
| 291 | if( zSeq ){ |
| 292 | blob_append_sql(&sql, ",'$[%d].seq',%d", id, nSeq); |
| 293 | } |
| 294 | blob_append_sql(&sql,") WHERE name='hooks';"); |
| 295 | db_multi_exec("%s", blob_sql_text(&sql)); |
| 296 | blob_reset(&sql); |
| 297 | } |
| 298 | db_commit_transaction(); |
| 299 | }else |
| 300 | if( strncmp(zCmd, "delete", nCmd)==0 ){ |
| 301 | int i; |
| 302 | verify_all_options(); |
| 303 | if( g.argc<4 ) usage("delete ID ..."); |
| 304 | db_begin_write(); |
| 305 | db_multi_exec( |
| 306 | "INSERT OR IGNORE INTO config(name,value) VALUES('hooks','[]');\n" |
| 307 | ); |
| 308 | for(i=3; i<g.argc; i++){ |
| 309 | const char *zId = g.argv[i]; |
| @@ -321,10 +328,11 @@ | |
| 321 | " mtime=now()" |
| 322 | " WHERE name='hooks';", |
| 323 | atoi(zId) |
| 324 | ); |
| 325 | } |
| 326 | db_commit_transaction(); |
| 327 | }else |
| 328 | if( strncmp(zCmd, "list", nCmd)==0 ){ |
| 329 | Stmt q; |
| 330 | int n = 0; |
| 331 |
| --- src/hook.c | |
| +++ src/hook.c | |
| @@ -123,15 +123,17 @@ | |
| 123 | ** If N==0, then there is no expectation of new artifacts arriving |
| 124 | ** soon and so post-receive hooks can be run without delay. |
| 125 | */ |
| 126 | void hook_expecting_more_artifacts(int N){ |
| 127 | if( N>0 ){ |
| 128 | db_unprotect(PROTECT_CONFIG); |
| 129 | db_multi_exec( |
| 130 | "REPLACE INTO config(name,value,mtime)" |
| 131 | "VALUES('hook-embargo',now()+%d,now())", |
| 132 | N |
| 133 | ); |
| 134 | db_protect_pop(); |
| 135 | }else{ |
| 136 | db_unset("hook-embargo",0); |
| 137 | } |
| 138 | } |
| 139 | |
| @@ -243,10 +245,11 @@ | |
| 245 | fossil_fatal("the --command and --type options are required"); |
| 246 | } |
| 247 | validate_type(zType); |
| 248 | nSeq = zSeq ? atoi(zSeq) : 10; |
| 249 | db_begin_write(); |
| 250 | db_unprotect(PROTECT_CONFIG); |
| 251 | db_multi_exec( |
| 252 | "INSERT OR IGNORE INTO config(name,value) VALUES('hooks','[]');\n" |
| 253 | "UPDATE config" |
| 254 | " SET value=json_insert(" |
| 255 | " CASE WHEN json_valid(value) THEN value ELSE '[]' END,'$[#]'," |
| @@ -253,10 +256,11 @@ | |
| 256 | " json_object('cmd',%Q,'type',%Q,'seq',%d))," |
| 257 | " mtime=now()" |
| 258 | " WHERE name='hooks';", |
| 259 | zCmd, zType, nSeq |
| 260 | ); |
| 261 | db_protect_pop(); |
| 262 | db_commit_transaction(); |
| 263 | }else |
| 264 | if( strncmp(zCmd, "edit", nCmd)==0 ){ |
| 265 | const char *zCmd = find_option("command",0,1); |
| 266 | const char *zType = find_option("type",0,1); |
| @@ -290,20 +294,23 @@ | |
| 294 | } |
| 295 | if( zSeq ){ |
| 296 | blob_append_sql(&sql, ",'$[%d].seq',%d", id, nSeq); |
| 297 | } |
| 298 | blob_append_sql(&sql,") WHERE name='hooks';"); |
| 299 | db_unprotect(PROTECT_CONFIG); |
| 300 | db_multi_exec("%s", blob_sql_text(&sql)); |
| 301 | db_protect_pop(); |
| 302 | blob_reset(&sql); |
| 303 | } |
| 304 | db_commit_transaction(); |
| 305 | }else |
| 306 | if( strncmp(zCmd, "delete", nCmd)==0 ){ |
| 307 | int i; |
| 308 | verify_all_options(); |
| 309 | if( g.argc<4 ) usage("delete ID ..."); |
| 310 | db_begin_write(); |
| 311 | db_unprotect(PROTECT_CONFIG); |
| 312 | db_multi_exec( |
| 313 | "INSERT OR IGNORE INTO config(name,value) VALUES('hooks','[]');\n" |
| 314 | ); |
| 315 | for(i=3; i<g.argc; i++){ |
| 316 | const char *zId = g.argv[i]; |
| @@ -321,10 +328,11 @@ | |
| 328 | " mtime=now()" |
| 329 | " WHERE name='hooks';", |
| 330 | atoi(zId) |
| 331 | ); |
| 332 | } |
| 333 | db_protect_pop(); |
| 334 | db_commit_transaction(); |
| 335 | }else |
| 336 | if( strncmp(zCmd, "list", nCmd)==0 ){ |
| 337 | Stmt q; |
| 338 | int n = 0; |
| 339 |
+10
-1
| --- src/http.c | ||
| +++ src/http.c | ||
| @@ -375,17 +375,26 @@ | ||
| 375 | 375 | j -= 4; |
| 376 | 376 | zLine[j] = 0; |
| 377 | 377 | } |
| 378 | 378 | if( (mHttpFlags & HTTP_QUIET)==0 ){ |
| 379 | 379 | fossil_print("redirect with status %d to %s\n", rc, &zLine[i]); |
| 380 | + } | |
| 381 | + if( g.url.isFile || g.url.isSsh ){ | |
| 382 | + fossil_warning("cannot redirect from %s to %s", g.url.canonical, | |
| 383 | + &zLine[i]); | |
| 384 | + goto write_err; | |
| 380 | 385 | } |
| 381 | 386 | wasHttps = g.url.isHttps; |
| 382 | 387 | url_parse(&zLine[i], 0); |
| 383 | 388 | if( wasHttps && !g.url.isHttps ){ |
| 384 | 389 | fossil_warning("cannot redirect from HTTPS to HTTP"); |
| 385 | 390 | goto write_err; |
| 386 | - } | |
| 391 | + } | |
| 392 | + if( g.url.isSsh || g.url.isFile ){ | |
| 393 | + fossil_warning("cannot redirect to %s", &zLine[i]); | |
| 394 | + goto write_err; | |
| 395 | + } | |
| 387 | 396 | transport_close(&g.url); |
| 388 | 397 | transport_global_shutdown(&g.url); |
| 389 | 398 | fSeenHttpAuth = 0; |
| 390 | 399 | if( g.zHttpAuth ) free(g.zHttpAuth); |
| 391 | 400 | g.zHttpAuth = get_httpauth(); |
| 392 | 401 |
| --- src/http.c | |
| +++ src/http.c | |
| @@ -375,17 +375,26 @@ | |
| 375 | j -= 4; |
| 376 | zLine[j] = 0; |
| 377 | } |
| 378 | if( (mHttpFlags & HTTP_QUIET)==0 ){ |
| 379 | fossil_print("redirect with status %d to %s\n", rc, &zLine[i]); |
| 380 | } |
| 381 | wasHttps = g.url.isHttps; |
| 382 | url_parse(&zLine[i], 0); |
| 383 | if( wasHttps && !g.url.isHttps ){ |
| 384 | fossil_warning("cannot redirect from HTTPS to HTTP"); |
| 385 | goto write_err; |
| 386 | } |
| 387 | transport_close(&g.url); |
| 388 | transport_global_shutdown(&g.url); |
| 389 | fSeenHttpAuth = 0; |
| 390 | if( g.zHttpAuth ) free(g.zHttpAuth); |
| 391 | g.zHttpAuth = get_httpauth(); |
| 392 |
| --- src/http.c | |
| +++ src/http.c | |
| @@ -375,17 +375,26 @@ | |
| 375 | j -= 4; |
| 376 | zLine[j] = 0; |
| 377 | } |
| 378 | if( (mHttpFlags & HTTP_QUIET)==0 ){ |
| 379 | fossil_print("redirect with status %d to %s\n", rc, &zLine[i]); |
| 380 | } |
| 381 | if( g.url.isFile || g.url.isSsh ){ |
| 382 | fossil_warning("cannot redirect from %s to %s", g.url.canonical, |
| 383 | &zLine[i]); |
| 384 | goto write_err; |
| 385 | } |
| 386 | wasHttps = g.url.isHttps; |
| 387 | url_parse(&zLine[i], 0); |
| 388 | if( wasHttps && !g.url.isHttps ){ |
| 389 | fossil_warning("cannot redirect from HTTPS to HTTP"); |
| 390 | goto write_err; |
| 391 | } |
| 392 | if( g.url.isSsh || g.url.isFile ){ |
| 393 | fossil_warning("cannot redirect to %s", &zLine[i]); |
| 394 | goto write_err; |
| 395 | } |
| 396 | transport_close(&g.url); |
| 397 | transport_global_shutdown(&g.url); |
| 398 | fSeenHttpAuth = 0; |
| 399 | if( g.zHttpAuth ) free(g.zHttpAuth); |
| 400 | g.zHttpAuth = get_httpauth(); |
| 401 |
+2
| --- src/http_ssl.c | ||
| +++ src/http_ssl.c | ||
| @@ -576,16 +576,18 @@ | ||
| 576 | 576 | Blob sql; |
| 577 | 577 | char *zSep = "("; |
| 578 | 578 | db_begin_transaction(); |
| 579 | 579 | blob_init(&sql, 0, 0); |
| 580 | 580 | if( g.argc==4 && find_option("all",0,0)!=0 ){ |
| 581 | + db_unprotect(PROTECT_CONFIG); | |
| 581 | 582 | blob_append_sql(&sql, |
| 582 | 583 | "DELETE FROM global_config WHERE name GLOB 'cert:*';\n" |
| 583 | 584 | "DELETE FROM global_config WHERE name GLOB 'trusted:*';\n" |
| 584 | 585 | "DELETE FROM config WHERE name GLOB 'cert:*';\n" |
| 585 | 586 | "DELETE FROM config WHERE name GLOB 'trusted:*';\n" |
| 586 | 587 | ); |
| 588 | + db_protect_pop(); | |
| 587 | 589 | }else{ |
| 588 | 590 | if( g.argc<4 ){ |
| 589 | 591 | usage("remove-exception DOMAIN-NAME ..."); |
| 590 | 592 | } |
| 591 | 593 | blob_append_sql(&sql,"DELETE FROM global_config WHERE name IN "); |
| 592 | 594 |
| --- src/http_ssl.c | |
| +++ src/http_ssl.c | |
| @@ -576,16 +576,18 @@ | |
| 576 | Blob sql; |
| 577 | char *zSep = "("; |
| 578 | db_begin_transaction(); |
| 579 | blob_init(&sql, 0, 0); |
| 580 | if( g.argc==4 && find_option("all",0,0)!=0 ){ |
| 581 | blob_append_sql(&sql, |
| 582 | "DELETE FROM global_config WHERE name GLOB 'cert:*';\n" |
| 583 | "DELETE FROM global_config WHERE name GLOB 'trusted:*';\n" |
| 584 | "DELETE FROM config WHERE name GLOB 'cert:*';\n" |
| 585 | "DELETE FROM config WHERE name GLOB 'trusted:*';\n" |
| 586 | ); |
| 587 | }else{ |
| 588 | if( g.argc<4 ){ |
| 589 | usage("remove-exception DOMAIN-NAME ..."); |
| 590 | } |
| 591 | blob_append_sql(&sql,"DELETE FROM global_config WHERE name IN "); |
| 592 |
| --- src/http_ssl.c | |
| +++ src/http_ssl.c | |
| @@ -576,16 +576,18 @@ | |
| 576 | Blob sql; |
| 577 | char *zSep = "("; |
| 578 | db_begin_transaction(); |
| 579 | blob_init(&sql, 0, 0); |
| 580 | if( g.argc==4 && find_option("all",0,0)!=0 ){ |
| 581 | db_unprotect(PROTECT_CONFIG); |
| 582 | blob_append_sql(&sql, |
| 583 | "DELETE FROM global_config WHERE name GLOB 'cert:*';\n" |
| 584 | "DELETE FROM global_config WHERE name GLOB 'trusted:*';\n" |
| 585 | "DELETE FROM config WHERE name GLOB 'cert:*';\n" |
| 586 | "DELETE FROM config WHERE name GLOB 'trusted:*';\n" |
| 587 | ); |
| 588 | db_protect_pop(); |
| 589 | }else{ |
| 590 | if( g.argc<4 ){ |
| 591 | usage("remove-exception DOMAIN-NAME ..."); |
| 592 | } |
| 593 | blob_append_sql(&sql,"DELETE FROM global_config WHERE name IN "); |
| 594 |
+1
| --- src/import.c | ||
| +++ src/import.c | ||
| @@ -1759,10 +1759,11 @@ | ||
| 1759 | 1759 | if( forceFlag ) file_delete(g.argv[2]); |
| 1760 | 1760 | db_create_repository(g.argv[2]); |
| 1761 | 1761 | } |
| 1762 | 1762 | db_open_repository(g.argv[2]); |
| 1763 | 1763 | db_open_config(0, 0); |
| 1764 | + db_unprotect(PROTECT_ALL); | |
| 1764 | 1765 | |
| 1765 | 1766 | db_begin_transaction(); |
| 1766 | 1767 | if( !incrFlag ){ |
| 1767 | 1768 | db_initial_setup(0, 0, zDefaultUser); |
| 1768 | 1769 | db_set("main-branch", gimport.zTrunkName, 0); |
| 1769 | 1770 |
| --- src/import.c | |
| +++ src/import.c | |
| @@ -1759,10 +1759,11 @@ | |
| 1759 | if( forceFlag ) file_delete(g.argv[2]); |
| 1760 | db_create_repository(g.argv[2]); |
| 1761 | } |
| 1762 | db_open_repository(g.argv[2]); |
| 1763 | db_open_config(0, 0); |
| 1764 | |
| 1765 | db_begin_transaction(); |
| 1766 | if( !incrFlag ){ |
| 1767 | db_initial_setup(0, 0, zDefaultUser); |
| 1768 | db_set("main-branch", gimport.zTrunkName, 0); |
| 1769 |
| --- src/import.c | |
| +++ src/import.c | |
| @@ -1759,10 +1759,11 @@ | |
| 1759 | if( forceFlag ) file_delete(g.argv[2]); |
| 1760 | db_create_repository(g.argv[2]); |
| 1761 | } |
| 1762 | db_open_repository(g.argv[2]); |
| 1763 | db_open_config(0, 0); |
| 1764 | db_unprotect(PROTECT_ALL); |
| 1765 | |
| 1766 | db_begin_transaction(); |
| 1767 | if( !incrFlag ){ |
| 1768 | db_initial_setup(0, 0, zDefaultUser); |
| 1769 | db_set("main-branch", gimport.zTrunkName, 0); |
| 1770 |
+10
-2
| --- src/interwiki.c | ||
| +++ src/interwiki.c | ||
| @@ -192,40 +192,44 @@ | ||
| 192 | 192 | const char *zHash = find_option("hash",0,1); |
| 193 | 193 | const char *zWiki = find_option("wiki",0,1); |
| 194 | 194 | verify_all_options(); |
| 195 | 195 | if( g.argc!=4 ) usage("add TAG ?OPTIONS?"); |
| 196 | 196 | zName = g.argv[3]; |
| 197 | - if( zBase){ | |
| 197 | + if( zBase==0 ){ | |
| 198 | 198 | fossil_fatal("the --base option is required"); |
| 199 | 199 | } |
| 200 | 200 | if( !interwiki_valid_name(zName) ){ |
| 201 | 201 | fossil_fatal("not a valid interwiki tag: \"%s\"", zName); |
| 202 | 202 | } |
| 203 | 203 | db_begin_write(); |
| 204 | + db_unprotect(PROTECT_CONFIG); | |
| 204 | 205 | db_multi_exec( |
| 205 | 206 | "REPLACE INTO config(name,value,mtime)" |
| 206 | 207 | " VALUES('interwiki:'||lower(%Q)," |
| 207 | 208 | " json_object('base',%Q,'hash',%Q,'wiki',%Q)," |
| 208 | 209 | " now());", |
| 209 | 210 | zName, zBase, zHash, zWiki |
| 210 | 211 | ); |
| 211 | 212 | setup_incr_cfgcnt(); |
| 213 | + db_protect_pop(); | |
| 212 | 214 | db_commit_transaction(); |
| 213 | 215 | }else |
| 214 | 216 | if( strncmp(zCmd, "delete", nCmd)==0 ){ |
| 215 | 217 | int i; |
| 216 | 218 | verify_all_options(); |
| 217 | 219 | if( g.argc<4 ) usage("delete ID ..."); |
| 218 | 220 | db_begin_write(); |
| 221 | + db_unprotect(PROTECT_CONFIG); | |
| 219 | 222 | for(i=3; i<g.argc; i++){ |
| 220 | 223 | const char *zName = g.argv[i]; |
| 221 | 224 | db_multi_exec( |
| 222 | 225 | "DELETE FROM config WHERE name='interwiki:%q'", |
| 223 | 226 | zName |
| 224 | 227 | ); |
| 225 | 228 | } |
| 226 | 229 | setup_incr_cfgcnt(); |
| 230 | + db_protect_pop(); | |
| 227 | 231 | db_commit_transaction(); |
| 228 | 232 | }else |
| 229 | 233 | if( strncmp(zCmd, "list", nCmd)==0 ){ |
| 230 | 234 | Stmt q; |
| 231 | 235 | int n = 0; |
| @@ -316,22 +320,26 @@ | ||
| 316 | 320 | zTag = PT("tag"); |
| 317 | 321 | zBase = PT("base"); |
| 318 | 322 | zHash = PT("hash"); |
| 319 | 323 | zWiki = PT("wiki"); |
| 320 | 324 | if( zTag==0 || zTag[0]==0 || !interwiki_valid_name(zTag) ){ |
| 321 | - zErr = mprintf("Not a valid interwiki tag name: \"%s\"", zTag ? zTag : ""); | |
| 325 | + zErr = mprintf("Not a valid interwiki tag name: \"%s\"", zTag?zTag : ""); | |
| 322 | 326 | }else if( zBase==0 || zBase[0]==0 ){ |
| 327 | + db_unprotect(PROTECT_CONFIG); | |
| 323 | 328 | db_multi_exec("DELETE FROM config WHERE name='interwiki:%q';", zTag); |
| 329 | + db_protect_pop(); | |
| 324 | 330 | }else{ |
| 325 | 331 | if( zHash && zHash[0]==0 ) zHash = 0; |
| 326 | 332 | if( zWiki && zWiki[0]==0 ) zWiki = 0; |
| 333 | + db_unprotect(PROTECT_CONFIG); | |
| 327 | 334 | db_multi_exec( |
| 328 | 335 | "REPLACE INTO config(name,value,mtime)" |
| 329 | 336 | "VALUES('interwiki:'||lower(%Q)," |
| 330 | 337 | " json_object('base',%Q,'hash',%Q,'wiki',%Q)," |
| 331 | 338 | " now());", |
| 332 | 339 | zTag, zBase, zHash, zWiki); |
| 340 | + db_protect_pop(); | |
| 333 | 341 | } |
| 334 | 342 | } |
| 335 | 343 | |
| 336 | 344 | style_header("Interwiki Map Configuration"); |
| 337 | 345 | @ <p>Interwiki links are hyperlink targets of the form |
| 338 | 346 |
| --- src/interwiki.c | |
| +++ src/interwiki.c | |
| @@ -192,40 +192,44 @@ | |
| 192 | const char *zHash = find_option("hash",0,1); |
| 193 | const char *zWiki = find_option("wiki",0,1); |
| 194 | verify_all_options(); |
| 195 | if( g.argc!=4 ) usage("add TAG ?OPTIONS?"); |
| 196 | zName = g.argv[3]; |
| 197 | if( zBase){ |
| 198 | fossil_fatal("the --base option is required"); |
| 199 | } |
| 200 | if( !interwiki_valid_name(zName) ){ |
| 201 | fossil_fatal("not a valid interwiki tag: \"%s\"", zName); |
| 202 | } |
| 203 | db_begin_write(); |
| 204 | db_multi_exec( |
| 205 | "REPLACE INTO config(name,value,mtime)" |
| 206 | " VALUES('interwiki:'||lower(%Q)," |
| 207 | " json_object('base',%Q,'hash',%Q,'wiki',%Q)," |
| 208 | " now());", |
| 209 | zName, zBase, zHash, zWiki |
| 210 | ); |
| 211 | setup_incr_cfgcnt(); |
| 212 | db_commit_transaction(); |
| 213 | }else |
| 214 | if( strncmp(zCmd, "delete", nCmd)==0 ){ |
| 215 | int i; |
| 216 | verify_all_options(); |
| 217 | if( g.argc<4 ) usage("delete ID ..."); |
| 218 | db_begin_write(); |
| 219 | for(i=3; i<g.argc; i++){ |
| 220 | const char *zName = g.argv[i]; |
| 221 | db_multi_exec( |
| 222 | "DELETE FROM config WHERE name='interwiki:%q'", |
| 223 | zName |
| 224 | ); |
| 225 | } |
| 226 | setup_incr_cfgcnt(); |
| 227 | db_commit_transaction(); |
| 228 | }else |
| 229 | if( strncmp(zCmd, "list", nCmd)==0 ){ |
| 230 | Stmt q; |
| 231 | int n = 0; |
| @@ -316,22 +320,26 @@ | |
| 316 | zTag = PT("tag"); |
| 317 | zBase = PT("base"); |
| 318 | zHash = PT("hash"); |
| 319 | zWiki = PT("wiki"); |
| 320 | if( zTag==0 || zTag[0]==0 || !interwiki_valid_name(zTag) ){ |
| 321 | zErr = mprintf("Not a valid interwiki tag name: \"%s\"", zTag ? zTag : ""); |
| 322 | }else if( zBase==0 || zBase[0]==0 ){ |
| 323 | db_multi_exec("DELETE FROM config WHERE name='interwiki:%q';", zTag); |
| 324 | }else{ |
| 325 | if( zHash && zHash[0]==0 ) zHash = 0; |
| 326 | if( zWiki && zWiki[0]==0 ) zWiki = 0; |
| 327 | db_multi_exec( |
| 328 | "REPLACE INTO config(name,value,mtime)" |
| 329 | "VALUES('interwiki:'||lower(%Q)," |
| 330 | " json_object('base',%Q,'hash',%Q,'wiki',%Q)," |
| 331 | " now());", |
| 332 | zTag, zBase, zHash, zWiki); |
| 333 | } |
| 334 | } |
| 335 | |
| 336 | style_header("Interwiki Map Configuration"); |
| 337 | @ <p>Interwiki links are hyperlink targets of the form |
| 338 |
| --- src/interwiki.c | |
| +++ src/interwiki.c | |
| @@ -192,40 +192,44 @@ | |
| 192 | const char *zHash = find_option("hash",0,1); |
| 193 | const char *zWiki = find_option("wiki",0,1); |
| 194 | verify_all_options(); |
| 195 | if( g.argc!=4 ) usage("add TAG ?OPTIONS?"); |
| 196 | zName = g.argv[3]; |
| 197 | if( zBase==0 ){ |
| 198 | fossil_fatal("the --base option is required"); |
| 199 | } |
| 200 | if( !interwiki_valid_name(zName) ){ |
| 201 | fossil_fatal("not a valid interwiki tag: \"%s\"", zName); |
| 202 | } |
| 203 | db_begin_write(); |
| 204 | db_unprotect(PROTECT_CONFIG); |
| 205 | db_multi_exec( |
| 206 | "REPLACE INTO config(name,value,mtime)" |
| 207 | " VALUES('interwiki:'||lower(%Q)," |
| 208 | " json_object('base',%Q,'hash',%Q,'wiki',%Q)," |
| 209 | " now());", |
| 210 | zName, zBase, zHash, zWiki |
| 211 | ); |
| 212 | setup_incr_cfgcnt(); |
| 213 | db_protect_pop(); |
| 214 | db_commit_transaction(); |
| 215 | }else |
| 216 | if( strncmp(zCmd, "delete", nCmd)==0 ){ |
| 217 | int i; |
| 218 | verify_all_options(); |
| 219 | if( g.argc<4 ) usage("delete ID ..."); |
| 220 | db_begin_write(); |
| 221 | db_unprotect(PROTECT_CONFIG); |
| 222 | for(i=3; i<g.argc; i++){ |
| 223 | const char *zName = g.argv[i]; |
| 224 | db_multi_exec( |
| 225 | "DELETE FROM config WHERE name='interwiki:%q'", |
| 226 | zName |
| 227 | ); |
| 228 | } |
| 229 | setup_incr_cfgcnt(); |
| 230 | db_protect_pop(); |
| 231 | db_commit_transaction(); |
| 232 | }else |
| 233 | if( strncmp(zCmd, "list", nCmd)==0 ){ |
| 234 | Stmt q; |
| 235 | int n = 0; |
| @@ -316,22 +320,26 @@ | |
| 320 | zTag = PT("tag"); |
| 321 | zBase = PT("base"); |
| 322 | zHash = PT("hash"); |
| 323 | zWiki = PT("wiki"); |
| 324 | if( zTag==0 || zTag[0]==0 || !interwiki_valid_name(zTag) ){ |
| 325 | zErr = mprintf("Not a valid interwiki tag name: \"%s\"", zTag?zTag : ""); |
| 326 | }else if( zBase==0 || zBase[0]==0 ){ |
| 327 | db_unprotect(PROTECT_CONFIG); |
| 328 | db_multi_exec("DELETE FROM config WHERE name='interwiki:%q';", zTag); |
| 329 | db_protect_pop(); |
| 330 | }else{ |
| 331 | if( zHash && zHash[0]==0 ) zHash = 0; |
| 332 | if( zWiki && zWiki[0]==0 ) zWiki = 0; |
| 333 | db_unprotect(PROTECT_CONFIG); |
| 334 | db_multi_exec( |
| 335 | "REPLACE INTO config(name,value,mtime)" |
| 336 | "VALUES('interwiki:'||lower(%Q)," |
| 337 | " json_object('base',%Q,'hash',%Q,'wiki',%Q)," |
| 338 | " now());", |
| 339 | zTag, zBase, zHash, zWiki); |
| 340 | db_protect_pop(); |
| 341 | } |
| 342 | } |
| 343 | |
| 344 | style_header("Interwiki Map Configuration"); |
| 345 | @ <p>Interwiki links are hyperlink targets of the form |
| 346 |
+10
-2
| --- src/interwiki.c | ||
| +++ src/interwiki.c | ||
| @@ -192,40 +192,44 @@ | ||
| 192 | 192 | const char *zHash = find_option("hash",0,1); |
| 193 | 193 | const char *zWiki = find_option("wiki",0,1); |
| 194 | 194 | verify_all_options(); |
| 195 | 195 | if( g.argc!=4 ) usage("add TAG ?OPTIONS?"); |
| 196 | 196 | zName = g.argv[3]; |
| 197 | - if( zBase){ | |
| 197 | + if( zBase==0 ){ | |
| 198 | 198 | fossil_fatal("the --base option is required"); |
| 199 | 199 | } |
| 200 | 200 | if( !interwiki_valid_name(zName) ){ |
| 201 | 201 | fossil_fatal("not a valid interwiki tag: \"%s\"", zName); |
| 202 | 202 | } |
| 203 | 203 | db_begin_write(); |
| 204 | + db_unprotect(PROTECT_CONFIG); | |
| 204 | 205 | db_multi_exec( |
| 205 | 206 | "REPLACE INTO config(name,value,mtime)" |
| 206 | 207 | " VALUES('interwiki:'||lower(%Q)," |
| 207 | 208 | " json_object('base',%Q,'hash',%Q,'wiki',%Q)," |
| 208 | 209 | " now());", |
| 209 | 210 | zName, zBase, zHash, zWiki |
| 210 | 211 | ); |
| 211 | 212 | setup_incr_cfgcnt(); |
| 213 | + db_protect_pop(); | |
| 212 | 214 | db_commit_transaction(); |
| 213 | 215 | }else |
| 214 | 216 | if( strncmp(zCmd, "delete", nCmd)==0 ){ |
| 215 | 217 | int i; |
| 216 | 218 | verify_all_options(); |
| 217 | 219 | if( g.argc<4 ) usage("delete ID ..."); |
| 218 | 220 | db_begin_write(); |
| 221 | + db_unprotect(PROTECT_CONFIG); | |
| 219 | 222 | for(i=3; i<g.argc; i++){ |
| 220 | 223 | const char *zName = g.argv[i]; |
| 221 | 224 | db_multi_exec( |
| 222 | 225 | "DELETE FROM config WHERE name='interwiki:%q'", |
| 223 | 226 | zName |
| 224 | 227 | ); |
| 225 | 228 | } |
| 226 | 229 | setup_incr_cfgcnt(); |
| 230 | + db_protect_pop(); | |
| 227 | 231 | db_commit_transaction(); |
| 228 | 232 | }else |
| 229 | 233 | if( strncmp(zCmd, "list", nCmd)==0 ){ |
| 230 | 234 | Stmt q; |
| 231 | 235 | int n = 0; |
| @@ -316,22 +320,26 @@ | ||
| 316 | 320 | zTag = PT("tag"); |
| 317 | 321 | zBase = PT("base"); |
| 318 | 322 | zHash = PT("hash"); |
| 319 | 323 | zWiki = PT("wiki"); |
| 320 | 324 | if( zTag==0 || zTag[0]==0 || !interwiki_valid_name(zTag) ){ |
| 321 | - zErr = mprintf("Not a valid interwiki tag name: \"%s\"", zTag ? zTag : ""); | |
| 325 | + zErr = mprintf("Not a valid interwiki tag name: \"%s\"", zTag?zTag : ""); | |
| 322 | 326 | }else if( zBase==0 || zBase[0]==0 ){ |
| 327 | + db_unprotect(PROTECT_CONFIG); | |
| 323 | 328 | db_multi_exec("DELETE FROM config WHERE name='interwiki:%q';", zTag); |
| 329 | + db_protect_pop(); | |
| 324 | 330 | }else{ |
| 325 | 331 | if( zHash && zHash[0]==0 ) zHash = 0; |
| 326 | 332 | if( zWiki && zWiki[0]==0 ) zWiki = 0; |
| 333 | + db_unprotect(PROTECT_CONFIG); | |
| 327 | 334 | db_multi_exec( |
| 328 | 335 | "REPLACE INTO config(name,value,mtime)" |
| 329 | 336 | "VALUES('interwiki:'||lower(%Q)," |
| 330 | 337 | " json_object('base',%Q,'hash',%Q,'wiki',%Q)," |
| 331 | 338 | " now());", |
| 332 | 339 | zTag, zBase, zHash, zWiki); |
| 340 | + db_protect_pop(); | |
| 333 | 341 | } |
| 334 | 342 | } |
| 335 | 343 | |
| 336 | 344 | style_header("Interwiki Map Configuration"); |
| 337 | 345 | @ <p>Interwiki links are hyperlink targets of the form |
| 338 | 346 |
| --- src/interwiki.c | |
| +++ src/interwiki.c | |
| @@ -192,40 +192,44 @@ | |
| 192 | const char *zHash = find_option("hash",0,1); |
| 193 | const char *zWiki = find_option("wiki",0,1); |
| 194 | verify_all_options(); |
| 195 | if( g.argc!=4 ) usage("add TAG ?OPTIONS?"); |
| 196 | zName = g.argv[3]; |
| 197 | if( zBase){ |
| 198 | fossil_fatal("the --base option is required"); |
| 199 | } |
| 200 | if( !interwiki_valid_name(zName) ){ |
| 201 | fossil_fatal("not a valid interwiki tag: \"%s\"", zName); |
| 202 | } |
| 203 | db_begin_write(); |
| 204 | db_multi_exec( |
| 205 | "REPLACE INTO config(name,value,mtime)" |
| 206 | " VALUES('interwiki:'||lower(%Q)," |
| 207 | " json_object('base',%Q,'hash',%Q,'wiki',%Q)," |
| 208 | " now());", |
| 209 | zName, zBase, zHash, zWiki |
| 210 | ); |
| 211 | setup_incr_cfgcnt(); |
| 212 | db_commit_transaction(); |
| 213 | }else |
| 214 | if( strncmp(zCmd, "delete", nCmd)==0 ){ |
| 215 | int i; |
| 216 | verify_all_options(); |
| 217 | if( g.argc<4 ) usage("delete ID ..."); |
| 218 | db_begin_write(); |
| 219 | for(i=3; i<g.argc; i++){ |
| 220 | const char *zName = g.argv[i]; |
| 221 | db_multi_exec( |
| 222 | "DELETE FROM config WHERE name='interwiki:%q'", |
| 223 | zName |
| 224 | ); |
| 225 | } |
| 226 | setup_incr_cfgcnt(); |
| 227 | db_commit_transaction(); |
| 228 | }else |
| 229 | if( strncmp(zCmd, "list", nCmd)==0 ){ |
| 230 | Stmt q; |
| 231 | int n = 0; |
| @@ -316,22 +320,26 @@ | |
| 316 | zTag = PT("tag"); |
| 317 | zBase = PT("base"); |
| 318 | zHash = PT("hash"); |
| 319 | zWiki = PT("wiki"); |
| 320 | if( zTag==0 || zTag[0]==0 || !interwiki_valid_name(zTag) ){ |
| 321 | zErr = mprintf("Not a valid interwiki tag name: \"%s\"", zTag ? zTag : ""); |
| 322 | }else if( zBase==0 || zBase[0]==0 ){ |
| 323 | db_multi_exec("DELETE FROM config WHERE name='interwiki:%q';", zTag); |
| 324 | }else{ |
| 325 | if( zHash && zHash[0]==0 ) zHash = 0; |
| 326 | if( zWiki && zWiki[0]==0 ) zWiki = 0; |
| 327 | db_multi_exec( |
| 328 | "REPLACE INTO config(name,value,mtime)" |
| 329 | "VALUES('interwiki:'||lower(%Q)," |
| 330 | " json_object('base',%Q,'hash',%Q,'wiki',%Q)," |
| 331 | " now());", |
| 332 | zTag, zBase, zHash, zWiki); |
| 333 | } |
| 334 | } |
| 335 | |
| 336 | style_header("Interwiki Map Configuration"); |
| 337 | @ <p>Interwiki links are hyperlink targets of the form |
| 338 |
| --- src/interwiki.c | |
| +++ src/interwiki.c | |
| @@ -192,40 +192,44 @@ | |
| 192 | const char *zHash = find_option("hash",0,1); |
| 193 | const char *zWiki = find_option("wiki",0,1); |
| 194 | verify_all_options(); |
| 195 | if( g.argc!=4 ) usage("add TAG ?OPTIONS?"); |
| 196 | zName = g.argv[3]; |
| 197 | if( zBase==0 ){ |
| 198 | fossil_fatal("the --base option is required"); |
| 199 | } |
| 200 | if( !interwiki_valid_name(zName) ){ |
| 201 | fossil_fatal("not a valid interwiki tag: \"%s\"", zName); |
| 202 | } |
| 203 | db_begin_write(); |
| 204 | db_unprotect(PROTECT_CONFIG); |
| 205 | db_multi_exec( |
| 206 | "REPLACE INTO config(name,value,mtime)" |
| 207 | " VALUES('interwiki:'||lower(%Q)," |
| 208 | " json_object('base',%Q,'hash',%Q,'wiki',%Q)," |
| 209 | " now());", |
| 210 | zName, zBase, zHash, zWiki |
| 211 | ); |
| 212 | setup_incr_cfgcnt(); |
| 213 | db_protect_pop(); |
| 214 | db_commit_transaction(); |
| 215 | }else |
| 216 | if( strncmp(zCmd, "delete", nCmd)==0 ){ |
| 217 | int i; |
| 218 | verify_all_options(); |
| 219 | if( g.argc<4 ) usage("delete ID ..."); |
| 220 | db_begin_write(); |
| 221 | db_unprotect(PROTECT_CONFIG); |
| 222 | for(i=3; i<g.argc; i++){ |
| 223 | const char *zName = g.argv[i]; |
| 224 | db_multi_exec( |
| 225 | "DELETE FROM config WHERE name='interwiki:%q'", |
| 226 | zName |
| 227 | ); |
| 228 | } |
| 229 | setup_incr_cfgcnt(); |
| 230 | db_protect_pop(); |
| 231 | db_commit_transaction(); |
| 232 | }else |
| 233 | if( strncmp(zCmd, "list", nCmd)==0 ){ |
| 234 | Stmt q; |
| 235 | int n = 0; |
| @@ -316,22 +320,26 @@ | |
| 320 | zTag = PT("tag"); |
| 321 | zBase = PT("base"); |
| 322 | zHash = PT("hash"); |
| 323 | zWiki = PT("wiki"); |
| 324 | if( zTag==0 || zTag[0]==0 || !interwiki_valid_name(zTag) ){ |
| 325 | zErr = mprintf("Not a valid interwiki tag name: \"%s\"", zTag?zTag : ""); |
| 326 | }else if( zBase==0 || zBase[0]==0 ){ |
| 327 | db_unprotect(PROTECT_CONFIG); |
| 328 | db_multi_exec("DELETE FROM config WHERE name='interwiki:%q';", zTag); |
| 329 | db_protect_pop(); |
| 330 | }else{ |
| 331 | if( zHash && zHash[0]==0 ) zHash = 0; |
| 332 | if( zWiki && zWiki[0]==0 ) zWiki = 0; |
| 333 | db_unprotect(PROTECT_CONFIG); |
| 334 | db_multi_exec( |
| 335 | "REPLACE INTO config(name,value,mtime)" |
| 336 | "VALUES('interwiki:'||lower(%Q)," |
| 337 | " json_object('base',%Q,'hash',%Q,'wiki',%Q)," |
| 338 | " now());", |
| 339 | zTag, zBase, zHash, zWiki); |
| 340 | db_protect_pop(); |
| 341 | } |
| 342 | } |
| 343 | |
| 344 | style_header("Interwiki Map Configuration"); |
| 345 | @ <p>Interwiki links are hyperlink targets of the form |
| 346 |
-1
| --- src/json_config.c | ||
| +++ src/json_config.c | ||
| @@ -83,11 +83,10 @@ | ||
| 83 | 83 | { "keep-glob", CONFIGSET_PROJ }, |
| 84 | 84 | { "crlf-glob", CONFIGSET_PROJ }, |
| 85 | 85 | { "crnl-glob", CONFIGSET_PROJ }, |
| 86 | 86 | { "encoding-glob", CONFIGSET_PROJ }, |
| 87 | 87 | { "empty-dirs", CONFIGSET_PROJ }, |
| 88 | -{ "allow-symlinks", CONFIGSET_PROJ }, | |
| 89 | 88 | { "dotfiles", CONFIGSET_PROJ }, |
| 90 | 89 | |
| 91 | 90 | { "ticket-table", CONFIGSET_TKT }, |
| 92 | 91 | { "ticket-common", CONFIGSET_TKT }, |
| 93 | 92 | { "ticket-change", CONFIGSET_TKT }, |
| 94 | 93 |
| --- src/json_config.c | |
| +++ src/json_config.c | |
| @@ -83,11 +83,10 @@ | |
| 83 | { "keep-glob", CONFIGSET_PROJ }, |
| 84 | { "crlf-glob", CONFIGSET_PROJ }, |
| 85 | { "crnl-glob", CONFIGSET_PROJ }, |
| 86 | { "encoding-glob", CONFIGSET_PROJ }, |
| 87 | { "empty-dirs", CONFIGSET_PROJ }, |
| 88 | { "allow-symlinks", CONFIGSET_PROJ }, |
| 89 | { "dotfiles", CONFIGSET_PROJ }, |
| 90 | |
| 91 | { "ticket-table", CONFIGSET_TKT }, |
| 92 | { "ticket-common", CONFIGSET_TKT }, |
| 93 | { "ticket-change", CONFIGSET_TKT }, |
| 94 |
| --- src/json_config.c | |
| +++ src/json_config.c | |
| @@ -83,11 +83,10 @@ | |
| 83 | { "keep-glob", CONFIGSET_PROJ }, |
| 84 | { "crlf-glob", CONFIGSET_PROJ }, |
| 85 | { "crnl-glob", CONFIGSET_PROJ }, |
| 86 | { "encoding-glob", CONFIGSET_PROJ }, |
| 87 | { "empty-dirs", CONFIGSET_PROJ }, |
| 88 | { "dotfiles", CONFIGSET_PROJ }, |
| 89 | |
| 90 | { "ticket-table", CONFIGSET_TKT }, |
| 91 | { "ticket-common", CONFIGSET_TKT }, |
| 92 | { "ticket-change", CONFIGSET_TKT }, |
| 93 |
+6
| --- src/json_user.c | ||
| +++ src/json_user.c | ||
| @@ -212,13 +212,15 @@ | ||
| 212 | 212 | json_set_err(FSL_JSON_E_RESOURCE_ALREADY_EXISTS, |
| 213 | 213 | "User %s already exists.", zName); |
| 214 | 214 | goto error; |
| 215 | 215 | }else{ |
| 216 | 216 | Stmt ins = empty_Stmt; |
| 217 | + db_unprotect(PROTECT_USER); | |
| 217 | 218 | db_prepare(&ins, "INSERT INTO user (login) VALUES(%Q)",zName); |
| 218 | 219 | db_step( &ins ); |
| 219 | 220 | db_finalize(&ins); |
| 221 | + db_protect_pop(); | |
| 220 | 222 | uid = db_int(0,"SELECT uid FROM user WHERE login=%Q", zName); |
| 221 | 223 | assert(uid>0); |
| 222 | 224 | zNameNew = zName; |
| 223 | 225 | cson_object_set( pUser, "uid", cson_value_new_integer(uid) ); |
| 224 | 226 | } |
| @@ -345,13 +347,15 @@ | ||
| 345 | 347 | #endif |
| 346 | 348 | #if 0 |
| 347 | 349 | puts(blob_str(&sql)); |
| 348 | 350 | cson_output_FILE( cson_object_value(pUser), stdout, NULL ); |
| 349 | 351 | #endif |
| 352 | + db_unprotect(PROTECT_USER); | |
| 350 | 353 | db_prepare(&q, "%s", blob_sql_text(&sql)); |
| 351 | 354 | db_exec(&q); |
| 352 | 355 | db_finalize(&q); |
| 356 | + db_protect_pop(); | |
| 353 | 357 | #if TRY_LOGIN_GROUP |
| 354 | 358 | if( zPW || cson_value_get_bool(forceLogout) ){ |
| 355 | 359 | Blob groupSql = empty_blob; |
| 356 | 360 | char * zErr = NULL; |
| 357 | 361 | blob_append_sql(&groupSql, |
| @@ -358,11 +362,13 @@ | ||
| 358 | 362 | "INSERT INTO user(login)" |
| 359 | 363 | " SELECT %Q WHERE NOT EXISTS(SELECT 1 FROM user WHERE login=%Q);", |
| 360 | 364 | zName, zName |
| 361 | 365 | ); |
| 362 | 366 | blob_append(&groupSql, blob_str(&sql), blob_size(&sql)); |
| 367 | + db_unprotect(PROTECT_USER); | |
| 363 | 368 | login_group_sql(blob_str(&groupSql), NULL, NULL, &zErr); |
| 369 | + db_protect_pop(); | |
| 364 | 370 | blob_reset(&groupSql); |
| 365 | 371 | if( zErr ){ |
| 366 | 372 | json_set_err( FSL_JSON_E_UNKNOWN, |
| 367 | 373 | "Repo-group update at least partially failed: %s", |
| 368 | 374 | zErr); |
| 369 | 375 |
| --- src/json_user.c | |
| +++ src/json_user.c | |
| @@ -212,13 +212,15 @@ | |
| 212 | json_set_err(FSL_JSON_E_RESOURCE_ALREADY_EXISTS, |
| 213 | "User %s already exists.", zName); |
| 214 | goto error; |
| 215 | }else{ |
| 216 | Stmt ins = empty_Stmt; |
| 217 | db_prepare(&ins, "INSERT INTO user (login) VALUES(%Q)",zName); |
| 218 | db_step( &ins ); |
| 219 | db_finalize(&ins); |
| 220 | uid = db_int(0,"SELECT uid FROM user WHERE login=%Q", zName); |
| 221 | assert(uid>0); |
| 222 | zNameNew = zName; |
| 223 | cson_object_set( pUser, "uid", cson_value_new_integer(uid) ); |
| 224 | } |
| @@ -345,13 +347,15 @@ | |
| 345 | #endif |
| 346 | #if 0 |
| 347 | puts(blob_str(&sql)); |
| 348 | cson_output_FILE( cson_object_value(pUser), stdout, NULL ); |
| 349 | #endif |
| 350 | db_prepare(&q, "%s", blob_sql_text(&sql)); |
| 351 | db_exec(&q); |
| 352 | db_finalize(&q); |
| 353 | #if TRY_LOGIN_GROUP |
| 354 | if( zPW || cson_value_get_bool(forceLogout) ){ |
| 355 | Blob groupSql = empty_blob; |
| 356 | char * zErr = NULL; |
| 357 | blob_append_sql(&groupSql, |
| @@ -358,11 +362,13 @@ | |
| 358 | "INSERT INTO user(login)" |
| 359 | " SELECT %Q WHERE NOT EXISTS(SELECT 1 FROM user WHERE login=%Q);", |
| 360 | zName, zName |
| 361 | ); |
| 362 | blob_append(&groupSql, blob_str(&sql), blob_size(&sql)); |
| 363 | login_group_sql(blob_str(&groupSql), NULL, NULL, &zErr); |
| 364 | blob_reset(&groupSql); |
| 365 | if( zErr ){ |
| 366 | json_set_err( FSL_JSON_E_UNKNOWN, |
| 367 | "Repo-group update at least partially failed: %s", |
| 368 | zErr); |
| 369 |
| --- src/json_user.c | |
| +++ src/json_user.c | |
| @@ -212,13 +212,15 @@ | |
| 212 | json_set_err(FSL_JSON_E_RESOURCE_ALREADY_EXISTS, |
| 213 | "User %s already exists.", zName); |
| 214 | goto error; |
| 215 | }else{ |
| 216 | Stmt ins = empty_Stmt; |
| 217 | db_unprotect(PROTECT_USER); |
| 218 | db_prepare(&ins, "INSERT INTO user (login) VALUES(%Q)",zName); |
| 219 | db_step( &ins ); |
| 220 | db_finalize(&ins); |
| 221 | db_protect_pop(); |
| 222 | uid = db_int(0,"SELECT uid FROM user WHERE login=%Q", zName); |
| 223 | assert(uid>0); |
| 224 | zNameNew = zName; |
| 225 | cson_object_set( pUser, "uid", cson_value_new_integer(uid) ); |
| 226 | } |
| @@ -345,13 +347,15 @@ | |
| 347 | #endif |
| 348 | #if 0 |
| 349 | puts(blob_str(&sql)); |
| 350 | cson_output_FILE( cson_object_value(pUser), stdout, NULL ); |
| 351 | #endif |
| 352 | db_unprotect(PROTECT_USER); |
| 353 | db_prepare(&q, "%s", blob_sql_text(&sql)); |
| 354 | db_exec(&q); |
| 355 | db_finalize(&q); |
| 356 | db_protect_pop(); |
| 357 | #if TRY_LOGIN_GROUP |
| 358 | if( zPW || cson_value_get_bool(forceLogout) ){ |
| 359 | Blob groupSql = empty_blob; |
| 360 | char * zErr = NULL; |
| 361 | blob_append_sql(&groupSql, |
| @@ -358,11 +362,13 @@ | |
| 362 | "INSERT INTO user(login)" |
| 363 | " SELECT %Q WHERE NOT EXISTS(SELECT 1 FROM user WHERE login=%Q);", |
| 364 | zName, zName |
| 365 | ); |
| 366 | blob_append(&groupSql, blob_str(&sql), blob_size(&sql)); |
| 367 | db_unprotect(PROTECT_USER); |
| 368 | login_group_sql(blob_str(&groupSql), NULL, NULL, &zErr); |
| 369 | db_protect_pop(); |
| 370 | blob_reset(&groupSql); |
| 371 | if( zErr ){ |
| 372 | json_set_err( FSL_JSON_E_UNKNOWN, |
| 373 | "Repo-group update at least partially failed: %s", |
| 374 | zErr); |
| 375 |
+21
-2
| --- src/login.c | ||
| +++ src/login.c | ||
| @@ -293,13 +293,15 @@ | ||
| 293 | 293 | if( zHash==0 ) zHash = db_text(0, "SELECT hex(randomblob(25))"); |
| 294 | 294 | zCookie = login_gen_user_cookie_value(zUsername, zHash); |
| 295 | 295 | cgi_set_cookie(zCookieName, zCookie, login_cookie_path(), |
| 296 | 296 | bSessionCookie ? 0 : expires); |
| 297 | 297 | record_login_attempt(zUsername, zIpAddr, 1); |
| 298 | + db_unprotect(PROTECT_USER); | |
| 298 | 299 | db_multi_exec("UPDATE user SET cookie=%Q," |
| 299 | 300 | " cexpire=julianday('now')+%d/86400.0 WHERE uid=%d", |
| 300 | 301 | zHash, expires, uid); |
| 302 | + db_protect_pop(); | |
| 301 | 303 | fossil_free(zHash); |
| 302 | 304 | if( zDest ){ |
| 303 | 305 | *zDest = zCookie; |
| 304 | 306 | }else{ |
| 305 | 307 | free(zCookie); |
| @@ -356,14 +358,16 @@ | ||
| 356 | 358 | }else{ |
| 357 | 359 | const char *cookie = login_cookie_name(); |
| 358 | 360 | /* To logout, change the cookie value to an empty string */ |
| 359 | 361 | cgi_set_cookie(cookie, "", |
| 360 | 362 | login_cookie_path(), -86400); |
| 363 | + db_unprotect(PROTECT_USER); | |
| 361 | 364 | db_multi_exec("UPDATE user SET cookie=NULL, ipaddr=NULL, " |
| 362 | 365 | " cexpire=0 WHERE uid=%d" |
| 363 | 366 | " AND login NOT IN ('anonymous','nobody'," |
| 364 | 367 | " 'developer','reader')", g.userUid); |
| 368 | + db_protect_pop(); | |
| 365 | 369 | cgi_replace_parameter(cookie, NULL); |
| 366 | 370 | cgi_replace_parameter("anon", NULL); |
| 367 | 371 | } |
| 368 | 372 | } |
| 369 | 373 | |
| @@ -580,22 +584,27 @@ | ||
| 580 | 584 | ; |
| 581 | 585 | }else{ |
| 582 | 586 | char *zNewPw = sha1_shared_secret(zNew1, g.zLogin, 0); |
| 583 | 587 | char *zChngPw; |
| 584 | 588 | char *zErr; |
| 589 | + int rc; | |
| 590 | + | |
| 591 | + db_unprotect(PROTECT_USER); | |
| 585 | 592 | db_multi_exec( |
| 586 | 593 | "UPDATE user SET pw=%Q WHERE uid=%d", zNewPw, g.userUid |
| 587 | 594 | ); |
| 588 | - fossil_free(zNewPw); | |
| 589 | 595 | zChngPw = mprintf( |
| 590 | 596 | "UPDATE user" |
| 591 | 597 | " SET pw=shared_secret(%Q,%Q," |
| 592 | 598 | " (SELECT value FROM config WHERE name='project-code'))" |
| 593 | 599 | " WHERE login=%Q", |
| 594 | 600 | zNew1, g.zLogin, g.zLogin |
| 595 | 601 | ); |
| 596 | - if( login_group_sql(zChngPw, "<p>", "</p>\n", &zErr) ){ | |
| 602 | + fossil_free(zNewPw); | |
| 603 | + rc = login_group_sql(zChngPw, "<p>", "</p>\n", &zErr); | |
| 604 | + db_protect_pop(); | |
| 605 | + if( rc ){ | |
| 597 | 606 | zErrMsg = mprintf("<span class=\"loginError\">%s</span>", zErr); |
| 598 | 607 | fossil_free(zErr); |
| 599 | 608 | }else{ |
| 600 | 609 | redirect_to_g(); |
| 601 | 610 | return; |
| @@ -835,16 +844,18 @@ | ||
| 835 | 844 | zLogin, zHash |
| 836 | 845 | ); |
| 837 | 846 | pStmt = 0; |
| 838 | 847 | rc = sqlite3_prepare_v2(pOther, zSQL, -1, &pStmt, 0); |
| 839 | 848 | if( rc==SQLITE_OK && sqlite3_step(pStmt)==SQLITE_ROW ){ |
| 849 | + db_unprotect(PROTECT_USER); | |
| 840 | 850 | db_multi_exec( |
| 841 | 851 | "UPDATE user SET cookie=%Q, cexpire=%.17g" |
| 842 | 852 | " WHERE login=%Q", |
| 843 | 853 | zHash, |
| 844 | 854 | sqlite3_column_double(pStmt, 0), zLogin |
| 845 | 855 | ); |
| 856 | + db_protect_pop(); | |
| 846 | 857 | nXfer++; |
| 847 | 858 | } |
| 848 | 859 | sqlite3_finalize(pStmt); |
| 849 | 860 | } |
| 850 | 861 | sqlite3_close(pOther); |
| @@ -1619,11 +1630,13 @@ | ||
| 1619 | 1630 | "INSERT INTO user(login,pw,cap,info,mtime)\n" |
| 1620 | 1631 | "VALUES(%Q,%Q,%Q," |
| 1621 | 1632 | "'%q <%q>\nself-register from ip %q on '||datetime('now'),now())", |
| 1622 | 1633 | zUserID, zPass, zStartPerms, zDName, zEAddr, g.zIpAddr); |
| 1623 | 1634 | fossil_free(zPass); |
| 1635 | + db_unprotect(PROTECT_USER); | |
| 1624 | 1636 | db_multi_exec("%s", blob_sql_text(&sql)); |
| 1637 | + db_protect_pop(); | |
| 1625 | 1638 | uid = db_int(0, "SELECT uid FROM user WHERE login=%Q", zUserID); |
| 1626 | 1639 | login_set_user_cookie(zUserID, uid, NULL, 0); |
| 1627 | 1640 | if( doAlerts ){ |
| 1628 | 1641 | /* Also make the new user a subscriber. */ |
| 1629 | 1642 | Blob hdr, body; |
| @@ -1832,14 +1845,16 @@ | ||
| 1832 | 1845 | while( db_step(&q)==SQLITE_ROW ){ |
| 1833 | 1846 | const char *zRepoName = db_column_text(&q, 1); |
| 1834 | 1847 | if( file_size(zRepoName, ExtFILE)<0 ){ |
| 1835 | 1848 | /* Silently remove non-existent repositories from the login group. */ |
| 1836 | 1849 | const char *zLabel = db_column_text(&q, 0); |
| 1850 | + db_unprotect(PROTECT_CONFIG); | |
| 1837 | 1851 | db_multi_exec( |
| 1838 | 1852 | "DELETE FROM config WHERE name GLOB 'peer-*-%q'", |
| 1839 | 1853 | &zLabel[10] |
| 1840 | 1854 | ); |
| 1855 | + db_protect_pop(); | |
| 1841 | 1856 | continue; |
| 1842 | 1857 | } |
| 1843 | 1858 | rc = sqlite3_open_v2( |
| 1844 | 1859 | zRepoName, &pPeer, |
| 1845 | 1860 | SQLITE_OPEN_READWRITE, |
| @@ -2004,11 +2019,13 @@ | ||
| 2004 | 2019 | "REPLACE INTO config(name,value,mtime) VALUES('peer-name-%q',%Q,now());" |
| 2005 | 2020 | "REPLACE INTO config(name,value,mtime) VALUES('peer-repo-%q',%Q,now());" |
| 2006 | 2021 | "COMMIT;", |
| 2007 | 2022 | zSelfProjCode, zSelfLabel, zSelfProjCode, zSelfRepo |
| 2008 | 2023 | ); |
| 2024 | + db_unprotect(PROTECT_CONFIG); | |
| 2009 | 2025 | login_group_sql(zSql, "<li> ", "</li>", pzErrMsg); |
| 2026 | + db_protect_pop(); | |
| 2010 | 2027 | fossil_free(zSql); |
| 2011 | 2028 | } |
| 2012 | 2029 | |
| 2013 | 2030 | /* |
| 2014 | 2031 | ** Leave the login group that we are currently part of. |
| @@ -2025,17 +2042,19 @@ | ||
| 2025 | 2042 | " WHERE name='login-group-name'" |
| 2026 | 2043 | " AND (SELECT count(*) FROM config WHERE name GLOB 'peer-*')==0;", |
| 2027 | 2044 | zProjCode |
| 2028 | 2045 | ); |
| 2029 | 2046 | fossil_free(zProjCode); |
| 2047 | + db_unprotect(PROTECT_CONFIG); | |
| 2030 | 2048 | login_group_sql(zSql, "<li> ", "</li>", pzErrMsg); |
| 2031 | 2049 | fossil_free(zSql); |
| 2032 | 2050 | db_multi_exec( |
| 2033 | 2051 | "DELETE FROM config " |
| 2034 | 2052 | " WHERE name GLOB 'peer-*'" |
| 2035 | 2053 | " OR name GLOB 'login-group-*';" |
| 2036 | 2054 | ); |
| 2055 | + db_protect_pop(); | |
| 2037 | 2056 | } |
| 2038 | 2057 | |
| 2039 | 2058 | /* |
| 2040 | 2059 | ** COMMAND: login-group* |
| 2041 | 2060 | ** |
| 2042 | 2061 |
| --- src/login.c | |
| +++ src/login.c | |
| @@ -293,13 +293,15 @@ | |
| 293 | if( zHash==0 ) zHash = db_text(0, "SELECT hex(randomblob(25))"); |
| 294 | zCookie = login_gen_user_cookie_value(zUsername, zHash); |
| 295 | cgi_set_cookie(zCookieName, zCookie, login_cookie_path(), |
| 296 | bSessionCookie ? 0 : expires); |
| 297 | record_login_attempt(zUsername, zIpAddr, 1); |
| 298 | db_multi_exec("UPDATE user SET cookie=%Q," |
| 299 | " cexpire=julianday('now')+%d/86400.0 WHERE uid=%d", |
| 300 | zHash, expires, uid); |
| 301 | fossil_free(zHash); |
| 302 | if( zDest ){ |
| 303 | *zDest = zCookie; |
| 304 | }else{ |
| 305 | free(zCookie); |
| @@ -356,14 +358,16 @@ | |
| 356 | }else{ |
| 357 | const char *cookie = login_cookie_name(); |
| 358 | /* To logout, change the cookie value to an empty string */ |
| 359 | cgi_set_cookie(cookie, "", |
| 360 | login_cookie_path(), -86400); |
| 361 | db_multi_exec("UPDATE user SET cookie=NULL, ipaddr=NULL, " |
| 362 | " cexpire=0 WHERE uid=%d" |
| 363 | " AND login NOT IN ('anonymous','nobody'," |
| 364 | " 'developer','reader')", g.userUid); |
| 365 | cgi_replace_parameter(cookie, NULL); |
| 366 | cgi_replace_parameter("anon", NULL); |
| 367 | } |
| 368 | } |
| 369 | |
| @@ -580,22 +584,27 @@ | |
| 580 | ; |
| 581 | }else{ |
| 582 | char *zNewPw = sha1_shared_secret(zNew1, g.zLogin, 0); |
| 583 | char *zChngPw; |
| 584 | char *zErr; |
| 585 | db_multi_exec( |
| 586 | "UPDATE user SET pw=%Q WHERE uid=%d", zNewPw, g.userUid |
| 587 | ); |
| 588 | fossil_free(zNewPw); |
| 589 | zChngPw = mprintf( |
| 590 | "UPDATE user" |
| 591 | " SET pw=shared_secret(%Q,%Q," |
| 592 | " (SELECT value FROM config WHERE name='project-code'))" |
| 593 | " WHERE login=%Q", |
| 594 | zNew1, g.zLogin, g.zLogin |
| 595 | ); |
| 596 | if( login_group_sql(zChngPw, "<p>", "</p>\n", &zErr) ){ |
| 597 | zErrMsg = mprintf("<span class=\"loginError\">%s</span>", zErr); |
| 598 | fossil_free(zErr); |
| 599 | }else{ |
| 600 | redirect_to_g(); |
| 601 | return; |
| @@ -835,16 +844,18 @@ | |
| 835 | zLogin, zHash |
| 836 | ); |
| 837 | pStmt = 0; |
| 838 | rc = sqlite3_prepare_v2(pOther, zSQL, -1, &pStmt, 0); |
| 839 | if( rc==SQLITE_OK && sqlite3_step(pStmt)==SQLITE_ROW ){ |
| 840 | db_multi_exec( |
| 841 | "UPDATE user SET cookie=%Q, cexpire=%.17g" |
| 842 | " WHERE login=%Q", |
| 843 | zHash, |
| 844 | sqlite3_column_double(pStmt, 0), zLogin |
| 845 | ); |
| 846 | nXfer++; |
| 847 | } |
| 848 | sqlite3_finalize(pStmt); |
| 849 | } |
| 850 | sqlite3_close(pOther); |
| @@ -1619,11 +1630,13 @@ | |
| 1619 | "INSERT INTO user(login,pw,cap,info,mtime)\n" |
| 1620 | "VALUES(%Q,%Q,%Q," |
| 1621 | "'%q <%q>\nself-register from ip %q on '||datetime('now'),now())", |
| 1622 | zUserID, zPass, zStartPerms, zDName, zEAddr, g.zIpAddr); |
| 1623 | fossil_free(zPass); |
| 1624 | db_multi_exec("%s", blob_sql_text(&sql)); |
| 1625 | uid = db_int(0, "SELECT uid FROM user WHERE login=%Q", zUserID); |
| 1626 | login_set_user_cookie(zUserID, uid, NULL, 0); |
| 1627 | if( doAlerts ){ |
| 1628 | /* Also make the new user a subscriber. */ |
| 1629 | Blob hdr, body; |
| @@ -1832,14 +1845,16 @@ | |
| 1832 | while( db_step(&q)==SQLITE_ROW ){ |
| 1833 | const char *zRepoName = db_column_text(&q, 1); |
| 1834 | if( file_size(zRepoName, ExtFILE)<0 ){ |
| 1835 | /* Silently remove non-existent repositories from the login group. */ |
| 1836 | const char *zLabel = db_column_text(&q, 0); |
| 1837 | db_multi_exec( |
| 1838 | "DELETE FROM config WHERE name GLOB 'peer-*-%q'", |
| 1839 | &zLabel[10] |
| 1840 | ); |
| 1841 | continue; |
| 1842 | } |
| 1843 | rc = sqlite3_open_v2( |
| 1844 | zRepoName, &pPeer, |
| 1845 | SQLITE_OPEN_READWRITE, |
| @@ -2004,11 +2019,13 @@ | |
| 2004 | "REPLACE INTO config(name,value,mtime) VALUES('peer-name-%q',%Q,now());" |
| 2005 | "REPLACE INTO config(name,value,mtime) VALUES('peer-repo-%q',%Q,now());" |
| 2006 | "COMMIT;", |
| 2007 | zSelfProjCode, zSelfLabel, zSelfProjCode, zSelfRepo |
| 2008 | ); |
| 2009 | login_group_sql(zSql, "<li> ", "</li>", pzErrMsg); |
| 2010 | fossil_free(zSql); |
| 2011 | } |
| 2012 | |
| 2013 | /* |
| 2014 | ** Leave the login group that we are currently part of. |
| @@ -2025,17 +2042,19 @@ | |
| 2025 | " WHERE name='login-group-name'" |
| 2026 | " AND (SELECT count(*) FROM config WHERE name GLOB 'peer-*')==0;", |
| 2027 | zProjCode |
| 2028 | ); |
| 2029 | fossil_free(zProjCode); |
| 2030 | login_group_sql(zSql, "<li> ", "</li>", pzErrMsg); |
| 2031 | fossil_free(zSql); |
| 2032 | db_multi_exec( |
| 2033 | "DELETE FROM config " |
| 2034 | " WHERE name GLOB 'peer-*'" |
| 2035 | " OR name GLOB 'login-group-*';" |
| 2036 | ); |
| 2037 | } |
| 2038 | |
| 2039 | /* |
| 2040 | ** COMMAND: login-group* |
| 2041 | ** |
| 2042 |
| --- src/login.c | |
| +++ src/login.c | |
| @@ -293,13 +293,15 @@ | |
| 293 | if( zHash==0 ) zHash = db_text(0, "SELECT hex(randomblob(25))"); |
| 294 | zCookie = login_gen_user_cookie_value(zUsername, zHash); |
| 295 | cgi_set_cookie(zCookieName, zCookie, login_cookie_path(), |
| 296 | bSessionCookie ? 0 : expires); |
| 297 | record_login_attempt(zUsername, zIpAddr, 1); |
| 298 | db_unprotect(PROTECT_USER); |
| 299 | db_multi_exec("UPDATE user SET cookie=%Q," |
| 300 | " cexpire=julianday('now')+%d/86400.0 WHERE uid=%d", |
| 301 | zHash, expires, uid); |
| 302 | db_protect_pop(); |
| 303 | fossil_free(zHash); |
| 304 | if( zDest ){ |
| 305 | *zDest = zCookie; |
| 306 | }else{ |
| 307 | free(zCookie); |
| @@ -356,14 +358,16 @@ | |
| 358 | }else{ |
| 359 | const char *cookie = login_cookie_name(); |
| 360 | /* To logout, change the cookie value to an empty string */ |
| 361 | cgi_set_cookie(cookie, "", |
| 362 | login_cookie_path(), -86400); |
| 363 | db_unprotect(PROTECT_USER); |
| 364 | db_multi_exec("UPDATE user SET cookie=NULL, ipaddr=NULL, " |
| 365 | " cexpire=0 WHERE uid=%d" |
| 366 | " AND login NOT IN ('anonymous','nobody'," |
| 367 | " 'developer','reader')", g.userUid); |
| 368 | db_protect_pop(); |
| 369 | cgi_replace_parameter(cookie, NULL); |
| 370 | cgi_replace_parameter("anon", NULL); |
| 371 | } |
| 372 | } |
| 373 | |
| @@ -580,22 +584,27 @@ | |
| 584 | ; |
| 585 | }else{ |
| 586 | char *zNewPw = sha1_shared_secret(zNew1, g.zLogin, 0); |
| 587 | char *zChngPw; |
| 588 | char *zErr; |
| 589 | int rc; |
| 590 | |
| 591 | db_unprotect(PROTECT_USER); |
| 592 | db_multi_exec( |
| 593 | "UPDATE user SET pw=%Q WHERE uid=%d", zNewPw, g.userUid |
| 594 | ); |
| 595 | zChngPw = mprintf( |
| 596 | "UPDATE user" |
| 597 | " SET pw=shared_secret(%Q,%Q," |
| 598 | " (SELECT value FROM config WHERE name='project-code'))" |
| 599 | " WHERE login=%Q", |
| 600 | zNew1, g.zLogin, g.zLogin |
| 601 | ); |
| 602 | fossil_free(zNewPw); |
| 603 | rc = login_group_sql(zChngPw, "<p>", "</p>\n", &zErr); |
| 604 | db_protect_pop(); |
| 605 | if( rc ){ |
| 606 | zErrMsg = mprintf("<span class=\"loginError\">%s</span>", zErr); |
| 607 | fossil_free(zErr); |
| 608 | }else{ |
| 609 | redirect_to_g(); |
| 610 | return; |
| @@ -835,16 +844,18 @@ | |
| 844 | zLogin, zHash |
| 845 | ); |
| 846 | pStmt = 0; |
| 847 | rc = sqlite3_prepare_v2(pOther, zSQL, -1, &pStmt, 0); |
| 848 | if( rc==SQLITE_OK && sqlite3_step(pStmt)==SQLITE_ROW ){ |
| 849 | db_unprotect(PROTECT_USER); |
| 850 | db_multi_exec( |
| 851 | "UPDATE user SET cookie=%Q, cexpire=%.17g" |
| 852 | " WHERE login=%Q", |
| 853 | zHash, |
| 854 | sqlite3_column_double(pStmt, 0), zLogin |
| 855 | ); |
| 856 | db_protect_pop(); |
| 857 | nXfer++; |
| 858 | } |
| 859 | sqlite3_finalize(pStmt); |
| 860 | } |
| 861 | sqlite3_close(pOther); |
| @@ -1619,11 +1630,13 @@ | |
| 1630 | "INSERT INTO user(login,pw,cap,info,mtime)\n" |
| 1631 | "VALUES(%Q,%Q,%Q," |
| 1632 | "'%q <%q>\nself-register from ip %q on '||datetime('now'),now())", |
| 1633 | zUserID, zPass, zStartPerms, zDName, zEAddr, g.zIpAddr); |
| 1634 | fossil_free(zPass); |
| 1635 | db_unprotect(PROTECT_USER); |
| 1636 | db_multi_exec("%s", blob_sql_text(&sql)); |
| 1637 | db_protect_pop(); |
| 1638 | uid = db_int(0, "SELECT uid FROM user WHERE login=%Q", zUserID); |
| 1639 | login_set_user_cookie(zUserID, uid, NULL, 0); |
| 1640 | if( doAlerts ){ |
| 1641 | /* Also make the new user a subscriber. */ |
| 1642 | Blob hdr, body; |
| @@ -1832,14 +1845,16 @@ | |
| 1845 | while( db_step(&q)==SQLITE_ROW ){ |
| 1846 | const char *zRepoName = db_column_text(&q, 1); |
| 1847 | if( file_size(zRepoName, ExtFILE)<0 ){ |
| 1848 | /* Silently remove non-existent repositories from the login group. */ |
| 1849 | const char *zLabel = db_column_text(&q, 0); |
| 1850 | db_unprotect(PROTECT_CONFIG); |
| 1851 | db_multi_exec( |
| 1852 | "DELETE FROM config WHERE name GLOB 'peer-*-%q'", |
| 1853 | &zLabel[10] |
| 1854 | ); |
| 1855 | db_protect_pop(); |
| 1856 | continue; |
| 1857 | } |
| 1858 | rc = sqlite3_open_v2( |
| 1859 | zRepoName, &pPeer, |
| 1860 | SQLITE_OPEN_READWRITE, |
| @@ -2004,11 +2019,13 @@ | |
| 2019 | "REPLACE INTO config(name,value,mtime) VALUES('peer-name-%q',%Q,now());" |
| 2020 | "REPLACE INTO config(name,value,mtime) VALUES('peer-repo-%q',%Q,now());" |
| 2021 | "COMMIT;", |
| 2022 | zSelfProjCode, zSelfLabel, zSelfProjCode, zSelfRepo |
| 2023 | ); |
| 2024 | db_unprotect(PROTECT_CONFIG); |
| 2025 | login_group_sql(zSql, "<li> ", "</li>", pzErrMsg); |
| 2026 | db_protect_pop(); |
| 2027 | fossil_free(zSql); |
| 2028 | } |
| 2029 | |
| 2030 | /* |
| 2031 | ** Leave the login group that we are currently part of. |
| @@ -2025,17 +2042,19 @@ | |
| 2042 | " WHERE name='login-group-name'" |
| 2043 | " AND (SELECT count(*) FROM config WHERE name GLOB 'peer-*')==0;", |
| 2044 | zProjCode |
| 2045 | ); |
| 2046 | fossil_free(zProjCode); |
| 2047 | db_unprotect(PROTECT_CONFIG); |
| 2048 | login_group_sql(zSql, "<li> ", "</li>", pzErrMsg); |
| 2049 | fossil_free(zSql); |
| 2050 | db_multi_exec( |
| 2051 | "DELETE FROM config " |
| 2052 | " WHERE name GLOB 'peer-*'" |
| 2053 | " OR name GLOB 'login-group-*';" |
| 2054 | ); |
| 2055 | db_protect_pop(); |
| 2056 | } |
| 2057 | |
| 2058 | /* |
| 2059 | ** COMMAND: login-group* |
| 2060 | ** |
| 2061 |
+2
| --- src/main.c | ||
| +++ src/main.c | ||
| @@ -1372,19 +1372,21 @@ | ||
| 1372 | 1372 | g.zTop = &g.zBaseURL[7+strlen(zHost)]; |
| 1373 | 1373 | g.zHttpsURL = mprintf("https://%s%.*s", zHost, i, zCur); |
| 1374 | 1374 | } |
| 1375 | 1375 | } |
| 1376 | 1376 | if( db_is_writeable("repository") ){ |
| 1377 | + db_unprotect(PROTECT_CONFIG); | |
| 1377 | 1378 | if( !db_exists("SELECT 1 FROM config WHERE name='baseurl:%q'", g.zBaseURL)){ |
| 1378 | 1379 | db_multi_exec("INSERT INTO config(name,value,mtime)" |
| 1379 | 1380 | "VALUES('baseurl:%q',1,now())", g.zBaseURL); |
| 1380 | 1381 | }else{ |
| 1381 | 1382 | db_optional_sql("repository", |
| 1382 | 1383 | "REPLACE INTO config(name,value,mtime)" |
| 1383 | 1384 | "VALUES('baseurl:%q',1,now())", g.zBaseURL |
| 1384 | 1385 | ); |
| 1385 | 1386 | } |
| 1387 | + db_protect_pop(); | |
| 1386 | 1388 | } |
| 1387 | 1389 | } |
| 1388 | 1390 | |
| 1389 | 1391 | /* |
| 1390 | 1392 | ** Send an HTTP redirect back to the designated Index Page. |
| 1391 | 1393 |
| --- src/main.c | |
| +++ src/main.c | |
| @@ -1372,19 +1372,21 @@ | |
| 1372 | g.zTop = &g.zBaseURL[7+strlen(zHost)]; |
| 1373 | g.zHttpsURL = mprintf("https://%s%.*s", zHost, i, zCur); |
| 1374 | } |
| 1375 | } |
| 1376 | if( db_is_writeable("repository") ){ |
| 1377 | if( !db_exists("SELECT 1 FROM config WHERE name='baseurl:%q'", g.zBaseURL)){ |
| 1378 | db_multi_exec("INSERT INTO config(name,value,mtime)" |
| 1379 | "VALUES('baseurl:%q',1,now())", g.zBaseURL); |
| 1380 | }else{ |
| 1381 | db_optional_sql("repository", |
| 1382 | "REPLACE INTO config(name,value,mtime)" |
| 1383 | "VALUES('baseurl:%q',1,now())", g.zBaseURL |
| 1384 | ); |
| 1385 | } |
| 1386 | } |
| 1387 | } |
| 1388 | |
| 1389 | /* |
| 1390 | ** Send an HTTP redirect back to the designated Index Page. |
| 1391 |
| --- src/main.c | |
| +++ src/main.c | |
| @@ -1372,19 +1372,21 @@ | |
| 1372 | g.zTop = &g.zBaseURL[7+strlen(zHost)]; |
| 1373 | g.zHttpsURL = mprintf("https://%s%.*s", zHost, i, zCur); |
| 1374 | } |
| 1375 | } |
| 1376 | if( db_is_writeable("repository") ){ |
| 1377 | db_unprotect(PROTECT_CONFIG); |
| 1378 | if( !db_exists("SELECT 1 FROM config WHERE name='baseurl:%q'", g.zBaseURL)){ |
| 1379 | db_multi_exec("INSERT INTO config(name,value,mtime)" |
| 1380 | "VALUES('baseurl:%q',1,now())", g.zBaseURL); |
| 1381 | }else{ |
| 1382 | db_optional_sql("repository", |
| 1383 | "REPLACE INTO config(name,value,mtime)" |
| 1384 | "VALUES('baseurl:%q',1,now())", g.zBaseURL |
| 1385 | ); |
| 1386 | } |
| 1387 | db_protect_pop(); |
| 1388 | } |
| 1389 | } |
| 1390 | |
| 1391 | /* |
| 1392 | ** Send an HTTP redirect back to the designated Index Page. |
| 1393 |
+21
-3
| --- src/manifest.c | ||
| +++ src/manifest.c | ||
| @@ -481,14 +481,23 @@ | ||
| 481 | 481 | blob_appendf(pErr, "line 1 not recognized"); |
| 482 | 482 | return 0; |
| 483 | 483 | } |
| 484 | 484 | /* Then verify the Z-card. |
| 485 | 485 | */ |
| 486 | +#if 1 | |
| 487 | + /* Disable this ***ONLY*** (ONLY!) when testing hand-written inputs | |
| 488 | + for card-related syntax errors. */ | |
| 486 | 489 | if( verify_z_card(z, n, pErr)==2 ){ |
| 487 | 490 | blob_reset(pContent); |
| 488 | 491 | return 0; |
| 489 | 492 | } |
| 493 | +#else | |
| 494 | +#warning ACHTUNG - z-card check is disabled for testing purposes. | |
| 495 | + if(0 && verify_z_card(NULL, 0, NULL)){ | |
| 496 | + /*avoid unused static func error*/ | |
| 497 | + } | |
| 498 | +#endif | |
| 490 | 499 | |
| 491 | 500 | /* Allocate a Manifest object to hold the parsed control artifact. |
| 492 | 501 | */ |
| 493 | 502 | p = fossil_malloc( sizeof(*p) ); |
| 494 | 503 | memset(p, 0, sizeof(*p)); |
| @@ -601,10 +610,11 @@ | ||
| 601 | 610 | case 'E': { |
| 602 | 611 | if( p->rEventDate>0.0 ) SYNTAX("more than one E-card"); |
| 603 | 612 | p->rEventDate = db_double(0.0,"SELECT julianday(%Q)", next_token(&x,0)); |
| 604 | 613 | if( p->rEventDate<=0.0 ) SYNTAX("malformed date on E-card"); |
| 605 | 614 | p->zEventId = next_token(&x, &sz); |
| 615 | + if( p->zEventId==0 ) SYNTAX("missing hash on E-card"); | |
| 606 | 616 | if( !hname_validate(p->zEventId, sz) ){ |
| 607 | 617 | SYNTAX("malformed hash on E-card"); |
| 608 | 618 | } |
| 609 | 619 | p->type = CFTYPE_EVENT; |
| 610 | 620 | break; |
| @@ -625,10 +635,11 @@ | ||
| 625 | 635 | if( !file_is_simple_pathname_nonstrict(zName) ){ |
| 626 | 636 | SYNTAX("F-card filename is not a simple path"); |
| 627 | 637 | } |
| 628 | 638 | zUuid = next_token(&x, &sz); |
| 629 | 639 | if( p->zBaseline==0 || zUuid!=0 ){ |
| 640 | + if( zUuid==0 ) SYNTAX("missing hash on F-card"); | |
| 630 | 641 | if( !hname_validate(zUuid,sz) ){ |
| 631 | 642 | SYNTAX("F-card hash invalid"); |
| 632 | 643 | } |
| 633 | 644 | } |
| 634 | 645 | zPerm = next_token(&x,0); |
| @@ -643,17 +654,24 @@ | ||
| 643 | 654 | p->nFileAlloc = p->nFileAlloc*2 + 10; |
| 644 | 655 | p->aFile = fossil_realloc(p->aFile, |
| 645 | 656 | p->nFileAlloc*sizeof(p->aFile[0]) ); |
| 646 | 657 | } |
| 647 | 658 | i = p->nFile++; |
| 659 | + if( i>0 && fossil_strcmp(p->aFile[i-1].zName, zName)>=0 ){ | |
| 660 | + SYNTAX("incorrect F-card sort order"); | |
| 661 | + } | |
| 662 | + if( file_is_reserved_name(zName,-1) ){ | |
| 663 | + /* If reserved names leaked into historical manifests due to | |
| 664 | + ** slack oversight by older versions of Fossil, simply ignore | |
| 665 | + ** those files */ | |
| 666 | + p->nFile--; | |
| 667 | + break; | |
| 668 | + } | |
| 648 | 669 | p->aFile[i].zName = zName; |
| 649 | 670 | p->aFile[i].zUuid = zUuid; |
| 650 | 671 | p->aFile[i].zPerm = zPerm; |
| 651 | 672 | p->aFile[i].zPrior = zPriorName; |
| 652 | - if( i>0 && fossil_strcmp(p->aFile[i-1].zName, zName)>=0 ){ | |
| 653 | - SYNTAX("incorrect F-card sort order"); | |
| 654 | - } | |
| 655 | 673 | p->type = CFTYPE_MANIFEST; |
| 656 | 674 | break; |
| 657 | 675 | } |
| 658 | 676 | |
| 659 | 677 | /* |
| 660 | 678 |
| --- src/manifest.c | |
| +++ src/manifest.c | |
| @@ -481,14 +481,23 @@ | |
| 481 | blob_appendf(pErr, "line 1 not recognized"); |
| 482 | return 0; |
| 483 | } |
| 484 | /* Then verify the Z-card. |
| 485 | */ |
| 486 | if( verify_z_card(z, n, pErr)==2 ){ |
| 487 | blob_reset(pContent); |
| 488 | return 0; |
| 489 | } |
| 490 | |
| 491 | /* Allocate a Manifest object to hold the parsed control artifact. |
| 492 | */ |
| 493 | p = fossil_malloc( sizeof(*p) ); |
| 494 | memset(p, 0, sizeof(*p)); |
| @@ -601,10 +610,11 @@ | |
| 601 | case 'E': { |
| 602 | if( p->rEventDate>0.0 ) SYNTAX("more than one E-card"); |
| 603 | p->rEventDate = db_double(0.0,"SELECT julianday(%Q)", next_token(&x,0)); |
| 604 | if( p->rEventDate<=0.0 ) SYNTAX("malformed date on E-card"); |
| 605 | p->zEventId = next_token(&x, &sz); |
| 606 | if( !hname_validate(p->zEventId, sz) ){ |
| 607 | SYNTAX("malformed hash on E-card"); |
| 608 | } |
| 609 | p->type = CFTYPE_EVENT; |
| 610 | break; |
| @@ -625,10 +635,11 @@ | |
| 625 | if( !file_is_simple_pathname_nonstrict(zName) ){ |
| 626 | SYNTAX("F-card filename is not a simple path"); |
| 627 | } |
| 628 | zUuid = next_token(&x, &sz); |
| 629 | if( p->zBaseline==0 || zUuid!=0 ){ |
| 630 | if( !hname_validate(zUuid,sz) ){ |
| 631 | SYNTAX("F-card hash invalid"); |
| 632 | } |
| 633 | } |
| 634 | zPerm = next_token(&x,0); |
| @@ -643,17 +654,24 @@ | |
| 643 | p->nFileAlloc = p->nFileAlloc*2 + 10; |
| 644 | p->aFile = fossil_realloc(p->aFile, |
| 645 | p->nFileAlloc*sizeof(p->aFile[0]) ); |
| 646 | } |
| 647 | i = p->nFile++; |
| 648 | p->aFile[i].zName = zName; |
| 649 | p->aFile[i].zUuid = zUuid; |
| 650 | p->aFile[i].zPerm = zPerm; |
| 651 | p->aFile[i].zPrior = zPriorName; |
| 652 | if( i>0 && fossil_strcmp(p->aFile[i-1].zName, zName)>=0 ){ |
| 653 | SYNTAX("incorrect F-card sort order"); |
| 654 | } |
| 655 | p->type = CFTYPE_MANIFEST; |
| 656 | break; |
| 657 | } |
| 658 | |
| 659 | /* |
| 660 |
| --- src/manifest.c | |
| +++ src/manifest.c | |
| @@ -481,14 +481,23 @@ | |
| 481 | blob_appendf(pErr, "line 1 not recognized"); |
| 482 | return 0; |
| 483 | } |
| 484 | /* Then verify the Z-card. |
| 485 | */ |
| 486 | #if 1 |
| 487 | /* Disable this ***ONLY*** (ONLY!) when testing hand-written inputs |
| 488 | for card-related syntax errors. */ |
| 489 | if( verify_z_card(z, n, pErr)==2 ){ |
| 490 | blob_reset(pContent); |
| 491 | return 0; |
| 492 | } |
| 493 | #else |
| 494 | #warning ACHTUNG - z-card check is disabled for testing purposes. |
| 495 | if(0 && verify_z_card(NULL, 0, NULL)){ |
| 496 | /*avoid unused static func error*/ |
| 497 | } |
| 498 | #endif |
| 499 | |
| 500 | /* Allocate a Manifest object to hold the parsed control artifact. |
| 501 | */ |
| 502 | p = fossil_malloc( sizeof(*p) ); |
| 503 | memset(p, 0, sizeof(*p)); |
| @@ -601,10 +610,11 @@ | |
| 610 | case 'E': { |
| 611 | if( p->rEventDate>0.0 ) SYNTAX("more than one E-card"); |
| 612 | p->rEventDate = db_double(0.0,"SELECT julianday(%Q)", next_token(&x,0)); |
| 613 | if( p->rEventDate<=0.0 ) SYNTAX("malformed date on E-card"); |
| 614 | p->zEventId = next_token(&x, &sz); |
| 615 | if( p->zEventId==0 ) SYNTAX("missing hash on E-card"); |
| 616 | if( !hname_validate(p->zEventId, sz) ){ |
| 617 | SYNTAX("malformed hash on E-card"); |
| 618 | } |
| 619 | p->type = CFTYPE_EVENT; |
| 620 | break; |
| @@ -625,10 +635,11 @@ | |
| 635 | if( !file_is_simple_pathname_nonstrict(zName) ){ |
| 636 | SYNTAX("F-card filename is not a simple path"); |
| 637 | } |
| 638 | zUuid = next_token(&x, &sz); |
| 639 | if( p->zBaseline==0 || zUuid!=0 ){ |
| 640 | if( zUuid==0 ) SYNTAX("missing hash on F-card"); |
| 641 | if( !hname_validate(zUuid,sz) ){ |
| 642 | SYNTAX("F-card hash invalid"); |
| 643 | } |
| 644 | } |
| 645 | zPerm = next_token(&x,0); |
| @@ -643,17 +654,24 @@ | |
| 654 | p->nFileAlloc = p->nFileAlloc*2 + 10; |
| 655 | p->aFile = fossil_realloc(p->aFile, |
| 656 | p->nFileAlloc*sizeof(p->aFile[0]) ); |
| 657 | } |
| 658 | i = p->nFile++; |
| 659 | if( i>0 && fossil_strcmp(p->aFile[i-1].zName, zName)>=0 ){ |
| 660 | SYNTAX("incorrect F-card sort order"); |
| 661 | } |
| 662 | if( file_is_reserved_name(zName,-1) ){ |
| 663 | /* If reserved names leaked into historical manifests due to |
| 664 | ** slack oversight by older versions of Fossil, simply ignore |
| 665 | ** those files */ |
| 666 | p->nFile--; |
| 667 | break; |
| 668 | } |
| 669 | p->aFile[i].zName = zName; |
| 670 | p->aFile[i].zUuid = zUuid; |
| 671 | p->aFile[i].zPerm = zPerm; |
| 672 | p->aFile[i].zPrior = zPriorName; |
| 673 | p->type = CFTYPE_MANIFEST; |
| 674 | break; |
| 675 | } |
| 676 | |
| 677 | /* |
| 678 |
+5
-1
| --- src/mkindex.c | ||
| +++ src/mkindex.c | ||
| @@ -90,10 +90,11 @@ | ||
| 90 | 90 | #define CMDFLAG_SETTING 0x0020 /* A setting */ |
| 91 | 91 | #define CMDFLAG_VERSIONABLE 0x0040 /* A versionable setting */ |
| 92 | 92 | #define CMDFLAG_BLOCKTEXT 0x0080 /* Multi-line text setting */ |
| 93 | 93 | #define CMDFLAG_BOOLEAN 0x0100 /* A boolean setting */ |
| 94 | 94 | #define CMDFLAG_RAWCONTENT 0x0200 /* Do not interpret webpage content */ |
| 95 | +#define CMDFLAG_SENSITIVE 0x0400 /* Security-sensitive setting */ | |
| 95 | 96 | /**************************************************************************/ |
| 96 | 97 | |
| 97 | 98 | /* |
| 98 | 99 | ** Each entry looks like this: |
| 99 | 100 | */ |
| @@ -248,10 +249,12 @@ | ||
| 248 | 249 | }else if( j==10 && strncmp(&zLine[i], "block-text", j)==0 ){ |
| 249 | 250 | aEntry[nUsed].eType &= ~(CMDFLAG_BOOLEAN); |
| 250 | 251 | aEntry[nUsed].eType |= CMDFLAG_BLOCKTEXT; |
| 251 | 252 | }else if( j==11 && strncmp(&zLine[i], "versionable", j)==0 ){ |
| 252 | 253 | aEntry[nUsed].eType |= CMDFLAG_VERSIONABLE; |
| 254 | + }else if( j==9 && strncmp(&zLine[i], "sensitive", j)==0 ){ | |
| 255 | + aEntry[nUsed].eType |= CMDFLAG_SENSITIVE; | |
| 253 | 256 | }else if( j>6 && strncmp(&zLine[i], "width=", 6)==0 ){ |
| 254 | 257 | aEntry[nUsed].iWidth = atoi(&zLine[i+6]); |
| 255 | 258 | }else if( j>8 && strncmp(&zLine[i], "default=", 8)==0 ){ |
| 256 | 259 | aEntry[nUsed].zDflt = string_dup(&zLine[i+8], j-8); |
| 257 | 260 | }else if( j>9 && strncmp(&zLine[i], "variable=", 9)==0 ){ |
| @@ -479,14 +482,15 @@ | ||
| 479 | 482 | if( zVar ){ |
| 480 | 483 | printf(" \"%s\",%*s", zVar, (int)(15-strlen(zVar)), ""); |
| 481 | 484 | }else{ |
| 482 | 485 | printf(" 0,%*s", 16, ""); |
| 483 | 486 | } |
| 484 | - printf(" %3d, %d, %d, \"%s\"%*s },\n", | |
| 487 | + printf(" %3d, %d, %d, %d, \"%s\"%*s },\n", | |
| 485 | 488 | aEntry[i].iWidth, |
| 486 | 489 | (aEntry[i].eType & CMDFLAG_VERSIONABLE)!=0, |
| 487 | 490 | (aEntry[i].eType & CMDFLAG_BLOCKTEXT)!=0, |
| 491 | + (aEntry[i].eType & CMDFLAG_SENSITIVE)!=0, | |
| 488 | 492 | zDef, (int)(10-strlen(zDef)), "" |
| 489 | 493 | ); |
| 490 | 494 | if( aEntry[i].zIf ){ |
| 491 | 495 | printf("#endif\n"); |
| 492 | 496 | } |
| 493 | 497 |
| --- src/mkindex.c | |
| +++ src/mkindex.c | |
| @@ -90,10 +90,11 @@ | |
| 90 | #define CMDFLAG_SETTING 0x0020 /* A setting */ |
| 91 | #define CMDFLAG_VERSIONABLE 0x0040 /* A versionable setting */ |
| 92 | #define CMDFLAG_BLOCKTEXT 0x0080 /* Multi-line text setting */ |
| 93 | #define CMDFLAG_BOOLEAN 0x0100 /* A boolean setting */ |
| 94 | #define CMDFLAG_RAWCONTENT 0x0200 /* Do not interpret webpage content */ |
| 95 | /**************************************************************************/ |
| 96 | |
| 97 | /* |
| 98 | ** Each entry looks like this: |
| 99 | */ |
| @@ -248,10 +249,12 @@ | |
| 248 | }else if( j==10 && strncmp(&zLine[i], "block-text", j)==0 ){ |
| 249 | aEntry[nUsed].eType &= ~(CMDFLAG_BOOLEAN); |
| 250 | aEntry[nUsed].eType |= CMDFLAG_BLOCKTEXT; |
| 251 | }else if( j==11 && strncmp(&zLine[i], "versionable", j)==0 ){ |
| 252 | aEntry[nUsed].eType |= CMDFLAG_VERSIONABLE; |
| 253 | }else if( j>6 && strncmp(&zLine[i], "width=", 6)==0 ){ |
| 254 | aEntry[nUsed].iWidth = atoi(&zLine[i+6]); |
| 255 | }else if( j>8 && strncmp(&zLine[i], "default=", 8)==0 ){ |
| 256 | aEntry[nUsed].zDflt = string_dup(&zLine[i+8], j-8); |
| 257 | }else if( j>9 && strncmp(&zLine[i], "variable=", 9)==0 ){ |
| @@ -479,14 +482,15 @@ | |
| 479 | if( zVar ){ |
| 480 | printf(" \"%s\",%*s", zVar, (int)(15-strlen(zVar)), ""); |
| 481 | }else{ |
| 482 | printf(" 0,%*s", 16, ""); |
| 483 | } |
| 484 | printf(" %3d, %d, %d, \"%s\"%*s },\n", |
| 485 | aEntry[i].iWidth, |
| 486 | (aEntry[i].eType & CMDFLAG_VERSIONABLE)!=0, |
| 487 | (aEntry[i].eType & CMDFLAG_BLOCKTEXT)!=0, |
| 488 | zDef, (int)(10-strlen(zDef)), "" |
| 489 | ); |
| 490 | if( aEntry[i].zIf ){ |
| 491 | printf("#endif\n"); |
| 492 | } |
| 493 |
| --- src/mkindex.c | |
| +++ src/mkindex.c | |
| @@ -90,10 +90,11 @@ | |
| 90 | #define CMDFLAG_SETTING 0x0020 /* A setting */ |
| 91 | #define CMDFLAG_VERSIONABLE 0x0040 /* A versionable setting */ |
| 92 | #define CMDFLAG_BLOCKTEXT 0x0080 /* Multi-line text setting */ |
| 93 | #define CMDFLAG_BOOLEAN 0x0100 /* A boolean setting */ |
| 94 | #define CMDFLAG_RAWCONTENT 0x0200 /* Do not interpret webpage content */ |
| 95 | #define CMDFLAG_SENSITIVE 0x0400 /* Security-sensitive setting */ |
| 96 | /**************************************************************************/ |
| 97 | |
| 98 | /* |
| 99 | ** Each entry looks like this: |
| 100 | */ |
| @@ -248,10 +249,12 @@ | |
| 249 | }else if( j==10 && strncmp(&zLine[i], "block-text", j)==0 ){ |
| 250 | aEntry[nUsed].eType &= ~(CMDFLAG_BOOLEAN); |
| 251 | aEntry[nUsed].eType |= CMDFLAG_BLOCKTEXT; |
| 252 | }else if( j==11 && strncmp(&zLine[i], "versionable", j)==0 ){ |
| 253 | aEntry[nUsed].eType |= CMDFLAG_VERSIONABLE; |
| 254 | }else if( j==9 && strncmp(&zLine[i], "sensitive", j)==0 ){ |
| 255 | aEntry[nUsed].eType |= CMDFLAG_SENSITIVE; |
| 256 | }else if( j>6 && strncmp(&zLine[i], "width=", 6)==0 ){ |
| 257 | aEntry[nUsed].iWidth = atoi(&zLine[i+6]); |
| 258 | }else if( j>8 && strncmp(&zLine[i], "default=", 8)==0 ){ |
| 259 | aEntry[nUsed].zDflt = string_dup(&zLine[i+8], j-8); |
| 260 | }else if( j>9 && strncmp(&zLine[i], "variable=", 9)==0 ){ |
| @@ -479,14 +482,15 @@ | |
| 482 | if( zVar ){ |
| 483 | printf(" \"%s\",%*s", zVar, (int)(15-strlen(zVar)), ""); |
| 484 | }else{ |
| 485 | printf(" 0,%*s", 16, ""); |
| 486 | } |
| 487 | printf(" %3d, %d, %d, %d, \"%s\"%*s },\n", |
| 488 | aEntry[i].iWidth, |
| 489 | (aEntry[i].eType & CMDFLAG_VERSIONABLE)!=0, |
| 490 | (aEntry[i].eType & CMDFLAG_BLOCKTEXT)!=0, |
| 491 | (aEntry[i].eType & CMDFLAG_SENSITIVE)!=0, |
| 492 | zDef, (int)(10-strlen(zDef)), "" |
| 493 | ); |
| 494 | if( aEntry[i].zIf ){ |
| 495 | printf("#endif\n"); |
| 496 | } |
| 497 |
+3
| --- src/printf.c | ||
| +++ src/printf.c | ||
| @@ -1148,12 +1148,15 @@ | ||
| 1148 | 1148 | rc = fossil_print_error(rc, z); |
| 1149 | 1149 | abort(); |
| 1150 | 1150 | exit(rc); |
| 1151 | 1151 | } |
| 1152 | 1152 | NORETURN void fossil_fatal(const char *zFormat, ...){ |
| 1153 | + static int once = 0; | |
| 1153 | 1154 | char *z; |
| 1154 | 1155 | int rc = 1; |
| 1156 | + if( once ) exit(1); | |
| 1157 | + once = 1; | |
| 1155 | 1158 | va_list ap; |
| 1156 | 1159 | mainInFatalError = 1; |
| 1157 | 1160 | va_start(ap, zFormat); |
| 1158 | 1161 | z = vmprintf(zFormat, ap); |
| 1159 | 1162 | va_end(ap); |
| 1160 | 1163 |
| --- src/printf.c | |
| +++ src/printf.c | |
| @@ -1148,12 +1148,15 @@ | |
| 1148 | rc = fossil_print_error(rc, z); |
| 1149 | abort(); |
| 1150 | exit(rc); |
| 1151 | } |
| 1152 | NORETURN void fossil_fatal(const char *zFormat, ...){ |
| 1153 | char *z; |
| 1154 | int rc = 1; |
| 1155 | va_list ap; |
| 1156 | mainInFatalError = 1; |
| 1157 | va_start(ap, zFormat); |
| 1158 | z = vmprintf(zFormat, ap); |
| 1159 | va_end(ap); |
| 1160 |
| --- src/printf.c | |
| +++ src/printf.c | |
| @@ -1148,12 +1148,15 @@ | |
| 1148 | rc = fossil_print_error(rc, z); |
| 1149 | abort(); |
| 1150 | exit(rc); |
| 1151 | } |
| 1152 | NORETURN void fossil_fatal(const char *zFormat, ...){ |
| 1153 | static int once = 0; |
| 1154 | char *z; |
| 1155 | int rc = 1; |
| 1156 | if( once ) exit(1); |
| 1157 | once = 1; |
| 1158 | va_list ap; |
| 1159 | mainInFatalError = 1; |
| 1160 | va_start(ap, zFormat); |
| 1161 | z = vmprintf(zFormat, ap); |
| 1162 | va_end(ap); |
| 1163 |
+14
| --- src/rebuild.c | ||
| +++ src/rebuild.c | ||
| @@ -52,10 +52,11 @@ | ||
| 52 | 52 | } |
| 53 | 53 | |
| 54 | 54 | /* Add the user.mtime column if it is missing. (2011-04-27) |
| 55 | 55 | */ |
| 56 | 56 | if( !db_table_has_column("repository", "user", "mtime") ){ |
| 57 | + db_unprotect(PROTECT_ALL); | |
| 57 | 58 | db_multi_exec( |
| 58 | 59 | "CREATE TEMP TABLE temp_user AS SELECT * FROM user;" |
| 59 | 60 | "DROP TABLE user;" |
| 60 | 61 | "CREATE TABLE user(\n" |
| 61 | 62 | " uid INTEGER PRIMARY KEY,\n" |
| @@ -72,19 +73,22 @@ | ||
| 72 | 73 | "INSERT OR IGNORE INTO user" |
| 73 | 74 | " SELECT uid, login, pw, cap, cookie," |
| 74 | 75 | " ipaddr, cexpire, info, now(), photo FROM temp_user;" |
| 75 | 76 | "DROP TABLE temp_user;" |
| 76 | 77 | ); |
| 78 | + db_protect_pop(); | |
| 77 | 79 | } |
| 78 | 80 | |
| 79 | 81 | /* Add the config.mtime column if it is missing. (2011-04-27) |
| 80 | 82 | */ |
| 81 | 83 | if( !db_table_has_column("repository", "config", "mtime") ){ |
| 84 | + db_unprotect(PROTECT_CONFIG); | |
| 82 | 85 | db_multi_exec( |
| 83 | 86 | "ALTER TABLE config ADD COLUMN mtime INTEGER;" |
| 84 | 87 | "UPDATE config SET mtime=now();" |
| 85 | 88 | ); |
| 89 | + db_protect_pop(); | |
| 86 | 90 | } |
| 87 | 91 | |
| 88 | 92 | /* Add the shun.mtime and shun.scom columns if they are missing. |
| 89 | 93 | ** (2011-04-27) |
| 90 | 94 | */ |
| @@ -382,10 +386,11 @@ | ||
| 382 | 386 | percent_complete(0); |
| 383 | 387 | } |
| 384 | 388 | alert_triggers_disable(); |
| 385 | 389 | rebuild_update_schema(); |
| 386 | 390 | blob_init(&sql, 0, 0); |
| 391 | + db_unprotect(PROTECT_ALL); | |
| 387 | 392 | db_prepare(&q, |
| 388 | 393 | "SELECT name FROM sqlite_schema /*scan*/" |
| 389 | 394 | " WHERE type='table'" |
| 390 | 395 | " AND name NOT IN ('admin_log', 'blob','delta','rcvfrom','user','alias'," |
| 391 | 396 | "'config','shun','private','reportfmt'," |
| @@ -475,10 +480,11 @@ | ||
| 475 | 480 | alert_triggers_enable(); |
| 476 | 481 | if(!g.fQuiet && ttyOutput ){ |
| 477 | 482 | percent_complete(1000); |
| 478 | 483 | fossil_print("\n"); |
| 479 | 484 | } |
| 485 | + db_protect_pop(); | |
| 480 | 486 | return errCnt; |
| 481 | 487 | } |
| 482 | 488 | |
| 483 | 489 | /* |
| 484 | 490 | ** Number of neighbors to search |
| @@ -667,10 +673,11 @@ | ||
| 667 | 673 | |
| 668 | 674 | /* We should be done with options.. */ |
| 669 | 675 | verify_all_options(); |
| 670 | 676 | |
| 671 | 677 | db_begin_transaction(); |
| 678 | + db_unprotect(PROTECT_ALL); | |
| 672 | 679 | if( !compressOnlyFlag ){ |
| 673 | 680 | search_drop_index(); |
| 674 | 681 | ttyOutput = 1; |
| 675 | 682 | errCnt = rebuild_db(randomizeFlag, 1, doClustering); |
| 676 | 683 | reconstruct_private_table(); |
| @@ -720,10 +727,11 @@ | ||
| 720 | 727 | if( activateWal ){ |
| 721 | 728 | db_multi_exec("PRAGMA journal_mode=WAL;"); |
| 722 | 729 | } |
| 723 | 730 | } |
| 724 | 731 | if( runReindex ) search_rebuild_index(); |
| 732 | + db_protect_pop(); | |
| 725 | 733 | if( showStats ){ |
| 726 | 734 | static const struct { int idx; const char *zLabel; } aStat[] = { |
| 727 | 735 | { CFTYPE_ANY, "Artifacts:" }, |
| 728 | 736 | { CFTYPE_MANIFEST, "Manifests:" }, |
| 729 | 737 | { CFTYPE_CLUSTER, "Clusters:" }, |
| @@ -755,18 +763,20 @@ | ||
| 755 | 763 | ** testing by cloning a working project repository. |
| 756 | 764 | */ |
| 757 | 765 | void test_detach_cmd(void){ |
| 758 | 766 | db_find_and_open_repository(0, 2); |
| 759 | 767 | db_begin_transaction(); |
| 768 | + db_unprotect(PROTECT_CONFIG); | |
| 760 | 769 | db_multi_exec( |
| 761 | 770 | "DELETE FROM config WHERE name GLOB 'last-sync-*';" |
| 762 | 771 | "DELETE FROM config WHERE name GLOB 'sync-*:*';" |
| 763 | 772 | "UPDATE config SET value=lower(hex(randomblob(20)))" |
| 764 | 773 | " WHERE name='project-code';" |
| 765 | 774 | "UPDATE config SET value='detached-' || value" |
| 766 | 775 | " WHERE name='project-name' AND value NOT GLOB 'detached-*';" |
| 767 | 776 | ); |
| 777 | + db_protect_pop(); | |
| 768 | 778 | db_end_transaction(0); |
| 769 | 779 | } |
| 770 | 780 | |
| 771 | 781 | /* |
| 772 | 782 | ** COMMAND: test-create-clusters |
| @@ -910,10 +920,11 @@ | ||
| 910 | 920 | if( privateOnly || bVerily ){ |
| 911 | 921 | bNeedRebuild = db_exists("SELECT 1 FROM private"); |
| 912 | 922 | delete_private_content(); |
| 913 | 923 | } |
| 914 | 924 | if( !privateOnly ){ |
| 925 | + db_unprotect(PROTECT_ALL); | |
| 915 | 926 | db_multi_exec( |
| 916 | 927 | "UPDATE user SET pw='';" |
| 917 | 928 | "DELETE FROM config WHERE name GLOB 'last-sync-*';" |
| 918 | 929 | "DELETE FROM config WHERE name GLOB 'sync-*:*';" |
| 919 | 930 | "DELETE FROM config WHERE name GLOB 'peer-*';" |
| @@ -933,14 +944,17 @@ | ||
| 933 | 944 | "DROP TABLE IF EXISTS purgeitem;\n" |
| 934 | 945 | "DROP TABLE IF EXISTS admin_log;\n" |
| 935 | 946 | "DROP TABLE IF EXISTS vcache;\n" |
| 936 | 947 | ); |
| 937 | 948 | } |
| 949 | + db_protect_pop(); | |
| 938 | 950 | } |
| 939 | 951 | if( !bNeedRebuild ){ |
| 940 | 952 | db_end_transaction(0); |
| 953 | + db_unprotect(PROTECT_ALL); | |
| 941 | 954 | db_multi_exec("VACUUM;"); |
| 955 | + db_protect_pop(); | |
| 942 | 956 | }else{ |
| 943 | 957 | rebuild_db(0, 1, 0); |
| 944 | 958 | db_end_transaction(0); |
| 945 | 959 | } |
| 946 | 960 | } |
| 947 | 961 |
| --- src/rebuild.c | |
| +++ src/rebuild.c | |
| @@ -52,10 +52,11 @@ | |
| 52 | } |
| 53 | |
| 54 | /* Add the user.mtime column if it is missing. (2011-04-27) |
| 55 | */ |
| 56 | if( !db_table_has_column("repository", "user", "mtime") ){ |
| 57 | db_multi_exec( |
| 58 | "CREATE TEMP TABLE temp_user AS SELECT * FROM user;" |
| 59 | "DROP TABLE user;" |
| 60 | "CREATE TABLE user(\n" |
| 61 | " uid INTEGER PRIMARY KEY,\n" |
| @@ -72,19 +73,22 @@ | |
| 72 | "INSERT OR IGNORE INTO user" |
| 73 | " SELECT uid, login, pw, cap, cookie," |
| 74 | " ipaddr, cexpire, info, now(), photo FROM temp_user;" |
| 75 | "DROP TABLE temp_user;" |
| 76 | ); |
| 77 | } |
| 78 | |
| 79 | /* Add the config.mtime column if it is missing. (2011-04-27) |
| 80 | */ |
| 81 | if( !db_table_has_column("repository", "config", "mtime") ){ |
| 82 | db_multi_exec( |
| 83 | "ALTER TABLE config ADD COLUMN mtime INTEGER;" |
| 84 | "UPDATE config SET mtime=now();" |
| 85 | ); |
| 86 | } |
| 87 | |
| 88 | /* Add the shun.mtime and shun.scom columns if they are missing. |
| 89 | ** (2011-04-27) |
| 90 | */ |
| @@ -382,10 +386,11 @@ | |
| 382 | percent_complete(0); |
| 383 | } |
| 384 | alert_triggers_disable(); |
| 385 | rebuild_update_schema(); |
| 386 | blob_init(&sql, 0, 0); |
| 387 | db_prepare(&q, |
| 388 | "SELECT name FROM sqlite_schema /*scan*/" |
| 389 | " WHERE type='table'" |
| 390 | " AND name NOT IN ('admin_log', 'blob','delta','rcvfrom','user','alias'," |
| 391 | "'config','shun','private','reportfmt'," |
| @@ -475,10 +480,11 @@ | |
| 475 | alert_triggers_enable(); |
| 476 | if(!g.fQuiet && ttyOutput ){ |
| 477 | percent_complete(1000); |
| 478 | fossil_print("\n"); |
| 479 | } |
| 480 | return errCnt; |
| 481 | } |
| 482 | |
| 483 | /* |
| 484 | ** Number of neighbors to search |
| @@ -667,10 +673,11 @@ | |
| 667 | |
| 668 | /* We should be done with options.. */ |
| 669 | verify_all_options(); |
| 670 | |
| 671 | db_begin_transaction(); |
| 672 | if( !compressOnlyFlag ){ |
| 673 | search_drop_index(); |
| 674 | ttyOutput = 1; |
| 675 | errCnt = rebuild_db(randomizeFlag, 1, doClustering); |
| 676 | reconstruct_private_table(); |
| @@ -720,10 +727,11 @@ | |
| 720 | if( activateWal ){ |
| 721 | db_multi_exec("PRAGMA journal_mode=WAL;"); |
| 722 | } |
| 723 | } |
| 724 | if( runReindex ) search_rebuild_index(); |
| 725 | if( showStats ){ |
| 726 | static const struct { int idx; const char *zLabel; } aStat[] = { |
| 727 | { CFTYPE_ANY, "Artifacts:" }, |
| 728 | { CFTYPE_MANIFEST, "Manifests:" }, |
| 729 | { CFTYPE_CLUSTER, "Clusters:" }, |
| @@ -755,18 +763,20 @@ | |
| 755 | ** testing by cloning a working project repository. |
| 756 | */ |
| 757 | void test_detach_cmd(void){ |
| 758 | db_find_and_open_repository(0, 2); |
| 759 | db_begin_transaction(); |
| 760 | db_multi_exec( |
| 761 | "DELETE FROM config WHERE name GLOB 'last-sync-*';" |
| 762 | "DELETE FROM config WHERE name GLOB 'sync-*:*';" |
| 763 | "UPDATE config SET value=lower(hex(randomblob(20)))" |
| 764 | " WHERE name='project-code';" |
| 765 | "UPDATE config SET value='detached-' || value" |
| 766 | " WHERE name='project-name' AND value NOT GLOB 'detached-*';" |
| 767 | ); |
| 768 | db_end_transaction(0); |
| 769 | } |
| 770 | |
| 771 | /* |
| 772 | ** COMMAND: test-create-clusters |
| @@ -910,10 +920,11 @@ | |
| 910 | if( privateOnly || bVerily ){ |
| 911 | bNeedRebuild = db_exists("SELECT 1 FROM private"); |
| 912 | delete_private_content(); |
| 913 | } |
| 914 | if( !privateOnly ){ |
| 915 | db_multi_exec( |
| 916 | "UPDATE user SET pw='';" |
| 917 | "DELETE FROM config WHERE name GLOB 'last-sync-*';" |
| 918 | "DELETE FROM config WHERE name GLOB 'sync-*:*';" |
| 919 | "DELETE FROM config WHERE name GLOB 'peer-*';" |
| @@ -933,14 +944,17 @@ | |
| 933 | "DROP TABLE IF EXISTS purgeitem;\n" |
| 934 | "DROP TABLE IF EXISTS admin_log;\n" |
| 935 | "DROP TABLE IF EXISTS vcache;\n" |
| 936 | ); |
| 937 | } |
| 938 | } |
| 939 | if( !bNeedRebuild ){ |
| 940 | db_end_transaction(0); |
| 941 | db_multi_exec("VACUUM;"); |
| 942 | }else{ |
| 943 | rebuild_db(0, 1, 0); |
| 944 | db_end_transaction(0); |
| 945 | } |
| 946 | } |
| 947 |
| --- src/rebuild.c | |
| +++ src/rebuild.c | |
| @@ -52,10 +52,11 @@ | |
| 52 | } |
| 53 | |
| 54 | /* Add the user.mtime column if it is missing. (2011-04-27) |
| 55 | */ |
| 56 | if( !db_table_has_column("repository", "user", "mtime") ){ |
| 57 | db_unprotect(PROTECT_ALL); |
| 58 | db_multi_exec( |
| 59 | "CREATE TEMP TABLE temp_user AS SELECT * FROM user;" |
| 60 | "DROP TABLE user;" |
| 61 | "CREATE TABLE user(\n" |
| 62 | " uid INTEGER PRIMARY KEY,\n" |
| @@ -72,19 +73,22 @@ | |
| 73 | "INSERT OR IGNORE INTO user" |
| 74 | " SELECT uid, login, pw, cap, cookie," |
| 75 | " ipaddr, cexpire, info, now(), photo FROM temp_user;" |
| 76 | "DROP TABLE temp_user;" |
| 77 | ); |
| 78 | db_protect_pop(); |
| 79 | } |
| 80 | |
| 81 | /* Add the config.mtime column if it is missing. (2011-04-27) |
| 82 | */ |
| 83 | if( !db_table_has_column("repository", "config", "mtime") ){ |
| 84 | db_unprotect(PROTECT_CONFIG); |
| 85 | db_multi_exec( |
| 86 | "ALTER TABLE config ADD COLUMN mtime INTEGER;" |
| 87 | "UPDATE config SET mtime=now();" |
| 88 | ); |
| 89 | db_protect_pop(); |
| 90 | } |
| 91 | |
| 92 | /* Add the shun.mtime and shun.scom columns if they are missing. |
| 93 | ** (2011-04-27) |
| 94 | */ |
| @@ -382,10 +386,11 @@ | |
| 386 | percent_complete(0); |
| 387 | } |
| 388 | alert_triggers_disable(); |
| 389 | rebuild_update_schema(); |
| 390 | blob_init(&sql, 0, 0); |
| 391 | db_unprotect(PROTECT_ALL); |
| 392 | db_prepare(&q, |
| 393 | "SELECT name FROM sqlite_schema /*scan*/" |
| 394 | " WHERE type='table'" |
| 395 | " AND name NOT IN ('admin_log', 'blob','delta','rcvfrom','user','alias'," |
| 396 | "'config','shun','private','reportfmt'," |
| @@ -475,10 +480,11 @@ | |
| 480 | alert_triggers_enable(); |
| 481 | if(!g.fQuiet && ttyOutput ){ |
| 482 | percent_complete(1000); |
| 483 | fossil_print("\n"); |
| 484 | } |
| 485 | db_protect_pop(); |
| 486 | return errCnt; |
| 487 | } |
| 488 | |
| 489 | /* |
| 490 | ** Number of neighbors to search |
| @@ -667,10 +673,11 @@ | |
| 673 | |
| 674 | /* We should be done with options.. */ |
| 675 | verify_all_options(); |
| 676 | |
| 677 | db_begin_transaction(); |
| 678 | db_unprotect(PROTECT_ALL); |
| 679 | if( !compressOnlyFlag ){ |
| 680 | search_drop_index(); |
| 681 | ttyOutput = 1; |
| 682 | errCnt = rebuild_db(randomizeFlag, 1, doClustering); |
| 683 | reconstruct_private_table(); |
| @@ -720,10 +727,11 @@ | |
| 727 | if( activateWal ){ |
| 728 | db_multi_exec("PRAGMA journal_mode=WAL;"); |
| 729 | } |
| 730 | } |
| 731 | if( runReindex ) search_rebuild_index(); |
| 732 | db_protect_pop(); |
| 733 | if( showStats ){ |
| 734 | static const struct { int idx; const char *zLabel; } aStat[] = { |
| 735 | { CFTYPE_ANY, "Artifacts:" }, |
| 736 | { CFTYPE_MANIFEST, "Manifests:" }, |
| 737 | { CFTYPE_CLUSTER, "Clusters:" }, |
| @@ -755,18 +763,20 @@ | |
| 763 | ** testing by cloning a working project repository. |
| 764 | */ |
| 765 | void test_detach_cmd(void){ |
| 766 | db_find_and_open_repository(0, 2); |
| 767 | db_begin_transaction(); |
| 768 | db_unprotect(PROTECT_CONFIG); |
| 769 | db_multi_exec( |
| 770 | "DELETE FROM config WHERE name GLOB 'last-sync-*';" |
| 771 | "DELETE FROM config WHERE name GLOB 'sync-*:*';" |
| 772 | "UPDATE config SET value=lower(hex(randomblob(20)))" |
| 773 | " WHERE name='project-code';" |
| 774 | "UPDATE config SET value='detached-' || value" |
| 775 | " WHERE name='project-name' AND value NOT GLOB 'detached-*';" |
| 776 | ); |
| 777 | db_protect_pop(); |
| 778 | db_end_transaction(0); |
| 779 | } |
| 780 | |
| 781 | /* |
| 782 | ** COMMAND: test-create-clusters |
| @@ -910,10 +920,11 @@ | |
| 920 | if( privateOnly || bVerily ){ |
| 921 | bNeedRebuild = db_exists("SELECT 1 FROM private"); |
| 922 | delete_private_content(); |
| 923 | } |
| 924 | if( !privateOnly ){ |
| 925 | db_unprotect(PROTECT_ALL); |
| 926 | db_multi_exec( |
| 927 | "UPDATE user SET pw='';" |
| 928 | "DELETE FROM config WHERE name GLOB 'last-sync-*';" |
| 929 | "DELETE FROM config WHERE name GLOB 'sync-*:*';" |
| 930 | "DELETE FROM config WHERE name GLOB 'peer-*';" |
| @@ -933,14 +944,17 @@ | |
| 944 | "DROP TABLE IF EXISTS purgeitem;\n" |
| 945 | "DROP TABLE IF EXISTS admin_log;\n" |
| 946 | "DROP TABLE IF EXISTS vcache;\n" |
| 947 | ); |
| 948 | } |
| 949 | db_protect_pop(); |
| 950 | } |
| 951 | if( !bNeedRebuild ){ |
| 952 | db_end_transaction(0); |
| 953 | db_unprotect(PROTECT_ALL); |
| 954 | db_multi_exec("VACUUM;"); |
| 955 | db_protect_pop(); |
| 956 | }else{ |
| 957 | rebuild_db(0, 1, 0); |
| 958 | db_end_transaction(0); |
| 959 | } |
| 960 | } |
| 961 |
+3
-3
| --- src/report.c | ||
| +++ src/report.c | ||
| @@ -230,15 +230,15 @@ | ||
| 230 | 230 | |
| 231 | 231 | /* |
| 232 | 232 | ** Activate the query authorizer |
| 233 | 233 | */ |
| 234 | 234 | void report_restrict_sql(char **pzErr){ |
| 235 | - sqlite3_set_authorizer(g.db, report_query_authorizer, (void*)pzErr); | |
| 235 | + db_set_authorizer(report_query_authorizer,(void*)pzErr,"Ticket-Report"); | |
| 236 | 236 | sqlite3_limit(g.db, SQLITE_LIMIT_VDBE_OP, 10000); |
| 237 | 237 | } |
| 238 | 238 | void report_unrestrict_sql(void){ |
| 239 | - sqlite3_set_authorizer(g.db, 0, 0); | |
| 239 | + db_clear_authorizer(); | |
| 240 | 240 | } |
| 241 | 241 | |
| 242 | 242 | |
| 243 | 243 | /* |
| 244 | 244 | ** Check the given SQL to see if is a valid query that does not |
| @@ -680,11 +680,11 @@ | ||
| 680 | 680 | */ |
| 681 | 681 | if( pState->nCount==0 ){ |
| 682 | 682 | /* Turn off the authorizer. It is no longer doing anything since the |
| 683 | 683 | ** query has already been prepared. |
| 684 | 684 | */ |
| 685 | - sqlite3_set_authorizer(g.db, 0, 0); | |
| 685 | + db_clear_authorizer(); | |
| 686 | 686 | |
| 687 | 687 | /* Figure out the number of columns, the column that determines background |
| 688 | 688 | ** color, and whether or not this row of data is represented by multiple |
| 689 | 689 | ** rows in the table. |
| 690 | 690 | */ |
| 691 | 691 |
| --- src/report.c | |
| +++ src/report.c | |
| @@ -230,15 +230,15 @@ | |
| 230 | |
| 231 | /* |
| 232 | ** Activate the query authorizer |
| 233 | */ |
| 234 | void report_restrict_sql(char **pzErr){ |
| 235 | sqlite3_set_authorizer(g.db, report_query_authorizer, (void*)pzErr); |
| 236 | sqlite3_limit(g.db, SQLITE_LIMIT_VDBE_OP, 10000); |
| 237 | } |
| 238 | void report_unrestrict_sql(void){ |
| 239 | sqlite3_set_authorizer(g.db, 0, 0); |
| 240 | } |
| 241 | |
| 242 | |
| 243 | /* |
| 244 | ** Check the given SQL to see if is a valid query that does not |
| @@ -680,11 +680,11 @@ | |
| 680 | */ |
| 681 | if( pState->nCount==0 ){ |
| 682 | /* Turn off the authorizer. It is no longer doing anything since the |
| 683 | ** query has already been prepared. |
| 684 | */ |
| 685 | sqlite3_set_authorizer(g.db, 0, 0); |
| 686 | |
| 687 | /* Figure out the number of columns, the column that determines background |
| 688 | ** color, and whether or not this row of data is represented by multiple |
| 689 | ** rows in the table. |
| 690 | */ |
| 691 |
| --- src/report.c | |
| +++ src/report.c | |
| @@ -230,15 +230,15 @@ | |
| 230 | |
| 231 | /* |
| 232 | ** Activate the query authorizer |
| 233 | */ |
| 234 | void report_restrict_sql(char **pzErr){ |
| 235 | db_set_authorizer(report_query_authorizer,(void*)pzErr,"Ticket-Report"); |
| 236 | sqlite3_limit(g.db, SQLITE_LIMIT_VDBE_OP, 10000); |
| 237 | } |
| 238 | void report_unrestrict_sql(void){ |
| 239 | db_clear_authorizer(); |
| 240 | } |
| 241 | |
| 242 | |
| 243 | /* |
| 244 | ** Check the given SQL to see if is a valid query that does not |
| @@ -680,11 +680,11 @@ | |
| 680 | */ |
| 681 | if( pState->nCount==0 ){ |
| 682 | /* Turn off the authorizer. It is no longer doing anything since the |
| 683 | ** query has already been prepared. |
| 684 | */ |
| 685 | db_clear_authorizer(); |
| 686 | |
| 687 | /* Figure out the number of columns, the column that determines background |
| 688 | ** color, and whether or not this row of data is represented by multiple |
| 689 | ** rows in the table. |
| 690 | */ |
| 691 |
+10
| --- src/security_audit.c | ||
| +++ src/security_audit.c | ||
| @@ -281,10 +281,18 @@ | ||
| 281 | 281 | @ <p>Fix this by removing the "Mod-Wiki", "Mod-Tkt", and "Mod-Forum" |
| 282 | 282 | @ privileges (<a href="%R/setup_ucap_list">capabilities</a> "fq5") |
| 283 | 283 | @ from users "anonymous" and "nobody" |
| 284 | 284 | @ on the <a href="setup_ulist">User Configuration</a> page. |
| 285 | 285 | } |
| 286 | + | |
| 287 | + /* The strict-manifest-syntax setting should be on. */ | |
| 288 | + if( db_get_boolean("strict-manifest-syntax",1)==0 ){ | |
| 289 | + @ <li><p><b>WARNING:</b> | |
| 290 | + @ The "strict-manifest-syntax" flag is off. This is a security | |
| 291 | + @ risk. Turn this setting on (its default) to protect the users | |
| 292 | + @ of this repository. | |
| 293 | + } | |
| 286 | 294 | |
| 287 | 295 | /* Obsolete: */ |
| 288 | 296 | if( hasAnyCap(zAnonCap, "d") || |
| 289 | 297 | hasAnyCap(zDevCap, "d") || |
| 290 | 298 | hasAnyCap(zReadCap, "d") ){ |
| @@ -596,15 +604,17 @@ | ||
| 596 | 604 | if( P("cancel") ){ |
| 597 | 605 | /* User pressed the cancel button. Go back */ |
| 598 | 606 | cgi_redirect("secaudit0"); |
| 599 | 607 | } |
| 600 | 608 | if( P("apply") ){ |
| 609 | + db_unprotect(PROTECT_USER); | |
| 601 | 610 | db_multi_exec( |
| 602 | 611 | "UPDATE user SET cap=''" |
| 603 | 612 | " WHERE login IN ('nobody','anonymous');" |
| 604 | 613 | "DELETE FROM config WHERE name='public-pages';" |
| 605 | 614 | ); |
| 615 | + db_protect_pop(); | |
| 606 | 616 | db_set("self-register","0",0); |
| 607 | 617 | cgi_redirect("secaudit0"); |
| 608 | 618 | } |
| 609 | 619 | style_header("Make This Website Private"); |
| 610 | 620 | @ <p>Click the "Make It Private" button below to disable all |
| 611 | 621 |
| --- src/security_audit.c | |
| +++ src/security_audit.c | |
| @@ -281,10 +281,18 @@ | |
| 281 | @ <p>Fix this by removing the "Mod-Wiki", "Mod-Tkt", and "Mod-Forum" |
| 282 | @ privileges (<a href="%R/setup_ucap_list">capabilities</a> "fq5") |
| 283 | @ from users "anonymous" and "nobody" |
| 284 | @ on the <a href="setup_ulist">User Configuration</a> page. |
| 285 | } |
| 286 | |
| 287 | /* Obsolete: */ |
| 288 | if( hasAnyCap(zAnonCap, "d") || |
| 289 | hasAnyCap(zDevCap, "d") || |
| 290 | hasAnyCap(zReadCap, "d") ){ |
| @@ -596,15 +604,17 @@ | |
| 596 | if( P("cancel") ){ |
| 597 | /* User pressed the cancel button. Go back */ |
| 598 | cgi_redirect("secaudit0"); |
| 599 | } |
| 600 | if( P("apply") ){ |
| 601 | db_multi_exec( |
| 602 | "UPDATE user SET cap=''" |
| 603 | " WHERE login IN ('nobody','anonymous');" |
| 604 | "DELETE FROM config WHERE name='public-pages';" |
| 605 | ); |
| 606 | db_set("self-register","0",0); |
| 607 | cgi_redirect("secaudit0"); |
| 608 | } |
| 609 | style_header("Make This Website Private"); |
| 610 | @ <p>Click the "Make It Private" button below to disable all |
| 611 |
| --- src/security_audit.c | |
| +++ src/security_audit.c | |
| @@ -281,10 +281,18 @@ | |
| 281 | @ <p>Fix this by removing the "Mod-Wiki", "Mod-Tkt", and "Mod-Forum" |
| 282 | @ privileges (<a href="%R/setup_ucap_list">capabilities</a> "fq5") |
| 283 | @ from users "anonymous" and "nobody" |
| 284 | @ on the <a href="setup_ulist">User Configuration</a> page. |
| 285 | } |
| 286 | |
| 287 | /* The strict-manifest-syntax setting should be on. */ |
| 288 | if( db_get_boolean("strict-manifest-syntax",1)==0 ){ |
| 289 | @ <li><p><b>WARNING:</b> |
| 290 | @ The "strict-manifest-syntax" flag is off. This is a security |
| 291 | @ risk. Turn this setting on (its default) to protect the users |
| 292 | @ of this repository. |
| 293 | } |
| 294 | |
| 295 | /* Obsolete: */ |
| 296 | if( hasAnyCap(zAnonCap, "d") || |
| 297 | hasAnyCap(zDevCap, "d") || |
| 298 | hasAnyCap(zReadCap, "d") ){ |
| @@ -596,15 +604,17 @@ | |
| 604 | if( P("cancel") ){ |
| 605 | /* User pressed the cancel button. Go back */ |
| 606 | cgi_redirect("secaudit0"); |
| 607 | } |
| 608 | if( P("apply") ){ |
| 609 | db_unprotect(PROTECT_USER); |
| 610 | db_multi_exec( |
| 611 | "UPDATE user SET cap=''" |
| 612 | " WHERE login IN ('nobody','anonymous');" |
| 613 | "DELETE FROM config WHERE name='public-pages';" |
| 614 | ); |
| 615 | db_protect_pop(); |
| 616 | db_set("self-register","0",0); |
| 617 | cgi_redirect("secaudit0"); |
| 618 | } |
| 619 | style_header("Make This Website Private"); |
| 620 | @ <p>Click the "Make It Private" button below to disable all |
| 621 |
+22
| --- src/setup.c | ||
| +++ src/setup.c | ||
| @@ -27,14 +27,16 @@ | ||
| 27 | 27 | */ |
| 28 | 28 | void setup_incr_cfgcnt(void){ |
| 29 | 29 | static int once = 1; |
| 30 | 30 | if( once ){ |
| 31 | 31 | once = 0; |
| 32 | + db_unprotect(PROTECT_CONFIG); | |
| 32 | 33 | db_multi_exec("UPDATE config SET value=value+1 WHERE name='cfgcnt'"); |
| 33 | 34 | if( db_changes()==0 ){ |
| 34 | 35 | db_multi_exec("INSERT INTO config(name,value) VALUES('cfgcnt',1)"); |
| 35 | 36 | } |
| 37 | + db_protect_pop(); | |
| 36 | 38 | } |
| 37 | 39 | } |
| 38 | 40 | |
| 39 | 41 | /* |
| 40 | 42 | ** Output a single entry for a menu generated using an HTML table. |
| @@ -195,11 +197,13 @@ | ||
| 195 | 197 | } |
| 196 | 198 | if( zQ ){ |
| 197 | 199 | int iQ = fossil_strcmp(zQ,"on")==0 || atoi(zQ); |
| 198 | 200 | if( iQ!=iVal ){ |
| 199 | 201 | login_verify_csrf_secret(); |
| 202 | + db_protect_only(PROTECT_NONE); | |
| 200 | 203 | db_set(zVar, iQ ? "1" : "0", 0); |
| 204 | + db_protect_pop(); | |
| 201 | 205 | setup_incr_cfgcnt(); |
| 202 | 206 | admin_log("Set option [%q] to [%q].", |
| 203 | 207 | zVar, iQ ? "on" : "off"); |
| 204 | 208 | iVal = iQ; |
| 205 | 209 | } |
| @@ -230,11 +234,13 @@ | ||
| 230 | 234 | const char *zQ = P(zQParm); |
| 231 | 235 | if( zQ && fossil_strcmp(zQ,zVal)!=0 ){ |
| 232 | 236 | const int nZQ = (int)strlen(zQ); |
| 233 | 237 | login_verify_csrf_secret(); |
| 234 | 238 | setup_incr_cfgcnt(); |
| 239 | + db_protect_only(PROTECT_NONE); | |
| 235 | 240 | db_set(zVar, zQ, 0); |
| 241 | + db_protect_pop(); | |
| 236 | 242 | admin_log("Set entry_attribute %Q to: %.*s%s", |
| 237 | 243 | zVar, 20, zQ, (nZQ>20 ? "..." : "")); |
| 238 | 244 | zVal = zQ; |
| 239 | 245 | } |
| 240 | 246 | @ <input aria-label="%h(zLabel[0]?zLabel:zQParm)" type="text" \ |
| @@ -260,11 +266,13 @@ | ||
| 260 | 266 | const char *z = db_get(zVar, zDflt); |
| 261 | 267 | const char *zQ = P(zQP); |
| 262 | 268 | if( zQ && !disabled && fossil_strcmp(zQ,z)!=0){ |
| 263 | 269 | const int nZQ = (int)strlen(zQ); |
| 264 | 270 | login_verify_csrf_secret(); |
| 271 | + db_protect_only(PROTECT_NONE); | |
| 265 | 272 | db_set(zVar, zQ, 0); |
| 273 | + db_protect_pop(); | |
| 266 | 274 | setup_incr_cfgcnt(); |
| 267 | 275 | admin_log("Set textarea_attribute %Q to: %.*s%s", |
| 268 | 276 | zVar, 20, zQ, (nZQ>20 ? "..." : "")); |
| 269 | 277 | z = zQ; |
| 270 | 278 | } |
| @@ -1162,11 +1170,13 @@ | ||
| 1162 | 1170 | login_needed(0); |
| 1163 | 1171 | return; |
| 1164 | 1172 | } |
| 1165 | 1173 | db_begin_transaction(); |
| 1166 | 1174 | if( P("clear")!=0 && cgi_csrf_safe(1) ){ |
| 1175 | + db_unprotect(PROTECT_CONFIG); | |
| 1167 | 1176 | db_multi_exec("DELETE FROM config WHERE name GLOB 'adunit*'"); |
| 1177 | + db_protect_pop(); | |
| 1168 | 1178 | cgi_replace_parameter("adunit",""); |
| 1169 | 1179 | cgi_replace_parameter("adright",""); |
| 1170 | 1180 | setup_incr_cfgcnt(); |
| 1171 | 1181 | } |
| 1172 | 1182 | |
| @@ -1260,10 +1270,11 @@ | ||
| 1260 | 1270 | if( !g.perm.Admin ){ |
| 1261 | 1271 | login_needed(0); |
| 1262 | 1272 | return; |
| 1263 | 1273 | } |
| 1264 | 1274 | db_begin_transaction(); |
| 1275 | + db_unprotect(PROTECT_CONFIG); | |
| 1265 | 1276 | if( !cgi_csrf_safe(1) ){ |
| 1266 | 1277 | /* Allow no state changes if not safe from CSRF */ |
| 1267 | 1278 | }else if( P("setlogo")!=0 && zLogoMime && zLogoMime[0] && szLogoImg>0 ){ |
| 1268 | 1279 | Blob img; |
| 1269 | 1280 | Stmt ins; |
| @@ -1290,10 +1301,11 @@ | ||
| 1290 | 1301 | cgi_redirect("setup_logo"); |
| 1291 | 1302 | }else if( P("setbg")!=0 && zBgMime && zBgMime[0] && szBgImg>0 ){ |
| 1292 | 1303 | Blob img; |
| 1293 | 1304 | Stmt ins; |
| 1294 | 1305 | blob_init(&img, aBgImg, szBgImg); |
| 1306 | + db_unprotect(PROTECT_CONFIG); | |
| 1295 | 1307 | db_prepare(&ins, |
| 1296 | 1308 | "REPLACE INTO config(name,value,mtime)" |
| 1297 | 1309 | " VALUES('background-image',:bytes,now())" |
| 1298 | 1310 | ); |
| 1299 | 1311 | db_bind_blob(&ins, ":bytes", &img); |
| @@ -1302,13 +1314,15 @@ | ||
| 1302 | 1314 | db_multi_exec( |
| 1303 | 1315 | "REPLACE INTO config(name,value,mtime)" |
| 1304 | 1316 | " VALUES('background-mimetype',%Q,now())", |
| 1305 | 1317 | zBgMime |
| 1306 | 1318 | ); |
| 1319 | + db_protect_pop(); | |
| 1307 | 1320 | db_end_transaction(0); |
| 1308 | 1321 | cgi_redirect("setup_logo"); |
| 1309 | 1322 | }else if( P("clrbg")!=0 ){ |
| 1323 | + db_unprotect(PROTECT_CONFIG); | |
| 1310 | 1324 | db_multi_exec( |
| 1311 | 1325 | "DELETE FROM config WHERE name IN " |
| 1312 | 1326 | "('background-image','background-mimetype')" |
| 1313 | 1327 | ); |
| 1314 | 1328 | db_end_transaction(0); |
| @@ -1315,10 +1329,11 @@ | ||
| 1315 | 1329 | cgi_redirect("setup_logo"); |
| 1316 | 1330 | }else if( P("seticon")!=0 && zIconMime && zIconMime[0] && szIconImg>0 ){ |
| 1317 | 1331 | Blob img; |
| 1318 | 1332 | Stmt ins; |
| 1319 | 1333 | blob_init(&img, aIconImg, szIconImg); |
| 1334 | + db_unprotect(PROTECT_CONFIG); | |
| 1320 | 1335 | db_prepare(&ins, |
| 1321 | 1336 | "REPLACE INTO config(name,value,mtime)" |
| 1322 | 1337 | " VALUES('icon-image',:bytes,now())" |
| 1323 | 1338 | ); |
| 1324 | 1339 | db_bind_blob(&ins, ":bytes", &img); |
| @@ -1327,10 +1342,11 @@ | ||
| 1327 | 1342 | db_multi_exec( |
| 1328 | 1343 | "REPLACE INTO config(name,value,mtime)" |
| 1329 | 1344 | " VALUES('icon-mimetype',%Q,now())", |
| 1330 | 1345 | zIconMime |
| 1331 | 1346 | ); |
| 1347 | + db_protect_pop(); | |
| 1332 | 1348 | db_end_transaction(0); |
| 1333 | 1349 | cgi_redirect("setup_logo"); |
| 1334 | 1350 | }else if( P("clricon")!=0 ){ |
| 1335 | 1351 | db_multi_exec( |
| 1336 | 1352 | "DELETE FROM config WHERE name IN " |
| @@ -1786,22 +1802,27 @@ | ||
| 1786 | 1802 | const char *zValue |
| 1787 | 1803 | ){ |
| 1788 | 1804 | if( !cgi_csrf_safe(1) ) return; |
| 1789 | 1805 | if( zNewName[0]==0 || zValue[0]==0 ){ |
| 1790 | 1806 | if( zOldName[0] ){ |
| 1807 | + db_unprotect(PROTECT_CONFIG); | |
| 1791 | 1808 | blob_append_sql(pSql, |
| 1792 | 1809 | "DELETE FROM config WHERE name='walias:%q';\n", |
| 1793 | 1810 | zOldName); |
| 1811 | + db_protect_pop(); | |
| 1794 | 1812 | } |
| 1795 | 1813 | return; |
| 1796 | 1814 | } |
| 1797 | 1815 | if( zOldName[0]==0 ){ |
| 1816 | + db_unprotect(PROTECT_CONFIG); | |
| 1798 | 1817 | blob_append_sql(pSql, |
| 1799 | 1818 | "INSERT INTO config(name,value,mtime) VALUES('walias:%q',%Q,now());\n", |
| 1800 | 1819 | zNewName, zValue); |
| 1820 | + db_protect_pop(); | |
| 1801 | 1821 | return; |
| 1802 | 1822 | } |
| 1823 | + db_unprotect(PROTECT_CONFIG); | |
| 1803 | 1824 | if( strcmp(zOldName, zNewName)!=0 ){ |
| 1804 | 1825 | blob_append_sql(pSql, |
| 1805 | 1826 | "UPDATE config SET name='walias:%q', value=%Q, mtime=now()" |
| 1806 | 1827 | " WHERE name='walias:%q';\n", |
| 1807 | 1828 | zNewName, zValue, zOldName); |
| @@ -1809,10 +1830,11 @@ | ||
| 1809 | 1830 | blob_append_sql(pSql, |
| 1810 | 1831 | "UPDATE config SET value=%Q, mtime=now()" |
| 1811 | 1832 | " WHERE name='walias:%q' AND value<>%Q;\n", |
| 1812 | 1833 | zValue, zOldName, zValue); |
| 1813 | 1834 | } |
| 1835 | + db_protect_pop(); | |
| 1814 | 1836 | } |
| 1815 | 1837 | |
| 1816 | 1838 | /* |
| 1817 | 1839 | ** WEBPAGE: waliassetup |
| 1818 | 1840 | ** |
| 1819 | 1841 |
| --- src/setup.c | |
| +++ src/setup.c | |
| @@ -27,14 +27,16 @@ | |
| 27 | */ |
| 28 | void setup_incr_cfgcnt(void){ |
| 29 | static int once = 1; |
| 30 | if( once ){ |
| 31 | once = 0; |
| 32 | db_multi_exec("UPDATE config SET value=value+1 WHERE name='cfgcnt'"); |
| 33 | if( db_changes()==0 ){ |
| 34 | db_multi_exec("INSERT INTO config(name,value) VALUES('cfgcnt',1)"); |
| 35 | } |
| 36 | } |
| 37 | } |
| 38 | |
| 39 | /* |
| 40 | ** Output a single entry for a menu generated using an HTML table. |
| @@ -195,11 +197,13 @@ | |
| 195 | } |
| 196 | if( zQ ){ |
| 197 | int iQ = fossil_strcmp(zQ,"on")==0 || atoi(zQ); |
| 198 | if( iQ!=iVal ){ |
| 199 | login_verify_csrf_secret(); |
| 200 | db_set(zVar, iQ ? "1" : "0", 0); |
| 201 | setup_incr_cfgcnt(); |
| 202 | admin_log("Set option [%q] to [%q].", |
| 203 | zVar, iQ ? "on" : "off"); |
| 204 | iVal = iQ; |
| 205 | } |
| @@ -230,11 +234,13 @@ | |
| 230 | const char *zQ = P(zQParm); |
| 231 | if( zQ && fossil_strcmp(zQ,zVal)!=0 ){ |
| 232 | const int nZQ = (int)strlen(zQ); |
| 233 | login_verify_csrf_secret(); |
| 234 | setup_incr_cfgcnt(); |
| 235 | db_set(zVar, zQ, 0); |
| 236 | admin_log("Set entry_attribute %Q to: %.*s%s", |
| 237 | zVar, 20, zQ, (nZQ>20 ? "..." : "")); |
| 238 | zVal = zQ; |
| 239 | } |
| 240 | @ <input aria-label="%h(zLabel[0]?zLabel:zQParm)" type="text" \ |
| @@ -260,11 +266,13 @@ | |
| 260 | const char *z = db_get(zVar, zDflt); |
| 261 | const char *zQ = P(zQP); |
| 262 | if( zQ && !disabled && fossil_strcmp(zQ,z)!=0){ |
| 263 | const int nZQ = (int)strlen(zQ); |
| 264 | login_verify_csrf_secret(); |
| 265 | db_set(zVar, zQ, 0); |
| 266 | setup_incr_cfgcnt(); |
| 267 | admin_log("Set textarea_attribute %Q to: %.*s%s", |
| 268 | zVar, 20, zQ, (nZQ>20 ? "..." : "")); |
| 269 | z = zQ; |
| 270 | } |
| @@ -1162,11 +1170,13 @@ | |
| 1162 | login_needed(0); |
| 1163 | return; |
| 1164 | } |
| 1165 | db_begin_transaction(); |
| 1166 | if( P("clear")!=0 && cgi_csrf_safe(1) ){ |
| 1167 | db_multi_exec("DELETE FROM config WHERE name GLOB 'adunit*'"); |
| 1168 | cgi_replace_parameter("adunit",""); |
| 1169 | cgi_replace_parameter("adright",""); |
| 1170 | setup_incr_cfgcnt(); |
| 1171 | } |
| 1172 | |
| @@ -1260,10 +1270,11 @@ | |
| 1260 | if( !g.perm.Admin ){ |
| 1261 | login_needed(0); |
| 1262 | return; |
| 1263 | } |
| 1264 | db_begin_transaction(); |
| 1265 | if( !cgi_csrf_safe(1) ){ |
| 1266 | /* Allow no state changes if not safe from CSRF */ |
| 1267 | }else if( P("setlogo")!=0 && zLogoMime && zLogoMime[0] && szLogoImg>0 ){ |
| 1268 | Blob img; |
| 1269 | Stmt ins; |
| @@ -1290,10 +1301,11 @@ | |
| 1290 | cgi_redirect("setup_logo"); |
| 1291 | }else if( P("setbg")!=0 && zBgMime && zBgMime[0] && szBgImg>0 ){ |
| 1292 | Blob img; |
| 1293 | Stmt ins; |
| 1294 | blob_init(&img, aBgImg, szBgImg); |
| 1295 | db_prepare(&ins, |
| 1296 | "REPLACE INTO config(name,value,mtime)" |
| 1297 | " VALUES('background-image',:bytes,now())" |
| 1298 | ); |
| 1299 | db_bind_blob(&ins, ":bytes", &img); |
| @@ -1302,13 +1314,15 @@ | |
| 1302 | db_multi_exec( |
| 1303 | "REPLACE INTO config(name,value,mtime)" |
| 1304 | " VALUES('background-mimetype',%Q,now())", |
| 1305 | zBgMime |
| 1306 | ); |
| 1307 | db_end_transaction(0); |
| 1308 | cgi_redirect("setup_logo"); |
| 1309 | }else if( P("clrbg")!=0 ){ |
| 1310 | db_multi_exec( |
| 1311 | "DELETE FROM config WHERE name IN " |
| 1312 | "('background-image','background-mimetype')" |
| 1313 | ); |
| 1314 | db_end_transaction(0); |
| @@ -1315,10 +1329,11 @@ | |
| 1315 | cgi_redirect("setup_logo"); |
| 1316 | }else if( P("seticon")!=0 && zIconMime && zIconMime[0] && szIconImg>0 ){ |
| 1317 | Blob img; |
| 1318 | Stmt ins; |
| 1319 | blob_init(&img, aIconImg, szIconImg); |
| 1320 | db_prepare(&ins, |
| 1321 | "REPLACE INTO config(name,value,mtime)" |
| 1322 | " VALUES('icon-image',:bytes,now())" |
| 1323 | ); |
| 1324 | db_bind_blob(&ins, ":bytes", &img); |
| @@ -1327,10 +1342,11 @@ | |
| 1327 | db_multi_exec( |
| 1328 | "REPLACE INTO config(name,value,mtime)" |
| 1329 | " VALUES('icon-mimetype',%Q,now())", |
| 1330 | zIconMime |
| 1331 | ); |
| 1332 | db_end_transaction(0); |
| 1333 | cgi_redirect("setup_logo"); |
| 1334 | }else if( P("clricon")!=0 ){ |
| 1335 | db_multi_exec( |
| 1336 | "DELETE FROM config WHERE name IN " |
| @@ -1786,22 +1802,27 @@ | |
| 1786 | const char *zValue |
| 1787 | ){ |
| 1788 | if( !cgi_csrf_safe(1) ) return; |
| 1789 | if( zNewName[0]==0 || zValue[0]==0 ){ |
| 1790 | if( zOldName[0] ){ |
| 1791 | blob_append_sql(pSql, |
| 1792 | "DELETE FROM config WHERE name='walias:%q';\n", |
| 1793 | zOldName); |
| 1794 | } |
| 1795 | return; |
| 1796 | } |
| 1797 | if( zOldName[0]==0 ){ |
| 1798 | blob_append_sql(pSql, |
| 1799 | "INSERT INTO config(name,value,mtime) VALUES('walias:%q',%Q,now());\n", |
| 1800 | zNewName, zValue); |
| 1801 | return; |
| 1802 | } |
| 1803 | if( strcmp(zOldName, zNewName)!=0 ){ |
| 1804 | blob_append_sql(pSql, |
| 1805 | "UPDATE config SET name='walias:%q', value=%Q, mtime=now()" |
| 1806 | " WHERE name='walias:%q';\n", |
| 1807 | zNewName, zValue, zOldName); |
| @@ -1809,10 +1830,11 @@ | |
| 1809 | blob_append_sql(pSql, |
| 1810 | "UPDATE config SET value=%Q, mtime=now()" |
| 1811 | " WHERE name='walias:%q' AND value<>%Q;\n", |
| 1812 | zValue, zOldName, zValue); |
| 1813 | } |
| 1814 | } |
| 1815 | |
| 1816 | /* |
| 1817 | ** WEBPAGE: waliassetup |
| 1818 | ** |
| 1819 |
| --- src/setup.c | |
| +++ src/setup.c | |
| @@ -27,14 +27,16 @@ | |
| 27 | */ |
| 28 | void setup_incr_cfgcnt(void){ |
| 29 | static int once = 1; |
| 30 | if( once ){ |
| 31 | once = 0; |
| 32 | db_unprotect(PROTECT_CONFIG); |
| 33 | db_multi_exec("UPDATE config SET value=value+1 WHERE name='cfgcnt'"); |
| 34 | if( db_changes()==0 ){ |
| 35 | db_multi_exec("INSERT INTO config(name,value) VALUES('cfgcnt',1)"); |
| 36 | } |
| 37 | db_protect_pop(); |
| 38 | } |
| 39 | } |
| 40 | |
| 41 | /* |
| 42 | ** Output a single entry for a menu generated using an HTML table. |
| @@ -195,11 +197,13 @@ | |
| 197 | } |
| 198 | if( zQ ){ |
| 199 | int iQ = fossil_strcmp(zQ,"on")==0 || atoi(zQ); |
| 200 | if( iQ!=iVal ){ |
| 201 | login_verify_csrf_secret(); |
| 202 | db_protect_only(PROTECT_NONE); |
| 203 | db_set(zVar, iQ ? "1" : "0", 0); |
| 204 | db_protect_pop(); |
| 205 | setup_incr_cfgcnt(); |
| 206 | admin_log("Set option [%q] to [%q].", |
| 207 | zVar, iQ ? "on" : "off"); |
| 208 | iVal = iQ; |
| 209 | } |
| @@ -230,11 +234,13 @@ | |
| 234 | const char *zQ = P(zQParm); |
| 235 | if( zQ && fossil_strcmp(zQ,zVal)!=0 ){ |
| 236 | const int nZQ = (int)strlen(zQ); |
| 237 | login_verify_csrf_secret(); |
| 238 | setup_incr_cfgcnt(); |
| 239 | db_protect_only(PROTECT_NONE); |
| 240 | db_set(zVar, zQ, 0); |
| 241 | db_protect_pop(); |
| 242 | admin_log("Set entry_attribute %Q to: %.*s%s", |
| 243 | zVar, 20, zQ, (nZQ>20 ? "..." : "")); |
| 244 | zVal = zQ; |
| 245 | } |
| 246 | @ <input aria-label="%h(zLabel[0]?zLabel:zQParm)" type="text" \ |
| @@ -260,11 +266,13 @@ | |
| 266 | const char *z = db_get(zVar, zDflt); |
| 267 | const char *zQ = P(zQP); |
| 268 | if( zQ && !disabled && fossil_strcmp(zQ,z)!=0){ |
| 269 | const int nZQ = (int)strlen(zQ); |
| 270 | login_verify_csrf_secret(); |
| 271 | db_protect_only(PROTECT_NONE); |
| 272 | db_set(zVar, zQ, 0); |
| 273 | db_protect_pop(); |
| 274 | setup_incr_cfgcnt(); |
| 275 | admin_log("Set textarea_attribute %Q to: %.*s%s", |
| 276 | zVar, 20, zQ, (nZQ>20 ? "..." : "")); |
| 277 | z = zQ; |
| 278 | } |
| @@ -1162,11 +1170,13 @@ | |
| 1170 | login_needed(0); |
| 1171 | return; |
| 1172 | } |
| 1173 | db_begin_transaction(); |
| 1174 | if( P("clear")!=0 && cgi_csrf_safe(1) ){ |
| 1175 | db_unprotect(PROTECT_CONFIG); |
| 1176 | db_multi_exec("DELETE FROM config WHERE name GLOB 'adunit*'"); |
| 1177 | db_protect_pop(); |
| 1178 | cgi_replace_parameter("adunit",""); |
| 1179 | cgi_replace_parameter("adright",""); |
| 1180 | setup_incr_cfgcnt(); |
| 1181 | } |
| 1182 | |
| @@ -1260,10 +1270,11 @@ | |
| 1270 | if( !g.perm.Admin ){ |
| 1271 | login_needed(0); |
| 1272 | return; |
| 1273 | } |
| 1274 | db_begin_transaction(); |
| 1275 | db_unprotect(PROTECT_CONFIG); |
| 1276 | if( !cgi_csrf_safe(1) ){ |
| 1277 | /* Allow no state changes if not safe from CSRF */ |
| 1278 | }else if( P("setlogo")!=0 && zLogoMime && zLogoMime[0] && szLogoImg>0 ){ |
| 1279 | Blob img; |
| 1280 | Stmt ins; |
| @@ -1290,10 +1301,11 @@ | |
| 1301 | cgi_redirect("setup_logo"); |
| 1302 | }else if( P("setbg")!=0 && zBgMime && zBgMime[0] && szBgImg>0 ){ |
| 1303 | Blob img; |
| 1304 | Stmt ins; |
| 1305 | blob_init(&img, aBgImg, szBgImg); |
| 1306 | db_unprotect(PROTECT_CONFIG); |
| 1307 | db_prepare(&ins, |
| 1308 | "REPLACE INTO config(name,value,mtime)" |
| 1309 | " VALUES('background-image',:bytes,now())" |
| 1310 | ); |
| 1311 | db_bind_blob(&ins, ":bytes", &img); |
| @@ -1302,13 +1314,15 @@ | |
| 1314 | db_multi_exec( |
| 1315 | "REPLACE INTO config(name,value,mtime)" |
| 1316 | " VALUES('background-mimetype',%Q,now())", |
| 1317 | zBgMime |
| 1318 | ); |
| 1319 | db_protect_pop(); |
| 1320 | db_end_transaction(0); |
| 1321 | cgi_redirect("setup_logo"); |
| 1322 | }else if( P("clrbg")!=0 ){ |
| 1323 | db_unprotect(PROTECT_CONFIG); |
| 1324 | db_multi_exec( |
| 1325 | "DELETE FROM config WHERE name IN " |
| 1326 | "('background-image','background-mimetype')" |
| 1327 | ); |
| 1328 | db_end_transaction(0); |
| @@ -1315,10 +1329,11 @@ | |
| 1329 | cgi_redirect("setup_logo"); |
| 1330 | }else if( P("seticon")!=0 && zIconMime && zIconMime[0] && szIconImg>0 ){ |
| 1331 | Blob img; |
| 1332 | Stmt ins; |
| 1333 | blob_init(&img, aIconImg, szIconImg); |
| 1334 | db_unprotect(PROTECT_CONFIG); |
| 1335 | db_prepare(&ins, |
| 1336 | "REPLACE INTO config(name,value,mtime)" |
| 1337 | " VALUES('icon-image',:bytes,now())" |
| 1338 | ); |
| 1339 | db_bind_blob(&ins, ":bytes", &img); |
| @@ -1327,10 +1342,11 @@ | |
| 1342 | db_multi_exec( |
| 1343 | "REPLACE INTO config(name,value,mtime)" |
| 1344 | " VALUES('icon-mimetype',%Q,now())", |
| 1345 | zIconMime |
| 1346 | ); |
| 1347 | db_protect_pop(); |
| 1348 | db_end_transaction(0); |
| 1349 | cgi_redirect("setup_logo"); |
| 1350 | }else if( P("clricon")!=0 ){ |
| 1351 | db_multi_exec( |
| 1352 | "DELETE FROM config WHERE name IN " |
| @@ -1786,22 +1802,27 @@ | |
| 1802 | const char *zValue |
| 1803 | ){ |
| 1804 | if( !cgi_csrf_safe(1) ) return; |
| 1805 | if( zNewName[0]==0 || zValue[0]==0 ){ |
| 1806 | if( zOldName[0] ){ |
| 1807 | db_unprotect(PROTECT_CONFIG); |
| 1808 | blob_append_sql(pSql, |
| 1809 | "DELETE FROM config WHERE name='walias:%q';\n", |
| 1810 | zOldName); |
| 1811 | db_protect_pop(); |
| 1812 | } |
| 1813 | return; |
| 1814 | } |
| 1815 | if( zOldName[0]==0 ){ |
| 1816 | db_unprotect(PROTECT_CONFIG); |
| 1817 | blob_append_sql(pSql, |
| 1818 | "INSERT INTO config(name,value,mtime) VALUES('walias:%q',%Q,now());\n", |
| 1819 | zNewName, zValue); |
| 1820 | db_protect_pop(); |
| 1821 | return; |
| 1822 | } |
| 1823 | db_unprotect(PROTECT_CONFIG); |
| 1824 | if( strcmp(zOldName, zNewName)!=0 ){ |
| 1825 | blob_append_sql(pSql, |
| 1826 | "UPDATE config SET name='walias:%q', value=%Q, mtime=now()" |
| 1827 | " WHERE name='walias:%q';\n", |
| 1828 | zNewName, zValue, zOldName); |
| @@ -1809,10 +1830,11 @@ | |
| 1830 | blob_append_sql(pSql, |
| 1831 | "UPDATE config SET value=%Q, mtime=now()" |
| 1832 | " WHERE name='walias:%q' AND value<>%Q;\n", |
| 1833 | zValue, zOldName, zValue); |
| 1834 | } |
| 1835 | db_protect_pop(); |
| 1836 | } |
| 1837 | |
| 1838 | /* |
| 1839 | ** WEBPAGE: waliassetup |
| 1840 | ** |
| 1841 |
+6
| --- src/setupuser.c | ||
| +++ src/setupuser.c | ||
| @@ -315,11 +315,13 @@ | ||
| 315 | 315 | /* Check for requests to delete the user */ |
| 316 | 316 | if( P("delete") && cgi_csrf_safe(1) ){ |
| 317 | 317 | int n; |
| 318 | 318 | if( P("verifydelete") ){ |
| 319 | 319 | /* Verified delete user request */ |
| 320 | + db_unprotect(PROTECT_USER); | |
| 320 | 321 | db_multi_exec("DELETE FROM user WHERE uid=%d", uid); |
| 322 | + db_protect_pop(); | |
| 321 | 323 | moderation_disapprove_for_missing_users(); |
| 322 | 324 | admin_log("Deleted user [%s] (uid %d).", |
| 323 | 325 | PD("login","???")/*safe-for-%s*/, uid); |
| 324 | 326 | cgi_redirect(cgi_referer("setup_ulist")); |
| 325 | 327 | return; |
| @@ -401,15 +403,17 @@ | ||
| 401 | 403 | @ [Bummer]</a></p> |
| 402 | 404 | style_footer(); |
| 403 | 405 | return; |
| 404 | 406 | } |
| 405 | 407 | login_verify_csrf_secret(); |
| 408 | + db_unprotect(PROTECT_USER); | |
| 406 | 409 | db_multi_exec( |
| 407 | 410 | "REPLACE INTO user(uid,login,info,pw,cap,mtime) " |
| 408 | 411 | "VALUES(nullif(%d,0),%Q,%Q,%Q,%Q,now())", |
| 409 | 412 | uid, zLogin, P("info"), zPw, zCap |
| 410 | 413 | ); |
| 414 | + db_protect_pop(); | |
| 411 | 415 | setup_incr_cfgcnt(); |
| 412 | 416 | admin_log( "Updated user [%q] with capabilities [%q].", |
| 413 | 417 | zLogin, zCap ); |
| 414 | 418 | if( atoi(PD("all","0"))>0 ){ |
| 415 | 419 | Blob sql; |
| @@ -432,11 +436,13 @@ | ||
| 432 | 436 | " mtime=now()" |
| 433 | 437 | " WHERE login=%Q;", |
| 434 | 438 | zLogin, P("pw"), zLogin, P("info"), zCap, |
| 435 | 439 | zOldLogin |
| 436 | 440 | ); |
| 441 | + db_unprotect(PROTECT_USER); | |
| 437 | 442 | login_group_sql(blob_str(&sql), "<li> ", " </li>\n", &zErr); |
| 443 | + db_protect_pop(); | |
| 438 | 444 | blob_reset(&sql); |
| 439 | 445 | admin_log( "Updated user [%q] in all login groups " |
| 440 | 446 | "with capabilities [%q].", |
| 441 | 447 | zLogin, zCap ); |
| 442 | 448 | if( zErr ){ |
| 443 | 449 |
| --- src/setupuser.c | |
| +++ src/setupuser.c | |
| @@ -315,11 +315,13 @@ | |
| 315 | /* Check for requests to delete the user */ |
| 316 | if( P("delete") && cgi_csrf_safe(1) ){ |
| 317 | int n; |
| 318 | if( P("verifydelete") ){ |
| 319 | /* Verified delete user request */ |
| 320 | db_multi_exec("DELETE FROM user WHERE uid=%d", uid); |
| 321 | moderation_disapprove_for_missing_users(); |
| 322 | admin_log("Deleted user [%s] (uid %d).", |
| 323 | PD("login","???")/*safe-for-%s*/, uid); |
| 324 | cgi_redirect(cgi_referer("setup_ulist")); |
| 325 | return; |
| @@ -401,15 +403,17 @@ | |
| 401 | @ [Bummer]</a></p> |
| 402 | style_footer(); |
| 403 | return; |
| 404 | } |
| 405 | login_verify_csrf_secret(); |
| 406 | db_multi_exec( |
| 407 | "REPLACE INTO user(uid,login,info,pw,cap,mtime) " |
| 408 | "VALUES(nullif(%d,0),%Q,%Q,%Q,%Q,now())", |
| 409 | uid, zLogin, P("info"), zPw, zCap |
| 410 | ); |
| 411 | setup_incr_cfgcnt(); |
| 412 | admin_log( "Updated user [%q] with capabilities [%q].", |
| 413 | zLogin, zCap ); |
| 414 | if( atoi(PD("all","0"))>0 ){ |
| 415 | Blob sql; |
| @@ -432,11 +436,13 @@ | |
| 432 | " mtime=now()" |
| 433 | " WHERE login=%Q;", |
| 434 | zLogin, P("pw"), zLogin, P("info"), zCap, |
| 435 | zOldLogin |
| 436 | ); |
| 437 | login_group_sql(blob_str(&sql), "<li> ", " </li>\n", &zErr); |
| 438 | blob_reset(&sql); |
| 439 | admin_log( "Updated user [%q] in all login groups " |
| 440 | "with capabilities [%q].", |
| 441 | zLogin, zCap ); |
| 442 | if( zErr ){ |
| 443 |
| --- src/setupuser.c | |
| +++ src/setupuser.c | |
| @@ -315,11 +315,13 @@ | |
| 315 | /* Check for requests to delete the user */ |
| 316 | if( P("delete") && cgi_csrf_safe(1) ){ |
| 317 | int n; |
| 318 | if( P("verifydelete") ){ |
| 319 | /* Verified delete user request */ |
| 320 | db_unprotect(PROTECT_USER); |
| 321 | db_multi_exec("DELETE FROM user WHERE uid=%d", uid); |
| 322 | db_protect_pop(); |
| 323 | moderation_disapprove_for_missing_users(); |
| 324 | admin_log("Deleted user [%s] (uid %d).", |
| 325 | PD("login","???")/*safe-for-%s*/, uid); |
| 326 | cgi_redirect(cgi_referer("setup_ulist")); |
| 327 | return; |
| @@ -401,15 +403,17 @@ | |
| 403 | @ [Bummer]</a></p> |
| 404 | style_footer(); |
| 405 | return; |
| 406 | } |
| 407 | login_verify_csrf_secret(); |
| 408 | db_unprotect(PROTECT_USER); |
| 409 | db_multi_exec( |
| 410 | "REPLACE INTO user(uid,login,info,pw,cap,mtime) " |
| 411 | "VALUES(nullif(%d,0),%Q,%Q,%Q,%Q,now())", |
| 412 | uid, zLogin, P("info"), zPw, zCap |
| 413 | ); |
| 414 | db_protect_pop(); |
| 415 | setup_incr_cfgcnt(); |
| 416 | admin_log( "Updated user [%q] with capabilities [%q].", |
| 417 | zLogin, zCap ); |
| 418 | if( atoi(PD("all","0"))>0 ){ |
| 419 | Blob sql; |
| @@ -432,11 +436,13 @@ | |
| 436 | " mtime=now()" |
| 437 | " WHERE login=%Q;", |
| 438 | zLogin, P("pw"), zLogin, P("info"), zCap, |
| 439 | zOldLogin |
| 440 | ); |
| 441 | db_unprotect(PROTECT_USER); |
| 442 | login_group_sql(blob_str(&sql), "<li> ", " </li>\n", &zErr); |
| 443 | db_protect_pop(); |
| 444 | blob_reset(&sql); |
| 445 | admin_log( "Updated user [%q] in all login groups " |
| 446 | "with capabilities [%q].", |
| 447 | zLogin, zCap ); |
| 448 | if( zErr ){ |
| 449 |
+14
| --- src/skins.c | ||
| +++ src/skins.c | ||
| @@ -360,14 +360,16 @@ | ||
| 360 | 360 | zLabel = mprintf("skins/default/%s.txt", azSkinFile[i]); |
| 361 | 361 | z = builtin_text(zLabel); |
| 362 | 362 | fossil_free(zLabel); |
| 363 | 363 | } |
| 364 | 364 | } |
| 365 | + db_unprotect(PROTECT_CONFIG); | |
| 365 | 366 | blob_appendf(&val, |
| 366 | 367 | "REPLACE INTO config(name,value,mtime) VALUES(%Q,%Q,now());\n", |
| 367 | 368 | azSkinFile[i], z |
| 368 | 369 | ); |
| 370 | + db_protect_pop(); | |
| 369 | 371 | } |
| 370 | 372 | return blob_str(&val); |
| 371 | 373 | } |
| 372 | 374 | |
| 373 | 375 | /* |
| @@ -402,14 +404,16 @@ | ||
| 402 | 404 | login_insert_csrf_secret(); |
| 403 | 405 | @ </div></form> |
| 404 | 406 | style_footer(); |
| 405 | 407 | return 1; |
| 406 | 408 | } |
| 409 | + db_unprotect(PROTECT_CONFIG); | |
| 407 | 410 | db_multi_exec( |
| 408 | 411 | "UPDATE config SET name='skin:%q' WHERE name='skin:%q';", |
| 409 | 412 | zNewName, zOldName |
| 410 | 413 | ); |
| 414 | + db_protect_pop(); | |
| 411 | 415 | return 0; |
| 412 | 416 | } |
| 413 | 417 | |
| 414 | 418 | /* |
| 415 | 419 | ** Respond to a Save button press. Return TRUE if a dialog was painted. |
| @@ -440,15 +444,17 @@ | ||
| 440 | 444 | login_insert_csrf_secret(); |
| 441 | 445 | @ </div></form> |
| 442 | 446 | style_footer(); |
| 443 | 447 | return 1; |
| 444 | 448 | } |
| 449 | + db_unprotect(PROTECT_CONFIG); | |
| 445 | 450 | db_multi_exec( |
| 446 | 451 | "INSERT OR IGNORE INTO config(name, value, mtime)" |
| 447 | 452 | "VALUES('skin:%q',%Q,now())", |
| 448 | 453 | zNewName, zCurrent |
| 449 | 454 | ); |
| 455 | + db_protect_pop(); | |
| 450 | 456 | return 0; |
| 451 | 457 | } |
| 452 | 458 | |
| 453 | 459 | /* |
| 454 | 460 | ** WEBPAGE: setup_skin_admin |
| @@ -491,16 +497,20 @@ | ||
| 491 | 497 | style_footer(); |
| 492 | 498 | db_end_transaction(1); |
| 493 | 499 | return; |
| 494 | 500 | } |
| 495 | 501 | if( P("del2")!=0 && (zName = skinVarName(P("sn"), 1))!=0 ){ |
| 502 | + db_unprotect(PROTECT_CONFIG); | |
| 496 | 503 | db_multi_exec("DELETE FROM config WHERE name=%Q", zName); |
| 504 | + db_protect_pop(); | |
| 497 | 505 | } |
| 498 | 506 | if( P("draftdel")!=0 ){ |
| 499 | 507 | const char *zDraft = P("name"); |
| 500 | 508 | if( sqlite3_strglob("draft[1-9]",zDraft)==0 ){ |
| 509 | + db_unprotect(PROTECT_CONFIG); | |
| 501 | 510 | db_multi_exec("DELETE FROM config WHERE name GLOB '%q-*'", zDraft); |
| 511 | + db_protect_pop(); | |
| 502 | 512 | } |
| 503 | 513 | } |
| 504 | 514 | if( skinRename() || skinSave(zCurrent) ){ |
| 505 | 515 | db_end_transaction(0); |
| 506 | 516 | return; |
| @@ -521,15 +531,17 @@ | ||
| 521 | 531 | } |
| 522 | 532 | if( !seen ){ |
| 523 | 533 | seen = db_exists("SELECT 1 FROM config WHERE name GLOB 'skin:*'" |
| 524 | 534 | " AND value=%Q", zCurrent); |
| 525 | 535 | if( !seen ){ |
| 536 | + db_unprotect(PROTECT_CONFIG); | |
| 526 | 537 | db_multi_exec( |
| 527 | 538 | "INSERT INTO config(name,value,mtime) VALUES(" |
| 528 | 539 | " strftime('skin:Backup On %%Y-%%m-%%d %%H:%%M:%%S')," |
| 529 | 540 | " %Q,now())", zCurrent |
| 530 | 541 | ); |
| 542 | + db_protect_pop(); | |
| 531 | 543 | } |
| 532 | 544 | } |
| 533 | 545 | seen = 0; |
| 534 | 546 | for(i=0; i<count(aBuiltinSkin); i++){ |
| 535 | 547 | if( fossil_strcmp(aBuiltinSkin[i].zDesc, z)==0 ){ |
| @@ -867,15 +879,17 @@ | ||
| 867 | 879 | if( !seen ){ |
| 868 | 880 | seen = db_exists("SELECT 1 FROM config WHERE name GLOB 'skin:*'" |
| 869 | 881 | " AND value=%Q", zCurrent); |
| 870 | 882 | } |
| 871 | 883 | if( !seen ){ |
| 884 | + db_unprotect(PROTECT_CONFIG); | |
| 872 | 885 | db_multi_exec( |
| 873 | 886 | "INSERT INTO config(name,value,mtime) VALUES(" |
| 874 | 887 | " strftime('skin:Backup On %%Y-%%m-%%d %%H:%%M:%%S')," |
| 875 | 888 | " %Q,now())", zCurrent |
| 876 | 889 | ); |
| 890 | + db_protect_pop(); | |
| 877 | 891 | } |
| 878 | 892 | |
| 879 | 893 | /* Publish draft iSkin */ |
| 880 | 894 | for(i=0; i<count(azSkinFile); i++){ |
| 881 | 895 | char *zNew = db_get_mprintf("", "draft%d-%s", iSkin, azSkinFile[i]); |
| 882 | 896 |
| --- src/skins.c | |
| +++ src/skins.c | |
| @@ -360,14 +360,16 @@ | |
| 360 | zLabel = mprintf("skins/default/%s.txt", azSkinFile[i]); |
| 361 | z = builtin_text(zLabel); |
| 362 | fossil_free(zLabel); |
| 363 | } |
| 364 | } |
| 365 | blob_appendf(&val, |
| 366 | "REPLACE INTO config(name,value,mtime) VALUES(%Q,%Q,now());\n", |
| 367 | azSkinFile[i], z |
| 368 | ); |
| 369 | } |
| 370 | return blob_str(&val); |
| 371 | } |
| 372 | |
| 373 | /* |
| @@ -402,14 +404,16 @@ | |
| 402 | login_insert_csrf_secret(); |
| 403 | @ </div></form> |
| 404 | style_footer(); |
| 405 | return 1; |
| 406 | } |
| 407 | db_multi_exec( |
| 408 | "UPDATE config SET name='skin:%q' WHERE name='skin:%q';", |
| 409 | zNewName, zOldName |
| 410 | ); |
| 411 | return 0; |
| 412 | } |
| 413 | |
| 414 | /* |
| 415 | ** Respond to a Save button press. Return TRUE if a dialog was painted. |
| @@ -440,15 +444,17 @@ | |
| 440 | login_insert_csrf_secret(); |
| 441 | @ </div></form> |
| 442 | style_footer(); |
| 443 | return 1; |
| 444 | } |
| 445 | db_multi_exec( |
| 446 | "INSERT OR IGNORE INTO config(name, value, mtime)" |
| 447 | "VALUES('skin:%q',%Q,now())", |
| 448 | zNewName, zCurrent |
| 449 | ); |
| 450 | return 0; |
| 451 | } |
| 452 | |
| 453 | /* |
| 454 | ** WEBPAGE: setup_skin_admin |
| @@ -491,16 +497,20 @@ | |
| 491 | style_footer(); |
| 492 | db_end_transaction(1); |
| 493 | return; |
| 494 | } |
| 495 | if( P("del2")!=0 && (zName = skinVarName(P("sn"), 1))!=0 ){ |
| 496 | db_multi_exec("DELETE FROM config WHERE name=%Q", zName); |
| 497 | } |
| 498 | if( P("draftdel")!=0 ){ |
| 499 | const char *zDraft = P("name"); |
| 500 | if( sqlite3_strglob("draft[1-9]",zDraft)==0 ){ |
| 501 | db_multi_exec("DELETE FROM config WHERE name GLOB '%q-*'", zDraft); |
| 502 | } |
| 503 | } |
| 504 | if( skinRename() || skinSave(zCurrent) ){ |
| 505 | db_end_transaction(0); |
| 506 | return; |
| @@ -521,15 +531,17 @@ | |
| 521 | } |
| 522 | if( !seen ){ |
| 523 | seen = db_exists("SELECT 1 FROM config WHERE name GLOB 'skin:*'" |
| 524 | " AND value=%Q", zCurrent); |
| 525 | if( !seen ){ |
| 526 | db_multi_exec( |
| 527 | "INSERT INTO config(name,value,mtime) VALUES(" |
| 528 | " strftime('skin:Backup On %%Y-%%m-%%d %%H:%%M:%%S')," |
| 529 | " %Q,now())", zCurrent |
| 530 | ); |
| 531 | } |
| 532 | } |
| 533 | seen = 0; |
| 534 | for(i=0; i<count(aBuiltinSkin); i++){ |
| 535 | if( fossil_strcmp(aBuiltinSkin[i].zDesc, z)==0 ){ |
| @@ -867,15 +879,17 @@ | |
| 867 | if( !seen ){ |
| 868 | seen = db_exists("SELECT 1 FROM config WHERE name GLOB 'skin:*'" |
| 869 | " AND value=%Q", zCurrent); |
| 870 | } |
| 871 | if( !seen ){ |
| 872 | db_multi_exec( |
| 873 | "INSERT INTO config(name,value,mtime) VALUES(" |
| 874 | " strftime('skin:Backup On %%Y-%%m-%%d %%H:%%M:%%S')," |
| 875 | " %Q,now())", zCurrent |
| 876 | ); |
| 877 | } |
| 878 | |
| 879 | /* Publish draft iSkin */ |
| 880 | for(i=0; i<count(azSkinFile); i++){ |
| 881 | char *zNew = db_get_mprintf("", "draft%d-%s", iSkin, azSkinFile[i]); |
| 882 |
| --- src/skins.c | |
| +++ src/skins.c | |
| @@ -360,14 +360,16 @@ | |
| 360 | zLabel = mprintf("skins/default/%s.txt", azSkinFile[i]); |
| 361 | z = builtin_text(zLabel); |
| 362 | fossil_free(zLabel); |
| 363 | } |
| 364 | } |
| 365 | db_unprotect(PROTECT_CONFIG); |
| 366 | blob_appendf(&val, |
| 367 | "REPLACE INTO config(name,value,mtime) VALUES(%Q,%Q,now());\n", |
| 368 | azSkinFile[i], z |
| 369 | ); |
| 370 | db_protect_pop(); |
| 371 | } |
| 372 | return blob_str(&val); |
| 373 | } |
| 374 | |
| 375 | /* |
| @@ -402,14 +404,16 @@ | |
| 404 | login_insert_csrf_secret(); |
| 405 | @ </div></form> |
| 406 | style_footer(); |
| 407 | return 1; |
| 408 | } |
| 409 | db_unprotect(PROTECT_CONFIG); |
| 410 | db_multi_exec( |
| 411 | "UPDATE config SET name='skin:%q' WHERE name='skin:%q';", |
| 412 | zNewName, zOldName |
| 413 | ); |
| 414 | db_protect_pop(); |
| 415 | return 0; |
| 416 | } |
| 417 | |
| 418 | /* |
| 419 | ** Respond to a Save button press. Return TRUE if a dialog was painted. |
| @@ -440,15 +444,17 @@ | |
| 444 | login_insert_csrf_secret(); |
| 445 | @ </div></form> |
| 446 | style_footer(); |
| 447 | return 1; |
| 448 | } |
| 449 | db_unprotect(PROTECT_CONFIG); |
| 450 | db_multi_exec( |
| 451 | "INSERT OR IGNORE INTO config(name, value, mtime)" |
| 452 | "VALUES('skin:%q',%Q,now())", |
| 453 | zNewName, zCurrent |
| 454 | ); |
| 455 | db_protect_pop(); |
| 456 | return 0; |
| 457 | } |
| 458 | |
| 459 | /* |
| 460 | ** WEBPAGE: setup_skin_admin |
| @@ -491,16 +497,20 @@ | |
| 497 | style_footer(); |
| 498 | db_end_transaction(1); |
| 499 | return; |
| 500 | } |
| 501 | if( P("del2")!=0 && (zName = skinVarName(P("sn"), 1))!=0 ){ |
| 502 | db_unprotect(PROTECT_CONFIG); |
| 503 | db_multi_exec("DELETE FROM config WHERE name=%Q", zName); |
| 504 | db_protect_pop(); |
| 505 | } |
| 506 | if( P("draftdel")!=0 ){ |
| 507 | const char *zDraft = P("name"); |
| 508 | if( sqlite3_strglob("draft[1-9]",zDraft)==0 ){ |
| 509 | db_unprotect(PROTECT_CONFIG); |
| 510 | db_multi_exec("DELETE FROM config WHERE name GLOB '%q-*'", zDraft); |
| 511 | db_protect_pop(); |
| 512 | } |
| 513 | } |
| 514 | if( skinRename() || skinSave(zCurrent) ){ |
| 515 | db_end_transaction(0); |
| 516 | return; |
| @@ -521,15 +531,17 @@ | |
| 531 | } |
| 532 | if( !seen ){ |
| 533 | seen = db_exists("SELECT 1 FROM config WHERE name GLOB 'skin:*'" |
| 534 | " AND value=%Q", zCurrent); |
| 535 | if( !seen ){ |
| 536 | db_unprotect(PROTECT_CONFIG); |
| 537 | db_multi_exec( |
| 538 | "INSERT INTO config(name,value,mtime) VALUES(" |
| 539 | " strftime('skin:Backup On %%Y-%%m-%%d %%H:%%M:%%S')," |
| 540 | " %Q,now())", zCurrent |
| 541 | ); |
| 542 | db_protect_pop(); |
| 543 | } |
| 544 | } |
| 545 | seen = 0; |
| 546 | for(i=0; i<count(aBuiltinSkin); i++){ |
| 547 | if( fossil_strcmp(aBuiltinSkin[i].zDesc, z)==0 ){ |
| @@ -867,15 +879,17 @@ | |
| 879 | if( !seen ){ |
| 880 | seen = db_exists("SELECT 1 FROM config WHERE name GLOB 'skin:*'" |
| 881 | " AND value=%Q", zCurrent); |
| 882 | } |
| 883 | if( !seen ){ |
| 884 | db_unprotect(PROTECT_CONFIG); |
| 885 | db_multi_exec( |
| 886 | "INSERT INTO config(name,value,mtime) VALUES(" |
| 887 | " strftime('skin:Backup On %%Y-%%m-%%d %%H:%%M:%%S')," |
| 888 | " %Q,now())", zCurrent |
| 889 | ); |
| 890 | db_protect_pop(); |
| 891 | } |
| 892 | |
| 893 | /* Publish draft iSkin */ |
| 894 | for(i=0; i<count(azSkinFile); i++){ |
| 895 | char *zNew = db_get_mprintf("", "draft%d-%s", iSkin, azSkinFile[i]); |
| 896 |
+40
| --- src/sqlcmd.c | ||
| +++ src/sqlcmd.c | ||
| @@ -153,10 +153,44 @@ | ||
| 153 | 153 | sqlcmd_decompress, 0, 0); |
| 154 | 154 | sqlite3_create_function(db, "gather_artifact_stats", 0, SQLITE_UTF8, 0, |
| 155 | 155 | sqlcmd_gather_artifact_stats, 0, 0); |
| 156 | 156 | return SQLITE_OK; |
| 157 | 157 | } |
| 158 | + | |
| 159 | +/* | |
| 160 | +** Undocumented test SQL functions: | |
| 161 | +** | |
| 162 | +** db_protect(X) | |
| 163 | +** db_protect_pop(X) | |
| 164 | +** | |
| 165 | +** These invoke the corresponding C routines. Misuse may result in | |
| 166 | +** an assertion fault. | |
| 167 | +*/ | |
| 168 | +static void sqlcmd_db_protect( | |
| 169 | + sqlite3_context *context, | |
| 170 | + int argc, | |
| 171 | + sqlite3_value **argv | |
| 172 | +){ | |
| 173 | + unsigned mask = 0; | |
| 174 | + const char *z = (const char*)sqlite3_value_text(argv[0]); | |
| 175 | + if( sqlite3_stricmp(z,"user")==0 ) mask |= PROTECT_USER; | |
| 176 | + if( sqlite3_stricmp(z,"config")==0 ) mask |= PROTECT_CONFIG; | |
| 177 | + if( sqlite3_stricmp(z,"sensitive")==0 ) mask |= PROTECT_SENSITIVE; | |
| 178 | + if( sqlite3_stricmp(z,"readonly")==0 ) mask |= PROTECT_READONLY; | |
| 179 | + if( sqlite3_stricmp(z,"all")==0 ) mask |= PROTECT_ALL; | |
| 180 | + db_protect(mask); | |
| 181 | +} | |
| 182 | +static void sqlcmd_db_protect_pop( | |
| 183 | + sqlite3_context *context, | |
| 184 | + int argc, | |
| 185 | + sqlite3_value **argv | |
| 186 | +){ | |
| 187 | + db_protect_pop(); | |
| 188 | +} | |
| 189 | + | |
| 190 | + | |
| 191 | + | |
| 158 | 192 | |
| 159 | 193 | /* |
| 160 | 194 | ** This is the "automatic extension" initializer that runs right after |
| 161 | 195 | ** the connection to the repository database is opened. Set up the |
| 162 | 196 | ** database connection to be more useful to the human operator. |
| @@ -193,10 +227,16 @@ | ||
| 193 | 227 | } |
| 194 | 228 | /* Arrange to trace close operations so that static prepared statements |
| 195 | 229 | ** will get cleaned up when the shell closes the database connection */ |
| 196 | 230 | if( g.fSqlTrace ) mTrace |= SQLITE_TRACE_PROFILE; |
| 197 | 231 | sqlite3_trace_v2(db, mTrace, db_sql_trace, 0); |
| 232 | + db_protect_only(PROTECT_NONE); | |
| 233 | + sqlite3_set_authorizer(db, db_top_authorizer, db); | |
| 234 | + sqlite3_create_function(db, "db_protect", 1, SQLITE_UTF8, 0, | |
| 235 | + sqlcmd_db_protect, 0, 0); | |
| 236 | + sqlite3_create_function(db, "db_protect_pop", 0, SQLITE_UTF8, 0, | |
| 237 | + sqlcmd_db_protect_pop, 0, 0); | |
| 198 | 238 | return SQLITE_OK; |
| 199 | 239 | } |
| 200 | 240 | |
| 201 | 241 | /* |
| 202 | 242 | ** atexit() handler that cleans up global state modified by this module. |
| 203 | 243 |
| --- src/sqlcmd.c | |
| +++ src/sqlcmd.c | |
| @@ -153,10 +153,44 @@ | |
| 153 | sqlcmd_decompress, 0, 0); |
| 154 | sqlite3_create_function(db, "gather_artifact_stats", 0, SQLITE_UTF8, 0, |
| 155 | sqlcmd_gather_artifact_stats, 0, 0); |
| 156 | return SQLITE_OK; |
| 157 | } |
| 158 | |
| 159 | /* |
| 160 | ** This is the "automatic extension" initializer that runs right after |
| 161 | ** the connection to the repository database is opened. Set up the |
| 162 | ** database connection to be more useful to the human operator. |
| @@ -193,10 +227,16 @@ | |
| 193 | } |
| 194 | /* Arrange to trace close operations so that static prepared statements |
| 195 | ** will get cleaned up when the shell closes the database connection */ |
| 196 | if( g.fSqlTrace ) mTrace |= SQLITE_TRACE_PROFILE; |
| 197 | sqlite3_trace_v2(db, mTrace, db_sql_trace, 0); |
| 198 | return SQLITE_OK; |
| 199 | } |
| 200 | |
| 201 | /* |
| 202 | ** atexit() handler that cleans up global state modified by this module. |
| 203 |
| --- src/sqlcmd.c | |
| +++ src/sqlcmd.c | |
| @@ -153,10 +153,44 @@ | |
| 153 | sqlcmd_decompress, 0, 0); |
| 154 | sqlite3_create_function(db, "gather_artifact_stats", 0, SQLITE_UTF8, 0, |
| 155 | sqlcmd_gather_artifact_stats, 0, 0); |
| 156 | return SQLITE_OK; |
| 157 | } |
| 158 | |
| 159 | /* |
| 160 | ** Undocumented test SQL functions: |
| 161 | ** |
| 162 | ** db_protect(X) |
| 163 | ** db_protect_pop(X) |
| 164 | ** |
| 165 | ** These invoke the corresponding C routines. Misuse may result in |
| 166 | ** an assertion fault. |
| 167 | */ |
| 168 | static void sqlcmd_db_protect( |
| 169 | sqlite3_context *context, |
| 170 | int argc, |
| 171 | sqlite3_value **argv |
| 172 | ){ |
| 173 | unsigned mask = 0; |
| 174 | const char *z = (const char*)sqlite3_value_text(argv[0]); |
| 175 | if( sqlite3_stricmp(z,"user")==0 ) mask |= PROTECT_USER; |
| 176 | if( sqlite3_stricmp(z,"config")==0 ) mask |= PROTECT_CONFIG; |
| 177 | if( sqlite3_stricmp(z,"sensitive")==0 ) mask |= PROTECT_SENSITIVE; |
| 178 | if( sqlite3_stricmp(z,"readonly")==0 ) mask |= PROTECT_READONLY; |
| 179 | if( sqlite3_stricmp(z,"all")==0 ) mask |= PROTECT_ALL; |
| 180 | db_protect(mask); |
| 181 | } |
| 182 | static void sqlcmd_db_protect_pop( |
| 183 | sqlite3_context *context, |
| 184 | int argc, |
| 185 | sqlite3_value **argv |
| 186 | ){ |
| 187 | db_protect_pop(); |
| 188 | } |
| 189 | |
| 190 | |
| 191 | |
| 192 | |
| 193 | /* |
| 194 | ** This is the "automatic extension" initializer that runs right after |
| 195 | ** the connection to the repository database is opened. Set up the |
| 196 | ** database connection to be more useful to the human operator. |
| @@ -193,10 +227,16 @@ | |
| 227 | } |
| 228 | /* Arrange to trace close operations so that static prepared statements |
| 229 | ** will get cleaned up when the shell closes the database connection */ |
| 230 | if( g.fSqlTrace ) mTrace |= SQLITE_TRACE_PROFILE; |
| 231 | sqlite3_trace_v2(db, mTrace, db_sql_trace, 0); |
| 232 | db_protect_only(PROTECT_NONE); |
| 233 | sqlite3_set_authorizer(db, db_top_authorizer, db); |
| 234 | sqlite3_create_function(db, "db_protect", 1, SQLITE_UTF8, 0, |
| 235 | sqlcmd_db_protect, 0, 0); |
| 236 | sqlite3_create_function(db, "db_protect_pop", 0, SQLITE_UTF8, 0, |
| 237 | sqlcmd_db_protect_pop, 0, 0); |
| 238 | return SQLITE_OK; |
| 239 | } |
| 240 | |
| 241 | /* |
| 242 | ** atexit() handler that cleans up global state modified by this module. |
| 243 |
+2
| --- src/stash.c | ||
| +++ src/stash.c | ||
| @@ -334,10 +334,12 @@ | ||
| 334 | 334 | blob_write_to_file(&delta, zNPath); |
| 335 | 335 | file_setexe(zNPath, isExec); |
| 336 | 336 | }else if( isRemoved ){ |
| 337 | 337 | fossil_print("DELETE %s\n", zOrig); |
| 338 | 338 | file_delete(zOPath); |
| 339 | + }else if( file_unsafe_in_tree_path(zNPath) ){ | |
| 340 | + /* Ignore the unsafe path */ | |
| 339 | 341 | }else{ |
| 340 | 342 | Blob a, b, out, disk; |
| 341 | 343 | int isNewLink = file_islink(zOPath); |
| 342 | 344 | db_ephemeral_blob(&q, 6, &delta); |
| 343 | 345 | blob_read_from_file(&disk, zOPath, RepoFILE); |
| 344 | 346 |
| --- src/stash.c | |
| +++ src/stash.c | |
| @@ -334,10 +334,12 @@ | |
| 334 | blob_write_to_file(&delta, zNPath); |
| 335 | file_setexe(zNPath, isExec); |
| 336 | }else if( isRemoved ){ |
| 337 | fossil_print("DELETE %s\n", zOrig); |
| 338 | file_delete(zOPath); |
| 339 | }else{ |
| 340 | Blob a, b, out, disk; |
| 341 | int isNewLink = file_islink(zOPath); |
| 342 | db_ephemeral_blob(&q, 6, &delta); |
| 343 | blob_read_from_file(&disk, zOPath, RepoFILE); |
| 344 |
| --- src/stash.c | |
| +++ src/stash.c | |
| @@ -334,10 +334,12 @@ | |
| 334 | blob_write_to_file(&delta, zNPath); |
| 335 | file_setexe(zNPath, isExec); |
| 336 | }else if( isRemoved ){ |
| 337 | fossil_print("DELETE %s\n", zOrig); |
| 338 | file_delete(zOPath); |
| 339 | }else if( file_unsafe_in_tree_path(zNPath) ){ |
| 340 | /* Ignore the unsafe path */ |
| 341 | }else{ |
| 342 | Blob a, b, out, disk; |
| 343 | int isNewLink = file_islink(zOPath); |
| 344 | db_ephemeral_blob(&q, 6, &delta); |
| 345 | blob_read_from_file(&disk, zOPath, RepoFILE); |
| 346 |
+7
| --- src/sync.c | ||
| +++ src/sync.c | ||
| @@ -425,13 +425,15 @@ | ||
| 425 | 425 | /* fossil remote off |
| 426 | 426 | ** Forget the last-sync-URL and its password |
| 427 | 427 | */ |
| 428 | 428 | if( g.argc!=3 ) usage("off"); |
| 429 | 429 | remote_delete_default: |
| 430 | + db_unprotect(PROTECT_CONFIG); | |
| 430 | 431 | db_multi_exec( |
| 431 | 432 | "DELETE FROM config WHERE name GLOB 'last-sync-*';" |
| 432 | 433 | ); |
| 434 | + db_protect_pop(); | |
| 433 | 435 | return; |
| 434 | 436 | } |
| 435 | 437 | if( strncmp(zArg, "list", nArg)==0 || strcmp(zArg,"ls")==0 ){ |
| 436 | 438 | Stmt q; |
| 437 | 439 | if( g.argc!=3 ) usage("list"); |
| @@ -457,10 +459,11 @@ | ||
| 457 | 459 | zName = g.argv[3]; |
| 458 | 460 | zUrl = g.argv[4]; |
| 459 | 461 | if( strcmp(zName,"default")==0 ) goto remote_add_default; |
| 460 | 462 | url_parse_local(zUrl, URL_PROMPT_PW, &x); |
| 461 | 463 | db_begin_write(); |
| 464 | + db_unprotect(PROTECT_CONFIG); | |
| 462 | 465 | db_multi_exec( |
| 463 | 466 | "REPLACE INTO config(name, value, mtime)" |
| 464 | 467 | " VALUES('sync-url:%q',%Q,now())", |
| 465 | 468 | zName, x.canonical |
| 466 | 469 | ); |
| @@ -467,21 +470,24 @@ | ||
| 467 | 470 | db_multi_exec( |
| 468 | 471 | "REPLACE INTO config(name, value, mtime)" |
| 469 | 472 | " VALUES('sync-pw:%q',obscure(%Q),now())", |
| 470 | 473 | zName, x.passwd |
| 471 | 474 | ); |
| 475 | + db_protect_pop(); | |
| 472 | 476 | db_commit_transaction(); |
| 473 | 477 | return; |
| 474 | 478 | } |
| 475 | 479 | if( strncmp(zArg, "delete", nArg)==0 ){ |
| 476 | 480 | char *zName; |
| 477 | 481 | if( g.argc!=4 ) usage("delete NAME"); |
| 478 | 482 | zName = g.argv[3]; |
| 479 | 483 | if( strcmp(zName,"default")==0 ) goto remote_delete_default; |
| 480 | 484 | db_begin_write(); |
| 485 | + db_unprotect(PROTECT_CONFIG); | |
| 481 | 486 | db_multi_exec("DELETE FROM config WHERE name glob 'sync-url:%q'", zName); |
| 482 | 487 | db_multi_exec("DELETE FROM config WHERE name glob 'sync-pw:%q'", zName); |
| 488 | + db_protect_pop(); | |
| 483 | 489 | db_commit_transaction(); |
| 484 | 490 | return; |
| 485 | 491 | } |
| 486 | 492 | if( sqlite3_strlike("http://%",zArg,0)==0 |
| 487 | 493 | || sqlite3_strlike("https://%",zArg,0)==0 |
| @@ -539,7 +545,8 @@ | ||
| 539 | 545 | } |
| 540 | 546 | }else{ |
| 541 | 547 | fossil_fatal("backup \"%s\" already exists", zDest); |
| 542 | 548 | } |
| 543 | 549 | } |
| 550 | + db_unprotect(PROTECT_ALL); | |
| 544 | 551 | db_multi_exec("VACUUM repository INTO %Q", zDest); |
| 545 | 552 | } |
| 546 | 553 |
| --- src/sync.c | |
| +++ src/sync.c | |
| @@ -425,13 +425,15 @@ | |
| 425 | /* fossil remote off |
| 426 | ** Forget the last-sync-URL and its password |
| 427 | */ |
| 428 | if( g.argc!=3 ) usage("off"); |
| 429 | remote_delete_default: |
| 430 | db_multi_exec( |
| 431 | "DELETE FROM config WHERE name GLOB 'last-sync-*';" |
| 432 | ); |
| 433 | return; |
| 434 | } |
| 435 | if( strncmp(zArg, "list", nArg)==0 || strcmp(zArg,"ls")==0 ){ |
| 436 | Stmt q; |
| 437 | if( g.argc!=3 ) usage("list"); |
| @@ -457,10 +459,11 @@ | |
| 457 | zName = g.argv[3]; |
| 458 | zUrl = g.argv[4]; |
| 459 | if( strcmp(zName,"default")==0 ) goto remote_add_default; |
| 460 | url_parse_local(zUrl, URL_PROMPT_PW, &x); |
| 461 | db_begin_write(); |
| 462 | db_multi_exec( |
| 463 | "REPLACE INTO config(name, value, mtime)" |
| 464 | " VALUES('sync-url:%q',%Q,now())", |
| 465 | zName, x.canonical |
| 466 | ); |
| @@ -467,21 +470,24 @@ | |
| 467 | db_multi_exec( |
| 468 | "REPLACE INTO config(name, value, mtime)" |
| 469 | " VALUES('sync-pw:%q',obscure(%Q),now())", |
| 470 | zName, x.passwd |
| 471 | ); |
| 472 | db_commit_transaction(); |
| 473 | return; |
| 474 | } |
| 475 | if( strncmp(zArg, "delete", nArg)==0 ){ |
| 476 | char *zName; |
| 477 | if( g.argc!=4 ) usage("delete NAME"); |
| 478 | zName = g.argv[3]; |
| 479 | if( strcmp(zName,"default")==0 ) goto remote_delete_default; |
| 480 | db_begin_write(); |
| 481 | db_multi_exec("DELETE FROM config WHERE name glob 'sync-url:%q'", zName); |
| 482 | db_multi_exec("DELETE FROM config WHERE name glob 'sync-pw:%q'", zName); |
| 483 | db_commit_transaction(); |
| 484 | return; |
| 485 | } |
| 486 | if( sqlite3_strlike("http://%",zArg,0)==0 |
| 487 | || sqlite3_strlike("https://%",zArg,0)==0 |
| @@ -539,7 +545,8 @@ | |
| 539 | } |
| 540 | }else{ |
| 541 | fossil_fatal("backup \"%s\" already exists", zDest); |
| 542 | } |
| 543 | } |
| 544 | db_multi_exec("VACUUM repository INTO %Q", zDest); |
| 545 | } |
| 546 |
| --- src/sync.c | |
| +++ src/sync.c | |
| @@ -425,13 +425,15 @@ | |
| 425 | /* fossil remote off |
| 426 | ** Forget the last-sync-URL and its password |
| 427 | */ |
| 428 | if( g.argc!=3 ) usage("off"); |
| 429 | remote_delete_default: |
| 430 | db_unprotect(PROTECT_CONFIG); |
| 431 | db_multi_exec( |
| 432 | "DELETE FROM config WHERE name GLOB 'last-sync-*';" |
| 433 | ); |
| 434 | db_protect_pop(); |
| 435 | return; |
| 436 | } |
| 437 | if( strncmp(zArg, "list", nArg)==0 || strcmp(zArg,"ls")==0 ){ |
| 438 | Stmt q; |
| 439 | if( g.argc!=3 ) usage("list"); |
| @@ -457,10 +459,11 @@ | |
| 459 | zName = g.argv[3]; |
| 460 | zUrl = g.argv[4]; |
| 461 | if( strcmp(zName,"default")==0 ) goto remote_add_default; |
| 462 | url_parse_local(zUrl, URL_PROMPT_PW, &x); |
| 463 | db_begin_write(); |
| 464 | db_unprotect(PROTECT_CONFIG); |
| 465 | db_multi_exec( |
| 466 | "REPLACE INTO config(name, value, mtime)" |
| 467 | " VALUES('sync-url:%q',%Q,now())", |
| 468 | zName, x.canonical |
| 469 | ); |
| @@ -467,21 +470,24 @@ | |
| 470 | db_multi_exec( |
| 471 | "REPLACE INTO config(name, value, mtime)" |
| 472 | " VALUES('sync-pw:%q',obscure(%Q),now())", |
| 473 | zName, x.passwd |
| 474 | ); |
| 475 | db_protect_pop(); |
| 476 | db_commit_transaction(); |
| 477 | return; |
| 478 | } |
| 479 | if( strncmp(zArg, "delete", nArg)==0 ){ |
| 480 | char *zName; |
| 481 | if( g.argc!=4 ) usage("delete NAME"); |
| 482 | zName = g.argv[3]; |
| 483 | if( strcmp(zName,"default")==0 ) goto remote_delete_default; |
| 484 | db_begin_write(); |
| 485 | db_unprotect(PROTECT_CONFIG); |
| 486 | db_multi_exec("DELETE FROM config WHERE name glob 'sync-url:%q'", zName); |
| 487 | db_multi_exec("DELETE FROM config WHERE name glob 'sync-pw:%q'", zName); |
| 488 | db_protect_pop(); |
| 489 | db_commit_transaction(); |
| 490 | return; |
| 491 | } |
| 492 | if( sqlite3_strlike("http://%",zArg,0)==0 |
| 493 | || sqlite3_strlike("https://%",zArg,0)==0 |
| @@ -539,7 +545,8 @@ | |
| 545 | } |
| 546 | }else{ |
| 547 | fossil_fatal("backup \"%s\" already exists", zDest); |
| 548 | } |
| 549 | } |
| 550 | db_unprotect(PROTECT_ALL); |
| 551 | db_multi_exec("VACUUM repository INTO %Q", zDest); |
| 552 | } |
| 553 |
+71
| --- src/tkt.c | ||
| +++ src/tkt.c | ||
| @@ -370,10 +370,79 @@ | ||
| 370 | 370 | Th_FossilInit(TH_INIT_DEFAULT); |
| 371 | 371 | Th_Store("uuid", zUuid); |
| 372 | 372 | zConfig = ticket_change_code(); |
| 373 | 373 | return Th_Eval(g.interp, 0, zConfig, -1); |
| 374 | 374 | } |
| 375 | + | |
| 376 | +/* | |
| 377 | +** An authorizer function for the SQL used to initialize the | |
| 378 | +** schema for the ticketing system. Only allow CREATE TABLE and | |
| 379 | +** CREATE INDEX for tables whose names begin with "ticket" and | |
| 380 | +** changes to tables whose names begin with "ticket". | |
| 381 | +*/ | |
| 382 | +static int ticket_schema_auth( | |
| 383 | + void *pNErr, | |
| 384 | + int eCode, | |
| 385 | + const char *z0, | |
| 386 | + const char *z1, | |
| 387 | + const char *z2, | |
| 388 | + const char *z3 | |
| 389 | +){ | |
| 390 | + switch( eCode ){ | |
| 391 | + case SQLITE_CREATE_TABLE: { | |
| 392 | + if( sqlite3_stricmp(z2,"main")!=0 | |
| 393 | + && sqlite3_stricmp(z2,"repository")!=0 | |
| 394 | + ){ | |
| 395 | + goto ticket_schema_error; | |
| 396 | + } | |
| 397 | + if( sqlite3_strnicmp(z0,"ticket",6)!=0 ){ | |
| 398 | + goto ticket_schema_error; | |
| 399 | + } | |
| 400 | + break; | |
| 401 | + } | |
| 402 | + case SQLITE_CREATE_INDEX: { | |
| 403 | + if( sqlite3_stricmp(z2,"main")!=0 | |
| 404 | + && sqlite3_stricmp(z2,"repository")!=0 | |
| 405 | + ){ | |
| 406 | + goto ticket_schema_error; | |
| 407 | + } | |
| 408 | + if( sqlite3_strnicmp(z1,"ticket",6)!=0 ){ | |
| 409 | + goto ticket_schema_error; | |
| 410 | + } | |
| 411 | + break; | |
| 412 | + } | |
| 413 | + case SQLITE_INSERT: | |
| 414 | + case SQLITE_UPDATE: | |
| 415 | + case SQLITE_DELETE: { | |
| 416 | + if( sqlite3_stricmp(z2,"main")!=0 | |
| 417 | + && sqlite3_stricmp(z2,"repository")!=0 | |
| 418 | + ){ | |
| 419 | + goto ticket_schema_error; | |
| 420 | + } | |
| 421 | + if( sqlite3_strnicmp(z0,"ticket",6)!=0 | |
| 422 | + && sqlite3_strnicmp(z0,"sqlite_",7)!=0 | |
| 423 | + ){ | |
| 424 | + goto ticket_schema_error; | |
| 425 | + } | |
| 426 | + break; | |
| 427 | + } | |
| 428 | + case SQLITE_REINDEX: | |
| 429 | + case SQLITE_TRANSACTION: | |
| 430 | + case SQLITE_READ: { | |
| 431 | + break; | |
| 432 | + } | |
| 433 | + default: { | |
| 434 | + goto ticket_schema_error; | |
| 435 | + } | |
| 436 | + } | |
| 437 | + return SQLITE_OK; | |
| 438 | + | |
| 439 | +ticket_schema_error: | |
| 440 | + if( pNErr ) *(int*)pNErr = 1; | |
| 441 | + return SQLITE_DENY; | |
| 442 | +} | |
| 443 | + | |
| 375 | 444 | |
| 376 | 445 | /* |
| 377 | 446 | ** Recreate the TICKET and TICKETCHNG tables. |
| 378 | 447 | */ |
| 379 | 448 | void ticket_create_table(int separateConnection){ |
| @@ -382,16 +451,18 @@ | ||
| 382 | 451 | db_multi_exec( |
| 383 | 452 | "DROP TABLE IF EXISTS ticket;" |
| 384 | 453 | "DROP TABLE IF EXISTS ticketchng;" |
| 385 | 454 | ); |
| 386 | 455 | zSql = ticket_table_schema(); |
| 456 | + db_set_authorizer(ticket_schema_auth,0,"Ticket-Schema"); | |
| 387 | 457 | if( separateConnection ){ |
| 388 | 458 | if( db_transaction_nesting_depth() ) db_end_transaction(0); |
| 389 | 459 | db_init_database(g.zRepositoryName, zSql, 0); |
| 390 | 460 | }else{ |
| 391 | 461 | db_multi_exec("%s", zSql/*safe-for-%s*/); |
| 392 | 462 | } |
| 463 | + db_clear_authorizer(); | |
| 393 | 464 | fossil_free(zSql); |
| 394 | 465 | } |
| 395 | 466 | |
| 396 | 467 | /* |
| 397 | 468 | ** Repopulate the TICKET and TICKETCHNG tables from scratch using all |
| 398 | 469 |
| --- src/tkt.c | |
| +++ src/tkt.c | |
| @@ -370,10 +370,79 @@ | |
| 370 | Th_FossilInit(TH_INIT_DEFAULT); |
| 371 | Th_Store("uuid", zUuid); |
| 372 | zConfig = ticket_change_code(); |
| 373 | return Th_Eval(g.interp, 0, zConfig, -1); |
| 374 | } |
| 375 | |
| 376 | /* |
| 377 | ** Recreate the TICKET and TICKETCHNG tables. |
| 378 | */ |
| 379 | void ticket_create_table(int separateConnection){ |
| @@ -382,16 +451,18 @@ | |
| 382 | db_multi_exec( |
| 383 | "DROP TABLE IF EXISTS ticket;" |
| 384 | "DROP TABLE IF EXISTS ticketchng;" |
| 385 | ); |
| 386 | zSql = ticket_table_schema(); |
| 387 | if( separateConnection ){ |
| 388 | if( db_transaction_nesting_depth() ) db_end_transaction(0); |
| 389 | db_init_database(g.zRepositoryName, zSql, 0); |
| 390 | }else{ |
| 391 | db_multi_exec("%s", zSql/*safe-for-%s*/); |
| 392 | } |
| 393 | fossil_free(zSql); |
| 394 | } |
| 395 | |
| 396 | /* |
| 397 | ** Repopulate the TICKET and TICKETCHNG tables from scratch using all |
| 398 |
| --- src/tkt.c | |
| +++ src/tkt.c | |
| @@ -370,10 +370,79 @@ | |
| 370 | Th_FossilInit(TH_INIT_DEFAULT); |
| 371 | Th_Store("uuid", zUuid); |
| 372 | zConfig = ticket_change_code(); |
| 373 | return Th_Eval(g.interp, 0, zConfig, -1); |
| 374 | } |
| 375 | |
| 376 | /* |
| 377 | ** An authorizer function for the SQL used to initialize the |
| 378 | ** schema for the ticketing system. Only allow CREATE TABLE and |
| 379 | ** CREATE INDEX for tables whose names begin with "ticket" and |
| 380 | ** changes to tables whose names begin with "ticket". |
| 381 | */ |
| 382 | static int ticket_schema_auth( |
| 383 | void *pNErr, |
| 384 | int eCode, |
| 385 | const char *z0, |
| 386 | const char *z1, |
| 387 | const char *z2, |
| 388 | const char *z3 |
| 389 | ){ |
| 390 | switch( eCode ){ |
| 391 | case SQLITE_CREATE_TABLE: { |
| 392 | if( sqlite3_stricmp(z2,"main")!=0 |
| 393 | && sqlite3_stricmp(z2,"repository")!=0 |
| 394 | ){ |
| 395 | goto ticket_schema_error; |
| 396 | } |
| 397 | if( sqlite3_strnicmp(z0,"ticket",6)!=0 ){ |
| 398 | goto ticket_schema_error; |
| 399 | } |
| 400 | break; |
| 401 | } |
| 402 | case SQLITE_CREATE_INDEX: { |
| 403 | if( sqlite3_stricmp(z2,"main")!=0 |
| 404 | && sqlite3_stricmp(z2,"repository")!=0 |
| 405 | ){ |
| 406 | goto ticket_schema_error; |
| 407 | } |
| 408 | if( sqlite3_strnicmp(z1,"ticket",6)!=0 ){ |
| 409 | goto ticket_schema_error; |
| 410 | } |
| 411 | break; |
| 412 | } |
| 413 | case SQLITE_INSERT: |
| 414 | case SQLITE_UPDATE: |
| 415 | case SQLITE_DELETE: { |
| 416 | if( sqlite3_stricmp(z2,"main")!=0 |
| 417 | && sqlite3_stricmp(z2,"repository")!=0 |
| 418 | ){ |
| 419 | goto ticket_schema_error; |
| 420 | } |
| 421 | if( sqlite3_strnicmp(z0,"ticket",6)!=0 |
| 422 | && sqlite3_strnicmp(z0,"sqlite_",7)!=0 |
| 423 | ){ |
| 424 | goto ticket_schema_error; |
| 425 | } |
| 426 | break; |
| 427 | } |
| 428 | case SQLITE_REINDEX: |
| 429 | case SQLITE_TRANSACTION: |
| 430 | case SQLITE_READ: { |
| 431 | break; |
| 432 | } |
| 433 | default: { |
| 434 | goto ticket_schema_error; |
| 435 | } |
| 436 | } |
| 437 | return SQLITE_OK; |
| 438 | |
| 439 | ticket_schema_error: |
| 440 | if( pNErr ) *(int*)pNErr = 1; |
| 441 | return SQLITE_DENY; |
| 442 | } |
| 443 | |
| 444 | |
| 445 | /* |
| 446 | ** Recreate the TICKET and TICKETCHNG tables. |
| 447 | */ |
| 448 | void ticket_create_table(int separateConnection){ |
| @@ -382,16 +451,18 @@ | |
| 451 | db_multi_exec( |
| 452 | "DROP TABLE IF EXISTS ticket;" |
| 453 | "DROP TABLE IF EXISTS ticketchng;" |
| 454 | ); |
| 455 | zSql = ticket_table_schema(); |
| 456 | db_set_authorizer(ticket_schema_auth,0,"Ticket-Schema"); |
| 457 | if( separateConnection ){ |
| 458 | if( db_transaction_nesting_depth() ) db_end_transaction(0); |
| 459 | db_init_database(g.zRepositoryName, zSql, 0); |
| 460 | }else{ |
| 461 | db_multi_exec("%s", zSql/*safe-for-%s*/); |
| 462 | } |
| 463 | db_clear_authorizer(); |
| 464 | fossil_free(zSql); |
| 465 | } |
| 466 | |
| 467 | /* |
| 468 | ** Repopulate the TICKET and TICKETCHNG tables from scratch using all |
| 469 |
+4
-2
| --- src/undo.c | ||
| +++ src/undo.c | ||
| @@ -52,11 +52,11 @@ | ||
| 52 | 52 | int new_exe; |
| 53 | 53 | int new_link; |
| 54 | 54 | int old_link; |
| 55 | 55 | Blob current; |
| 56 | 56 | Blob new; |
| 57 | - zFullname = mprintf("%s/%s", g.zLocalRoot, zPathname); | |
| 57 | + zFullname = mprintf("%s%s", g.zLocalRoot, zPathname); | |
| 58 | 58 | old_link = db_column_int(&q, 3); |
| 59 | 59 | new_exists = file_size(zFullname, RepoFILE)>=0; |
| 60 | 60 | new_link = file_islink(0); |
| 61 | 61 | if( new_exists ){ |
| 62 | 62 | blob_read_from_file(¤t, zFullname, RepoFILE); |
| @@ -69,11 +69,13 @@ | ||
| 69 | 69 | old_exists = db_column_int(&q, 1); |
| 70 | 70 | old_exe = db_column_int(&q, 2); |
| 71 | 71 | if( old_exists ){ |
| 72 | 72 | db_ephemeral_blob(&q, 0, &new); |
| 73 | 73 | } |
| 74 | - if( old_exists ){ | |
| 74 | + if( file_unsafe_in_tree_path(zFullname) ){ | |
| 75 | + /* do nothign with this unsafe file */ | |
| 76 | + }else if( old_exists ){ | |
| 75 | 77 | if( new_exists ){ |
| 76 | 78 | fossil_print("%s %s\n", redoFlag ? "REDO" : "UNDO", zPathname); |
| 77 | 79 | }else{ |
| 78 | 80 | fossil_print("NEW %s\n", zPathname); |
| 79 | 81 | } |
| 80 | 82 |
| --- src/undo.c | |
| +++ src/undo.c | |
| @@ -52,11 +52,11 @@ | |
| 52 | int new_exe; |
| 53 | int new_link; |
| 54 | int old_link; |
| 55 | Blob current; |
| 56 | Blob new; |
| 57 | zFullname = mprintf("%s/%s", g.zLocalRoot, zPathname); |
| 58 | old_link = db_column_int(&q, 3); |
| 59 | new_exists = file_size(zFullname, RepoFILE)>=0; |
| 60 | new_link = file_islink(0); |
| 61 | if( new_exists ){ |
| 62 | blob_read_from_file(¤t, zFullname, RepoFILE); |
| @@ -69,11 +69,13 @@ | |
| 69 | old_exists = db_column_int(&q, 1); |
| 70 | old_exe = db_column_int(&q, 2); |
| 71 | if( old_exists ){ |
| 72 | db_ephemeral_blob(&q, 0, &new); |
| 73 | } |
| 74 | if( old_exists ){ |
| 75 | if( new_exists ){ |
| 76 | fossil_print("%s %s\n", redoFlag ? "REDO" : "UNDO", zPathname); |
| 77 | }else{ |
| 78 | fossil_print("NEW %s\n", zPathname); |
| 79 | } |
| 80 |
| --- src/undo.c | |
| +++ src/undo.c | |
| @@ -52,11 +52,11 @@ | |
| 52 | int new_exe; |
| 53 | int new_link; |
| 54 | int old_link; |
| 55 | Blob current; |
| 56 | Blob new; |
| 57 | zFullname = mprintf("%s%s", g.zLocalRoot, zPathname); |
| 58 | old_link = db_column_int(&q, 3); |
| 59 | new_exists = file_size(zFullname, RepoFILE)>=0; |
| 60 | new_link = file_islink(0); |
| 61 | if( new_exists ){ |
| 62 | blob_read_from_file(¤t, zFullname, RepoFILE); |
| @@ -69,11 +69,13 @@ | |
| 69 | old_exists = db_column_int(&q, 1); |
| 70 | old_exe = db_column_int(&q, 2); |
| 71 | if( old_exists ){ |
| 72 | db_ephemeral_blob(&q, 0, &new); |
| 73 | } |
| 74 | if( file_unsafe_in_tree_path(zFullname) ){ |
| 75 | /* do nothign with this unsafe file */ |
| 76 | }else if( old_exists ){ |
| 77 | if( new_exists ){ |
| 78 | fossil_print("%s %s\n", redoFlag ? "REDO" : "UNDO", zPathname); |
| 79 | }else{ |
| 80 | fossil_print("NEW %s\n", zPathname); |
| 81 | } |
| 82 |
+2
| --- src/update.c | ||
| +++ src/update.c | ||
| @@ -927,10 +927,12 @@ | ||
| 927 | 927 | " SET pathname=origname, origname=NULL" |
| 928 | 928 | " WHERE pathname=%Q AND origname!=pathname;" |
| 929 | 929 | "DELETE FROM vfile WHERE pathname=%Q", |
| 930 | 930 | zFile, zFile |
| 931 | 931 | ); |
| 932 | + }else if( file_unsafe_in_tree_path(zFull) ){ | |
| 933 | + /* Ignore this file */ | |
| 932 | 934 | }else{ |
| 933 | 935 | sqlite3_int64 mtime; |
| 934 | 936 | int rvChnged = 0; |
| 935 | 937 | int rvPerm = manifest_file_mperm(pRvFile); |
| 936 | 938 | |
| 937 | 939 |
| --- src/update.c | |
| +++ src/update.c | |
| @@ -927,10 +927,12 @@ | |
| 927 | " SET pathname=origname, origname=NULL" |
| 928 | " WHERE pathname=%Q AND origname!=pathname;" |
| 929 | "DELETE FROM vfile WHERE pathname=%Q", |
| 930 | zFile, zFile |
| 931 | ); |
| 932 | }else{ |
| 933 | sqlite3_int64 mtime; |
| 934 | int rvChnged = 0; |
| 935 | int rvPerm = manifest_file_mperm(pRvFile); |
| 936 | |
| 937 |
| --- src/update.c | |
| +++ src/update.c | |
| @@ -927,10 +927,12 @@ | |
| 927 | " SET pathname=origname, origname=NULL" |
| 928 | " WHERE pathname=%Q AND origname!=pathname;" |
| 929 | "DELETE FROM vfile WHERE pathname=%Q", |
| 930 | zFile, zFile |
| 931 | ); |
| 932 | }else if( file_unsafe_in_tree_path(zFull) ){ |
| 933 | /* Ignore this file */ |
| 934 | }else{ |
| 935 | sqlite3_int64 mtime; |
| 936 | int rvChnged = 0; |
| 937 | int rvPerm = manifest_file_mperm(pRvFile); |
| 938 | |
| 939 |
+3
-3
| --- src/url.c | ||
| +++ src/url.c | ||
| @@ -50,11 +50,11 @@ | ||
| 50 | 50 | int isHttps; /* True if a "https:" url */ |
| 51 | 51 | int isSsh; /* True if an "ssh:" url */ |
| 52 | 52 | int isAlias; /* Input URL was an alias */ |
| 53 | 53 | char *name; /* Hostname for http: or filename for file: */ |
| 54 | 54 | char *hostname; /* The HOST: parameter on http headers */ |
| 55 | - const char *protocol; /* "http" or "https" or "ssh" */ | |
| 55 | + const char *protocol; /* "http" or "https" or "ssh" or "file" */ | |
| 56 | 56 | int port; /* TCP port number for http: or https: */ |
| 57 | 57 | int dfltPort; /* The default port for the given protocol */ |
| 58 | 58 | char *path; /* Pathname for http: */ |
| 59 | 59 | char *user; /* User id for http: */ |
| 60 | 60 | char *passwd; /* Password for http: */ |
| @@ -76,11 +76,11 @@ | ||
| 76 | 76 | ** as follows: |
| 77 | 77 | ** |
| 78 | 78 | ** isFile True if FILE: |
| 79 | 79 | ** isHttps True if HTTPS: |
| 80 | 80 | ** isSsh True if SSH: |
| 81 | -** protocol "http" or "https" or "file" | |
| 81 | +** protocol "http" or "https" or "file" or "ssh" | |
| 82 | 82 | ** name Hostname for HTTP:, HTTPS:, SSH:. Filename for FILE: |
| 83 | 83 | ** port TCP port number for HTTP or HTTPS. |
| 84 | 84 | ** dfltPort Default TCP port number (80 or 443). |
| 85 | 85 | ** path Path name for HTTP or HTTPS. |
| 86 | 86 | ** user Userid. |
| @@ -305,11 +305,11 @@ | ||
| 305 | 305 | ** form last-sync-pw. |
| 306 | 306 | ** |
| 307 | 307 | ** g.url.isFile True if FILE: |
| 308 | 308 | ** g.url.isHttps True if HTTPS: |
| 309 | 309 | ** g.url.isSsh True if SSH: |
| 310 | -** g.url.protocol "http" or "https" or "file" | |
| 310 | +** g.url.protocol "http" or "https" or "file" or "ssh" | |
| 311 | 311 | ** g.url.name Hostname for HTTP:, HTTPS:, SSH:. Filename for FILE: |
| 312 | 312 | ** g.url.port TCP port number for HTTP or HTTPS. |
| 313 | 313 | ** g.url.dfltPort Default TCP port number (80 or 443). |
| 314 | 314 | ** g.url.path Path name for HTTP or HTTPS. |
| 315 | 315 | ** g.url.user Userid. |
| 316 | 316 |
| --- src/url.c | |
| +++ src/url.c | |
| @@ -50,11 +50,11 @@ | |
| 50 | int isHttps; /* True if a "https:" url */ |
| 51 | int isSsh; /* True if an "ssh:" url */ |
| 52 | int isAlias; /* Input URL was an alias */ |
| 53 | char *name; /* Hostname for http: or filename for file: */ |
| 54 | char *hostname; /* The HOST: parameter on http headers */ |
| 55 | const char *protocol; /* "http" or "https" or "ssh" */ |
| 56 | int port; /* TCP port number for http: or https: */ |
| 57 | int dfltPort; /* The default port for the given protocol */ |
| 58 | char *path; /* Pathname for http: */ |
| 59 | char *user; /* User id for http: */ |
| 60 | char *passwd; /* Password for http: */ |
| @@ -76,11 +76,11 @@ | |
| 76 | ** as follows: |
| 77 | ** |
| 78 | ** isFile True if FILE: |
| 79 | ** isHttps True if HTTPS: |
| 80 | ** isSsh True if SSH: |
| 81 | ** protocol "http" or "https" or "file" |
| 82 | ** name Hostname for HTTP:, HTTPS:, SSH:. Filename for FILE: |
| 83 | ** port TCP port number for HTTP or HTTPS. |
| 84 | ** dfltPort Default TCP port number (80 or 443). |
| 85 | ** path Path name for HTTP or HTTPS. |
| 86 | ** user Userid. |
| @@ -305,11 +305,11 @@ | |
| 305 | ** form last-sync-pw. |
| 306 | ** |
| 307 | ** g.url.isFile True if FILE: |
| 308 | ** g.url.isHttps True if HTTPS: |
| 309 | ** g.url.isSsh True if SSH: |
| 310 | ** g.url.protocol "http" or "https" or "file" |
| 311 | ** g.url.name Hostname for HTTP:, HTTPS:, SSH:. Filename for FILE: |
| 312 | ** g.url.port TCP port number for HTTP or HTTPS. |
| 313 | ** g.url.dfltPort Default TCP port number (80 or 443). |
| 314 | ** g.url.path Path name for HTTP or HTTPS. |
| 315 | ** g.url.user Userid. |
| 316 |
| --- src/url.c | |
| +++ src/url.c | |
| @@ -50,11 +50,11 @@ | |
| 50 | int isHttps; /* True if a "https:" url */ |
| 51 | int isSsh; /* True if an "ssh:" url */ |
| 52 | int isAlias; /* Input URL was an alias */ |
| 53 | char *name; /* Hostname for http: or filename for file: */ |
| 54 | char *hostname; /* The HOST: parameter on http headers */ |
| 55 | const char *protocol; /* "http" or "https" or "ssh" or "file" */ |
| 56 | int port; /* TCP port number for http: or https: */ |
| 57 | int dfltPort; /* The default port for the given protocol */ |
| 58 | char *path; /* Pathname for http: */ |
| 59 | char *user; /* User id for http: */ |
| 60 | char *passwd; /* Password for http: */ |
| @@ -76,11 +76,11 @@ | |
| 76 | ** as follows: |
| 77 | ** |
| 78 | ** isFile True if FILE: |
| 79 | ** isHttps True if HTTPS: |
| 80 | ** isSsh True if SSH: |
| 81 | ** protocol "http" or "https" or "file" or "ssh" |
| 82 | ** name Hostname for HTTP:, HTTPS:, SSH:. Filename for FILE: |
| 83 | ** port TCP port number for HTTP or HTTPS. |
| 84 | ** dfltPort Default TCP port number (80 or 443). |
| 85 | ** path Path name for HTTP or HTTPS. |
| 86 | ** user Userid. |
| @@ -305,11 +305,11 @@ | |
| 305 | ** form last-sync-pw. |
| 306 | ** |
| 307 | ** g.url.isFile True if FILE: |
| 308 | ** g.url.isHttps True if HTTPS: |
| 309 | ** g.url.isSsh True if SSH: |
| 310 | ** g.url.protocol "http" or "https" or "file" or "ssh" |
| 311 | ** g.url.name Hostname for HTTP:, HTTPS:, SSH:. Filename for FILE: |
| 312 | ** g.url.port TCP port number for HTTP or HTTPS. |
| 313 | ** g.url.dfltPort Default TCP port number (80 or 443). |
| 314 | ** g.url.path Path name for HTTP or HTTPS. |
| 315 | ** g.url.user Userid. |
| 316 |
+5
| --- src/user.c | ||
| +++ src/user.c | ||
| @@ -432,12 +432,14 @@ | ||
| 432 | 432 | } |
| 433 | 433 | if( blob_size(&pw)==0 ){ |
| 434 | 434 | fossil_print("password unchanged\n"); |
| 435 | 435 | }else{ |
| 436 | 436 | char *zSecret = sha1_shared_secret(blob_str(&pw), g.argv[3], 0); |
| 437 | + db_unprotect(PROTECT_USER); | |
| 437 | 438 | db_multi_exec("UPDATE user SET pw=%Q, mtime=now() WHERE uid=%d", |
| 438 | 439 | zSecret, uid); |
| 440 | + db_protect_pop(); | |
| 439 | 441 | free(zSecret); |
| 440 | 442 | } |
| 441 | 443 | }else if( n>=2 && strncmp(g.argv[2],"capabilities",2)==0 ){ |
| 442 | 444 | int uid; |
| 443 | 445 | if( g.argc!=4 && g.argc!=5 ){ |
| @@ -446,14 +448,16 @@ | ||
| 446 | 448 | uid = db_int(0, "SELECT uid FROM user WHERE login=%Q", g.argv[3]); |
| 447 | 449 | if( uid==0 ){ |
| 448 | 450 | fossil_fatal("no such user: %s", g.argv[3]); |
| 449 | 451 | } |
| 450 | 452 | if( g.argc==5 ){ |
| 453 | + db_unprotect(PROTECT_USER); | |
| 451 | 454 | db_multi_exec( |
| 452 | 455 | "UPDATE user SET cap=%Q, mtime=now() WHERE uid=%d", |
| 453 | 456 | g.argv[4], uid |
| 454 | 457 | ); |
| 458 | + db_protect_pop(); | |
| 455 | 459 | } |
| 456 | 460 | fossil_print("%s\n", db_text(0, "SELECT cap FROM user WHERE uid=%d", uid)); |
| 457 | 461 | }else{ |
| 458 | 462 | fossil_fatal("user subcommand should be one of: " |
| 459 | 463 | "capabilities default list new password"); |
| @@ -573,10 +577,11 @@ | ||
| 573 | 577 | void user_hash_passwords_cmd(void){ |
| 574 | 578 | if( g.argc!=3 ) usage("REPOSITORY"); |
| 575 | 579 | db_open_repository(g.argv[2]); |
| 576 | 580 | sqlite3_create_function(g.db, "shared_secret", 2, SQLITE_UTF8, 0, |
| 577 | 581 | sha1_shared_secret_sql_function, 0, 0); |
| 582 | + db_unprotect(PROTECT_ALL); | |
| 578 | 583 | db_multi_exec( |
| 579 | 584 | "UPDATE user SET pw=shared_secret(pw,login), mtime=now()" |
| 580 | 585 | " WHERE length(pw)>0 AND length(pw)!=40" |
| 581 | 586 | ); |
| 582 | 587 | } |
| 583 | 588 |
| --- src/user.c | |
| +++ src/user.c | |
| @@ -432,12 +432,14 @@ | |
| 432 | } |
| 433 | if( blob_size(&pw)==0 ){ |
| 434 | fossil_print("password unchanged\n"); |
| 435 | }else{ |
| 436 | char *zSecret = sha1_shared_secret(blob_str(&pw), g.argv[3], 0); |
| 437 | db_multi_exec("UPDATE user SET pw=%Q, mtime=now() WHERE uid=%d", |
| 438 | zSecret, uid); |
| 439 | free(zSecret); |
| 440 | } |
| 441 | }else if( n>=2 && strncmp(g.argv[2],"capabilities",2)==0 ){ |
| 442 | int uid; |
| 443 | if( g.argc!=4 && g.argc!=5 ){ |
| @@ -446,14 +448,16 @@ | |
| 446 | uid = db_int(0, "SELECT uid FROM user WHERE login=%Q", g.argv[3]); |
| 447 | if( uid==0 ){ |
| 448 | fossil_fatal("no such user: %s", g.argv[3]); |
| 449 | } |
| 450 | if( g.argc==5 ){ |
| 451 | db_multi_exec( |
| 452 | "UPDATE user SET cap=%Q, mtime=now() WHERE uid=%d", |
| 453 | g.argv[4], uid |
| 454 | ); |
| 455 | } |
| 456 | fossil_print("%s\n", db_text(0, "SELECT cap FROM user WHERE uid=%d", uid)); |
| 457 | }else{ |
| 458 | fossil_fatal("user subcommand should be one of: " |
| 459 | "capabilities default list new password"); |
| @@ -573,10 +577,11 @@ | |
| 573 | void user_hash_passwords_cmd(void){ |
| 574 | if( g.argc!=3 ) usage("REPOSITORY"); |
| 575 | db_open_repository(g.argv[2]); |
| 576 | sqlite3_create_function(g.db, "shared_secret", 2, SQLITE_UTF8, 0, |
| 577 | sha1_shared_secret_sql_function, 0, 0); |
| 578 | db_multi_exec( |
| 579 | "UPDATE user SET pw=shared_secret(pw,login), mtime=now()" |
| 580 | " WHERE length(pw)>0 AND length(pw)!=40" |
| 581 | ); |
| 582 | } |
| 583 |
| --- src/user.c | |
| +++ src/user.c | |
| @@ -432,12 +432,14 @@ | |
| 432 | } |
| 433 | if( blob_size(&pw)==0 ){ |
| 434 | fossil_print("password unchanged\n"); |
| 435 | }else{ |
| 436 | char *zSecret = sha1_shared_secret(blob_str(&pw), g.argv[3], 0); |
| 437 | db_unprotect(PROTECT_USER); |
| 438 | db_multi_exec("UPDATE user SET pw=%Q, mtime=now() WHERE uid=%d", |
| 439 | zSecret, uid); |
| 440 | db_protect_pop(); |
| 441 | free(zSecret); |
| 442 | } |
| 443 | }else if( n>=2 && strncmp(g.argv[2],"capabilities",2)==0 ){ |
| 444 | int uid; |
| 445 | if( g.argc!=4 && g.argc!=5 ){ |
| @@ -446,14 +448,16 @@ | |
| 448 | uid = db_int(0, "SELECT uid FROM user WHERE login=%Q", g.argv[3]); |
| 449 | if( uid==0 ){ |
| 450 | fossil_fatal("no such user: %s", g.argv[3]); |
| 451 | } |
| 452 | if( g.argc==5 ){ |
| 453 | db_unprotect(PROTECT_USER); |
| 454 | db_multi_exec( |
| 455 | "UPDATE user SET cap=%Q, mtime=now() WHERE uid=%d", |
| 456 | g.argv[4], uid |
| 457 | ); |
| 458 | db_protect_pop(); |
| 459 | } |
| 460 | fossil_print("%s\n", db_text(0, "SELECT cap FROM user WHERE uid=%d", uid)); |
| 461 | }else{ |
| 462 | fossil_fatal("user subcommand should be one of: " |
| 463 | "capabilities default list new password"); |
| @@ -573,10 +577,11 @@ | |
| 577 | void user_hash_passwords_cmd(void){ |
| 578 | if( g.argc!=3 ) usage("REPOSITORY"); |
| 579 | db_open_repository(g.argv[2]); |
| 580 | sqlite3_create_function(g.db, "shared_secret", 2, SQLITE_UTF8, 0, |
| 581 | sha1_shared_secret_sql_function, 0, 0); |
| 582 | db_unprotect(PROTECT_ALL); |
| 583 | db_multi_exec( |
| 584 | "UPDATE user SET pw=shared_secret(pw,login), mtime=now()" |
| 585 | " WHERE length(pw)>0 AND length(pw)!=40" |
| 586 | ); |
| 587 | } |
| 588 |
+3
| --- src/vfile.c | ||
| +++ src/vfile.c | ||
| @@ -313,10 +313,13 @@ | ||
| 313 | 313 | id = db_column_int(&q, 0); |
| 314 | 314 | zName = db_column_text(&q, 1); |
| 315 | 315 | rid = db_column_int(&q, 2); |
| 316 | 316 | isExe = db_column_int(&q, 3); |
| 317 | 317 | isLink = db_column_int(&q, 4); |
| 318 | + if( file_unsafe_in_tree_path(zName) ){ | |
| 319 | + continue; | |
| 320 | + } | |
| 318 | 321 | content_get(rid, &content); |
| 319 | 322 | if( file_is_the_same(&content, zName) ){ |
| 320 | 323 | blob_reset(&content); |
| 321 | 324 | if( file_setexe(zName, isExe) ){ |
| 322 | 325 | db_multi_exec("UPDATE vfile SET mtime=%lld WHERE id=%d", |
| 323 | 326 |
| --- src/vfile.c | |
| +++ src/vfile.c | |
| @@ -313,10 +313,13 @@ | |
| 313 | id = db_column_int(&q, 0); |
| 314 | zName = db_column_text(&q, 1); |
| 315 | rid = db_column_int(&q, 2); |
| 316 | isExe = db_column_int(&q, 3); |
| 317 | isLink = db_column_int(&q, 4); |
| 318 | content_get(rid, &content); |
| 319 | if( file_is_the_same(&content, zName) ){ |
| 320 | blob_reset(&content); |
| 321 | if( file_setexe(zName, isExe) ){ |
| 322 | db_multi_exec("UPDATE vfile SET mtime=%lld WHERE id=%d", |
| 323 |
| --- src/vfile.c | |
| +++ src/vfile.c | |
| @@ -313,10 +313,13 @@ | |
| 313 | id = db_column_int(&q, 0); |
| 314 | zName = db_column_text(&q, 1); |
| 315 | rid = db_column_int(&q, 2); |
| 316 | isExe = db_column_int(&q, 3); |
| 317 | isLink = db_column_int(&q, 4); |
| 318 | if( file_unsafe_in_tree_path(zName) ){ |
| 319 | continue; |
| 320 | } |
| 321 | content_get(rid, &content); |
| 322 | if( file_is_the_same(&content, zName) ){ |
| 323 | blob_reset(&content); |
| 324 | if( file_setexe(zName, isExe) ){ |
| 325 | db_multi_exec("UPDATE vfile SET mtime=%lld WHERE id=%d", |
| 326 |
+6
| --- src/xfer.c | ||
| +++ src/xfer.c | ||
| @@ -1657,11 +1657,13 @@ | ||
| 1657 | 1657 | int x = db_column_int(&q,3); |
| 1658 | 1658 | const char *zName = db_column_text(&q,4); |
| 1659 | 1659 | if( db_column_int64(&q,1)<=iNow-maxAge || !is_a_leaf(x) ){ |
| 1660 | 1660 | /* check-in locks expire after maxAge seconds, or when the |
| 1661 | 1661 | ** check-in is no longer a leaf */ |
| 1662 | + db_unprotect(PROTECT_CONFIG); | |
| 1662 | 1663 | db_multi_exec("DELETE FROM config WHERE name=%Q", zName); |
| 1664 | + db_protect_pop(); | |
| 1663 | 1665 | continue; |
| 1664 | 1666 | } |
| 1665 | 1667 | if( fossil_strcmp(zName+8, blob_str(&xfer.aToken[2]))==0 ){ |
| 1666 | 1668 | const char *zClientId = db_column_text(&q, 2); |
| 1667 | 1669 | const char *zLogin = db_column_text(&q,0); |
| @@ -1672,16 +1674,18 @@ | ||
| 1672 | 1674 | seenFault = 1; |
| 1673 | 1675 | } |
| 1674 | 1676 | } |
| 1675 | 1677 | db_finalize(&q); |
| 1676 | 1678 | if( !seenFault ){ |
| 1679 | + db_unprotect(PROTECT_CONFIG); | |
| 1677 | 1680 | db_multi_exec( |
| 1678 | 1681 | "REPLACE INTO config(name,value,mtime)" |
| 1679 | 1682 | "VALUES('ci-lock-%q',json_object('login',%Q,'clientid',%Q),now())", |
| 1680 | 1683 | blob_str(&xfer.aToken[2]), g.zLogin, |
| 1681 | 1684 | blob_str(&xfer.aToken[3]) |
| 1682 | 1685 | ); |
| 1686 | + db_protect_pop(); | |
| 1683 | 1687 | } |
| 1684 | 1688 | if( db_get_boolean("forbid-delta-manifests",0) ){ |
| 1685 | 1689 | @ pragma avoid-delta-manifests |
| 1686 | 1690 | } |
| 1687 | 1691 | } |
| @@ -1694,16 +1698,18 @@ | ||
| 1694 | 1698 | */ |
| 1695 | 1699 | if( blob_eq(&xfer.aToken[1], "ci-unlock") |
| 1696 | 1700 | && xfer.nToken==3 |
| 1697 | 1701 | && blob_is_hname(&xfer.aToken[2]) |
| 1698 | 1702 | ){ |
| 1703 | + db_unprotect(PROTECT_CONFIG); | |
| 1699 | 1704 | db_multi_exec( |
| 1700 | 1705 | "DELETE FROM config" |
| 1701 | 1706 | " WHERE name GLOB 'ci-lock-*'" |
| 1702 | 1707 | " AND json_extract(value,'$.clientid')=%Q", |
| 1703 | 1708 | blob_str(&xfer.aToken[2]) |
| 1704 | 1709 | ); |
| 1710 | + db_protect_pop(); | |
| 1705 | 1711 | } |
| 1706 | 1712 | |
| 1707 | 1713 | }else |
| 1708 | 1714 | |
| 1709 | 1715 | /* Unknown message |
| 1710 | 1716 | |
| 1711 | 1717 | ADDED test/reserved-names.test |
| --- src/xfer.c | |
| +++ src/xfer.c | |
| @@ -1657,11 +1657,13 @@ | |
| 1657 | int x = db_column_int(&q,3); |
| 1658 | const char *zName = db_column_text(&q,4); |
| 1659 | if( db_column_int64(&q,1)<=iNow-maxAge || !is_a_leaf(x) ){ |
| 1660 | /* check-in locks expire after maxAge seconds, or when the |
| 1661 | ** check-in is no longer a leaf */ |
| 1662 | db_multi_exec("DELETE FROM config WHERE name=%Q", zName); |
| 1663 | continue; |
| 1664 | } |
| 1665 | if( fossil_strcmp(zName+8, blob_str(&xfer.aToken[2]))==0 ){ |
| 1666 | const char *zClientId = db_column_text(&q, 2); |
| 1667 | const char *zLogin = db_column_text(&q,0); |
| @@ -1672,16 +1674,18 @@ | |
| 1672 | seenFault = 1; |
| 1673 | } |
| 1674 | } |
| 1675 | db_finalize(&q); |
| 1676 | if( !seenFault ){ |
| 1677 | db_multi_exec( |
| 1678 | "REPLACE INTO config(name,value,mtime)" |
| 1679 | "VALUES('ci-lock-%q',json_object('login',%Q,'clientid',%Q),now())", |
| 1680 | blob_str(&xfer.aToken[2]), g.zLogin, |
| 1681 | blob_str(&xfer.aToken[3]) |
| 1682 | ); |
| 1683 | } |
| 1684 | if( db_get_boolean("forbid-delta-manifests",0) ){ |
| 1685 | @ pragma avoid-delta-manifests |
| 1686 | } |
| 1687 | } |
| @@ -1694,16 +1698,18 @@ | |
| 1694 | */ |
| 1695 | if( blob_eq(&xfer.aToken[1], "ci-unlock") |
| 1696 | && xfer.nToken==3 |
| 1697 | && blob_is_hname(&xfer.aToken[2]) |
| 1698 | ){ |
| 1699 | db_multi_exec( |
| 1700 | "DELETE FROM config" |
| 1701 | " WHERE name GLOB 'ci-lock-*'" |
| 1702 | " AND json_extract(value,'$.clientid')=%Q", |
| 1703 | blob_str(&xfer.aToken[2]) |
| 1704 | ); |
| 1705 | } |
| 1706 | |
| 1707 | }else |
| 1708 | |
| 1709 | /* Unknown message |
| 1710 | |
| 1711 | DDED test/reserved-names.test |
| --- src/xfer.c | |
| +++ src/xfer.c | |
| @@ -1657,11 +1657,13 @@ | |
| 1657 | int x = db_column_int(&q,3); |
| 1658 | const char *zName = db_column_text(&q,4); |
| 1659 | if( db_column_int64(&q,1)<=iNow-maxAge || !is_a_leaf(x) ){ |
| 1660 | /* check-in locks expire after maxAge seconds, or when the |
| 1661 | ** check-in is no longer a leaf */ |
| 1662 | db_unprotect(PROTECT_CONFIG); |
| 1663 | db_multi_exec("DELETE FROM config WHERE name=%Q", zName); |
| 1664 | db_protect_pop(); |
| 1665 | continue; |
| 1666 | } |
| 1667 | if( fossil_strcmp(zName+8, blob_str(&xfer.aToken[2]))==0 ){ |
| 1668 | const char *zClientId = db_column_text(&q, 2); |
| 1669 | const char *zLogin = db_column_text(&q,0); |
| @@ -1672,16 +1674,18 @@ | |
| 1674 | seenFault = 1; |
| 1675 | } |
| 1676 | } |
| 1677 | db_finalize(&q); |
| 1678 | if( !seenFault ){ |
| 1679 | db_unprotect(PROTECT_CONFIG); |
| 1680 | db_multi_exec( |
| 1681 | "REPLACE INTO config(name,value,mtime)" |
| 1682 | "VALUES('ci-lock-%q',json_object('login',%Q,'clientid',%Q),now())", |
| 1683 | blob_str(&xfer.aToken[2]), g.zLogin, |
| 1684 | blob_str(&xfer.aToken[3]) |
| 1685 | ); |
| 1686 | db_protect_pop(); |
| 1687 | } |
| 1688 | if( db_get_boolean("forbid-delta-manifests",0) ){ |
| 1689 | @ pragma avoid-delta-manifests |
| 1690 | } |
| 1691 | } |
| @@ -1694,16 +1698,18 @@ | |
| 1698 | */ |
| 1699 | if( blob_eq(&xfer.aToken[1], "ci-unlock") |
| 1700 | && xfer.nToken==3 |
| 1701 | && blob_is_hname(&xfer.aToken[2]) |
| 1702 | ){ |
| 1703 | db_unprotect(PROTECT_CONFIG); |
| 1704 | db_multi_exec( |
| 1705 | "DELETE FROM config" |
| 1706 | " WHERE name GLOB 'ci-lock-*'" |
| 1707 | " AND json_extract(value,'$.clientid')=%Q", |
| 1708 | blob_str(&xfer.aToken[2]) |
| 1709 | ); |
| 1710 | db_protect_pop(); |
| 1711 | } |
| 1712 | |
| 1713 | }else |
| 1714 | |
| 1715 | /* Unknown message |
| 1716 | |
| 1717 | DDED test/reserved-names.test |
+121
| --- a/test/reserved-names.test | ||
| +++ b/test/reserved-names.test | ||
| @@ -0,0 +1,121 @@ | ||
| 1 | +# | |
| 2 | +# Copyright (c) 2020 D. Richard Hipp | |
| 3 | +# | |
| 4 | +# This program is free software; you can redistribute it and/or | |
| 5 | +# modify it under the terms of the Simplified BSD License (also | |
| 6 | +# known as the "2-Clause License" or "FreeBSD License".) | |
| 7 | +# | |
| 8 | +# This program is distributed in the hope that it will be useful, | |
| 9 | +# but without any warranty; without even the implied warranty of | |
| 10 | +# merchantability or fitness for a particular purpose. | |
| 11 | +# | |
| 12 | +# Author contact information: | |
| 13 | +# [email protected] | |
| 14 | +# http://www.hwaci.com/drh/ | |
| 15 | +# | |
| 16 | +############################################################################ | |
| 17 | +# | |
| 18 | +# Tests for reserved names. | |
| 19 | +# | |
| 20 | + | |
| 21 | +test_setup | |
| 22 | + | |
| 23 | +############################################################################### | |
| 24 | + | |
| 25 | +set reserved_names_tests [list \ | |
| 26 | + {0 {}} \ | |
| 27 | + {0 a.fslckout} \ | |
| 28 | + {1 .fslckout} \ | |
| 29 | + {1 .FSlckOUT} \ | |
| 30 | + {2 a/.fslckout} \ | |
| 31 | + {0 .fslckout/b} \ | |
| 32 | + {0 fslckout} \ | |
| 33 | + {0 .fslckoutx} \ | |
| 34 | + {1 _FOSSIL_} \ | |
| 35 | + {0 _FOSSIL} \ | |
| 36 | + {0 FOSSIL_} \ | |
| 37 | + {0 FOSSIL_} \ | |
| 38 | + {0 a_FOSSIL_} \ | |
| 39 | + {0 _FOSSIL__} \ | |
| 40 | + {0 __FOSSIL__} \ | |
| 41 | + {0 __FOssIL__} \ | |
| 42 | + {0 _FOSSIL_/a} \ | |
| 43 | + {2 a/_FOSSIL_} \ | |
| 44 | + {2 _FOSSIL_/c/.fslckout} \ | |
| 45 | + {2 _FOSSIL_/c/.fslckout/_FOSSIL_} \ | |
| 46 | + {0 _FOSSIL_/c/.fslckout/._FOSSIL_t} \ | |
| 47 | + {0 _FOSSIL_/c/.fslckout/t._FOSSIL_} \ | |
| 48 | + {0 a} \ | |
| 49 | + {0 a/b} \ | |
| 50 | + {0 a/b/c} \ | |
| 51 | + {0 a/b/c/} \ | |
| 52 | + {0 a/_FOSSIL/} \ | |
| 53 | + {0 a/fslckout/} \ | |
| 54 | + {0 a/_fslckout/} \ | |
| 55 | + {0 _FOSSIL-wal} \ | |
| 56 | + {0 _FOSSIL-shm} \ | |
| 57 | + {0 _FOSSIL-journal} \ | |
| 58 | + {0 _FOSSIL_-wal/a} \ | |
| 59 | + {0 _FOSSIL_-shm/a} \ | |
| 60 | + {0 _FOSSIL_-journal/a} \ | |
| 61 | + {1 _FOSSIL_-wal} \ | |
| 62 | + {1 _FOSSIL_-shm} \ | |
| 63 | + {1 _FOSSIL_-journal} \ | |
| 64 | + {2 a/_FOSSIL_-wal} \ | |
| 65 | + {2 a/_FOSSIL_-shm} \ | |
| 66 | + {2 a/_FOSSIL_-journal} \ | |
| 67 | + {0 .fslckout-wal/a} \ | |
| 68 | + {0 .fslckout-shm/a} \ | |
| 69 | + {0 .fslckout-journal/a} \ | |
| 70 | + {1 .fslckout-wal} \ | |
| 71 | + {1 .fslckout-shm} \ | |
| 72 | + {1 .fslckout-journal} \ | |
| 73 | + {2 a/.fslckout-wal} \ | |
| 74 | + {2 a/.fslckout-shm} \ | |
| 75 | + {2 a/.fslckout-journal} \ | |
| 76 | +] | |
| 77 | + | |
| 78 | +############################################################################### | |
| 79 | + | |
| 80 | +set testNo 0 | |
| 81 | + | |
| 82 | +foreach reserved_names_test $reserved_names_tests { | |
| 83 | + incr testNo | |
| 84 | + | |
| 85 | + set reserved_result [lindex $reserved_names_test 0] | |
| 86 | + set reserved_name [lindex $reserved_names_test 1] | |
| 87 | + | |
| 88 | + fossil test-is-reserved-name $reserved_name | |
| 89 | + | |
| 90 | + test reserved-result-$testNo { | |
| 91 | + [lindex [normalize_result] 0] eq $reserved_result | |
| 92 | + } | |
| 93 | + | |
| 94 | + test reserved-name-$testNo { | |
| 95 | + [lindex [normalize_result] 1] eq $reserved_name | |
| 96 | + } | |
| 97 | + | |
| 98 | + fossil test-is-reserved-name [string toupper $reserved_name] | |
| 99 | + | |
| 100 | + test reserved-result-upper-$testNo { | |
| 101 | + [lindex [normalize_result] 0] eq $reserved_result | |
| 102 | + } | |
| 103 | + | |
| 104 | + test reserved-name-upper-$testNo { | |
| 105 | + [lindex [normalize_result] 1] eq [string toupper $reserved_name] | |
| 106 | + } | |
| 107 | + | |
| 108 | + fossil test-is-reserved-name [string tolower $reserved_name] | |
| 109 | + | |
| 110 | + test reserved-result-lower-$testNo { | |
| 111 | + [lindex [normalize_result] 0] eq $reserved_result | |
| 112 | + } | |
| 113 | + | |
| 114 | + test reserved-name-lower-$testNo { | |
| 115 | + [lindex [normalize_result] 1] eq [string tolower $reserved_name] | |
| 116 | + } | |
| 117 | +} | |
| 118 | + | |
| 119 | +############################################################################### | |
| 120 | + | |
| 121 | +test_cleanup |
| --- a/test/reserved-names.test | |
| +++ b/test/reserved-names.test | |
| @@ -0,0 +1,121 @@ | |
| --- a/test/reserved-names.test | |
| +++ b/test/reserved-names.test | |
| @@ -0,0 +1,121 @@ | |
| 1 | # |
| 2 | # Copyright (c) 2020 D. Richard Hipp |
| 3 | # |
| 4 | # This program is free software; you can redistribute it and/or |
| 5 | # modify it under the terms of the Simplified BSD License (also |
| 6 | # known as the "2-Clause License" or "FreeBSD License".) |
| 7 | # |
| 8 | # This program is distributed in the hope that it will be useful, |
| 9 | # but without any warranty; without even the implied warranty of |
| 10 | # merchantability or fitness for a particular purpose. |
| 11 | # |
| 12 | # Author contact information: |
| 13 | # [email protected] |
| 14 | # http://www.hwaci.com/drh/ |
| 15 | # |
| 16 | ############################################################################ |
| 17 | # |
| 18 | # Tests for reserved names. |
| 19 | # |
| 20 | |
| 21 | test_setup |
| 22 | |
| 23 | ############################################################################### |
| 24 | |
| 25 | set reserved_names_tests [list \ |
| 26 | {0 {}} \ |
| 27 | {0 a.fslckout} \ |
| 28 | {1 .fslckout} \ |
| 29 | {1 .FSlckOUT} \ |
| 30 | {2 a/.fslckout} \ |
| 31 | {0 .fslckout/b} \ |
| 32 | {0 fslckout} \ |
| 33 | {0 .fslckoutx} \ |
| 34 | {1 _FOSSIL_} \ |
| 35 | {0 _FOSSIL} \ |
| 36 | {0 FOSSIL_} \ |
| 37 | {0 FOSSIL_} \ |
| 38 | {0 a_FOSSIL_} \ |
| 39 | {0 _FOSSIL__} \ |
| 40 | {0 __FOSSIL__} \ |
| 41 | {0 __FOssIL__} \ |
| 42 | {0 _FOSSIL_/a} \ |
| 43 | {2 a/_FOSSIL_} \ |
| 44 | {2 _FOSSIL_/c/.fslckout} \ |
| 45 | {2 _FOSSIL_/c/.fslckout/_FOSSIL_} \ |
| 46 | {0 _FOSSIL_/c/.fslckout/._FOSSIL_t} \ |
| 47 | {0 _FOSSIL_/c/.fslckout/t._FOSSIL_} \ |
| 48 | {0 a} \ |
| 49 | {0 a/b} \ |
| 50 | {0 a/b/c} \ |
| 51 | {0 a/b/c/} \ |
| 52 | {0 a/_FOSSIL/} \ |
| 53 | {0 a/fslckout/} \ |
| 54 | {0 a/_fslckout/} \ |
| 55 | {0 _FOSSIL-wal} \ |
| 56 | {0 _FOSSIL-shm} \ |
| 57 | {0 _FOSSIL-journal} \ |
| 58 | {0 _FOSSIL_-wal/a} \ |
| 59 | {0 _FOSSIL_-shm/a} \ |
| 60 | {0 _FOSSIL_-journal/a} \ |
| 61 | {1 _FOSSIL_-wal} \ |
| 62 | {1 _FOSSIL_-shm} \ |
| 63 | {1 _FOSSIL_-journal} \ |
| 64 | {2 a/_FOSSIL_-wal} \ |
| 65 | {2 a/_FOSSIL_-shm} \ |
| 66 | {2 a/_FOSSIL_-journal} \ |
| 67 | {0 .fslckout-wal/a} \ |
| 68 | {0 .fslckout-shm/a} \ |
| 69 | {0 .fslckout-journal/a} \ |
| 70 | {1 .fslckout-wal} \ |
| 71 | {1 .fslckout-shm} \ |
| 72 | {1 .fslckout-journal} \ |
| 73 | {2 a/.fslckout-wal} \ |
| 74 | {2 a/.fslckout-shm} \ |
| 75 | {2 a/.fslckout-journal} \ |
| 76 | ] |
| 77 | |
| 78 | ############################################################################### |
| 79 | |
| 80 | set testNo 0 |
| 81 | |
| 82 | foreach reserved_names_test $reserved_names_tests { |
| 83 | incr testNo |
| 84 | |
| 85 | set reserved_result [lindex $reserved_names_test 0] |
| 86 | set reserved_name [lindex $reserved_names_test 1] |
| 87 | |
| 88 | fossil test-is-reserved-name $reserved_name |
| 89 | |
| 90 | test reserved-result-$testNo { |
| 91 | [lindex [normalize_result] 0] eq $reserved_result |
| 92 | } |
| 93 | |
| 94 | test reserved-name-$testNo { |
| 95 | [lindex [normalize_result] 1] eq $reserved_name |
| 96 | } |
| 97 | |
| 98 | fossil test-is-reserved-name [string toupper $reserved_name] |
| 99 | |
| 100 | test reserved-result-upper-$testNo { |
| 101 | [lindex [normalize_result] 0] eq $reserved_result |
| 102 | } |
| 103 | |
| 104 | test reserved-name-upper-$testNo { |
| 105 | [lindex [normalize_result] 1] eq [string toupper $reserved_name] |
| 106 | } |
| 107 | |
| 108 | fossil test-is-reserved-name [string tolower $reserved_name] |
| 109 | |
| 110 | test reserved-result-lower-$testNo { |
| 111 | [lindex [normalize_result] 0] eq $reserved_result |
| 112 | } |
| 113 | |
| 114 | test reserved-name-lower-$testNo { |
| 115 | [lindex [normalize_result] 1] eq [string tolower $reserved_name] |
| 116 | } |
| 117 | } |
| 118 | |
| 119 | ############################################################################### |
| 120 | |
| 121 | test_cleanup |