Fossil SCM
Add "taint mode" to TH1. Attempts to output values that are derived from user input as unescaped HTML, or to use such values unescaped in SQL, raises errors. The resolution of these errors depends on the value of the new "vuln-report" setting.
Commit
2116238e80cc3dcb1c012c60df9915c525a3ed3e15f908570d1bb8ef939f1b36
Parent
f1db9ead1d44290…
19 files changed
+1
-1
+1
-1
+1
-1
+1
-1
+2
-2
+1
-1
+1
-1
+29
-9
+1
-1
+25
-12
+3
-2
+80
-53
+53
-14
+70
-48
+196
-37
+18
-18
+1
-1
+10
-9
+13
-13
~
skins/default/header.txt
~
skins/eagle/header.txt
~
skins/original/header.txt
~
skins/xekri/header.txt
~
src/browse.c
~
src/doc.c
~
src/info.c
~
src/main.c
~
src/printf.c
~
src/security_audit.c
~
src/style.c
~
src/th.c
~
src/th.h
~
src/th_lang.c
~
src/th_main.c
~
src/th_tcl.c
~
src/timeline.c
~
src/tkt.c
~
src/tktsetup.c
+1
-1
| --- skins/default/header.txt | ||
| +++ skins/default/header.txt | ||
| @@ -28,11 +28,11 @@ | ||
| 28 | 28 | return $logourl |
| 29 | 29 | } |
| 30 | 30 | set logourl [getLogoUrl $baseurl] |
| 31 | 31 | </th1> |
| 32 | 32 | <a href="$logourl"> |
| 33 | - <img src="$logo_image_url" border="0" alt="$project_name"> | |
| 33 | + <img src="$logo_image_url" border="0" alt="$<project_name>"> | |
| 34 | 34 | </a> |
| 35 | 35 | </div> |
| 36 | 36 | <div class="title"> |
| 37 | 37 | <h1>$<project_name></h1> |
| 38 | 38 | <span class="page-title">$<title></span> |
| 39 | 39 |
| --- skins/default/header.txt | |
| +++ skins/default/header.txt | |
| @@ -28,11 +28,11 @@ | |
| 28 | return $logourl |
| 29 | } |
| 30 | set logourl [getLogoUrl $baseurl] |
| 31 | </th1> |
| 32 | <a href="$logourl"> |
| 33 | <img src="$logo_image_url" border="0" alt="$project_name"> |
| 34 | </a> |
| 35 | </div> |
| 36 | <div class="title"> |
| 37 | <h1>$<project_name></h1> |
| 38 | <span class="page-title">$<title></span> |
| 39 |
| --- skins/default/header.txt | |
| +++ skins/default/header.txt | |
| @@ -28,11 +28,11 @@ | |
| 28 | return $logourl |
| 29 | } |
| 30 | set logourl [getLogoUrl $baseurl] |
| 31 | </th1> |
| 32 | <a href="$logourl"> |
| 33 | <img src="$logo_image_url" border="0" alt="$<project_name>"> |
| 34 | </a> |
| 35 | </div> |
| 36 | <div class="title"> |
| 37 | <h1>$<project_name></h1> |
| 38 | <span class="page-title">$<title></span> |
| 39 |
+1
-1
| --- skins/eagle/header.txt | ||
| +++ skins/eagle/header.txt | ||
| @@ -65,11 +65,11 @@ | ||
| 65 | 65 | # Link logo to the top of the current repo |
| 66 | 66 | set logourl $baseurl |
| 67 | 67 | } |
| 68 | 68 | </th1> |
| 69 | 69 | <a href="$logourl"> |
| 70 | - <img src="$logo_image_url" border="0" alt="$project_name"> | |
| 70 | + <img src="$logo_image_url" border="0" alt="$<project_name>"> | |
| 71 | 71 | </a> |
| 72 | 72 | </div> |
| 73 | 73 | <div class="title">$<title></div> |
| 74 | 74 | <div class="status"><nobr><th1> |
| 75 | 75 | if {[info exists login]} { |
| 76 | 76 |
| --- skins/eagle/header.txt | |
| +++ skins/eagle/header.txt | |
| @@ -65,11 +65,11 @@ | |
| 65 | # Link logo to the top of the current repo |
| 66 | set logourl $baseurl |
| 67 | } |
| 68 | </th1> |
| 69 | <a href="$logourl"> |
| 70 | <img src="$logo_image_url" border="0" alt="$project_name"> |
| 71 | </a> |
| 72 | </div> |
| 73 | <div class="title">$<title></div> |
| 74 | <div class="status"><nobr><th1> |
| 75 | if {[info exists login]} { |
| 76 |
| --- skins/eagle/header.txt | |
| +++ skins/eagle/header.txt | |
| @@ -65,11 +65,11 @@ | |
| 65 | # Link logo to the top of the current repo |
| 66 | set logourl $baseurl |
| 67 | } |
| 68 | </th1> |
| 69 | <a href="$logourl"> |
| 70 | <img src="$logo_image_url" border="0" alt="$<project_name>"> |
| 71 | </a> |
| 72 | </div> |
| 73 | <div class="title">$<title></div> |
| 74 | <div class="status"><nobr><th1> |
| 75 | if {[info exists login]} { |
| 76 |
+1
-1
| --- skins/original/header.txt | ||
| +++ skins/original/header.txt | ||
| @@ -59,11 +59,11 @@ | ||
| 59 | 59 | return $logourl |
| 60 | 60 | } |
| 61 | 61 | set logourl [getLogoUrl $baseurl] |
| 62 | 62 | </th1> |
| 63 | 63 | <a href="$logourl"> |
| 64 | - <img src="$logo_image_url" border="0" alt="$project_name"> | |
| 64 | + <img src="$logo_image_url" border="0" alt="$<project_name>"> | |
| 65 | 65 | </a> |
| 66 | 66 | </div> |
| 67 | 67 | <div class="title">$<title></div> |
| 68 | 68 | <div class="status"><nobr><th1> |
| 69 | 69 | if {[info exists login]} { |
| 70 | 70 |
| --- skins/original/header.txt | |
| +++ skins/original/header.txt | |
| @@ -59,11 +59,11 @@ | |
| 59 | return $logourl |
| 60 | } |
| 61 | set logourl [getLogoUrl $baseurl] |
| 62 | </th1> |
| 63 | <a href="$logourl"> |
| 64 | <img src="$logo_image_url" border="0" alt="$project_name"> |
| 65 | </a> |
| 66 | </div> |
| 67 | <div class="title">$<title></div> |
| 68 | <div class="status"><nobr><th1> |
| 69 | if {[info exists login]} { |
| 70 |
| --- skins/original/header.txt | |
| +++ skins/original/header.txt | |
| @@ -59,11 +59,11 @@ | |
| 59 | return $logourl |
| 60 | } |
| 61 | set logourl [getLogoUrl $baseurl] |
| 62 | </th1> |
| 63 | <a href="$logourl"> |
| 64 | <img src="$logo_image_url" border="0" alt="$<project_name>"> |
| 65 | </a> |
| 66 | </div> |
| 67 | <div class="title">$<title></div> |
| 68 | <div class="status"><nobr><th1> |
| 69 | if {[info exists login]} { |
| 70 |
+1
-1
| --- skins/xekri/header.txt | ||
| +++ skins/xekri/header.txt | ||
| @@ -65,11 +65,11 @@ | ||
| 65 | 65 | # Link logo to the top of the current repo |
| 66 | 66 | set logourl $baseurl |
| 67 | 67 | } |
| 68 | 68 | </th1> |
| 69 | 69 | <a href="$logourl"> |
| 70 | - <img src="$logo_image_url" border="0" alt="$project_name"> | |
| 70 | + <img src="$logo_image_url" border="0" alt="$<project_name>"> | |
| 71 | 71 | </a> |
| 72 | 72 | </div> |
| 73 | 73 | <div class="title">$<title></div> |
| 74 | 74 | <div class="status"><nobr> |
| 75 | 75 | <th1> |
| 76 | 76 |
| --- skins/xekri/header.txt | |
| +++ skins/xekri/header.txt | |
| @@ -65,11 +65,11 @@ | |
| 65 | # Link logo to the top of the current repo |
| 66 | set logourl $baseurl |
| 67 | } |
| 68 | </th1> |
| 69 | <a href="$logourl"> |
| 70 | <img src="$logo_image_url" border="0" alt="$project_name"> |
| 71 | </a> |
| 72 | </div> |
| 73 | <div class="title">$<title></div> |
| 74 | <div class="status"><nobr> |
| 75 | <th1> |
| 76 |
| --- skins/xekri/header.txt | |
| +++ skins/xekri/header.txt | |
| @@ -65,11 +65,11 @@ | |
| 65 | # Link logo to the top of the current repo |
| 66 | set logourl $baseurl |
| 67 | } |
| 68 | </th1> |
| 69 | <a href="$logourl"> |
| 70 | <img src="$logo_image_url" border="0" alt="$<project_name>"> |
| 71 | </a> |
| 72 | </div> |
| 73 | <div class="title">$<title></div> |
| 74 | <div class="status"><nobr> |
| 75 | <th1> |
| 76 |
+2
-2
| --- src/browse.c | ||
| +++ src/browse.c | ||
| @@ -205,11 +205,11 @@ | ||
| 205 | 205 | linkTip = rid != symbolic_name_to_rid("tip", "ci"); |
| 206 | 206 | zUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", rid); |
| 207 | 207 | isSymbolicCI = (sqlite3_strnicmp(zUuid, zCI, strlen(zCI))!=0); |
| 208 | 208 | isBranchCI = branch_includes_uuid(zCI, zUuid); |
| 209 | 209 | if( bDocDir ) zCI = mprintf("%S", zUuid); |
| 210 | - Th_Store("current_checkin", zCI); | |
| 210 | + Th_StoreUnsafe("current_checkin", zCI); | |
| 211 | 211 | }else{ |
| 212 | 212 | zCI = 0; |
| 213 | 213 | } |
| 214 | 214 | } |
| 215 | 215 | |
| @@ -771,11 +771,11 @@ | ||
| 771 | 771 | rNow = db_double(0.0, "SELECT mtime FROM event WHERE objid=%d", rid); |
| 772 | 772 | zNow = db_text("", "SELECT datetime(mtime,toLocal())" |
| 773 | 773 | " FROM event WHERE objid=%d", rid); |
| 774 | 774 | isSymbolicCI = (sqlite3_strnicmp(zUuid, zCI, strlen(zCI)) != 0); |
| 775 | 775 | isBranchCI = branch_includes_uuid(zCI, zUuid); |
| 776 | - Th_Store("current_checkin", zCI); | |
| 776 | + Th_StoreUnsafe("current_checkin", zCI); | |
| 777 | 777 | }else{ |
| 778 | 778 | zCI = 0; |
| 779 | 779 | } |
| 780 | 780 | } |
| 781 | 781 | if( zCI==0 ){ |
| 782 | 782 |
| --- src/browse.c | |
| +++ src/browse.c | |
| @@ -205,11 +205,11 @@ | |
| 205 | linkTip = rid != symbolic_name_to_rid("tip", "ci"); |
| 206 | zUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", rid); |
| 207 | isSymbolicCI = (sqlite3_strnicmp(zUuid, zCI, strlen(zCI))!=0); |
| 208 | isBranchCI = branch_includes_uuid(zCI, zUuid); |
| 209 | if( bDocDir ) zCI = mprintf("%S", zUuid); |
| 210 | Th_Store("current_checkin", zCI); |
| 211 | }else{ |
| 212 | zCI = 0; |
| 213 | } |
| 214 | } |
| 215 | |
| @@ -771,11 +771,11 @@ | |
| 771 | rNow = db_double(0.0, "SELECT mtime FROM event WHERE objid=%d", rid); |
| 772 | zNow = db_text("", "SELECT datetime(mtime,toLocal())" |
| 773 | " FROM event WHERE objid=%d", rid); |
| 774 | isSymbolicCI = (sqlite3_strnicmp(zUuid, zCI, strlen(zCI)) != 0); |
| 775 | isBranchCI = branch_includes_uuid(zCI, zUuid); |
| 776 | Th_Store("current_checkin", zCI); |
| 777 | }else{ |
| 778 | zCI = 0; |
| 779 | } |
| 780 | } |
| 781 | if( zCI==0 ){ |
| 782 |
| --- src/browse.c | |
| +++ src/browse.c | |
| @@ -205,11 +205,11 @@ | |
| 205 | linkTip = rid != symbolic_name_to_rid("tip", "ci"); |
| 206 | zUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", rid); |
| 207 | isSymbolicCI = (sqlite3_strnicmp(zUuid, zCI, strlen(zCI))!=0); |
| 208 | isBranchCI = branch_includes_uuid(zCI, zUuid); |
| 209 | if( bDocDir ) zCI = mprintf("%S", zUuid); |
| 210 | Th_StoreUnsafe("current_checkin", zCI); |
| 211 | }else{ |
| 212 | zCI = 0; |
| 213 | } |
| 214 | } |
| 215 | |
| @@ -771,11 +771,11 @@ | |
| 771 | rNow = db_double(0.0, "SELECT mtime FROM event WHERE objid=%d", rid); |
| 772 | zNow = db_text("", "SELECT datetime(mtime,toLocal())" |
| 773 | " FROM event WHERE objid=%d", rid); |
| 774 | isSymbolicCI = (sqlite3_strnicmp(zUuid, zCI, strlen(zCI)) != 0); |
| 775 | isBranchCI = branch_includes_uuid(zCI, zUuid); |
| 776 | Th_StoreUnsafe("current_checkin", zCI); |
| 777 | }else{ |
| 778 | zCI = 0; |
| 779 | } |
| 780 | } |
| 781 | if( zCI==0 ){ |
| 782 |
+1
-1
| --- src/doc.c | ||
| +++ src/doc.c | ||
| @@ -1052,11 +1052,11 @@ | ||
| 1052 | 1052 | */ |
| 1053 | 1053 | zMime = nMiss==0 ? P("mimetype") : 0; |
| 1054 | 1054 | if( zMime==0 ){ |
| 1055 | 1055 | zMime = mimetype_from_name(zName); |
| 1056 | 1056 | } |
| 1057 | - Th_Store("doc_name", zName); | |
| 1057 | + Th_StoreUnsafe("doc_name", zName); | |
| 1058 | 1058 | if( vid ){ |
| 1059 | 1059 | Th_Store("doc_version", db_text(0, "SELECT '[' || substr(uuid,1,10) || ']'" |
| 1060 | 1060 | " FROM blob WHERE rid=%d", vid)); |
| 1061 | 1061 | Th_Store("doc_date", db_text(0, "SELECT datetime(mtime) FROM event" |
| 1062 | 1062 | " WHERE objid=%d AND type='ci'", vid)); |
| 1063 | 1063 |
| --- src/doc.c | |
| +++ src/doc.c | |
| @@ -1052,11 +1052,11 @@ | |
| 1052 | */ |
| 1053 | zMime = nMiss==0 ? P("mimetype") : 0; |
| 1054 | if( zMime==0 ){ |
| 1055 | zMime = mimetype_from_name(zName); |
| 1056 | } |
| 1057 | Th_Store("doc_name", zName); |
| 1058 | if( vid ){ |
| 1059 | Th_Store("doc_version", db_text(0, "SELECT '[' || substr(uuid,1,10) || ']'" |
| 1060 | " FROM blob WHERE rid=%d", vid)); |
| 1061 | Th_Store("doc_date", db_text(0, "SELECT datetime(mtime) FROM event" |
| 1062 | " WHERE objid=%d AND type='ci'", vid)); |
| 1063 |
| --- src/doc.c | |
| +++ src/doc.c | |
| @@ -1052,11 +1052,11 @@ | |
| 1052 | */ |
| 1053 | zMime = nMiss==0 ? P("mimetype") : 0; |
| 1054 | if( zMime==0 ){ |
| 1055 | zMime = mimetype_from_name(zName); |
| 1056 | } |
| 1057 | Th_StoreUnsafe("doc_name", zName); |
| 1058 | if( vid ){ |
| 1059 | Th_Store("doc_version", db_text(0, "SELECT '[' || substr(uuid,1,10) || ']'" |
| 1060 | " FROM blob WHERE rid=%d", vid)); |
| 1061 | Th_Store("doc_date", db_text(0, "SELECT datetime(mtime) FROM event" |
| 1062 | " WHERE objid=%d AND type='ci'", vid)); |
| 1063 |
+1
-1
| --- src/info.c | ||
| +++ src/info.c | ||
| @@ -951,11 +951,11 @@ | ||
| 951 | 951 | const char *zOrigDate; |
| 952 | 952 | int okWiki = 0; |
| 953 | 953 | Blob wiki_read_links = BLOB_INITIALIZER; |
| 954 | 954 | Blob wiki_add_links = BLOB_INITIALIZER; |
| 955 | 955 | |
| 956 | - Th_Store("current_checkin", zName); | |
| 956 | + Th_StoreUnsafe("current_checkin", zName); | |
| 957 | 957 | style_header("Check-in [%S]", zUuid); |
| 958 | 958 | login_anonymous_available(); |
| 959 | 959 | zEUser = db_text(0, |
| 960 | 960 | "SELECT value FROM tagxref" |
| 961 | 961 | " WHERE tagid=%d AND rid=%d AND tagtype>0", |
| 962 | 962 |
| --- src/info.c | |
| +++ src/info.c | |
| @@ -951,11 +951,11 @@ | |
| 951 | const char *zOrigDate; |
| 952 | int okWiki = 0; |
| 953 | Blob wiki_read_links = BLOB_INITIALIZER; |
| 954 | Blob wiki_add_links = BLOB_INITIALIZER; |
| 955 | |
| 956 | Th_Store("current_checkin", zName); |
| 957 | style_header("Check-in [%S]", zUuid); |
| 958 | login_anonymous_available(); |
| 959 | zEUser = db_text(0, |
| 960 | "SELECT value FROM tagxref" |
| 961 | " WHERE tagid=%d AND rid=%d AND tagtype>0", |
| 962 |
| --- src/info.c | |
| +++ src/info.c | |
| @@ -951,11 +951,11 @@ | |
| 951 | const char *zOrigDate; |
| 952 | int okWiki = 0; |
| 953 | Blob wiki_read_links = BLOB_INITIALIZER; |
| 954 | Blob wiki_add_links = BLOB_INITIALIZER; |
| 955 | |
| 956 | Th_StoreUnsafe("current_checkin", zName); |
| 957 | style_header("Check-in [%S]", zUuid); |
| 958 | login_anonymous_available(); |
| 959 | zEUser = db_text(0, |
| 960 | "SELECT value FROM tagxref" |
| 961 | " WHERE tagid=%d AND rid=%d AND tagtype>0", |
| 962 |
+29
-9
| --- src/main.c | ||
| +++ src/main.c | ||
| @@ -3718,10 +3718,13 @@ | ||
| 3718 | 3718 | ** case=3 Extra db_end_transaction() |
| 3719 | 3719 | ** case=4 Error during SQL processing |
| 3720 | 3720 | ** case=5 Call the segfault handler |
| 3721 | 3721 | ** case=6 Call webpage_assert() |
| 3722 | 3722 | ** case=7 Call webpage_error() |
| 3723 | +** case=8 Simulate a timeout | |
| 3724 | +** case=9 Simulate a TH1 XSS vulnerability | |
| 3725 | +** case=10 Simulate a TH1 SQL-injection vulnerability | |
| 3723 | 3726 | */ |
| 3724 | 3727 | void test_warning_page(void){ |
| 3725 | 3728 | int iCase = atoi(PD("case","0")); |
| 3726 | 3729 | int i; |
| 3727 | 3730 | login_check_credentials(); |
| @@ -3730,17 +3733,15 @@ | ||
| 3730 | 3733 | return; |
| 3731 | 3734 | } |
| 3732 | 3735 | style_set_current_feature("test"); |
| 3733 | 3736 | style_header("Warning Test Page"); |
| 3734 | 3737 | style_submenu_element("Error Log","%R/errorlog"); |
| 3735 | - if( iCase<1 || iCase>4 ){ | |
| 3736 | - @ <p>Generate a message to the <a href="%R/errorlog">error log</a> | |
| 3737 | - @ by clicking on one of the following cases: | |
| 3738 | - }else{ | |
| 3739 | - @ <p>This is the test page for case=%d(iCase). All possible cases: | |
| 3740 | - } | |
| 3741 | - for(i=1; i<=8; i++){ | |
| 3738 | + @ <p>This page will generate various kinds of errors to test Fossil's | |
| 3739 | + @ reaction. Depending on settings, a message might be written | |
| 3740 | + @ into the <a href="%R/errorlog">error log</a>. Click on | |
| 3741 | + @ one of the following hyperlinks to generate a simulated error: | |
| 3742 | + for(i=1; i<=10; i++){ | |
| 3742 | 3743 | @ <a href='./test-warning?case=%d(i)'>[%d(i)]</a> |
| 3743 | 3744 | } |
| 3744 | 3745 | @ </p> |
| 3745 | 3746 | @ <p><ol> |
| 3746 | 3747 | @ <li value='1'> Call fossil_warning() |
| @@ -3769,20 +3770,39 @@ | ||
| 3769 | 3770 | } |
| 3770 | 3771 | @ <li value='6'> call webpage_assert(0) |
| 3771 | 3772 | if( iCase==6 ){ |
| 3772 | 3773 | webpage_assert( 5==7 ); |
| 3773 | 3774 | } |
| 3774 | - @ <li value='7'> call webpage_error()" | |
| 3775 | + @ <li value='7'> call webpage_error() | |
| 3775 | 3776 | if( iCase==7 ){ |
| 3776 | 3777 | cgi_reset_content(); |
| 3777 | 3778 | webpage_error("Case 7 from /test-warning"); |
| 3778 | 3779 | } |
| 3779 | - @ <li value='8'> simulated timeout" | |
| 3780 | + @ <li value='8'> simulated timeout | |
| 3780 | 3781 | if( iCase==8 ){ |
| 3781 | 3782 | fossil_set_timeout(1); |
| 3782 | 3783 | cgi_reset_content(); |
| 3783 | 3784 | sqlite3_sleep(1100); |
| 3785 | + } | |
| 3786 | + @ <li value='9'> simulated TH1 XSS vulnerability | |
| 3787 | + @ <li value='10'> simulated TH1 SQL-injection vulnerability | |
| 3788 | + if( iCase==9 || iCase==10 ){ | |
| 3789 | + const char *zR; | |
| 3790 | + int n, rc; | |
| 3791 | + static const char *zTH1[] = { | |
| 3792 | + /* case 9 */ "html [taint {<b>XSS</b>}]", | |
| 3793 | + /* case 10 */ "query [taint {SELECT 'SQL-injection' AS msg}] {\n" | |
| 3794 | + " html \"<b>[htmlize $msg]</b>\"\n" | |
| 3795 | + "}" | |
| 3796 | + }; | |
| 3797 | + rc = Th_Eval(g.interp, 0, zTH1[iCase==10], -1); | |
| 3798 | + zR = Th_GetResult(g.interp, &n); | |
| 3799 | + if( rc==TH_OK ){ | |
| 3800 | + @ <pre class="th1result">%h(zR)</pre> | |
| 3801 | + }else{ | |
| 3802 | + @ <pre class="th1error">%h(zR)</pre> | |
| 3803 | + } | |
| 3784 | 3804 | } |
| 3785 | 3805 | @ </ol> |
| 3786 | 3806 | @ <p>End of test</p> |
| 3787 | 3807 | style_finish_page(); |
| 3788 | 3808 | } |
| 3789 | 3809 |
| --- src/main.c | |
| +++ src/main.c | |
| @@ -3718,10 +3718,13 @@ | |
| 3718 | ** case=3 Extra db_end_transaction() |
| 3719 | ** case=4 Error during SQL processing |
| 3720 | ** case=5 Call the segfault handler |
| 3721 | ** case=6 Call webpage_assert() |
| 3722 | ** case=7 Call webpage_error() |
| 3723 | */ |
| 3724 | void test_warning_page(void){ |
| 3725 | int iCase = atoi(PD("case","0")); |
| 3726 | int i; |
| 3727 | login_check_credentials(); |
| @@ -3730,17 +3733,15 @@ | |
| 3730 | return; |
| 3731 | } |
| 3732 | style_set_current_feature("test"); |
| 3733 | style_header("Warning Test Page"); |
| 3734 | style_submenu_element("Error Log","%R/errorlog"); |
| 3735 | if( iCase<1 || iCase>4 ){ |
| 3736 | @ <p>Generate a message to the <a href="%R/errorlog">error log</a> |
| 3737 | @ by clicking on one of the following cases: |
| 3738 | }else{ |
| 3739 | @ <p>This is the test page for case=%d(iCase). All possible cases: |
| 3740 | } |
| 3741 | for(i=1; i<=8; i++){ |
| 3742 | @ <a href='./test-warning?case=%d(i)'>[%d(i)]</a> |
| 3743 | } |
| 3744 | @ </p> |
| 3745 | @ <p><ol> |
| 3746 | @ <li value='1'> Call fossil_warning() |
| @@ -3769,20 +3770,39 @@ | |
| 3769 | } |
| 3770 | @ <li value='6'> call webpage_assert(0) |
| 3771 | if( iCase==6 ){ |
| 3772 | webpage_assert( 5==7 ); |
| 3773 | } |
| 3774 | @ <li value='7'> call webpage_error()" |
| 3775 | if( iCase==7 ){ |
| 3776 | cgi_reset_content(); |
| 3777 | webpage_error("Case 7 from /test-warning"); |
| 3778 | } |
| 3779 | @ <li value='8'> simulated timeout" |
| 3780 | if( iCase==8 ){ |
| 3781 | fossil_set_timeout(1); |
| 3782 | cgi_reset_content(); |
| 3783 | sqlite3_sleep(1100); |
| 3784 | } |
| 3785 | @ </ol> |
| 3786 | @ <p>End of test</p> |
| 3787 | style_finish_page(); |
| 3788 | } |
| 3789 |
| --- src/main.c | |
| +++ src/main.c | |
| @@ -3718,10 +3718,13 @@ | |
| 3718 | ** case=3 Extra db_end_transaction() |
| 3719 | ** case=4 Error during SQL processing |
| 3720 | ** case=5 Call the segfault handler |
| 3721 | ** case=6 Call webpage_assert() |
| 3722 | ** case=7 Call webpage_error() |
| 3723 | ** case=8 Simulate a timeout |
| 3724 | ** case=9 Simulate a TH1 XSS vulnerability |
| 3725 | ** case=10 Simulate a TH1 SQL-injection vulnerability |
| 3726 | */ |
| 3727 | void test_warning_page(void){ |
| 3728 | int iCase = atoi(PD("case","0")); |
| 3729 | int i; |
| 3730 | login_check_credentials(); |
| @@ -3730,17 +3733,15 @@ | |
| 3733 | return; |
| 3734 | } |
| 3735 | style_set_current_feature("test"); |
| 3736 | style_header("Warning Test Page"); |
| 3737 | style_submenu_element("Error Log","%R/errorlog"); |
| 3738 | @ <p>This page will generate various kinds of errors to test Fossil's |
| 3739 | @ reaction. Depending on settings, a message might be written |
| 3740 | @ into the <a href="%R/errorlog">error log</a>. Click on |
| 3741 | @ one of the following hyperlinks to generate a simulated error: |
| 3742 | for(i=1; i<=10; i++){ |
| 3743 | @ <a href='./test-warning?case=%d(i)'>[%d(i)]</a> |
| 3744 | } |
| 3745 | @ </p> |
| 3746 | @ <p><ol> |
| 3747 | @ <li value='1'> Call fossil_warning() |
| @@ -3769,20 +3770,39 @@ | |
| 3770 | } |
| 3771 | @ <li value='6'> call webpage_assert(0) |
| 3772 | if( iCase==6 ){ |
| 3773 | webpage_assert( 5==7 ); |
| 3774 | } |
| 3775 | @ <li value='7'> call webpage_error() |
| 3776 | if( iCase==7 ){ |
| 3777 | cgi_reset_content(); |
| 3778 | webpage_error("Case 7 from /test-warning"); |
| 3779 | } |
| 3780 | @ <li value='8'> simulated timeout |
| 3781 | if( iCase==8 ){ |
| 3782 | fossil_set_timeout(1); |
| 3783 | cgi_reset_content(); |
| 3784 | sqlite3_sleep(1100); |
| 3785 | } |
| 3786 | @ <li value='9'> simulated TH1 XSS vulnerability |
| 3787 | @ <li value='10'> simulated TH1 SQL-injection vulnerability |
| 3788 | if( iCase==9 || iCase==10 ){ |
| 3789 | const char *zR; |
| 3790 | int n, rc; |
| 3791 | static const char *zTH1[] = { |
| 3792 | /* case 9 */ "html [taint {<b>XSS</b>}]", |
| 3793 | /* case 10 */ "query [taint {SELECT 'SQL-injection' AS msg}] {\n" |
| 3794 | " html \"<b>[htmlize $msg]</b>\"\n" |
| 3795 | "}" |
| 3796 | }; |
| 3797 | rc = Th_Eval(g.interp, 0, zTH1[iCase==10], -1); |
| 3798 | zR = Th_GetResult(g.interp, &n); |
| 3799 | if( rc==TH_OK ){ |
| 3800 | @ <pre class="th1result">%h(zR)</pre> |
| 3801 | }else{ |
| 3802 | @ <pre class="th1error">%h(zR)</pre> |
| 3803 | } |
| 3804 | } |
| 3805 | @ </ol> |
| 3806 | @ <p>End of test</p> |
| 3807 | style_finish_page(); |
| 3808 | } |
| 3809 |
+1
-1
| --- src/printf.c | ||
| +++ src/printf.c | ||
| @@ -1121,11 +1121,11 @@ | ||
| 1121 | 1121 | }else if( (z = P(azEnv[i]))!=0 && z[0]!=0 ){ |
| 1122 | 1122 | fprintf(out, "%s=%s\n", azEnv[i], z); |
| 1123 | 1123 | } |
| 1124 | 1124 | } |
| 1125 | 1125 | } |
| 1126 | - fclose(out); | |
| 1126 | + if( out!=stderr ) fclose(out); | |
| 1127 | 1127 | } |
| 1128 | 1128 | |
| 1129 | 1129 | /* |
| 1130 | 1130 | ** The following variable becomes true while processing a fatal error |
| 1131 | 1131 | ** or a panic. If additional "recursive-fatal" errors occur while |
| 1132 | 1132 |
| --- src/printf.c | |
| +++ src/printf.c | |
| @@ -1121,11 +1121,11 @@ | |
| 1121 | }else if( (z = P(azEnv[i]))!=0 && z[0]!=0 ){ |
| 1122 | fprintf(out, "%s=%s\n", azEnv[i], z); |
| 1123 | } |
| 1124 | } |
| 1125 | } |
| 1126 | fclose(out); |
| 1127 | } |
| 1128 | |
| 1129 | /* |
| 1130 | ** The following variable becomes true while processing a fatal error |
| 1131 | ** or a panic. If additional "recursive-fatal" errors occur while |
| 1132 |
| --- src/printf.c | |
| +++ src/printf.c | |
| @@ -1121,11 +1121,11 @@ | |
| 1121 | }else if( (z = P(azEnv[i]))!=0 && z[0]!=0 ){ |
| 1122 | fprintf(out, "%s=%s\n", azEnv[i], z); |
| 1123 | } |
| 1124 | } |
| 1125 | } |
| 1126 | if( out!=stderr ) fclose(out); |
| 1127 | } |
| 1128 | |
| 1129 | /* |
| 1130 | ** The following variable becomes true while processing a fatal error |
| 1131 | ** or a panic. If additional "recursive-fatal" errors occur while |
| 1132 |
+25
-12
| --- src/security_audit.c | ||
| +++ src/security_audit.c | ||
| @@ -810,27 +810,28 @@ | ||
| 810 | 810 | ** WEBPAGE: errorlog |
| 811 | 811 | ** |
| 812 | 812 | ** Show the content of the error log. Only the administrator can view |
| 813 | 813 | ** this page. |
| 814 | 814 | ** |
| 815 | -** y=0x01 Show only hack attempts | |
| 816 | -** y=0x02 Show only panics and assertion faults | |
| 817 | -** y=0x04 Show hung backoffice processes | |
| 818 | -** y=0x08 Show POST requests from a different origin | |
| 819 | -** y=0x10 Show SQLITE_AUTH and similar | |
| 820 | -** y=0x20 Show SMTP error reports | |
| 821 | -** y=0x40 Show other uncategorized messages | |
| 815 | +** y=0x001 Show only hack attempts | |
| 816 | +** y=0x002 Show only panics and assertion faults | |
| 817 | +** y=0x004 Show hung backoffice processes | |
| 818 | +** y=0x008 Show POST requests from a different origin | |
| 819 | +** y=0x010 Show SQLITE_AUTH and similar | |
| 820 | +** y=0x020 Show SMTP error reports | |
| 821 | +** y=0x040 Show TH1 vulnerability reports | |
| 822 | +** y=0x800 Show other uncategorized messages | |
| 822 | 823 | ** |
| 823 | 824 | ** If y is omitted or is zero, a count of the various message types is |
| 824 | 825 | ** shown. |
| 825 | 826 | */ |
| 826 | 827 | void errorlog_page(void){ |
| 827 | 828 | i64 szFile; |
| 828 | 829 | FILE *in; |
| 829 | 830 | char *zLog; |
| 830 | 831 | const char *zType = P("y"); |
| 831 | - static const int eAllTypes = 0x7f; | |
| 832 | + static const int eAllTypes = 0x87f; | |
| 832 | 833 | long eType = 0; |
| 833 | 834 | int bOutput = 0; |
| 834 | 835 | int prevWasTime = 0; |
| 835 | 836 | int nHack = 0; |
| 836 | 837 | int nPanic = 0; |
| @@ -837,10 +838,11 @@ | ||
| 837 | 838 | int nOther = 0; |
| 838 | 839 | int nHang = 0; |
| 839 | 840 | int nXPost = 0; |
| 840 | 841 | int nAuth = 0; |
| 841 | 842 | int nSmtp = 0; |
| 843 | + int nVuln = 0; | |
| 842 | 844 | char z[10000]; |
| 843 | 845 | char zTime[10000]; |
| 844 | 846 | |
| 845 | 847 | login_check_credentials(); |
| 846 | 848 | if( !g.perm.Admin ){ |
| @@ -917,10 +919,13 @@ | ||
| 917 | 919 | } |
| 918 | 920 | if( eType & 0x20 ){ |
| 919 | 921 | @ <li>SMTP malfunctions |
| 920 | 922 | } |
| 921 | 923 | if( eType & 0x40 ){ |
| 924 | + @ <li>TH1 vulnerabilities | |
| 925 | + } | |
| 926 | + if( eType & 0x800 ){ | |
| 922 | 927 | @ <li>Other uncategorized messages |
| 923 | 928 | } |
| 924 | 929 | @ </ul> |
| 925 | 930 | } |
| 926 | 931 | @ <hr> |
| @@ -953,12 +958,16 @@ | ||
| 953 | 958 | || sqlite3_strglob("warning: SQLITE_AUTH*",z)==0 |
| 954 | 959 | ){ |
| 955 | 960 | bOutput = (eType & 0x10)!=0; |
| 956 | 961 | nAuth++; |
| 957 | 962 | }else |
| 958 | - { | |
| 963 | + if( strncmp(z,"possible", 8)==0 && strstr(z,"tainted")!=0 ){ | |
| 959 | 964 | bOutput = (eType & 0x40)!=0; |
| 965 | + nVuln++; | |
| 966 | + }else | |
| 967 | + { | |
| 968 | + bOutput = (eType & 0x800)!=0; | |
| 960 | 969 | nOther++; |
| 961 | 970 | } |
| 962 | 971 | if( bOutput ){ |
| 963 | 972 | @ %h(zTime)\ |
| 964 | 973 | } |
| @@ -978,17 +987,21 @@ | ||
| 978 | 987 | fclose(in); |
| 979 | 988 | if( eType ){ |
| 980 | 989 | @ </pre> |
| 981 | 990 | } |
| 982 | 991 | if( eType==0 ){ |
| 983 | - int nNonHack = nPanic + nHang + nAuth + nSmtp + nOther; | |
| 992 | + int nNonHack = nPanic + nHang + nAuth + nSmtp + nVuln + nOther; | |
| 984 | 993 | int nTotal = nNonHack + nHack + nXPost; |
| 985 | 994 | @ <p><table border="a" cellspacing="0" cellpadding="5"> |
| 986 | 995 | if( nPanic>0 ){ |
| 987 | 996 | @ <tr><td align="right">%d(nPanic)</td> |
| 988 | 997 | @ <td><a href="./errorlog?y=2">Panics</a></td> |
| 989 | 998 | } |
| 999 | + if( nVuln>0 ){ | |
| 1000 | + @ <tr><td align="right">%d(nVuln)</td> | |
| 1001 | + @ <td><a href="./errorlog?y=64">TH1 Vulnerabilities</a></td> | |
| 1002 | + } | |
| 990 | 1003 | if( nHack>0 ){ |
| 991 | 1004 | @ <tr><td align="right">%d(nHack)</td> |
| 992 | 1005 | @ <td><a href="./errorlog?y=1">Hack Attempts</a></td> |
| 993 | 1006 | } |
| 994 | 1007 | if( nHang>0 ){ |
| @@ -1007,17 +1020,17 @@ | ||
| 1007 | 1020 | @ <tr><td align="right">%d(nSmtp)</td> |
| 1008 | 1021 | @ <td><a href="./errorlog?y=32">SMTP faults</a></td> |
| 1009 | 1022 | } |
| 1010 | 1023 | if( nOther>0 ){ |
| 1011 | 1024 | @ <tr><td align="right">%d(nOther)</td> |
| 1012 | - @ <td><a href="./errorlog?y=64">Other</a></td> | |
| 1025 | + @ <td><a href="./errorlog?y=2048">Other</a></td> | |
| 1013 | 1026 | } |
| 1014 | 1027 | @ <tr><td align="right">%d(nTotal)</td> |
| 1015 | 1028 | if( nTotal>0 ){ |
| 1016 | - @ <td><a href="./errorlog?y=255">All Messages</a></td> | |
| 1029 | + @ <td><a href="./errorlog?y=4095">All Messages</a></td> | |
| 1017 | 1030 | }else{ |
| 1018 | 1031 | @ <td>All Messages</td> |
| 1019 | 1032 | } |
| 1020 | 1033 | @ </table> |
| 1021 | 1034 | } |
| 1022 | 1035 | style_finish_page(); |
| 1023 | 1036 | } |
| 1024 | 1037 |
| --- src/security_audit.c | |
| +++ src/security_audit.c | |
| @@ -810,27 +810,28 @@ | |
| 810 | ** WEBPAGE: errorlog |
| 811 | ** |
| 812 | ** Show the content of the error log. Only the administrator can view |
| 813 | ** this page. |
| 814 | ** |
| 815 | ** y=0x01 Show only hack attempts |
| 816 | ** y=0x02 Show only panics and assertion faults |
| 817 | ** y=0x04 Show hung backoffice processes |
| 818 | ** y=0x08 Show POST requests from a different origin |
| 819 | ** y=0x10 Show SQLITE_AUTH and similar |
| 820 | ** y=0x20 Show SMTP error reports |
| 821 | ** y=0x40 Show other uncategorized messages |
| 822 | ** |
| 823 | ** If y is omitted or is zero, a count of the various message types is |
| 824 | ** shown. |
| 825 | */ |
| 826 | void errorlog_page(void){ |
| 827 | i64 szFile; |
| 828 | FILE *in; |
| 829 | char *zLog; |
| 830 | const char *zType = P("y"); |
| 831 | static const int eAllTypes = 0x7f; |
| 832 | long eType = 0; |
| 833 | int bOutput = 0; |
| 834 | int prevWasTime = 0; |
| 835 | int nHack = 0; |
| 836 | int nPanic = 0; |
| @@ -837,10 +838,11 @@ | |
| 837 | int nOther = 0; |
| 838 | int nHang = 0; |
| 839 | int nXPost = 0; |
| 840 | int nAuth = 0; |
| 841 | int nSmtp = 0; |
| 842 | char z[10000]; |
| 843 | char zTime[10000]; |
| 844 | |
| 845 | login_check_credentials(); |
| 846 | if( !g.perm.Admin ){ |
| @@ -917,10 +919,13 @@ | |
| 917 | } |
| 918 | if( eType & 0x20 ){ |
| 919 | @ <li>SMTP malfunctions |
| 920 | } |
| 921 | if( eType & 0x40 ){ |
| 922 | @ <li>Other uncategorized messages |
| 923 | } |
| 924 | @ </ul> |
| 925 | } |
| 926 | @ <hr> |
| @@ -953,12 +958,16 @@ | |
| 953 | || sqlite3_strglob("warning: SQLITE_AUTH*",z)==0 |
| 954 | ){ |
| 955 | bOutput = (eType & 0x10)!=0; |
| 956 | nAuth++; |
| 957 | }else |
| 958 | { |
| 959 | bOutput = (eType & 0x40)!=0; |
| 960 | nOther++; |
| 961 | } |
| 962 | if( bOutput ){ |
| 963 | @ %h(zTime)\ |
| 964 | } |
| @@ -978,17 +987,21 @@ | |
| 978 | fclose(in); |
| 979 | if( eType ){ |
| 980 | @ </pre> |
| 981 | } |
| 982 | if( eType==0 ){ |
| 983 | int nNonHack = nPanic + nHang + nAuth + nSmtp + nOther; |
| 984 | int nTotal = nNonHack + nHack + nXPost; |
| 985 | @ <p><table border="a" cellspacing="0" cellpadding="5"> |
| 986 | if( nPanic>0 ){ |
| 987 | @ <tr><td align="right">%d(nPanic)</td> |
| 988 | @ <td><a href="./errorlog?y=2">Panics</a></td> |
| 989 | } |
| 990 | if( nHack>0 ){ |
| 991 | @ <tr><td align="right">%d(nHack)</td> |
| 992 | @ <td><a href="./errorlog?y=1">Hack Attempts</a></td> |
| 993 | } |
| 994 | if( nHang>0 ){ |
| @@ -1007,17 +1020,17 @@ | |
| 1007 | @ <tr><td align="right">%d(nSmtp)</td> |
| 1008 | @ <td><a href="./errorlog?y=32">SMTP faults</a></td> |
| 1009 | } |
| 1010 | if( nOther>0 ){ |
| 1011 | @ <tr><td align="right">%d(nOther)</td> |
| 1012 | @ <td><a href="./errorlog?y=64">Other</a></td> |
| 1013 | } |
| 1014 | @ <tr><td align="right">%d(nTotal)</td> |
| 1015 | if( nTotal>0 ){ |
| 1016 | @ <td><a href="./errorlog?y=255">All Messages</a></td> |
| 1017 | }else{ |
| 1018 | @ <td>All Messages</td> |
| 1019 | } |
| 1020 | @ </table> |
| 1021 | } |
| 1022 | style_finish_page(); |
| 1023 | } |
| 1024 |
| --- src/security_audit.c | |
| +++ src/security_audit.c | |
| @@ -810,27 +810,28 @@ | |
| 810 | ** WEBPAGE: errorlog |
| 811 | ** |
| 812 | ** Show the content of the error log. Only the administrator can view |
| 813 | ** this page. |
| 814 | ** |
| 815 | ** y=0x001 Show only hack attempts |
| 816 | ** y=0x002 Show only panics and assertion faults |
| 817 | ** y=0x004 Show hung backoffice processes |
| 818 | ** y=0x008 Show POST requests from a different origin |
| 819 | ** y=0x010 Show SQLITE_AUTH and similar |
| 820 | ** y=0x020 Show SMTP error reports |
| 821 | ** y=0x040 Show TH1 vulnerability reports |
| 822 | ** y=0x800 Show other uncategorized messages |
| 823 | ** |
| 824 | ** If y is omitted or is zero, a count of the various message types is |
| 825 | ** shown. |
| 826 | */ |
| 827 | void errorlog_page(void){ |
| 828 | i64 szFile; |
| 829 | FILE *in; |
| 830 | char *zLog; |
| 831 | const char *zType = P("y"); |
| 832 | static const int eAllTypes = 0x87f; |
| 833 | long eType = 0; |
| 834 | int bOutput = 0; |
| 835 | int prevWasTime = 0; |
| 836 | int nHack = 0; |
| 837 | int nPanic = 0; |
| @@ -837,10 +838,11 @@ | |
| 838 | int nOther = 0; |
| 839 | int nHang = 0; |
| 840 | int nXPost = 0; |
| 841 | int nAuth = 0; |
| 842 | int nSmtp = 0; |
| 843 | int nVuln = 0; |
| 844 | char z[10000]; |
| 845 | char zTime[10000]; |
| 846 | |
| 847 | login_check_credentials(); |
| 848 | if( !g.perm.Admin ){ |
| @@ -917,10 +919,13 @@ | |
| 919 | } |
| 920 | if( eType & 0x20 ){ |
| 921 | @ <li>SMTP malfunctions |
| 922 | } |
| 923 | if( eType & 0x40 ){ |
| 924 | @ <li>TH1 vulnerabilities |
| 925 | } |
| 926 | if( eType & 0x800 ){ |
| 927 | @ <li>Other uncategorized messages |
| 928 | } |
| 929 | @ </ul> |
| 930 | } |
| 931 | @ <hr> |
| @@ -953,12 +958,16 @@ | |
| 958 | || sqlite3_strglob("warning: SQLITE_AUTH*",z)==0 |
| 959 | ){ |
| 960 | bOutput = (eType & 0x10)!=0; |
| 961 | nAuth++; |
| 962 | }else |
| 963 | if( strncmp(z,"possible", 8)==0 && strstr(z,"tainted")!=0 ){ |
| 964 | bOutput = (eType & 0x40)!=0; |
| 965 | nVuln++; |
| 966 | }else |
| 967 | { |
| 968 | bOutput = (eType & 0x800)!=0; |
| 969 | nOther++; |
| 970 | } |
| 971 | if( bOutput ){ |
| 972 | @ %h(zTime)\ |
| 973 | } |
| @@ -978,17 +987,21 @@ | |
| 987 | fclose(in); |
| 988 | if( eType ){ |
| 989 | @ </pre> |
| 990 | } |
| 991 | if( eType==0 ){ |
| 992 | int nNonHack = nPanic + nHang + nAuth + nSmtp + nVuln + nOther; |
| 993 | int nTotal = nNonHack + nHack + nXPost; |
| 994 | @ <p><table border="a" cellspacing="0" cellpadding="5"> |
| 995 | if( nPanic>0 ){ |
| 996 | @ <tr><td align="right">%d(nPanic)</td> |
| 997 | @ <td><a href="./errorlog?y=2">Panics</a></td> |
| 998 | } |
| 999 | if( nVuln>0 ){ |
| 1000 | @ <tr><td align="right">%d(nVuln)</td> |
| 1001 | @ <td><a href="./errorlog?y=64">TH1 Vulnerabilities</a></td> |
| 1002 | } |
| 1003 | if( nHack>0 ){ |
| 1004 | @ <tr><td align="right">%d(nHack)</td> |
| 1005 | @ <td><a href="./errorlog?y=1">Hack Attempts</a></td> |
| 1006 | } |
| 1007 | if( nHang>0 ){ |
| @@ -1007,17 +1020,17 @@ | |
| 1020 | @ <tr><td align="right">%d(nSmtp)</td> |
| 1021 | @ <td><a href="./errorlog?y=32">SMTP faults</a></td> |
| 1022 | } |
| 1023 | if( nOther>0 ){ |
| 1024 | @ <tr><td align="right">%d(nOther)</td> |
| 1025 | @ <td><a href="./errorlog?y=2048">Other</a></td> |
| 1026 | } |
| 1027 | @ <tr><td align="right">%d(nTotal)</td> |
| 1028 | if( nTotal>0 ){ |
| 1029 | @ <td><a href="./errorlog?y=4095">All Messages</a></td> |
| 1030 | }else{ |
| 1031 | @ <td>All Messages</td> |
| 1032 | } |
| 1033 | @ </table> |
| 1034 | } |
| 1035 | style_finish_page(); |
| 1036 | } |
| 1037 |
+3
-2
| --- src/style.c | ||
| +++ src/style.c | ||
| @@ -744,12 +744,13 @@ | ||
| 744 | 744 | ** is evaluated before the header is rendered). |
| 745 | 745 | */ |
| 746 | 746 | Th_MaybeStore("default_csp", zDfltCsp); |
| 747 | 747 | fossil_free(zDfltCsp); |
| 748 | 748 | Th_Store("nonce", zNonce); |
| 749 | - Th_Store("project_name", db_get("project-name","Unnamed Fossil Project")); | |
| 750 | - Th_Store("project_description", db_get("project-description","")); | |
| 749 | + Th_StoreUnsafe("project_name", | |
| 750 | + db_get("project-name","Unnamed Fossil Project")); | |
| 751 | + Th_StoreUnsafe("project_description", db_get("project-description","")); | |
| 751 | 752 | if( zTitle ) Th_Store("title", html_lookalike(zTitle,-1)); |
| 752 | 753 | Th_Store("baseurl", g.zBaseURL); |
| 753 | 754 | Th_Store("secureurl", fossil_wants_https(1)? g.zHttpsURL: g.zBaseURL); |
| 754 | 755 | Th_Store("home", g.zTop); |
| 755 | 756 | Th_Store("index_page", db_get("index-page","/home")); |
| 756 | 757 |
| --- src/style.c | |
| +++ src/style.c | |
| @@ -744,12 +744,13 @@ | |
| 744 | ** is evaluated before the header is rendered). |
| 745 | */ |
| 746 | Th_MaybeStore("default_csp", zDfltCsp); |
| 747 | fossil_free(zDfltCsp); |
| 748 | Th_Store("nonce", zNonce); |
| 749 | Th_Store("project_name", db_get("project-name","Unnamed Fossil Project")); |
| 750 | Th_Store("project_description", db_get("project-description","")); |
| 751 | if( zTitle ) Th_Store("title", html_lookalike(zTitle,-1)); |
| 752 | Th_Store("baseurl", g.zBaseURL); |
| 753 | Th_Store("secureurl", fossil_wants_https(1)? g.zHttpsURL: g.zBaseURL); |
| 754 | Th_Store("home", g.zTop); |
| 755 | Th_Store("index_page", db_get("index-page","/home")); |
| 756 |
| --- src/style.c | |
| +++ src/style.c | |
| @@ -744,12 +744,13 @@ | |
| 744 | ** is evaluated before the header is rendered). |
| 745 | */ |
| 746 | Th_MaybeStore("default_csp", zDfltCsp); |
| 747 | fossil_free(zDfltCsp); |
| 748 | Th_Store("nonce", zNonce); |
| 749 | Th_StoreUnsafe("project_name", |
| 750 | db_get("project-name","Unnamed Fossil Project")); |
| 751 | Th_StoreUnsafe("project_description", db_get("project-description","")); |
| 752 | if( zTitle ) Th_Store("title", html_lookalike(zTitle,-1)); |
| 753 | Th_Store("baseurl", g.zBaseURL); |
| 754 | Th_Store("secureurl", fossil_wants_https(1)? g.zHttpsURL: g.zBaseURL); |
| 755 | Th_Store("home", g.zTop); |
| 756 | Th_Store("index_page", db_get("index-page","/home")); |
| 757 |
M
src/th.c
+80
-53
| --- src/th.c | ||
| +++ src/th.c | ||
| @@ -7,10 +7,16 @@ | ||
| 7 | 7 | #include "config.h" |
| 8 | 8 | #include "th.h" |
| 9 | 9 | #include <string.h> |
| 10 | 10 | #include <assert.h> |
| 11 | 11 | |
| 12 | +/* | |
| 13 | +** External routines | |
| 14 | +*/ | |
| 15 | +void fossil_panic(const char*,...); | |
| 16 | +void fossil_errorlog(const char*,...); | |
| 17 | + | |
| 12 | 18 | /* |
| 13 | 19 | ** Values used for element values in the tcl_platform array. |
| 14 | 20 | */ |
| 15 | 21 | |
| 16 | 22 | #if !defined(TH_ENGINE) |
| @@ -197,10 +203,11 @@ | ||
| 197 | 203 | */ |
| 198 | 204 | struct Buffer { |
| 199 | 205 | char *zBuf; |
| 200 | 206 | int nBuf; |
| 201 | 207 | int nBufAlloc; |
| 208 | + int bTaint; | |
| 202 | 209 | }; |
| 203 | 210 | typedef struct Buffer Buffer; |
| 204 | 211 | static void thBufferInit(Buffer *); |
| 205 | 212 | static void thBufferFree(Th_Interp *interp, Buffer *); |
| 206 | 213 | |
| @@ -209,10 +216,18 @@ | ||
| 209 | 216 | ** be NULL as long as the number of bytes to copy is zero. |
| 210 | 217 | */ |
| 211 | 218 | static void th_memcpy(void *dest, const void *src, size_t n){ |
| 212 | 219 | if( n>0 ) memcpy(dest,src,n); |
| 213 | 220 | } |
| 221 | + | |
| 222 | +/* | |
| 223 | +** An oversized string has been encountered. Do not try to recover. | |
| 224 | +** Panic the process. | |
| 225 | +*/ | |
| 226 | +void Th_OversizeString(void){ | |
| 227 | + fossil_panic("string too large. maximum size 286MB."); | |
| 228 | +} | |
| 214 | 229 | |
| 215 | 230 | /* |
| 216 | 231 | ** Append nAdd bytes of content copied from zAdd to the end of buffer |
| 217 | 232 | ** pBuffer. If there is not enough space currently allocated, resize |
| 218 | 233 | ** the allocation to make space. |
| @@ -219,40 +234,46 @@ | ||
| 219 | 234 | */ |
| 220 | 235 | static void thBufferWriteResize( |
| 221 | 236 | Th_Interp *interp, |
| 222 | 237 | Buffer *pBuffer, |
| 223 | 238 | const char *zAdd, |
| 224 | - int nAdd | |
| 239 | + int nAddX | |
| 225 | 240 | ){ |
| 241 | + int nAdd = TH1_LEN(nAddX); | |
| 226 | 242 | int nNew = (pBuffer->nBuf+nAdd)*2+32; |
| 227 | 243 | #if defined(TH_MEMDEBUG) |
| 228 | 244 | char *zNew = (char *)Th_Malloc(interp, nNew); |
| 245 | + TH1_SIZECHECK(nNew); | |
| 229 | 246 | th_memcpy(zNew, pBuffer->zBuf, pBuffer->nBuf); |
| 230 | 247 | Th_Free(interp, pBuffer->zBuf); |
| 231 | 248 | pBuffer->zBuf = zNew; |
| 232 | 249 | #else |
| 233 | 250 | int nOld = pBuffer->nBufAlloc; |
| 251 | + TH1_SIZECHECK(nNew); | |
| 234 | 252 | pBuffer->zBuf = Th_Realloc(interp, pBuffer->zBuf, nNew); |
| 235 | 253 | memset(pBuffer->zBuf+nOld, 0, nNew-nOld); |
| 236 | 254 | #endif |
| 237 | 255 | pBuffer->nBufAlloc = nNew; |
| 238 | 256 | th_memcpy(&pBuffer->zBuf[pBuffer->nBuf], zAdd, nAdd); |
| 239 | 257 | pBuffer->nBuf += nAdd; |
| 258 | + TH1_XFER_TAINT(pBuffer->bTaint, nAddX); | |
| 240 | 259 | } |
| 241 | 260 | static void thBufferWriteFast( |
| 242 | 261 | Th_Interp *interp, |
| 243 | 262 | Buffer *pBuffer, |
| 244 | 263 | const char *zAdd, |
| 245 | - int nAdd | |
| 264 | + int nAddX | |
| 246 | 265 | ){ |
| 266 | + int nAdd = TH1_LEN(nAddX); | |
| 247 | 267 | if( pBuffer->nBuf+nAdd > pBuffer->nBufAlloc ){ |
| 248 | - thBufferWriteResize(interp, pBuffer, zAdd, nAdd); | |
| 268 | + thBufferWriteResize(interp, pBuffer, zAdd, nAddX); | |
| 249 | 269 | }else{ |
| 250 | 270 | if( pBuffer->zBuf ){ |
| 251 | 271 | memcpy(pBuffer->zBuf + pBuffer->nBuf, zAdd, nAdd); |
| 252 | 272 | } |
| 253 | 273 | pBuffer->nBuf += nAdd; |
| 274 | + TH1_XFER_TAINT(pBuffer->bTaint, nAddX); | |
| 254 | 275 | } |
| 255 | 276 | } |
| 256 | 277 | #define thBufferWrite(a,b,c,d) thBufferWriteFast(a,b,(const char *)c,d) |
| 257 | 278 | |
| 258 | 279 | /* |
| @@ -704,24 +725,25 @@ | ||
| 704 | 725 | int nWord |
| 705 | 726 | ){ |
| 706 | 727 | int rc = TH_OK; |
| 707 | 728 | Buffer output; |
| 708 | 729 | int i; |
| 730 | + int nn = TH1_LEN(nWord); | |
| 709 | 731 | |
| 710 | 732 | thBufferInit(&output); |
| 711 | 733 | |
| 712 | - if( nWord>1 && (zWord[0]=='{' && zWord[nWord-1]=='}') ){ | |
| 713 | - thBufferWrite(interp, &output, &zWord[1], nWord-2); | |
| 734 | + if( nn>1 && (zWord[0]=='{' && zWord[nn-1]=='}') ){ | |
| 735 | + thBufferWrite(interp, &output, &zWord[1], nn-2); | |
| 714 | 736 | }else{ |
| 715 | 737 | |
| 716 | 738 | /* If the word is surrounded by double-quotes strip these away. */ |
| 717 | - if( nWord>1 && (zWord[0]=='"' && zWord[nWord-1]=='"') ){ | |
| 739 | + if( nn>1 && (zWord[0]=='"' && zWord[nn-1]=='"') ){ | |
| 718 | 740 | zWord++; |
| 719 | - nWord -= 2; | |
| 741 | + nn -= 2; | |
| 720 | 742 | } |
| 721 | 743 | |
| 722 | - for(i=0; rc==TH_OK && i<nWord; i++){ | |
| 744 | + for(i=0; rc==TH_OK && i<nn; i++){ | |
| 723 | 745 | int nGet; |
| 724 | 746 | |
| 725 | 747 | int (*xGet)(Th_Interp *, const char*, int, int *) = 0; |
| 726 | 748 | int (*xSubst)(Th_Interp *, const char*, int) = 0; |
| 727 | 749 | |
| @@ -743,11 +765,11 @@ | ||
| 743 | 765 | thBufferAddChar(interp, &output, zWord[i]); |
| 744 | 766 | continue; /* Go to the next iteration of the for(...) loop */ |
| 745 | 767 | } |
| 746 | 768 | } |
| 747 | 769 | |
| 748 | - rc = xGet(interp, &zWord[i], nWord-i, &nGet); | |
| 770 | + rc = xGet(interp, &zWord[i], nn-i, &nGet); | |
| 749 | 771 | if( rc==TH_OK ){ |
| 750 | 772 | rc = xSubst(interp, &zWord[i], nGet); |
| 751 | 773 | } |
| 752 | 774 | if( rc==TH_OK ){ |
| 753 | 775 | const char *zRes; |
| @@ -758,11 +780,11 @@ | ||
| 758 | 780 | } |
| 759 | 781 | } |
| 760 | 782 | } |
| 761 | 783 | |
| 762 | 784 | if( rc==TH_OK ){ |
| 763 | - Th_SetResult(interp, output.zBuf, output.nBuf); | |
| 785 | + Th_SetResult(interp, output.zBuf, output.nBuf|output.bTaint); | |
| 764 | 786 | } |
| 765 | 787 | thBufferFree(interp, &output); |
| 766 | 788 | return rc; |
| 767 | 789 | } |
| 768 | 790 | |
| @@ -826,11 +848,11 @@ | ||
| 826 | 848 | Buffer strbuf; |
| 827 | 849 | Buffer lenbuf; |
| 828 | 850 | int nCount = 0; |
| 829 | 851 | |
| 830 | 852 | const char *zInput = zList; |
| 831 | - int nInput = nList; | |
| 853 | + int nInput = TH1_LEN(nList); | |
| 832 | 854 | |
| 833 | 855 | thBufferInit(&strbuf); |
| 834 | 856 | thBufferInit(&lenbuf); |
| 835 | 857 | |
| 836 | 858 | while( nInput>0 ){ |
| @@ -837,19 +859,19 @@ | ||
| 837 | 859 | const char *zWord; |
| 838 | 860 | int nWord; |
| 839 | 861 | |
| 840 | 862 | thNextSpace(interp, zInput, nInput, &nWord); |
| 841 | 863 | zInput += nWord; |
| 842 | - nInput = nList-(zInput-zList); | |
| 864 | + nInput = TH1_LEN(nList)-(zInput-zList); | |
| 843 | 865 | |
| 844 | 866 | if( TH_OK!=(rc = thNextWord(interp, zInput, nInput, &nWord, 0)) |
| 845 | 867 | || TH_OK!=(rc = thSubstWord(interp, zInput, nWord)) |
| 846 | 868 | ){ |
| 847 | 869 | goto finish; |
| 848 | 870 | } |
| 849 | - zInput = &zInput[nWord]; | |
| 850 | - nInput = nList-(zInput-zList); | |
| 871 | + zInput = &zInput[TH1_LEN(nWord)]; | |
| 872 | + nInput = TH1_LEN(nList)-(zInput-zList); | |
| 851 | 873 | if( nWord>0 ){ |
| 852 | 874 | zWord = Th_GetResult(interp, &nWord); |
| 853 | 875 | thBufferWrite(interp, &strbuf, zWord, nWord); |
| 854 | 876 | thBufferAddChar(interp, &strbuf, 0); |
| 855 | 877 | thBufferWrite(interp, &lenbuf, &nWord, sizeof(int)); |
| @@ -872,11 +894,11 @@ | ||
| 872 | 894 | zElem = (char *)&anElem[nCount]; |
| 873 | 895 | th_memcpy(anElem, lenbuf.zBuf, lenbuf.nBuf); |
| 874 | 896 | th_memcpy(zElem, strbuf.zBuf, strbuf.nBuf); |
| 875 | 897 | for(i=0; i<nCount;i++){ |
| 876 | 898 | azElem[i] = zElem; |
| 877 | - zElem += (anElem[i] + 1); | |
| 899 | + zElem += (TH1_LEN(anElem[i]) + 1); | |
| 878 | 900 | } |
| 879 | 901 | *pazElem = azElem; |
| 880 | 902 | *panElem = anElem; |
| 881 | 903 | } |
| 882 | 904 | if( pnCount ){ |
| @@ -894,12 +916,17 @@ | ||
| 894 | 916 | ** in the current stack frame. |
| 895 | 917 | */ |
| 896 | 918 | static int thEvalLocal(Th_Interp *interp, const char *zProgram, int nProgram){ |
| 897 | 919 | int rc = TH_OK; |
| 898 | 920 | const char *zInput = zProgram; |
| 899 | - int nInput = nProgram; | |
| 921 | + int nInput = TH1_LEN(nProgram); | |
| 900 | 922 | |
| 923 | + if( TH1_TAINTED(nProgram) | |
| 924 | + && Th_ReportTaint(interp, "script", zProgram, nProgram) | |
| 925 | + ){ | |
| 926 | + return TH_ERROR; | |
| 927 | + } | |
| 901 | 928 | while( rc==TH_OK && nInput ){ |
| 902 | 929 | Th_HashEntry *pEntry; |
| 903 | 930 | int nSpace; |
| 904 | 931 | const char *zFirst; |
| 905 | 932 | |
| @@ -949,13 +976,13 @@ | ||
| 949 | 976 | if( rc!=TH_OK ) continue; |
| 950 | 977 | |
| 951 | 978 | if( argc>0 ){ |
| 952 | 979 | |
| 953 | 980 | /* Look up the command name in the command hash-table. */ |
| 954 | - pEntry = Th_HashFind(interp, interp->paCmd, argv[0], argl[0], 0); | |
| 981 | + pEntry = Th_HashFind(interp, interp->paCmd, argv[0], TH1_LEN(argl[0]),0); | |
| 955 | 982 | if( !pEntry ){ |
| 956 | - Th_ErrorMessage(interp, "no such command: ", argv[0], argl[0]); | |
| 983 | + Th_ErrorMessage(interp, "no such command: ", argv[0], TH1_LEN(argl[0])); | |
| 957 | 984 | rc = TH_ERROR; |
| 958 | 985 | } |
| 959 | 986 | |
| 960 | 987 | /* Call the command procedure. */ |
| 961 | 988 | if( rc==TH_OK ){ |
| @@ -1053,10 +1080,12 @@ | ||
| 1053 | 1080 | }else{ |
| 1054 | 1081 | int nInput = nProgram; |
| 1055 | 1082 | |
| 1056 | 1083 | if( nInput<0 ){ |
| 1057 | 1084 | nInput = th_strlen(zProgram); |
| 1085 | + }else{ | |
| 1086 | + nInput = TH1_LEN(nInput); | |
| 1058 | 1087 | } |
| 1059 | 1088 | rc = thEvalLocal(interp, zProgram, nInput); |
| 1060 | 1089 | } |
| 1061 | 1090 | |
| 1062 | 1091 | interp->pFrame = pSavedFrame; |
| @@ -1095,10 +1124,12 @@ | ||
| 1095 | 1124 | int isGlobal = 0; |
| 1096 | 1125 | int i; |
| 1097 | 1126 | |
| 1098 | 1127 | if( nVarname<0 ){ |
| 1099 | 1128 | nVarname = th_strlen(zVarname); |
| 1129 | + }else{ | |
| 1130 | + nVarname = TH1_LEN(nVarname); | |
| 1100 | 1131 | } |
| 1101 | 1132 | nOuter = nVarname; |
| 1102 | 1133 | |
| 1103 | 1134 | /* If the variable name starts with "::", then do the lookup is in the |
| 1104 | 1135 | ** uppermost (global) frame. |
| @@ -1271,31 +1302,10 @@ | ||
| 1271 | 1302 | } |
| 1272 | 1303 | |
| 1273 | 1304 | return Th_SetResult(interp, pValue->zData, pValue->nData); |
| 1274 | 1305 | } |
| 1275 | 1306 | |
| 1276 | -/* | |
| 1277 | -** If interp has a variable with the given name, its value is returned | |
| 1278 | -** and its length is returned via *nOut if nOut is not NULL. If | |
| 1279 | -** interp has no such var then NULL is returned without setting any | |
| 1280 | -** error state and *nOut, if not NULL, is set to -1. The returned value | |
| 1281 | -** is owned by the interpreter and may be invalidated the next time | |
| 1282 | -** the interpreter is modified. | |
| 1283 | -*/ | |
| 1284 | -const char * Th_MaybeGetVar(Th_Interp *interp, const char *zVarName, | |
| 1285 | - int *nOut){ | |
| 1286 | - Th_Variable *pValue; | |
| 1287 | - | |
| 1288 | - pValue = thFindValue(interp, zVarName, -1, 0, 0, 1, 0); | |
| 1289 | - if( !pValue || !pValue->zData ){ | |
| 1290 | - if( nOut!=0 ) *nOut = -1; | |
| 1291 | - return NULL; | |
| 1292 | - } | |
| 1293 | - if( nOut!=0 ) *nOut = pValue->nData; | |
| 1294 | - return pValue->zData; | |
| 1295 | -} | |
| 1296 | - | |
| 1297 | 1307 | /* |
| 1298 | 1308 | ** Return true if variable (zVar, nVar) exists. |
| 1299 | 1309 | */ |
| 1300 | 1310 | int Th_ExistsVar(Th_Interp *interp, const char *zVar, int nVar){ |
| 1301 | 1311 | Th_Variable *pValue = thFindValue(interp, zVar, nVar, 0, 1, 1, 0); |
| @@ -1324,28 +1334,32 @@ | ||
| 1324 | 1334 | int nVar, |
| 1325 | 1335 | const char *zValue, |
| 1326 | 1336 | int nValue |
| 1327 | 1337 | ){ |
| 1328 | 1338 | Th_Variable *pValue; |
| 1339 | + int nn; | |
| 1329 | 1340 | |
| 1341 | + nVar = TH1_LEN(nVar); | |
| 1330 | 1342 | pValue = thFindValue(interp, zVar, nVar, 1, 0, 0, 0); |
| 1331 | 1343 | if( !pValue ){ |
| 1332 | 1344 | return TH_ERROR; |
| 1333 | 1345 | } |
| 1334 | 1346 | |
| 1335 | 1347 | if( nValue<0 ){ |
| 1336 | - nValue = th_strlen(zValue); | |
| 1348 | + nn = th_strlen(zValue); | |
| 1349 | + }else{ | |
| 1350 | + nn = TH1_LEN(nValue); | |
| 1337 | 1351 | } |
| 1338 | 1352 | if( pValue->zData ){ |
| 1339 | 1353 | Th_Free(interp, pValue->zData); |
| 1340 | 1354 | pValue->zData = 0; |
| 1341 | 1355 | } |
| 1342 | 1356 | |
| 1343 | - assert(zValue || nValue==0); | |
| 1344 | - pValue->zData = Th_Malloc(interp, nValue+1); | |
| 1345 | - pValue->zData[nValue] = '\0'; | |
| 1346 | - th_memcpy(pValue->zData, zValue, nValue); | |
| 1357 | + assert(zValue || nn==0); | |
| 1358 | + pValue->zData = Th_Malloc(interp, nn+1); | |
| 1359 | + pValue->zData[nn] = '\0'; | |
| 1360 | + th_memcpy(pValue->zData, zValue, nn); | |
| 1347 | 1361 | pValue->nData = nValue; |
| 1348 | 1362 | |
| 1349 | 1363 | return TH_OK; |
| 1350 | 1364 | } |
| 1351 | 1365 | |
| @@ -1458,10 +1472,12 @@ | ||
| 1458 | 1472 | */ |
| 1459 | 1473 | char *th_strdup(Th_Interp *interp, const char *z, int n){ |
| 1460 | 1474 | char *zRes; |
| 1461 | 1475 | if( n<0 ){ |
| 1462 | 1476 | n = th_strlen(z); |
| 1477 | + }else{ | |
| 1478 | + n = TH1_LEN(n); | |
| 1463 | 1479 | } |
| 1464 | 1480 | zRes = Th_Malloc(interp, n+1); |
| 1465 | 1481 | th_memcpy(zRes, z, n); |
| 1466 | 1482 | zRes[n] = '\0'; |
| 1467 | 1483 | return zRes; |
| @@ -1519,13 +1535,14 @@ | ||
| 1519 | 1535 | n = th_strlen(z); |
| 1520 | 1536 | } |
| 1521 | 1537 | |
| 1522 | 1538 | if( z && n>0 ){ |
| 1523 | 1539 | char *zResult; |
| 1524 | - zResult = Th_Malloc(pInterp, n+1); | |
| 1525 | - th_memcpy(zResult, z, n); | |
| 1526 | - zResult[n] = '\0'; | |
| 1540 | + int nn = TH1_LEN(n); | |
| 1541 | + zResult = Th_Malloc(pInterp, nn+1); | |
| 1542 | + th_memcpy(zResult, z, nn); | |
| 1543 | + zResult[nn] = '\0'; | |
| 1527 | 1544 | pInterp->zResult = zResult; |
| 1528 | 1545 | pInterp->nResult = n; |
| 1529 | 1546 | } |
| 1530 | 1547 | |
| 1531 | 1548 | return TH_OK; |
| @@ -1834,24 +1851,28 @@ | ||
| 1834 | 1851 | int *pnStr, /* IN/OUT: Current length of *pzStr */ |
| 1835 | 1852 | const char *zElem, /* Data to append */ |
| 1836 | 1853 | int nElem /* Length of nElem */ |
| 1837 | 1854 | ){ |
| 1838 | 1855 | char *zNew; |
| 1839 | - int nNew; | |
| 1856 | + long long int nNew; | |
| 1857 | + int nn; | |
| 1840 | 1858 | |
| 1841 | 1859 | if( nElem<0 ){ |
| 1842 | - nElem = th_strlen(zElem); | |
| 1860 | + nn = th_strlen(zElem); | |
| 1861 | + }else{ | |
| 1862 | + nn = TH1_LEN(nElem); | |
| 1843 | 1863 | } |
| 1844 | 1864 | |
| 1845 | - nNew = *pnStr + nElem; | |
| 1865 | + nNew = TH1_LEN(*pnStr) + nn; | |
| 1866 | + TH1_SIZECHECK(nNew); | |
| 1846 | 1867 | zNew = Th_Malloc(interp, nNew); |
| 1847 | 1868 | th_memcpy(zNew, *pzStr, *pnStr); |
| 1848 | - th_memcpy(&zNew[*pnStr], zElem, nElem); | |
| 1869 | + th_memcpy(&zNew[TH1_LEN(*pnStr)], zElem, nn); | |
| 1849 | 1870 | |
| 1850 | 1871 | Th_Free(interp, *pzStr); |
| 1851 | 1872 | *pzStr = zNew; |
| 1852 | - *pnStr = nNew; | |
| 1873 | + *pnStr = (int)nNew; | |
| 1853 | 1874 | |
| 1854 | 1875 | return TH_OK; |
| 1855 | 1876 | } |
| 1856 | 1877 | |
| 1857 | 1878 | /* |
| @@ -2456,10 +2477,12 @@ | ||
| 2456 | 2477 | int nToken = 0; |
| 2457 | 2478 | Expr **apToken = 0; |
| 2458 | 2479 | |
| 2459 | 2480 | if( nExpr<0 ){ |
| 2460 | 2481 | nExpr = th_strlen(zExpr); |
| 2482 | + }else{ | |
| 2483 | + nExpr = TH1_LEN(nExpr); | |
| 2461 | 2484 | } |
| 2462 | 2485 | |
| 2463 | 2486 | /* Parse the expression to a list of tokens. */ |
| 2464 | 2487 | rc = exprParse(interp, zExpr, nExpr, &apToken, &nToken); |
| 2465 | 2488 | |
| @@ -2567,10 +2590,12 @@ | ||
| 2567 | 2590 | Th_HashEntry *pRet; |
| 2568 | 2591 | Th_HashEntry **ppRet; |
| 2569 | 2592 | |
| 2570 | 2593 | if( nKey<0 ){ |
| 2571 | 2594 | nKey = th_strlen(zKey); |
| 2595 | + }else{ | |
| 2596 | + nKey = TH1_LEN(nKey); | |
| 2572 | 2597 | } |
| 2573 | 2598 | |
| 2574 | 2599 | for(i=0; i<nKey; i++){ |
| 2575 | 2600 | iKey = (iKey<<3) ^ iKey ^ zKey[i]; |
| 2576 | 2601 | } |
| @@ -2800,10 +2825,12 @@ | ||
| 2800 | 2825 | int base = 10; |
| 2801 | 2826 | int (*isdigit)(char) = th_isdigit; |
| 2802 | 2827 | |
| 2803 | 2828 | if( n<0 ){ |
| 2804 | 2829 | n = th_strlen(z); |
| 2830 | + }else{ | |
| 2831 | + n = TH1_LEN(n); | |
| 2805 | 2832 | } |
| 2806 | 2833 | |
| 2807 | 2834 | if( n>1 && (z[0]=='-' || z[0]=='+') ){ |
| 2808 | 2835 | i = 1; |
| 2809 | 2836 | } |
| @@ -2859,11 +2886,11 @@ | ||
| 2859 | 2886 | const char *z, |
| 2860 | 2887 | int n, |
| 2861 | 2888 | double *pfOut |
| 2862 | 2889 | ){ |
| 2863 | 2890 | if( !sqlite3IsNumber((const char *)z, 0) ){ |
| 2864 | - Th_ErrorMessage(interp, "expected number, got: \"", z, n); | |
| 2891 | + Th_ErrorMessage(interp, "expected number, got: \"", z, TH1_LEN(n)); | |
| 2865 | 2892 | return TH_ERROR; |
| 2866 | 2893 | } |
| 2867 | 2894 | |
| 2868 | 2895 | sqlite3AtoF((const char *)z, pfOut); |
| 2869 | 2896 | return TH_OK; |
| 2870 | 2897 |
| --- src/th.c | |
| +++ src/th.c | |
| @@ -7,10 +7,16 @@ | |
| 7 | #include "config.h" |
| 8 | #include "th.h" |
| 9 | #include <string.h> |
| 10 | #include <assert.h> |
| 11 | |
| 12 | /* |
| 13 | ** Values used for element values in the tcl_platform array. |
| 14 | */ |
| 15 | |
| 16 | #if !defined(TH_ENGINE) |
| @@ -197,10 +203,11 @@ | |
| 197 | */ |
| 198 | struct Buffer { |
| 199 | char *zBuf; |
| 200 | int nBuf; |
| 201 | int nBufAlloc; |
| 202 | }; |
| 203 | typedef struct Buffer Buffer; |
| 204 | static void thBufferInit(Buffer *); |
| 205 | static void thBufferFree(Th_Interp *interp, Buffer *); |
| 206 | |
| @@ -209,10 +216,18 @@ | |
| 209 | ** be NULL as long as the number of bytes to copy is zero. |
| 210 | */ |
| 211 | static void th_memcpy(void *dest, const void *src, size_t n){ |
| 212 | if( n>0 ) memcpy(dest,src,n); |
| 213 | } |
| 214 | |
| 215 | /* |
| 216 | ** Append nAdd bytes of content copied from zAdd to the end of buffer |
| 217 | ** pBuffer. If there is not enough space currently allocated, resize |
| 218 | ** the allocation to make space. |
| @@ -219,40 +234,46 @@ | |
| 219 | */ |
| 220 | static void thBufferWriteResize( |
| 221 | Th_Interp *interp, |
| 222 | Buffer *pBuffer, |
| 223 | const char *zAdd, |
| 224 | int nAdd |
| 225 | ){ |
| 226 | int nNew = (pBuffer->nBuf+nAdd)*2+32; |
| 227 | #if defined(TH_MEMDEBUG) |
| 228 | char *zNew = (char *)Th_Malloc(interp, nNew); |
| 229 | th_memcpy(zNew, pBuffer->zBuf, pBuffer->nBuf); |
| 230 | Th_Free(interp, pBuffer->zBuf); |
| 231 | pBuffer->zBuf = zNew; |
| 232 | #else |
| 233 | int nOld = pBuffer->nBufAlloc; |
| 234 | pBuffer->zBuf = Th_Realloc(interp, pBuffer->zBuf, nNew); |
| 235 | memset(pBuffer->zBuf+nOld, 0, nNew-nOld); |
| 236 | #endif |
| 237 | pBuffer->nBufAlloc = nNew; |
| 238 | th_memcpy(&pBuffer->zBuf[pBuffer->nBuf], zAdd, nAdd); |
| 239 | pBuffer->nBuf += nAdd; |
| 240 | } |
| 241 | static void thBufferWriteFast( |
| 242 | Th_Interp *interp, |
| 243 | Buffer *pBuffer, |
| 244 | const char *zAdd, |
| 245 | int nAdd |
| 246 | ){ |
| 247 | if( pBuffer->nBuf+nAdd > pBuffer->nBufAlloc ){ |
| 248 | thBufferWriteResize(interp, pBuffer, zAdd, nAdd); |
| 249 | }else{ |
| 250 | if( pBuffer->zBuf ){ |
| 251 | memcpy(pBuffer->zBuf + pBuffer->nBuf, zAdd, nAdd); |
| 252 | } |
| 253 | pBuffer->nBuf += nAdd; |
| 254 | } |
| 255 | } |
| 256 | #define thBufferWrite(a,b,c,d) thBufferWriteFast(a,b,(const char *)c,d) |
| 257 | |
| 258 | /* |
| @@ -704,24 +725,25 @@ | |
| 704 | int nWord |
| 705 | ){ |
| 706 | int rc = TH_OK; |
| 707 | Buffer output; |
| 708 | int i; |
| 709 | |
| 710 | thBufferInit(&output); |
| 711 | |
| 712 | if( nWord>1 && (zWord[0]=='{' && zWord[nWord-1]=='}') ){ |
| 713 | thBufferWrite(interp, &output, &zWord[1], nWord-2); |
| 714 | }else{ |
| 715 | |
| 716 | /* If the word is surrounded by double-quotes strip these away. */ |
| 717 | if( nWord>1 && (zWord[0]=='"' && zWord[nWord-1]=='"') ){ |
| 718 | zWord++; |
| 719 | nWord -= 2; |
| 720 | } |
| 721 | |
| 722 | for(i=0; rc==TH_OK && i<nWord; i++){ |
| 723 | int nGet; |
| 724 | |
| 725 | int (*xGet)(Th_Interp *, const char*, int, int *) = 0; |
| 726 | int (*xSubst)(Th_Interp *, const char*, int) = 0; |
| 727 | |
| @@ -743,11 +765,11 @@ | |
| 743 | thBufferAddChar(interp, &output, zWord[i]); |
| 744 | continue; /* Go to the next iteration of the for(...) loop */ |
| 745 | } |
| 746 | } |
| 747 | |
| 748 | rc = xGet(interp, &zWord[i], nWord-i, &nGet); |
| 749 | if( rc==TH_OK ){ |
| 750 | rc = xSubst(interp, &zWord[i], nGet); |
| 751 | } |
| 752 | if( rc==TH_OK ){ |
| 753 | const char *zRes; |
| @@ -758,11 +780,11 @@ | |
| 758 | } |
| 759 | } |
| 760 | } |
| 761 | |
| 762 | if( rc==TH_OK ){ |
| 763 | Th_SetResult(interp, output.zBuf, output.nBuf); |
| 764 | } |
| 765 | thBufferFree(interp, &output); |
| 766 | return rc; |
| 767 | } |
| 768 | |
| @@ -826,11 +848,11 @@ | |
| 826 | Buffer strbuf; |
| 827 | Buffer lenbuf; |
| 828 | int nCount = 0; |
| 829 | |
| 830 | const char *zInput = zList; |
| 831 | int nInput = nList; |
| 832 | |
| 833 | thBufferInit(&strbuf); |
| 834 | thBufferInit(&lenbuf); |
| 835 | |
| 836 | while( nInput>0 ){ |
| @@ -837,19 +859,19 @@ | |
| 837 | const char *zWord; |
| 838 | int nWord; |
| 839 | |
| 840 | thNextSpace(interp, zInput, nInput, &nWord); |
| 841 | zInput += nWord; |
| 842 | nInput = nList-(zInput-zList); |
| 843 | |
| 844 | if( TH_OK!=(rc = thNextWord(interp, zInput, nInput, &nWord, 0)) |
| 845 | || TH_OK!=(rc = thSubstWord(interp, zInput, nWord)) |
| 846 | ){ |
| 847 | goto finish; |
| 848 | } |
| 849 | zInput = &zInput[nWord]; |
| 850 | nInput = nList-(zInput-zList); |
| 851 | if( nWord>0 ){ |
| 852 | zWord = Th_GetResult(interp, &nWord); |
| 853 | thBufferWrite(interp, &strbuf, zWord, nWord); |
| 854 | thBufferAddChar(interp, &strbuf, 0); |
| 855 | thBufferWrite(interp, &lenbuf, &nWord, sizeof(int)); |
| @@ -872,11 +894,11 @@ | |
| 872 | zElem = (char *)&anElem[nCount]; |
| 873 | th_memcpy(anElem, lenbuf.zBuf, lenbuf.nBuf); |
| 874 | th_memcpy(zElem, strbuf.zBuf, strbuf.nBuf); |
| 875 | for(i=0; i<nCount;i++){ |
| 876 | azElem[i] = zElem; |
| 877 | zElem += (anElem[i] + 1); |
| 878 | } |
| 879 | *pazElem = azElem; |
| 880 | *panElem = anElem; |
| 881 | } |
| 882 | if( pnCount ){ |
| @@ -894,12 +916,17 @@ | |
| 894 | ** in the current stack frame. |
| 895 | */ |
| 896 | static int thEvalLocal(Th_Interp *interp, const char *zProgram, int nProgram){ |
| 897 | int rc = TH_OK; |
| 898 | const char *zInput = zProgram; |
| 899 | int nInput = nProgram; |
| 900 | |
| 901 | while( rc==TH_OK && nInput ){ |
| 902 | Th_HashEntry *pEntry; |
| 903 | int nSpace; |
| 904 | const char *zFirst; |
| 905 | |
| @@ -949,13 +976,13 @@ | |
| 949 | if( rc!=TH_OK ) continue; |
| 950 | |
| 951 | if( argc>0 ){ |
| 952 | |
| 953 | /* Look up the command name in the command hash-table. */ |
| 954 | pEntry = Th_HashFind(interp, interp->paCmd, argv[0], argl[0], 0); |
| 955 | if( !pEntry ){ |
| 956 | Th_ErrorMessage(interp, "no such command: ", argv[0], argl[0]); |
| 957 | rc = TH_ERROR; |
| 958 | } |
| 959 | |
| 960 | /* Call the command procedure. */ |
| 961 | if( rc==TH_OK ){ |
| @@ -1053,10 +1080,12 @@ | |
| 1053 | }else{ |
| 1054 | int nInput = nProgram; |
| 1055 | |
| 1056 | if( nInput<0 ){ |
| 1057 | nInput = th_strlen(zProgram); |
| 1058 | } |
| 1059 | rc = thEvalLocal(interp, zProgram, nInput); |
| 1060 | } |
| 1061 | |
| 1062 | interp->pFrame = pSavedFrame; |
| @@ -1095,10 +1124,12 @@ | |
| 1095 | int isGlobal = 0; |
| 1096 | int i; |
| 1097 | |
| 1098 | if( nVarname<0 ){ |
| 1099 | nVarname = th_strlen(zVarname); |
| 1100 | } |
| 1101 | nOuter = nVarname; |
| 1102 | |
| 1103 | /* If the variable name starts with "::", then do the lookup is in the |
| 1104 | ** uppermost (global) frame. |
| @@ -1271,31 +1302,10 @@ | |
| 1271 | } |
| 1272 | |
| 1273 | return Th_SetResult(interp, pValue->zData, pValue->nData); |
| 1274 | } |
| 1275 | |
| 1276 | /* |
| 1277 | ** If interp has a variable with the given name, its value is returned |
| 1278 | ** and its length is returned via *nOut if nOut is not NULL. If |
| 1279 | ** interp has no such var then NULL is returned without setting any |
| 1280 | ** error state and *nOut, if not NULL, is set to -1. The returned value |
| 1281 | ** is owned by the interpreter and may be invalidated the next time |
| 1282 | ** the interpreter is modified. |
| 1283 | */ |
| 1284 | const char * Th_MaybeGetVar(Th_Interp *interp, const char *zVarName, |
| 1285 | int *nOut){ |
| 1286 | Th_Variable *pValue; |
| 1287 | |
| 1288 | pValue = thFindValue(interp, zVarName, -1, 0, 0, 1, 0); |
| 1289 | if( !pValue || !pValue->zData ){ |
| 1290 | if( nOut!=0 ) *nOut = -1; |
| 1291 | return NULL; |
| 1292 | } |
| 1293 | if( nOut!=0 ) *nOut = pValue->nData; |
| 1294 | return pValue->zData; |
| 1295 | } |
| 1296 | |
| 1297 | /* |
| 1298 | ** Return true if variable (zVar, nVar) exists. |
| 1299 | */ |
| 1300 | int Th_ExistsVar(Th_Interp *interp, const char *zVar, int nVar){ |
| 1301 | Th_Variable *pValue = thFindValue(interp, zVar, nVar, 0, 1, 1, 0); |
| @@ -1324,28 +1334,32 @@ | |
| 1324 | int nVar, |
| 1325 | const char *zValue, |
| 1326 | int nValue |
| 1327 | ){ |
| 1328 | Th_Variable *pValue; |
| 1329 | |
| 1330 | pValue = thFindValue(interp, zVar, nVar, 1, 0, 0, 0); |
| 1331 | if( !pValue ){ |
| 1332 | return TH_ERROR; |
| 1333 | } |
| 1334 | |
| 1335 | if( nValue<0 ){ |
| 1336 | nValue = th_strlen(zValue); |
| 1337 | } |
| 1338 | if( pValue->zData ){ |
| 1339 | Th_Free(interp, pValue->zData); |
| 1340 | pValue->zData = 0; |
| 1341 | } |
| 1342 | |
| 1343 | assert(zValue || nValue==0); |
| 1344 | pValue->zData = Th_Malloc(interp, nValue+1); |
| 1345 | pValue->zData[nValue] = '\0'; |
| 1346 | th_memcpy(pValue->zData, zValue, nValue); |
| 1347 | pValue->nData = nValue; |
| 1348 | |
| 1349 | return TH_OK; |
| 1350 | } |
| 1351 | |
| @@ -1458,10 +1472,12 @@ | |
| 1458 | */ |
| 1459 | char *th_strdup(Th_Interp *interp, const char *z, int n){ |
| 1460 | char *zRes; |
| 1461 | if( n<0 ){ |
| 1462 | n = th_strlen(z); |
| 1463 | } |
| 1464 | zRes = Th_Malloc(interp, n+1); |
| 1465 | th_memcpy(zRes, z, n); |
| 1466 | zRes[n] = '\0'; |
| 1467 | return zRes; |
| @@ -1519,13 +1535,14 @@ | |
| 1519 | n = th_strlen(z); |
| 1520 | } |
| 1521 | |
| 1522 | if( z && n>0 ){ |
| 1523 | char *zResult; |
| 1524 | zResult = Th_Malloc(pInterp, n+1); |
| 1525 | th_memcpy(zResult, z, n); |
| 1526 | zResult[n] = '\0'; |
| 1527 | pInterp->zResult = zResult; |
| 1528 | pInterp->nResult = n; |
| 1529 | } |
| 1530 | |
| 1531 | return TH_OK; |
| @@ -1834,24 +1851,28 @@ | |
| 1834 | int *pnStr, /* IN/OUT: Current length of *pzStr */ |
| 1835 | const char *zElem, /* Data to append */ |
| 1836 | int nElem /* Length of nElem */ |
| 1837 | ){ |
| 1838 | char *zNew; |
| 1839 | int nNew; |
| 1840 | |
| 1841 | if( nElem<0 ){ |
| 1842 | nElem = th_strlen(zElem); |
| 1843 | } |
| 1844 | |
| 1845 | nNew = *pnStr + nElem; |
| 1846 | zNew = Th_Malloc(interp, nNew); |
| 1847 | th_memcpy(zNew, *pzStr, *pnStr); |
| 1848 | th_memcpy(&zNew[*pnStr], zElem, nElem); |
| 1849 | |
| 1850 | Th_Free(interp, *pzStr); |
| 1851 | *pzStr = zNew; |
| 1852 | *pnStr = nNew; |
| 1853 | |
| 1854 | return TH_OK; |
| 1855 | } |
| 1856 | |
| 1857 | /* |
| @@ -2456,10 +2477,12 @@ | |
| 2456 | int nToken = 0; |
| 2457 | Expr **apToken = 0; |
| 2458 | |
| 2459 | if( nExpr<0 ){ |
| 2460 | nExpr = th_strlen(zExpr); |
| 2461 | } |
| 2462 | |
| 2463 | /* Parse the expression to a list of tokens. */ |
| 2464 | rc = exprParse(interp, zExpr, nExpr, &apToken, &nToken); |
| 2465 | |
| @@ -2567,10 +2590,12 @@ | |
| 2567 | Th_HashEntry *pRet; |
| 2568 | Th_HashEntry **ppRet; |
| 2569 | |
| 2570 | if( nKey<0 ){ |
| 2571 | nKey = th_strlen(zKey); |
| 2572 | } |
| 2573 | |
| 2574 | for(i=0; i<nKey; i++){ |
| 2575 | iKey = (iKey<<3) ^ iKey ^ zKey[i]; |
| 2576 | } |
| @@ -2800,10 +2825,12 @@ | |
| 2800 | int base = 10; |
| 2801 | int (*isdigit)(char) = th_isdigit; |
| 2802 | |
| 2803 | if( n<0 ){ |
| 2804 | n = th_strlen(z); |
| 2805 | } |
| 2806 | |
| 2807 | if( n>1 && (z[0]=='-' || z[0]=='+') ){ |
| 2808 | i = 1; |
| 2809 | } |
| @@ -2859,11 +2886,11 @@ | |
| 2859 | const char *z, |
| 2860 | int n, |
| 2861 | double *pfOut |
| 2862 | ){ |
| 2863 | if( !sqlite3IsNumber((const char *)z, 0) ){ |
| 2864 | Th_ErrorMessage(interp, "expected number, got: \"", z, n); |
| 2865 | return TH_ERROR; |
| 2866 | } |
| 2867 | |
| 2868 | sqlite3AtoF((const char *)z, pfOut); |
| 2869 | return TH_OK; |
| 2870 |
| --- src/th.c | |
| +++ src/th.c | |
| @@ -7,10 +7,16 @@ | |
| 7 | #include "config.h" |
| 8 | #include "th.h" |
| 9 | #include <string.h> |
| 10 | #include <assert.h> |
| 11 | |
| 12 | /* |
| 13 | ** External routines |
| 14 | */ |
| 15 | void fossil_panic(const char*,...); |
| 16 | void fossil_errorlog(const char*,...); |
| 17 | |
| 18 | /* |
| 19 | ** Values used for element values in the tcl_platform array. |
| 20 | */ |
| 21 | |
| 22 | #if !defined(TH_ENGINE) |
| @@ -197,10 +203,11 @@ | |
| 203 | */ |
| 204 | struct Buffer { |
| 205 | char *zBuf; |
| 206 | int nBuf; |
| 207 | int nBufAlloc; |
| 208 | int bTaint; |
| 209 | }; |
| 210 | typedef struct Buffer Buffer; |
| 211 | static void thBufferInit(Buffer *); |
| 212 | static void thBufferFree(Th_Interp *interp, Buffer *); |
| 213 | |
| @@ -209,10 +216,18 @@ | |
| 216 | ** be NULL as long as the number of bytes to copy is zero. |
| 217 | */ |
| 218 | static void th_memcpy(void *dest, const void *src, size_t n){ |
| 219 | if( n>0 ) memcpy(dest,src,n); |
| 220 | } |
| 221 | |
| 222 | /* |
| 223 | ** An oversized string has been encountered. Do not try to recover. |
| 224 | ** Panic the process. |
| 225 | */ |
| 226 | void Th_OversizeString(void){ |
| 227 | fossil_panic("string too large. maximum size 286MB."); |
| 228 | } |
| 229 | |
| 230 | /* |
| 231 | ** Append nAdd bytes of content copied from zAdd to the end of buffer |
| 232 | ** pBuffer. If there is not enough space currently allocated, resize |
| 233 | ** the allocation to make space. |
| @@ -219,40 +234,46 @@ | |
| 234 | */ |
| 235 | static void thBufferWriteResize( |
| 236 | Th_Interp *interp, |
| 237 | Buffer *pBuffer, |
| 238 | const char *zAdd, |
| 239 | int nAddX |
| 240 | ){ |
| 241 | int nAdd = TH1_LEN(nAddX); |
| 242 | int nNew = (pBuffer->nBuf+nAdd)*2+32; |
| 243 | #if defined(TH_MEMDEBUG) |
| 244 | char *zNew = (char *)Th_Malloc(interp, nNew); |
| 245 | TH1_SIZECHECK(nNew); |
| 246 | th_memcpy(zNew, pBuffer->zBuf, pBuffer->nBuf); |
| 247 | Th_Free(interp, pBuffer->zBuf); |
| 248 | pBuffer->zBuf = zNew; |
| 249 | #else |
| 250 | int nOld = pBuffer->nBufAlloc; |
| 251 | TH1_SIZECHECK(nNew); |
| 252 | pBuffer->zBuf = Th_Realloc(interp, pBuffer->zBuf, nNew); |
| 253 | memset(pBuffer->zBuf+nOld, 0, nNew-nOld); |
| 254 | #endif |
| 255 | pBuffer->nBufAlloc = nNew; |
| 256 | th_memcpy(&pBuffer->zBuf[pBuffer->nBuf], zAdd, nAdd); |
| 257 | pBuffer->nBuf += nAdd; |
| 258 | TH1_XFER_TAINT(pBuffer->bTaint, nAddX); |
| 259 | } |
| 260 | static void thBufferWriteFast( |
| 261 | Th_Interp *interp, |
| 262 | Buffer *pBuffer, |
| 263 | const char *zAdd, |
| 264 | int nAddX |
| 265 | ){ |
| 266 | int nAdd = TH1_LEN(nAddX); |
| 267 | if( pBuffer->nBuf+nAdd > pBuffer->nBufAlloc ){ |
| 268 | thBufferWriteResize(interp, pBuffer, zAdd, nAddX); |
| 269 | }else{ |
| 270 | if( pBuffer->zBuf ){ |
| 271 | memcpy(pBuffer->zBuf + pBuffer->nBuf, zAdd, nAdd); |
| 272 | } |
| 273 | pBuffer->nBuf += nAdd; |
| 274 | TH1_XFER_TAINT(pBuffer->bTaint, nAddX); |
| 275 | } |
| 276 | } |
| 277 | #define thBufferWrite(a,b,c,d) thBufferWriteFast(a,b,(const char *)c,d) |
| 278 | |
| 279 | /* |
| @@ -704,24 +725,25 @@ | |
| 725 | int nWord |
| 726 | ){ |
| 727 | int rc = TH_OK; |
| 728 | Buffer output; |
| 729 | int i; |
| 730 | int nn = TH1_LEN(nWord); |
| 731 | |
| 732 | thBufferInit(&output); |
| 733 | |
| 734 | if( nn>1 && (zWord[0]=='{' && zWord[nn-1]=='}') ){ |
| 735 | thBufferWrite(interp, &output, &zWord[1], nn-2); |
| 736 | }else{ |
| 737 | |
| 738 | /* If the word is surrounded by double-quotes strip these away. */ |
| 739 | if( nn>1 && (zWord[0]=='"' && zWord[nn-1]=='"') ){ |
| 740 | zWord++; |
| 741 | nn -= 2; |
| 742 | } |
| 743 | |
| 744 | for(i=0; rc==TH_OK && i<nn; i++){ |
| 745 | int nGet; |
| 746 | |
| 747 | int (*xGet)(Th_Interp *, const char*, int, int *) = 0; |
| 748 | int (*xSubst)(Th_Interp *, const char*, int) = 0; |
| 749 | |
| @@ -743,11 +765,11 @@ | |
| 765 | thBufferAddChar(interp, &output, zWord[i]); |
| 766 | continue; /* Go to the next iteration of the for(...) loop */ |
| 767 | } |
| 768 | } |
| 769 | |
| 770 | rc = xGet(interp, &zWord[i], nn-i, &nGet); |
| 771 | if( rc==TH_OK ){ |
| 772 | rc = xSubst(interp, &zWord[i], nGet); |
| 773 | } |
| 774 | if( rc==TH_OK ){ |
| 775 | const char *zRes; |
| @@ -758,11 +780,11 @@ | |
| 780 | } |
| 781 | } |
| 782 | } |
| 783 | |
| 784 | if( rc==TH_OK ){ |
| 785 | Th_SetResult(interp, output.zBuf, output.nBuf|output.bTaint); |
| 786 | } |
| 787 | thBufferFree(interp, &output); |
| 788 | return rc; |
| 789 | } |
| 790 | |
| @@ -826,11 +848,11 @@ | |
| 848 | Buffer strbuf; |
| 849 | Buffer lenbuf; |
| 850 | int nCount = 0; |
| 851 | |
| 852 | const char *zInput = zList; |
| 853 | int nInput = TH1_LEN(nList); |
| 854 | |
| 855 | thBufferInit(&strbuf); |
| 856 | thBufferInit(&lenbuf); |
| 857 | |
| 858 | while( nInput>0 ){ |
| @@ -837,19 +859,19 @@ | |
| 859 | const char *zWord; |
| 860 | int nWord; |
| 861 | |
| 862 | thNextSpace(interp, zInput, nInput, &nWord); |
| 863 | zInput += nWord; |
| 864 | nInput = TH1_LEN(nList)-(zInput-zList); |
| 865 | |
| 866 | if( TH_OK!=(rc = thNextWord(interp, zInput, nInput, &nWord, 0)) |
| 867 | || TH_OK!=(rc = thSubstWord(interp, zInput, nWord)) |
| 868 | ){ |
| 869 | goto finish; |
| 870 | } |
| 871 | zInput = &zInput[TH1_LEN(nWord)]; |
| 872 | nInput = TH1_LEN(nList)-(zInput-zList); |
| 873 | if( nWord>0 ){ |
| 874 | zWord = Th_GetResult(interp, &nWord); |
| 875 | thBufferWrite(interp, &strbuf, zWord, nWord); |
| 876 | thBufferAddChar(interp, &strbuf, 0); |
| 877 | thBufferWrite(interp, &lenbuf, &nWord, sizeof(int)); |
| @@ -872,11 +894,11 @@ | |
| 894 | zElem = (char *)&anElem[nCount]; |
| 895 | th_memcpy(anElem, lenbuf.zBuf, lenbuf.nBuf); |
| 896 | th_memcpy(zElem, strbuf.zBuf, strbuf.nBuf); |
| 897 | for(i=0; i<nCount;i++){ |
| 898 | azElem[i] = zElem; |
| 899 | zElem += (TH1_LEN(anElem[i]) + 1); |
| 900 | } |
| 901 | *pazElem = azElem; |
| 902 | *panElem = anElem; |
| 903 | } |
| 904 | if( pnCount ){ |
| @@ -894,12 +916,17 @@ | |
| 916 | ** in the current stack frame. |
| 917 | */ |
| 918 | static int thEvalLocal(Th_Interp *interp, const char *zProgram, int nProgram){ |
| 919 | int rc = TH_OK; |
| 920 | const char *zInput = zProgram; |
| 921 | int nInput = TH1_LEN(nProgram); |
| 922 | |
| 923 | if( TH1_TAINTED(nProgram) |
| 924 | && Th_ReportTaint(interp, "script", zProgram, nProgram) |
| 925 | ){ |
| 926 | return TH_ERROR; |
| 927 | } |
| 928 | while( rc==TH_OK && nInput ){ |
| 929 | Th_HashEntry *pEntry; |
| 930 | int nSpace; |
| 931 | const char *zFirst; |
| 932 | |
| @@ -949,13 +976,13 @@ | |
| 976 | if( rc!=TH_OK ) continue; |
| 977 | |
| 978 | if( argc>0 ){ |
| 979 | |
| 980 | /* Look up the command name in the command hash-table. */ |
| 981 | pEntry = Th_HashFind(interp, interp->paCmd, argv[0], TH1_LEN(argl[0]),0); |
| 982 | if( !pEntry ){ |
| 983 | Th_ErrorMessage(interp, "no such command: ", argv[0], TH1_LEN(argl[0])); |
| 984 | rc = TH_ERROR; |
| 985 | } |
| 986 | |
| 987 | /* Call the command procedure. */ |
| 988 | if( rc==TH_OK ){ |
| @@ -1053,10 +1080,12 @@ | |
| 1080 | }else{ |
| 1081 | int nInput = nProgram; |
| 1082 | |
| 1083 | if( nInput<0 ){ |
| 1084 | nInput = th_strlen(zProgram); |
| 1085 | }else{ |
| 1086 | nInput = TH1_LEN(nInput); |
| 1087 | } |
| 1088 | rc = thEvalLocal(interp, zProgram, nInput); |
| 1089 | } |
| 1090 | |
| 1091 | interp->pFrame = pSavedFrame; |
| @@ -1095,10 +1124,12 @@ | |
| 1124 | int isGlobal = 0; |
| 1125 | int i; |
| 1126 | |
| 1127 | if( nVarname<0 ){ |
| 1128 | nVarname = th_strlen(zVarname); |
| 1129 | }else{ |
| 1130 | nVarname = TH1_LEN(nVarname); |
| 1131 | } |
| 1132 | nOuter = nVarname; |
| 1133 | |
| 1134 | /* If the variable name starts with "::", then do the lookup is in the |
| 1135 | ** uppermost (global) frame. |
| @@ -1271,31 +1302,10 @@ | |
| 1302 | } |
| 1303 | |
| 1304 | return Th_SetResult(interp, pValue->zData, pValue->nData); |
| 1305 | } |
| 1306 | |
| 1307 | /* |
| 1308 | ** Return true if variable (zVar, nVar) exists. |
| 1309 | */ |
| 1310 | int Th_ExistsVar(Th_Interp *interp, const char *zVar, int nVar){ |
| 1311 | Th_Variable *pValue = thFindValue(interp, zVar, nVar, 0, 1, 1, 0); |
| @@ -1324,28 +1334,32 @@ | |
| 1334 | int nVar, |
| 1335 | const char *zValue, |
| 1336 | int nValue |
| 1337 | ){ |
| 1338 | Th_Variable *pValue; |
| 1339 | int nn; |
| 1340 | |
| 1341 | nVar = TH1_LEN(nVar); |
| 1342 | pValue = thFindValue(interp, zVar, nVar, 1, 0, 0, 0); |
| 1343 | if( !pValue ){ |
| 1344 | return TH_ERROR; |
| 1345 | } |
| 1346 | |
| 1347 | if( nValue<0 ){ |
| 1348 | nn = th_strlen(zValue); |
| 1349 | }else{ |
| 1350 | nn = TH1_LEN(nValue); |
| 1351 | } |
| 1352 | if( pValue->zData ){ |
| 1353 | Th_Free(interp, pValue->zData); |
| 1354 | pValue->zData = 0; |
| 1355 | } |
| 1356 | |
| 1357 | assert(zValue || nn==0); |
| 1358 | pValue->zData = Th_Malloc(interp, nn+1); |
| 1359 | pValue->zData[nn] = '\0'; |
| 1360 | th_memcpy(pValue->zData, zValue, nn); |
| 1361 | pValue->nData = nValue; |
| 1362 | |
| 1363 | return TH_OK; |
| 1364 | } |
| 1365 | |
| @@ -1458,10 +1472,12 @@ | |
| 1472 | */ |
| 1473 | char *th_strdup(Th_Interp *interp, const char *z, int n){ |
| 1474 | char *zRes; |
| 1475 | if( n<0 ){ |
| 1476 | n = th_strlen(z); |
| 1477 | }else{ |
| 1478 | n = TH1_LEN(n); |
| 1479 | } |
| 1480 | zRes = Th_Malloc(interp, n+1); |
| 1481 | th_memcpy(zRes, z, n); |
| 1482 | zRes[n] = '\0'; |
| 1483 | return zRes; |
| @@ -1519,13 +1535,14 @@ | |
| 1535 | n = th_strlen(z); |
| 1536 | } |
| 1537 | |
| 1538 | if( z && n>0 ){ |
| 1539 | char *zResult; |
| 1540 | int nn = TH1_LEN(n); |
| 1541 | zResult = Th_Malloc(pInterp, nn+1); |
| 1542 | th_memcpy(zResult, z, nn); |
| 1543 | zResult[nn] = '\0'; |
| 1544 | pInterp->zResult = zResult; |
| 1545 | pInterp->nResult = n; |
| 1546 | } |
| 1547 | |
| 1548 | return TH_OK; |
| @@ -1834,24 +1851,28 @@ | |
| 1851 | int *pnStr, /* IN/OUT: Current length of *pzStr */ |
| 1852 | const char *zElem, /* Data to append */ |
| 1853 | int nElem /* Length of nElem */ |
| 1854 | ){ |
| 1855 | char *zNew; |
| 1856 | long long int nNew; |
| 1857 | int nn; |
| 1858 | |
| 1859 | if( nElem<0 ){ |
| 1860 | nn = th_strlen(zElem); |
| 1861 | }else{ |
| 1862 | nn = TH1_LEN(nElem); |
| 1863 | } |
| 1864 | |
| 1865 | nNew = TH1_LEN(*pnStr) + nn; |
| 1866 | TH1_SIZECHECK(nNew); |
| 1867 | zNew = Th_Malloc(interp, nNew); |
| 1868 | th_memcpy(zNew, *pzStr, *pnStr); |
| 1869 | th_memcpy(&zNew[TH1_LEN(*pnStr)], zElem, nn); |
| 1870 | |
| 1871 | Th_Free(interp, *pzStr); |
| 1872 | *pzStr = zNew; |
| 1873 | *pnStr = (int)nNew; |
| 1874 | |
| 1875 | return TH_OK; |
| 1876 | } |
| 1877 | |
| 1878 | /* |
| @@ -2456,10 +2477,12 @@ | |
| 2477 | int nToken = 0; |
| 2478 | Expr **apToken = 0; |
| 2479 | |
| 2480 | if( nExpr<0 ){ |
| 2481 | nExpr = th_strlen(zExpr); |
| 2482 | }else{ |
| 2483 | nExpr = TH1_LEN(nExpr); |
| 2484 | } |
| 2485 | |
| 2486 | /* Parse the expression to a list of tokens. */ |
| 2487 | rc = exprParse(interp, zExpr, nExpr, &apToken, &nToken); |
| 2488 | |
| @@ -2567,10 +2590,12 @@ | |
| 2590 | Th_HashEntry *pRet; |
| 2591 | Th_HashEntry **ppRet; |
| 2592 | |
| 2593 | if( nKey<0 ){ |
| 2594 | nKey = th_strlen(zKey); |
| 2595 | }else{ |
| 2596 | nKey = TH1_LEN(nKey); |
| 2597 | } |
| 2598 | |
| 2599 | for(i=0; i<nKey; i++){ |
| 2600 | iKey = (iKey<<3) ^ iKey ^ zKey[i]; |
| 2601 | } |
| @@ -2800,10 +2825,12 @@ | |
| 2825 | int base = 10; |
| 2826 | int (*isdigit)(char) = th_isdigit; |
| 2827 | |
| 2828 | if( n<0 ){ |
| 2829 | n = th_strlen(z); |
| 2830 | }else{ |
| 2831 | n = TH1_LEN(n); |
| 2832 | } |
| 2833 | |
| 2834 | if( n>1 && (z[0]=='-' || z[0]=='+') ){ |
| 2835 | i = 1; |
| 2836 | } |
| @@ -2859,11 +2886,11 @@ | |
| 2886 | const char *z, |
| 2887 | int n, |
| 2888 | double *pfOut |
| 2889 | ){ |
| 2890 | if( !sqlite3IsNumber((const char *)z, 0) ){ |
| 2891 | Th_ErrorMessage(interp, "expected number, got: \"", z, TH1_LEN(n)); |
| 2892 | return TH_ERROR; |
| 2893 | } |
| 2894 | |
| 2895 | sqlite3AtoF((const char *)z, pfOut); |
| 2896 | return TH_OK; |
| 2897 |
M
src/th.h
+53
-14
| --- src/th.h | ||
| +++ src/th.h | ||
| @@ -1,10 +1,56 @@ | ||
| 1 | - | |
| 2 | 1 | /* This header file defines the external interface to the custom Scripting |
| 3 | 2 | ** Language (TH) interpreter. TH is very similar to Tcl but is not an |
| 4 | 3 | ** exact clone. |
| 4 | +** | |
| 5 | +** TH1 was original developed to run SQLite tests on SymbianOS. This version | |
| 6 | +** of TH1 was repurposed as a scripted language for Fossil, and was heavily | |
| 7 | +** modified for that purpose, beginning in early 2008. | |
| 8 | +** | |
| 9 | +** More recently, TH1 has been enhanced to distinguish between regular text | |
| 10 | +** and "tainted" text. "Tainted" text is text that might have originated | |
| 11 | +** from an outside source and hence might not be trustworthy. To prevent | |
| 12 | +** cross-site scripting (XSS) and SQL-injections and similar attacks, | |
| 13 | +** tainted text should not be used for the following purposes: | |
| 14 | +** | |
| 15 | +** * executed as TH1 script or expression. | |
| 16 | +** * output as HTML or Javascript | |
| 17 | +** * used as part of an SQL query | |
| 18 | +** | |
| 19 | +** Tainted text can be converted into a safe form using commands like | |
| 20 | +** "htmlize". And some commands ("query" and "expr") know how to use | |
| 21 | +** potentially tainted variable values directly, and thus can bypass | |
| 22 | +** the restrictions above. | |
| 23 | +** | |
| 24 | +** Whether a string is clean or tainted is determined by its length integer. | |
| 25 | +** TH1 limits strings to be no more than 0x0fffffff bytes bytes in length | |
| 26 | +** (about 268MB - more than sufficient for the purposes of Fossil). The top | |
| 27 | +** bit of the length integer is the sign bit, of course. The next three bits | |
| 28 | +** are reserved. One of those, the 0x10000000 bit, marks tainted strings. | |
| 5 | 29 | */ |
| 30 | +#define TH1_MX_STRLEN 0x0fffffff /* Maximum length of a TH1-C string */ | |
| 31 | +#define TH1_TAINT_BIT 0x10000000 /* The taint bit */ | |
| 32 | +#define TH1_SIGN 0x80000000 | |
| 33 | + | |
| 34 | +/* Convert an integer into a string length. Negative values remain negative */ | |
| 35 | +#define TH1_LEN(X) ((TH1_SIGN|TH1_MX_STRLEN)&(X)) | |
| 36 | + | |
| 37 | +/* Return true if the string is tainted */ | |
| 38 | +#define TH1_TAINTED(X) (((X)&TH1_TAINT_BIT)!=0) | |
| 39 | + | |
| 40 | +/* Remove taint from a string */ | |
| 41 | +#define TH1_RM_TAINT(X) ((X)&~TH1_TAINT_BIT) | |
| 42 | + | |
| 43 | +/* Add taint to a string */ | |
| 44 | +#define TH1_ADD_TAINT(X) ((X)|TH1_TAINT_BIT) | |
| 45 | + | |
| 46 | +/* If B is tainted, make A tainted too */ | |
| 47 | +#define TH1_XFER_TAINT(A,B) (A)|=(TH1_TAINT_BIT&(B)) | |
| 48 | + | |
| 49 | +/* Check to see if a string is too big for TH1 */ | |
| 50 | +#define TH1_SIZECHECK(N) if((N)>TH1_MX_STRLEN){Th_OversizeString();} | |
| 51 | +void Th_OversizeString(void); | |
| 6 | 52 | |
| 7 | 53 | /* |
| 8 | 54 | ** Before creating an interpreter, the application must allocate and |
| 9 | 55 | ** populate an instance of the following structure. It must remain valid |
| 10 | 56 | ** for the lifetime of the interpreter. |
| @@ -24,10 +70,16 @@ | ||
| 24 | 70 | ** Create and delete interpreters. |
| 25 | 71 | */ |
| 26 | 72 | Th_Interp * Th_CreateInterp(Th_Vtab *); |
| 27 | 73 | void Th_DeleteInterp(Th_Interp *); |
| 28 | 74 | |
| 75 | +/* | |
| 76 | +** Report taint in the string zStr,nStr. That string represents "zTitle" | |
| 77 | +** If non-zero is returned error out of the caller. | |
| 78 | +*/ | |
| 79 | +int Th_ReportTaint(Th_Interp*,const char*,const char*zStr,int nStr); | |
| 80 | + | |
| 29 | 81 | /* |
| 30 | 82 | ** Evaluate an TH program in the stack frame identified by parameter |
| 31 | 83 | ** iFrame, according to the following rules: |
| 32 | 84 | ** |
| 33 | 85 | ** * If iFrame is 0, this means the current frame. |
| @@ -56,23 +108,10 @@ | ||
| 56 | 108 | int Th_GetVar(Th_Interp *, const char *, int); |
| 57 | 109 | int Th_SetVar(Th_Interp *, const char *, int, const char *, int); |
| 58 | 110 | int Th_LinkVar(Th_Interp *, const char *, int, int, const char *, int); |
| 59 | 111 | int Th_UnsetVar(Th_Interp *, const char *, int); |
| 60 | 112 | |
| 61 | -/* | |
| 62 | -** If interp has a variable with the given name, its value is returned | |
| 63 | -** and its length is returned via *nOut if nOut is not NULL. If | |
| 64 | -** interp has no such var then NULL is returned without setting any | |
| 65 | -** error state and *nOut, if not NULL, is set to 0. The returned value | |
| 66 | -** is owned by the interpreter and may be invalidated the next time | |
| 67 | -** the interpreter is modified. | |
| 68 | -** | |
| 69 | -** zVarName must be NUL-terminated. | |
| 70 | -*/ | |
| 71 | -const char * Th_MaybeGetVar(Th_Interp *interp, const char *zVarName, | |
| 72 | - int *nOut); | |
| 73 | - | |
| 74 | 113 | typedef int (*Th_CommandProc)(Th_Interp *, void *, int, const char **, int *); |
| 75 | 114 | |
| 76 | 115 | /* |
| 77 | 116 | ** Register new commands. |
| 78 | 117 | */ |
| 79 | 118 |
| --- src/th.h | |
| +++ src/th.h | |
| @@ -1,10 +1,56 @@ | |
| 1 | |
| 2 | /* This header file defines the external interface to the custom Scripting |
| 3 | ** Language (TH) interpreter. TH is very similar to Tcl but is not an |
| 4 | ** exact clone. |
| 5 | */ |
| 6 | |
| 7 | /* |
| 8 | ** Before creating an interpreter, the application must allocate and |
| 9 | ** populate an instance of the following structure. It must remain valid |
| 10 | ** for the lifetime of the interpreter. |
| @@ -24,10 +70,16 @@ | |
| 24 | ** Create and delete interpreters. |
| 25 | */ |
| 26 | Th_Interp * Th_CreateInterp(Th_Vtab *); |
| 27 | void Th_DeleteInterp(Th_Interp *); |
| 28 | |
| 29 | /* |
| 30 | ** Evaluate an TH program in the stack frame identified by parameter |
| 31 | ** iFrame, according to the following rules: |
| 32 | ** |
| 33 | ** * If iFrame is 0, this means the current frame. |
| @@ -56,23 +108,10 @@ | |
| 56 | int Th_GetVar(Th_Interp *, const char *, int); |
| 57 | int Th_SetVar(Th_Interp *, const char *, int, const char *, int); |
| 58 | int Th_LinkVar(Th_Interp *, const char *, int, int, const char *, int); |
| 59 | int Th_UnsetVar(Th_Interp *, const char *, int); |
| 60 | |
| 61 | /* |
| 62 | ** If interp has a variable with the given name, its value is returned |
| 63 | ** and its length is returned via *nOut if nOut is not NULL. If |
| 64 | ** interp has no such var then NULL is returned without setting any |
| 65 | ** error state and *nOut, if not NULL, is set to 0. The returned value |
| 66 | ** is owned by the interpreter and may be invalidated the next time |
| 67 | ** the interpreter is modified. |
| 68 | ** |
| 69 | ** zVarName must be NUL-terminated. |
| 70 | */ |
| 71 | const char * Th_MaybeGetVar(Th_Interp *interp, const char *zVarName, |
| 72 | int *nOut); |
| 73 | |
| 74 | typedef int (*Th_CommandProc)(Th_Interp *, void *, int, const char **, int *); |
| 75 | |
| 76 | /* |
| 77 | ** Register new commands. |
| 78 | */ |
| 79 |
| --- src/th.h | |
| +++ src/th.h | |
| @@ -1,10 +1,56 @@ | |
| 1 | /* This header file defines the external interface to the custom Scripting |
| 2 | ** Language (TH) interpreter. TH is very similar to Tcl but is not an |
| 3 | ** exact clone. |
| 4 | ** |
| 5 | ** TH1 was original developed to run SQLite tests on SymbianOS. This version |
| 6 | ** of TH1 was repurposed as a scripted language for Fossil, and was heavily |
| 7 | ** modified for that purpose, beginning in early 2008. |
| 8 | ** |
| 9 | ** More recently, TH1 has been enhanced to distinguish between regular text |
| 10 | ** and "tainted" text. "Tainted" text is text that might have originated |
| 11 | ** from an outside source and hence might not be trustworthy. To prevent |
| 12 | ** cross-site scripting (XSS) and SQL-injections and similar attacks, |
| 13 | ** tainted text should not be used for the following purposes: |
| 14 | ** |
| 15 | ** * executed as TH1 script or expression. |
| 16 | ** * output as HTML or Javascript |
| 17 | ** * used as part of an SQL query |
| 18 | ** |
| 19 | ** Tainted text can be converted into a safe form using commands like |
| 20 | ** "htmlize". And some commands ("query" and "expr") know how to use |
| 21 | ** potentially tainted variable values directly, and thus can bypass |
| 22 | ** the restrictions above. |
| 23 | ** |
| 24 | ** Whether a string is clean or tainted is determined by its length integer. |
| 25 | ** TH1 limits strings to be no more than 0x0fffffff bytes bytes in length |
| 26 | ** (about 268MB - more than sufficient for the purposes of Fossil). The top |
| 27 | ** bit of the length integer is the sign bit, of course. The next three bits |
| 28 | ** are reserved. One of those, the 0x10000000 bit, marks tainted strings. |
| 29 | */ |
| 30 | #define TH1_MX_STRLEN 0x0fffffff /* Maximum length of a TH1-C string */ |
| 31 | #define TH1_TAINT_BIT 0x10000000 /* The taint bit */ |
| 32 | #define TH1_SIGN 0x80000000 |
| 33 | |
| 34 | /* Convert an integer into a string length. Negative values remain negative */ |
| 35 | #define TH1_LEN(X) ((TH1_SIGN|TH1_MX_STRLEN)&(X)) |
| 36 | |
| 37 | /* Return true if the string is tainted */ |
| 38 | #define TH1_TAINTED(X) (((X)&TH1_TAINT_BIT)!=0) |
| 39 | |
| 40 | /* Remove taint from a string */ |
| 41 | #define TH1_RM_TAINT(X) ((X)&~TH1_TAINT_BIT) |
| 42 | |
| 43 | /* Add taint to a string */ |
| 44 | #define TH1_ADD_TAINT(X) ((X)|TH1_TAINT_BIT) |
| 45 | |
| 46 | /* If B is tainted, make A tainted too */ |
| 47 | #define TH1_XFER_TAINT(A,B) (A)|=(TH1_TAINT_BIT&(B)) |
| 48 | |
| 49 | /* Check to see if a string is too big for TH1 */ |
| 50 | #define TH1_SIZECHECK(N) if((N)>TH1_MX_STRLEN){Th_OversizeString();} |
| 51 | void Th_OversizeString(void); |
| 52 | |
| 53 | /* |
| 54 | ** Before creating an interpreter, the application must allocate and |
| 55 | ** populate an instance of the following structure. It must remain valid |
| 56 | ** for the lifetime of the interpreter. |
| @@ -24,10 +70,16 @@ | |
| 70 | ** Create and delete interpreters. |
| 71 | */ |
| 72 | Th_Interp * Th_CreateInterp(Th_Vtab *); |
| 73 | void Th_DeleteInterp(Th_Interp *); |
| 74 | |
| 75 | /* |
| 76 | ** Report taint in the string zStr,nStr. That string represents "zTitle" |
| 77 | ** If non-zero is returned error out of the caller. |
| 78 | */ |
| 79 | int Th_ReportTaint(Th_Interp*,const char*,const char*zStr,int nStr); |
| 80 | |
| 81 | /* |
| 82 | ** Evaluate an TH program in the stack frame identified by parameter |
| 83 | ** iFrame, according to the following rules: |
| 84 | ** |
| 85 | ** * If iFrame is 0, this means the current frame. |
| @@ -56,23 +108,10 @@ | |
| 108 | int Th_GetVar(Th_Interp *, const char *, int); |
| 109 | int Th_SetVar(Th_Interp *, const char *, int, const char *, int); |
| 110 | int Th_LinkVar(Th_Interp *, const char *, int, int, const char *, int); |
| 111 | int Th_UnsetVar(Th_Interp *, const char *, int); |
| 112 | |
| 113 | typedef int (*Th_CommandProc)(Th_Interp *, void *, int, const char **, int *); |
| 114 | |
| 115 | /* |
| 116 | ** Register new commands. |
| 117 | */ |
| 118 |
+70
-48
| --- src/th_lang.c | ||
| +++ src/th_lang.c | ||
| @@ -39,11 +39,11 @@ | ||
| 39 | 39 | |
| 40 | 40 | rc = Th_Eval(interp, 0, argv[1], -1); |
| 41 | 41 | if( argc==3 ){ |
| 42 | 42 | int nResult; |
| 43 | 43 | const char *zResult = Th_GetResult(interp, &nResult); |
| 44 | - Th_SetVar(interp, argv[2], argl[2], zResult, nResult); | |
| 44 | + Th_SetVar(interp, argv[2], TH1_LEN(argl[2]), zResult, nResult); | |
| 45 | 45 | } |
| 46 | 46 | |
| 47 | 47 | Th_SetResultInt(interp, rc); |
| 48 | 48 | return TH_OK; |
| 49 | 49 | } |
| @@ -215,15 +215,18 @@ | ||
| 215 | 215 | int *argl |
| 216 | 216 | ){ |
| 217 | 217 | char *zList = 0; |
| 218 | 218 | int nList = 0; |
| 219 | 219 | int i; |
| 220 | + int bTaint = 0; | |
| 220 | 221 | |
| 221 | 222 | for(i=1; i<argc; i++){ |
| 223 | + TH1_XFER_TAINT(bTaint,argl[i]); | |
| 222 | 224 | Th_ListAppend(interp, &zList, &nList, argv[i], argl[i]); |
| 223 | 225 | } |
| 224 | 226 | |
| 227 | + TH1_XFER_TAINT(nList, bTaint); | |
| 225 | 228 | Th_SetResult(interp, zList, nList); |
| 226 | 229 | Th_Free(interp, zList); |
| 227 | 230 | |
| 228 | 231 | return TH_OK; |
| 229 | 232 | } |
| @@ -244,10 +247,11 @@ | ||
| 244 | 247 | int *argl |
| 245 | 248 | ){ |
| 246 | 249 | char *zList = 0; |
| 247 | 250 | int nList = 0; |
| 248 | 251 | int i, rc; |
| 252 | + int bTaint = 0; | |
| 249 | 253 | |
| 250 | 254 | if( argc<2 ){ |
| 251 | 255 | return Th_WrongNumArgs(interp, "lappend var ..."); |
| 252 | 256 | } |
| 253 | 257 | rc = Th_GetVar(interp, argv[1], argl[1]); |
| @@ -254,13 +258,15 @@ | ||
| 254 | 258 | if( rc==TH_OK ){ |
| 255 | 259 | zList = Th_TakeResult(interp, &nList); |
| 256 | 260 | } |
| 257 | 261 | |
| 258 | 262 | for(i=2; i<argc; i++){ |
| 263 | + TH1_XFER_TAINT(bTaint, argl[i]); | |
| 259 | 264 | Th_ListAppend(interp, &zList, &nList, argv[i], argl[i]); |
| 260 | 265 | } |
| 261 | 266 | |
| 267 | + TH1_XFER_TAINT(nList, bTaint); | |
| 262 | 268 | Th_SetVar(interp, argv[1], argl[1], zList, nList); |
| 263 | 269 | Th_SetResult(interp, zList, nList); |
| 264 | 270 | Th_Free(interp, zList); |
| 265 | 271 | |
| 266 | 272 | return TH_OK; |
| @@ -356,13 +362,14 @@ | ||
| 356 | 362 | return Th_WrongNumArgs(interp, "lsearch list string"); |
| 357 | 363 | } |
| 358 | 364 | |
| 359 | 365 | rc = Th_SplitList(interp, argv[1], argl[1], &azElem, &anElem, &nCount); |
| 360 | 366 | if( rc==TH_OK ){ |
| 367 | + int nn = TH1_LEN(argl[2]); | |
| 361 | 368 | Th_SetResultInt(interp, -1); |
| 362 | 369 | for(i=0; i<nCount; i++){ |
| 363 | - if( anElem[i]==argl[2] && 0==memcmp(azElem[i], argv[2], argl[2]) ){ | |
| 370 | + if( TH1_LEN(anElem[i])==nn && 0==memcmp(azElem[i], argv[2], nn) ){ | |
| 364 | 371 | Th_SetResultInt(interp, i); |
| 365 | 372 | break; |
| 366 | 373 | } |
| 367 | 374 | } |
| 368 | 375 | Th_Free(interp, azElem); |
| @@ -561,20 +568,21 @@ | ||
| 561 | 568 | int nUsage = 0; /* Number of bytes at zUsage */ |
| 562 | 569 | |
| 563 | 570 | if( argc!=4 ){ |
| 564 | 571 | return Th_WrongNumArgs(interp, "proc name arglist code"); |
| 565 | 572 | } |
| 566 | - if( Th_SplitList(interp, argv[2], argl[2], &azParam, &anParam, &nParam) ){ | |
| 573 | + if( Th_SplitList(interp, argv[2], TH1_LEN(argl[2]), | |
| 574 | + &azParam, &anParam, &nParam) ){ | |
| 567 | 575 | return TH_ERROR; |
| 568 | 576 | } |
| 569 | 577 | |
| 570 | 578 | /* Allocate the new ProcDefn structure. */ |
| 571 | 579 | nByte = sizeof(ProcDefn) + /* ProcDefn structure */ |
| 572 | 580 | (sizeof(char *) + sizeof(int)) * nParam + /* azParam, anParam */ |
| 573 | 581 | (sizeof(char *) + sizeof(int)) * nParam + /* azDefault, anDefault */ |
| 574 | - argl[3] + /* zProgram */ | |
| 575 | - argl[2]; /* Space for copies of parameter names and default values */ | |
| 582 | + TH1_LEN(argl[3]) + /* zProgram */ | |
| 583 | + TH1_LEN(argl[2]); /* Space for copies of param names and dflt values */ | |
| 576 | 584 | p = (ProcDefn *)Th_Malloc(interp, nByte); |
| 577 | 585 | |
| 578 | 586 | /* If the last parameter in the parameter list is "args", then set the |
| 579 | 587 | ** ProcDefn.hasArgs flag. The "args" parameter does not require an |
| 580 | 588 | ** entry in the ProcDefn.azParam[] or ProcDefn.azDefault[] arrays. |
| @@ -590,12 +598,12 @@ | ||
| 590 | 598 | p->azParam = (char **)&p[1]; |
| 591 | 599 | p->anParam = (int *)&p->azParam[nParam]; |
| 592 | 600 | p->azDefault = (char **)&p->anParam[nParam]; |
| 593 | 601 | p->anDefault = (int *)&p->azDefault[nParam]; |
| 594 | 602 | p->zProgram = (char *)&p->anDefault[nParam]; |
| 595 | - memcpy(p->zProgram, argv[3], argl[3]); | |
| 596 | - p->nProgram = argl[3]; | |
| 603 | + memcpy(p->zProgram, argv[3], TH1_LEN(argl[3])); | |
| 604 | + p->nProgram = TH1_LEN(argl[3]); | |
| 597 | 605 | zSpace = &p->zProgram[p->nProgram]; |
| 598 | 606 | |
| 599 | 607 | for(i=0; i<nParam; i++){ |
| 600 | 608 | char **az; |
| 601 | 609 | int *an; |
| @@ -672,11 +680,12 @@ | ||
| 672 | 680 | int *argl |
| 673 | 681 | ){ |
| 674 | 682 | if( argc!=3 ){ |
| 675 | 683 | return Th_WrongNumArgs(interp, "rename oldcmd newcmd"); |
| 676 | 684 | } |
| 677 | - return Th_RenameCommand(interp, argv[1], argl[1], argv[2], argl[2]); | |
| 685 | + return Th_RenameCommand(interp, argv[1], TH1_LEN(argl[1]), | |
| 686 | + argv[2], TH1_LEN(argl[2])); | |
| 678 | 687 | } |
| 679 | 688 | |
| 680 | 689 | /* |
| 681 | 690 | ** TH Syntax: |
| 682 | 691 | ** |
| @@ -746,13 +755,13 @@ | ||
| 746 | 755 | if( argc!=4 ){ |
| 747 | 756 | return Th_WrongNumArgs(interp, "string compare str1 str2"); |
| 748 | 757 | } |
| 749 | 758 | |
| 750 | 759 | zLeft = argv[2]; |
| 751 | - nLeft = argl[2]; | |
| 760 | + nLeft = TH1_LEN(argl[2]); | |
| 752 | 761 | zRight = argv[3]; |
| 753 | - nRight = argl[3]; | |
| 762 | + nRight = TH1_LEN(argl[3]); | |
| 754 | 763 | |
| 755 | 764 | for(i=0; iRes==0 && i<nLeft && i<nRight; i++){ |
| 756 | 765 | iRes = zLeft[i]-zRight[i]; |
| 757 | 766 | } |
| 758 | 767 | if( iRes==0 ){ |
| @@ -779,12 +788,12 @@ | ||
| 779 | 788 | |
| 780 | 789 | if( argc!=4 ){ |
| 781 | 790 | return Th_WrongNumArgs(interp, "string first needle haystack"); |
| 782 | 791 | } |
| 783 | 792 | |
| 784 | - nNeedle = argl[2]; | |
| 785 | - nHaystack = argl[3]; | |
| 793 | + nNeedle = TH1_LEN(argl[2]); | |
| 794 | + nHaystack = TH1_LEN(argl[3]); | |
| 786 | 795 | |
| 787 | 796 | if( nNeedle && nHaystack && nNeedle<=nHaystack ){ |
| 788 | 797 | const char *zNeedle = argv[2]; |
| 789 | 798 | const char *zHaystack = argv[3]; |
| 790 | 799 | int i; |
| @@ -812,19 +821,19 @@ | ||
| 812 | 821 | |
| 813 | 822 | if( argc!=4 ){ |
| 814 | 823 | return Th_WrongNumArgs(interp, "string index string index"); |
| 815 | 824 | } |
| 816 | 825 | |
| 817 | - if( argl[3]==3 && 0==memcmp("end", argv[3], 3) ){ | |
| 818 | - iIndex = argl[2]-1; | |
| 826 | + if( TH1_LEN(argl[3])==3 && 0==memcmp("end", argv[3], 3) ){ | |
| 827 | + iIndex = TH1_LEN(argl[2])-1; | |
| 819 | 828 | }else if( Th_ToInt(interp, argv[3], argl[3], &iIndex) ){ |
| 820 | 829 | Th_ErrorMessage( |
| 821 | 830 | interp, "Expected \"end\" or integer, got:", argv[3], argl[3]); |
| 822 | 831 | return TH_ERROR; |
| 823 | 832 | } |
| 824 | 833 | |
| 825 | - if( iIndex>=0 && iIndex<argl[2] ){ | |
| 834 | + if( iIndex>=0 && iIndex<TH1_LEN(argl[2]) ){ | |
| 826 | 835 | return Th_SetResult(interp, &argv[2][iIndex], 1); |
| 827 | 836 | }else{ |
| 828 | 837 | return Th_SetResult(interp, 0, 0); |
| 829 | 838 | } |
| 830 | 839 | } |
| @@ -838,41 +847,44 @@ | ||
| 838 | 847 | Th_Interp *interp, void *ctx, int argc, const char **argv, int *argl |
| 839 | 848 | ){ |
| 840 | 849 | if( argc!=4 ){ |
| 841 | 850 | return Th_WrongNumArgs(interp, "string is class string"); |
| 842 | 851 | } |
| 843 | - if( argl[2]==5 && 0==memcmp(argv[2], "alnum", 5) ){ | |
| 852 | + if( TH1_LEN(argl[2])==5 && 0==memcmp(argv[2], "alnum", 5) ){ | |
| 844 | 853 | int i; |
| 845 | 854 | int iRes = 1; |
| 846 | 855 | |
| 847 | - for(i=0; i<argl[3]; i++){ | |
| 856 | + for(i=0; i<TH1_LEN(argl[3]); i++){ | |
| 848 | 857 | if( !th_isalnum(argv[3][i]) ){ |
| 849 | 858 | iRes = 0; |
| 850 | 859 | } |
| 851 | 860 | } |
| 852 | 861 | |
| 853 | 862 | return Th_SetResultInt(interp, iRes); |
| 854 | - }else if( argl[2]==6 && 0==memcmp(argv[2], "double", 6) ){ | |
| 863 | + }else if( TH1_LEN(argl[2])==6 && 0==memcmp(argv[2], "double", 6) ){ | |
| 855 | 864 | double fVal; |
| 856 | 865 | if( Th_ToDouble(interp, argv[3], argl[3], &fVal)==TH_OK ){ |
| 857 | 866 | return Th_SetResultInt(interp, 1); |
| 858 | 867 | } |
| 859 | 868 | return Th_SetResultInt(interp, 0); |
| 860 | - }else if( argl[2]==7 && 0==memcmp(argv[2], "integer", 7) ){ | |
| 869 | + }else if( TH1_LEN(argl[2])==7 && 0==memcmp(argv[2], "integer", 7) ){ | |
| 861 | 870 | int iVal; |
| 862 | 871 | if( Th_ToInt(interp, argv[3], argl[3], &iVal)==TH_OK ){ |
| 863 | 872 | return Th_SetResultInt(interp, 1); |
| 864 | 873 | } |
| 865 | 874 | return Th_SetResultInt(interp, 0); |
| 866 | - }else if( argl[2]==4 && 0==memcmp(argv[2], "list", 4) ){ | |
| 875 | + }else if( TH1_LEN(argl[2])==4 && 0==memcmp(argv[2], "list", 4) ){ | |
| 867 | 876 | if( Th_SplitList(interp, argv[3], argl[3], 0, 0, 0)==TH_OK ){ |
| 868 | 877 | return Th_SetResultInt(interp, 1); |
| 869 | 878 | } |
| 870 | 879 | return Th_SetResultInt(interp, 0); |
| 880 | + }else if( TH1_LEN(argl[2])==7 && 0==memcmp(argv[2], "tainted", 7) ){ | |
| 881 | + return Th_SetResultInt(interp, TH1_TAINTED(argl[3])); | |
| 871 | 882 | }else{ |
| 872 | 883 | Th_ErrorMessage(interp, |
| 873 | - "Expected alnum, double, integer, or list, got:", argv[2], argl[2]); | |
| 884 | + "Expected alnum, double, integer, list, or tainted, got:", | |
| 885 | + argv[2], TH1_LEN(argl[2])); | |
| 874 | 886 | return TH_ERROR; |
| 875 | 887 | } |
| 876 | 888 | } |
| 877 | 889 | |
| 878 | 890 | /* |
| @@ -889,12 +901,12 @@ | ||
| 889 | 901 | |
| 890 | 902 | if( argc!=4 ){ |
| 891 | 903 | return Th_WrongNumArgs(interp, "string last needle haystack"); |
| 892 | 904 | } |
| 893 | 905 | |
| 894 | - nNeedle = argl[2]; | |
| 895 | - nHaystack = argl[3]; | |
| 906 | + nNeedle = TH1_LEN(argl[2]); | |
| 907 | + nHaystack = TH1_LEN(argl[3]); | |
| 896 | 908 | |
| 897 | 909 | if( nNeedle && nHaystack && nNeedle<=nHaystack ){ |
| 898 | 910 | const char *zNeedle = argv[2]; |
| 899 | 911 | const char *zHaystack = argv[3]; |
| 900 | 912 | int i; |
| @@ -919,11 +931,11 @@ | ||
| 919 | 931 | Th_Interp *interp, void *ctx, int argc, const char **argv, int *argl |
| 920 | 932 | ){ |
| 921 | 933 | if( argc!=3 ){ |
| 922 | 934 | return Th_WrongNumArgs(interp, "string length string"); |
| 923 | 935 | } |
| 924 | - return Th_SetResultInt(interp, argl[2]); | |
| 936 | + return Th_SetResultInt(interp, TH1_LEN(argl[2])); | |
| 925 | 937 | } |
| 926 | 938 | |
| 927 | 939 | /* |
| 928 | 940 | ** TH Syntax: |
| 929 | 941 | ** |
| @@ -938,12 +950,12 @@ | ||
| 938 | 950 | char *zPat, *zStr; |
| 939 | 951 | int rc; |
| 940 | 952 | if( argc!=4 ){ |
| 941 | 953 | return Th_WrongNumArgs(interp, "string match pattern string"); |
| 942 | 954 | } |
| 943 | - zPat = fossil_strndup(argv[2],argl[2]); | |
| 944 | - zStr = fossil_strndup(argv[3],argl[3]); | |
| 955 | + zPat = fossil_strndup(argv[2],TH1_LEN(argl[2])); | |
| 956 | + zStr = fossil_strndup(argv[3],TH1_LEN(argl[3])); | |
| 945 | 957 | rc = sqlite3_strglob(zPat,zStr); |
| 946 | 958 | fossil_free(zPat); |
| 947 | 959 | fossil_free(zStr); |
| 948 | 960 | return Th_SetResultInt(interp, !rc); |
| 949 | 961 | } |
| @@ -961,23 +973,23 @@ | ||
| 961 | 973 | |
| 962 | 974 | if( argc!=5 ){ |
| 963 | 975 | return Th_WrongNumArgs(interp, "string range string first last"); |
| 964 | 976 | } |
| 965 | 977 | |
| 966 | - if( argl[4]==3 && 0==memcmp("end", argv[4], 3) ){ | |
| 967 | - iEnd = argl[2]; | |
| 978 | + if( TH1_LEN(argl[4])==3 && 0==memcmp("end", argv[4], 3) ){ | |
| 979 | + iEnd = TH1_LEN(argl[2]); | |
| 968 | 980 | }else if( Th_ToInt(interp, argv[4], argl[4], &iEnd) ){ |
| 969 | 981 | Th_ErrorMessage( |
| 970 | - interp, "Expected \"end\" or integer, got:", argv[4], argl[4]); | |
| 982 | + interp, "Expected \"end\" or integer, got:", argv[4], TH1_LEN(argl[4])); | |
| 971 | 983 | return TH_ERROR; |
| 972 | 984 | } |
| 973 | 985 | if( Th_ToInt(interp, argv[3], argl[3], &iStart) ){ |
| 974 | 986 | return TH_ERROR; |
| 975 | 987 | } |
| 976 | 988 | |
| 977 | 989 | if( iStart<0 ) iStart = 0; |
| 978 | - if( iEnd>=argl[2] ) iEnd = argl[2]-1; | |
| 990 | + if( iEnd>=TH1_LEN(argl[2]) ) iEnd = TH1_LEN(argl[2])-1; | |
| 979 | 991 | if( iStart>iEnd ) iEnd = iStart-1; |
| 980 | 992 | |
| 981 | 993 | return Th_SetResult(interp, &argv[2][iStart], iEnd-iStart+1); |
| 982 | 994 | } |
| 983 | 995 | |
| @@ -989,27 +1001,33 @@ | ||
| 989 | 1001 | static int string_repeat_command( |
| 990 | 1002 | Th_Interp *interp, void *ctx, int argc, const char **argv, int *argl |
| 991 | 1003 | ){ |
| 992 | 1004 | int n; |
| 993 | 1005 | int i; |
| 994 | - int nByte; | |
| 1006 | + int sz; | |
| 1007 | + long long int nByte; | |
| 995 | 1008 | char *zByte; |
| 996 | 1009 | |
| 997 | 1010 | if( argc!=4 ){ |
| 998 | 1011 | return Th_WrongNumArgs(interp, "string repeat string n"); |
| 999 | 1012 | } |
| 1000 | 1013 | if( Th_ToInt(interp, argv[3], argl[3], &n) ){ |
| 1001 | 1014 | return TH_ERROR; |
| 1002 | 1015 | } |
| 1003 | 1016 | |
| 1004 | - nByte = argl[2] * n; | |
| 1017 | + nByte = n; | |
| 1018 | + sz = TH1_LEN(argl[2]); | |
| 1019 | + nByte *= sz; | |
| 1020 | + TH1_SIZECHECK(nByte+1); | |
| 1005 | 1021 | zByte = Th_Malloc(interp, nByte+1); |
| 1006 | - for(i=0; i<nByte; i+=argl[2]){ | |
| 1007 | - memcpy(&zByte[i], argv[2], argl[2]); | |
| 1022 | + for(i=0; i<nByte; i+=sz){ | |
| 1023 | + memcpy(&zByte[i], argv[2], sz); | |
| 1008 | 1024 | } |
| 1009 | 1025 | |
| 1010 | - Th_SetResult(interp, zByte, nByte); | |
| 1026 | + n = nByte; | |
| 1027 | + TH1_XFER_TAINT(n, argl[2]); | |
| 1028 | + Th_SetResult(interp, zByte, n); | |
| 1011 | 1029 | Th_Free(interp, zByte); |
| 1012 | 1030 | return TH_OK; |
| 1013 | 1031 | } |
| 1014 | 1032 | |
| 1015 | 1033 | /* |
| @@ -1027,15 +1045,15 @@ | ||
| 1027 | 1045 | |
| 1028 | 1046 | if( argc!=3 ){ |
| 1029 | 1047 | return Th_WrongNumArgs(interp, "string trim string"); |
| 1030 | 1048 | } |
| 1031 | 1049 | z = argv[2]; |
| 1032 | - n = argl[2]; | |
| 1033 | - if( argl[1]<5 || argv[1][4]=='l' ){ | |
| 1050 | + n = TH1_LEN(argl[2]); | |
| 1051 | + if( TH1_LEN(argl[1])<5 || argv[1][4]=='l' ){ | |
| 1034 | 1052 | while( n && th_isspace(z[0]) ){ z++; n--; } |
| 1035 | 1053 | } |
| 1036 | - if( argl[1]<5 || argv[1][4]=='r' ){ | |
| 1054 | + if( TH1_LEN(argl[1])<5 || argv[1][4]=='r' ){ | |
| 1037 | 1055 | while( n && th_isspace(z[n-1]) ){ n--; } |
| 1038 | 1056 | } |
| 1039 | 1057 | Th_SetResult(interp, z, n); |
| 1040 | 1058 | return TH_OK; |
| 1041 | 1059 | } |
| @@ -1051,11 +1069,11 @@ | ||
| 1051 | 1069 | int rc; |
| 1052 | 1070 | |
| 1053 | 1071 | if( argc!=3 ){ |
| 1054 | 1072 | return Th_WrongNumArgs(interp, "info exists var"); |
| 1055 | 1073 | } |
| 1056 | - rc = Th_ExistsVar(interp, argv[2], argl[2]); | |
| 1074 | + rc = Th_ExistsVar(interp, argv[2], TH1_LEN(argl[2])); | |
| 1057 | 1075 | Th_SetResultInt(interp, rc); |
| 1058 | 1076 | return TH_OK; |
| 1059 | 1077 | } |
| 1060 | 1078 | |
| 1061 | 1079 | /* |
| @@ -1117,11 +1135,11 @@ | ||
| 1117 | 1135 | int rc; |
| 1118 | 1136 | |
| 1119 | 1137 | if( argc!=3 ){ |
| 1120 | 1138 | return Th_WrongNumArgs(interp, "array exists var"); |
| 1121 | 1139 | } |
| 1122 | - rc = Th_ExistsArrayVar(interp, argv[2], argl[2]); | |
| 1140 | + rc = Th_ExistsArrayVar(interp, argv[2], TH1_LEN(argl[2])); | |
| 1123 | 1141 | Th_SetResultInt(interp, rc); |
| 1124 | 1142 | return TH_OK; |
| 1125 | 1143 | } |
| 1126 | 1144 | |
| 1127 | 1145 | /* |
| @@ -1137,11 +1155,11 @@ | ||
| 1137 | 1155 | int nElem = 0; |
| 1138 | 1156 | |
| 1139 | 1157 | if( argc!=3 ){ |
| 1140 | 1158 | return Th_WrongNumArgs(interp, "array names varname"); |
| 1141 | 1159 | } |
| 1142 | - rc = Th_ListAppendArray(interp, argv[2], argl[2], &zElem, &nElem); | |
| 1160 | + rc = Th_ListAppendArray(interp, argv[2], TH1_LEN(argl[2]), &zElem, &nElem); | |
| 1143 | 1161 | if( rc!=TH_OK ){ |
| 1144 | 1162 | return rc; |
| 1145 | 1163 | } |
| 1146 | 1164 | Th_SetResult(interp, zElem, nElem); |
| 1147 | 1165 | if( zElem ) Th_Free(interp, zElem); |
| @@ -1161,11 +1179,11 @@ | ||
| 1161 | 1179 | int *argl |
| 1162 | 1180 | ){ |
| 1163 | 1181 | if( argc!=2 ){ |
| 1164 | 1182 | return Th_WrongNumArgs(interp, "unset var"); |
| 1165 | 1183 | } |
| 1166 | - return Th_UnsetVar(interp, argv[1], argl[1]); | |
| 1184 | + return Th_UnsetVar(interp, argv[1], TH1_LEN(argl[1])); | |
| 1167 | 1185 | } |
| 1168 | 1186 | |
| 1169 | 1187 | int Th_CallSubCommand( |
| 1170 | 1188 | Th_Interp *interp, |
| 1171 | 1189 | void *ctx, |
| @@ -1176,19 +1194,22 @@ | ||
| 1176 | 1194 | ){ |
| 1177 | 1195 | if( argc>1 ){ |
| 1178 | 1196 | int i; |
| 1179 | 1197 | for(i=0; aSub[i].zName; i++){ |
| 1180 | 1198 | const char *zName = aSub[i].zName; |
| 1181 | - if( th_strlen(zName)==argl[1] && 0==memcmp(zName, argv[1], argl[1]) ){ | |
| 1199 | + if( th_strlen(zName)==TH1_LEN(argl[1]) | |
| 1200 | + && 0==memcmp(zName, argv[1], TH1_LEN(argl[1])) ){ | |
| 1182 | 1201 | return aSub[i].xProc(interp, ctx, argc, argv, argl); |
| 1183 | 1202 | } |
| 1184 | 1203 | } |
| 1185 | 1204 | } |
| 1186 | 1205 | if(argc<2){ |
| 1187 | - Th_ErrorMessage(interp, "Expected sub-command for", argv[0], argl[0]); | |
| 1206 | + Th_ErrorMessage(interp, "Expected sub-command for", | |
| 1207 | + argv[0], TH1_LEN(argl[0])); | |
| 1188 | 1208 | }else{ |
| 1189 | - Th_ErrorMessage(interp, "Expected sub-command, got:", argv[1], argl[1]); | |
| 1209 | + Th_ErrorMessage(interp, "Expected sub-command, got:", | |
| 1210 | + argv[1], TH1_LEN(argl[1])); | |
| 1190 | 1211 | } |
| 1191 | 1212 | return TH_ERROR; |
| 1192 | 1213 | } |
| 1193 | 1214 | |
| 1194 | 1215 | /* |
| @@ -1319,11 +1340,11 @@ | ||
| 1319 | 1340 | int iFrame = -1; |
| 1320 | 1341 | |
| 1321 | 1342 | if( argc!=2 && argc!=3 ){ |
| 1322 | 1343 | return Th_WrongNumArgs(interp, "uplevel ?level? script..."); |
| 1323 | 1344 | } |
| 1324 | - if( argc==3 && TH_OK!=thToFrame(interp, argv[1], argl[1], &iFrame) ){ | |
| 1345 | + if( argc==3 && TH_OK!=thToFrame(interp, argv[1], TH1_LEN(argl[1]), &iFrame) ){ | |
| 1325 | 1346 | return TH_ERROR; |
| 1326 | 1347 | } |
| 1327 | 1348 | return Th_Eval(interp, iFrame, argv[argc-1], -1); |
| 1328 | 1349 | } |
| 1329 | 1350 | |
| @@ -1342,19 +1363,20 @@ | ||
| 1342 | 1363 | int iVar = 1; |
| 1343 | 1364 | int iFrame = -1; |
| 1344 | 1365 | int rc = TH_OK; |
| 1345 | 1366 | int i; |
| 1346 | 1367 | |
| 1347 | - if( TH_OK==thToFrame(0, argv[1], argl[1], &iFrame) ){ | |
| 1368 | + if( TH_OK==thToFrame(0, argv[1], TH1_LEN(argl[1]), &iFrame) ){ | |
| 1348 | 1369 | iVar++; |
| 1349 | 1370 | } |
| 1350 | 1371 | if( argc==iVar || (argc-iVar)%2 ){ |
| 1351 | 1372 | return Th_WrongNumArgs(interp, |
| 1352 | 1373 | "upvar frame othervar myvar ?othervar myvar...?"); |
| 1353 | 1374 | } |
| 1354 | 1375 | for(i=iVar; rc==TH_OK && i<argc; i=i+2){ |
| 1355 | - rc = Th_LinkVar(interp, argv[i+1], argl[i+1], iFrame, argv[i], argl[i]); | |
| 1376 | + rc = Th_LinkVar(interp, argv[i+1], TH1_LEN(argl[i+1]), | |
| 1377 | + iFrame, argv[i], TH1_LEN(argl[i])); | |
| 1356 | 1378 | } |
| 1357 | 1379 | return rc; |
| 1358 | 1380 | } |
| 1359 | 1381 | |
| 1360 | 1382 | /* |
| 1361 | 1383 |
| --- src/th_lang.c | |
| +++ src/th_lang.c | |
| @@ -39,11 +39,11 @@ | |
| 39 | |
| 40 | rc = Th_Eval(interp, 0, argv[1], -1); |
| 41 | if( argc==3 ){ |
| 42 | int nResult; |
| 43 | const char *zResult = Th_GetResult(interp, &nResult); |
| 44 | Th_SetVar(interp, argv[2], argl[2], zResult, nResult); |
| 45 | } |
| 46 | |
| 47 | Th_SetResultInt(interp, rc); |
| 48 | return TH_OK; |
| 49 | } |
| @@ -215,15 +215,18 @@ | |
| 215 | int *argl |
| 216 | ){ |
| 217 | char *zList = 0; |
| 218 | int nList = 0; |
| 219 | int i; |
| 220 | |
| 221 | for(i=1; i<argc; i++){ |
| 222 | Th_ListAppend(interp, &zList, &nList, argv[i], argl[i]); |
| 223 | } |
| 224 | |
| 225 | Th_SetResult(interp, zList, nList); |
| 226 | Th_Free(interp, zList); |
| 227 | |
| 228 | return TH_OK; |
| 229 | } |
| @@ -244,10 +247,11 @@ | |
| 244 | int *argl |
| 245 | ){ |
| 246 | char *zList = 0; |
| 247 | int nList = 0; |
| 248 | int i, rc; |
| 249 | |
| 250 | if( argc<2 ){ |
| 251 | return Th_WrongNumArgs(interp, "lappend var ..."); |
| 252 | } |
| 253 | rc = Th_GetVar(interp, argv[1], argl[1]); |
| @@ -254,13 +258,15 @@ | |
| 254 | if( rc==TH_OK ){ |
| 255 | zList = Th_TakeResult(interp, &nList); |
| 256 | } |
| 257 | |
| 258 | for(i=2; i<argc; i++){ |
| 259 | Th_ListAppend(interp, &zList, &nList, argv[i], argl[i]); |
| 260 | } |
| 261 | |
| 262 | Th_SetVar(interp, argv[1], argl[1], zList, nList); |
| 263 | Th_SetResult(interp, zList, nList); |
| 264 | Th_Free(interp, zList); |
| 265 | |
| 266 | return TH_OK; |
| @@ -356,13 +362,14 @@ | |
| 356 | return Th_WrongNumArgs(interp, "lsearch list string"); |
| 357 | } |
| 358 | |
| 359 | rc = Th_SplitList(interp, argv[1], argl[1], &azElem, &anElem, &nCount); |
| 360 | if( rc==TH_OK ){ |
| 361 | Th_SetResultInt(interp, -1); |
| 362 | for(i=0; i<nCount; i++){ |
| 363 | if( anElem[i]==argl[2] && 0==memcmp(azElem[i], argv[2], argl[2]) ){ |
| 364 | Th_SetResultInt(interp, i); |
| 365 | break; |
| 366 | } |
| 367 | } |
| 368 | Th_Free(interp, azElem); |
| @@ -561,20 +568,21 @@ | |
| 561 | int nUsage = 0; /* Number of bytes at zUsage */ |
| 562 | |
| 563 | if( argc!=4 ){ |
| 564 | return Th_WrongNumArgs(interp, "proc name arglist code"); |
| 565 | } |
| 566 | if( Th_SplitList(interp, argv[2], argl[2], &azParam, &anParam, &nParam) ){ |
| 567 | return TH_ERROR; |
| 568 | } |
| 569 | |
| 570 | /* Allocate the new ProcDefn structure. */ |
| 571 | nByte = sizeof(ProcDefn) + /* ProcDefn structure */ |
| 572 | (sizeof(char *) + sizeof(int)) * nParam + /* azParam, anParam */ |
| 573 | (sizeof(char *) + sizeof(int)) * nParam + /* azDefault, anDefault */ |
| 574 | argl[3] + /* zProgram */ |
| 575 | argl[2]; /* Space for copies of parameter names and default values */ |
| 576 | p = (ProcDefn *)Th_Malloc(interp, nByte); |
| 577 | |
| 578 | /* If the last parameter in the parameter list is "args", then set the |
| 579 | ** ProcDefn.hasArgs flag. The "args" parameter does not require an |
| 580 | ** entry in the ProcDefn.azParam[] or ProcDefn.azDefault[] arrays. |
| @@ -590,12 +598,12 @@ | |
| 590 | p->azParam = (char **)&p[1]; |
| 591 | p->anParam = (int *)&p->azParam[nParam]; |
| 592 | p->azDefault = (char **)&p->anParam[nParam]; |
| 593 | p->anDefault = (int *)&p->azDefault[nParam]; |
| 594 | p->zProgram = (char *)&p->anDefault[nParam]; |
| 595 | memcpy(p->zProgram, argv[3], argl[3]); |
| 596 | p->nProgram = argl[3]; |
| 597 | zSpace = &p->zProgram[p->nProgram]; |
| 598 | |
| 599 | for(i=0; i<nParam; i++){ |
| 600 | char **az; |
| 601 | int *an; |
| @@ -672,11 +680,12 @@ | |
| 672 | int *argl |
| 673 | ){ |
| 674 | if( argc!=3 ){ |
| 675 | return Th_WrongNumArgs(interp, "rename oldcmd newcmd"); |
| 676 | } |
| 677 | return Th_RenameCommand(interp, argv[1], argl[1], argv[2], argl[2]); |
| 678 | } |
| 679 | |
| 680 | /* |
| 681 | ** TH Syntax: |
| 682 | ** |
| @@ -746,13 +755,13 @@ | |
| 746 | if( argc!=4 ){ |
| 747 | return Th_WrongNumArgs(interp, "string compare str1 str2"); |
| 748 | } |
| 749 | |
| 750 | zLeft = argv[2]; |
| 751 | nLeft = argl[2]; |
| 752 | zRight = argv[3]; |
| 753 | nRight = argl[3]; |
| 754 | |
| 755 | for(i=0; iRes==0 && i<nLeft && i<nRight; i++){ |
| 756 | iRes = zLeft[i]-zRight[i]; |
| 757 | } |
| 758 | if( iRes==0 ){ |
| @@ -779,12 +788,12 @@ | |
| 779 | |
| 780 | if( argc!=4 ){ |
| 781 | return Th_WrongNumArgs(interp, "string first needle haystack"); |
| 782 | } |
| 783 | |
| 784 | nNeedle = argl[2]; |
| 785 | nHaystack = argl[3]; |
| 786 | |
| 787 | if( nNeedle && nHaystack && nNeedle<=nHaystack ){ |
| 788 | const char *zNeedle = argv[2]; |
| 789 | const char *zHaystack = argv[3]; |
| 790 | int i; |
| @@ -812,19 +821,19 @@ | |
| 812 | |
| 813 | if( argc!=4 ){ |
| 814 | return Th_WrongNumArgs(interp, "string index string index"); |
| 815 | } |
| 816 | |
| 817 | if( argl[3]==3 && 0==memcmp("end", argv[3], 3) ){ |
| 818 | iIndex = argl[2]-1; |
| 819 | }else if( Th_ToInt(interp, argv[3], argl[3], &iIndex) ){ |
| 820 | Th_ErrorMessage( |
| 821 | interp, "Expected \"end\" or integer, got:", argv[3], argl[3]); |
| 822 | return TH_ERROR; |
| 823 | } |
| 824 | |
| 825 | if( iIndex>=0 && iIndex<argl[2] ){ |
| 826 | return Th_SetResult(interp, &argv[2][iIndex], 1); |
| 827 | }else{ |
| 828 | return Th_SetResult(interp, 0, 0); |
| 829 | } |
| 830 | } |
| @@ -838,41 +847,44 @@ | |
| 838 | Th_Interp *interp, void *ctx, int argc, const char **argv, int *argl |
| 839 | ){ |
| 840 | if( argc!=4 ){ |
| 841 | return Th_WrongNumArgs(interp, "string is class string"); |
| 842 | } |
| 843 | if( argl[2]==5 && 0==memcmp(argv[2], "alnum", 5) ){ |
| 844 | int i; |
| 845 | int iRes = 1; |
| 846 | |
| 847 | for(i=0; i<argl[3]; i++){ |
| 848 | if( !th_isalnum(argv[3][i]) ){ |
| 849 | iRes = 0; |
| 850 | } |
| 851 | } |
| 852 | |
| 853 | return Th_SetResultInt(interp, iRes); |
| 854 | }else if( argl[2]==6 && 0==memcmp(argv[2], "double", 6) ){ |
| 855 | double fVal; |
| 856 | if( Th_ToDouble(interp, argv[3], argl[3], &fVal)==TH_OK ){ |
| 857 | return Th_SetResultInt(interp, 1); |
| 858 | } |
| 859 | return Th_SetResultInt(interp, 0); |
| 860 | }else if( argl[2]==7 && 0==memcmp(argv[2], "integer", 7) ){ |
| 861 | int iVal; |
| 862 | if( Th_ToInt(interp, argv[3], argl[3], &iVal)==TH_OK ){ |
| 863 | return Th_SetResultInt(interp, 1); |
| 864 | } |
| 865 | return Th_SetResultInt(interp, 0); |
| 866 | }else if( argl[2]==4 && 0==memcmp(argv[2], "list", 4) ){ |
| 867 | if( Th_SplitList(interp, argv[3], argl[3], 0, 0, 0)==TH_OK ){ |
| 868 | return Th_SetResultInt(interp, 1); |
| 869 | } |
| 870 | return Th_SetResultInt(interp, 0); |
| 871 | }else{ |
| 872 | Th_ErrorMessage(interp, |
| 873 | "Expected alnum, double, integer, or list, got:", argv[2], argl[2]); |
| 874 | return TH_ERROR; |
| 875 | } |
| 876 | } |
| 877 | |
| 878 | /* |
| @@ -889,12 +901,12 @@ | |
| 889 | |
| 890 | if( argc!=4 ){ |
| 891 | return Th_WrongNumArgs(interp, "string last needle haystack"); |
| 892 | } |
| 893 | |
| 894 | nNeedle = argl[2]; |
| 895 | nHaystack = argl[3]; |
| 896 | |
| 897 | if( nNeedle && nHaystack && nNeedle<=nHaystack ){ |
| 898 | const char *zNeedle = argv[2]; |
| 899 | const char *zHaystack = argv[3]; |
| 900 | int i; |
| @@ -919,11 +931,11 @@ | |
| 919 | Th_Interp *interp, void *ctx, int argc, const char **argv, int *argl |
| 920 | ){ |
| 921 | if( argc!=3 ){ |
| 922 | return Th_WrongNumArgs(interp, "string length string"); |
| 923 | } |
| 924 | return Th_SetResultInt(interp, argl[2]); |
| 925 | } |
| 926 | |
| 927 | /* |
| 928 | ** TH Syntax: |
| 929 | ** |
| @@ -938,12 +950,12 @@ | |
| 938 | char *zPat, *zStr; |
| 939 | int rc; |
| 940 | if( argc!=4 ){ |
| 941 | return Th_WrongNumArgs(interp, "string match pattern string"); |
| 942 | } |
| 943 | zPat = fossil_strndup(argv[2],argl[2]); |
| 944 | zStr = fossil_strndup(argv[3],argl[3]); |
| 945 | rc = sqlite3_strglob(zPat,zStr); |
| 946 | fossil_free(zPat); |
| 947 | fossil_free(zStr); |
| 948 | return Th_SetResultInt(interp, !rc); |
| 949 | } |
| @@ -961,23 +973,23 @@ | |
| 961 | |
| 962 | if( argc!=5 ){ |
| 963 | return Th_WrongNumArgs(interp, "string range string first last"); |
| 964 | } |
| 965 | |
| 966 | if( argl[4]==3 && 0==memcmp("end", argv[4], 3) ){ |
| 967 | iEnd = argl[2]; |
| 968 | }else if( Th_ToInt(interp, argv[4], argl[4], &iEnd) ){ |
| 969 | Th_ErrorMessage( |
| 970 | interp, "Expected \"end\" or integer, got:", argv[4], argl[4]); |
| 971 | return TH_ERROR; |
| 972 | } |
| 973 | if( Th_ToInt(interp, argv[3], argl[3], &iStart) ){ |
| 974 | return TH_ERROR; |
| 975 | } |
| 976 | |
| 977 | if( iStart<0 ) iStart = 0; |
| 978 | if( iEnd>=argl[2] ) iEnd = argl[2]-1; |
| 979 | if( iStart>iEnd ) iEnd = iStart-1; |
| 980 | |
| 981 | return Th_SetResult(interp, &argv[2][iStart], iEnd-iStart+1); |
| 982 | } |
| 983 | |
| @@ -989,27 +1001,33 @@ | |
| 989 | static int string_repeat_command( |
| 990 | Th_Interp *interp, void *ctx, int argc, const char **argv, int *argl |
| 991 | ){ |
| 992 | int n; |
| 993 | int i; |
| 994 | int nByte; |
| 995 | char *zByte; |
| 996 | |
| 997 | if( argc!=4 ){ |
| 998 | return Th_WrongNumArgs(interp, "string repeat string n"); |
| 999 | } |
| 1000 | if( Th_ToInt(interp, argv[3], argl[3], &n) ){ |
| 1001 | return TH_ERROR; |
| 1002 | } |
| 1003 | |
| 1004 | nByte = argl[2] * n; |
| 1005 | zByte = Th_Malloc(interp, nByte+1); |
| 1006 | for(i=0; i<nByte; i+=argl[2]){ |
| 1007 | memcpy(&zByte[i], argv[2], argl[2]); |
| 1008 | } |
| 1009 | |
| 1010 | Th_SetResult(interp, zByte, nByte); |
| 1011 | Th_Free(interp, zByte); |
| 1012 | return TH_OK; |
| 1013 | } |
| 1014 | |
| 1015 | /* |
| @@ -1027,15 +1045,15 @@ | |
| 1027 | |
| 1028 | if( argc!=3 ){ |
| 1029 | return Th_WrongNumArgs(interp, "string trim string"); |
| 1030 | } |
| 1031 | z = argv[2]; |
| 1032 | n = argl[2]; |
| 1033 | if( argl[1]<5 || argv[1][4]=='l' ){ |
| 1034 | while( n && th_isspace(z[0]) ){ z++; n--; } |
| 1035 | } |
| 1036 | if( argl[1]<5 || argv[1][4]=='r' ){ |
| 1037 | while( n && th_isspace(z[n-1]) ){ n--; } |
| 1038 | } |
| 1039 | Th_SetResult(interp, z, n); |
| 1040 | return TH_OK; |
| 1041 | } |
| @@ -1051,11 +1069,11 @@ | |
| 1051 | int rc; |
| 1052 | |
| 1053 | if( argc!=3 ){ |
| 1054 | return Th_WrongNumArgs(interp, "info exists var"); |
| 1055 | } |
| 1056 | rc = Th_ExistsVar(interp, argv[2], argl[2]); |
| 1057 | Th_SetResultInt(interp, rc); |
| 1058 | return TH_OK; |
| 1059 | } |
| 1060 | |
| 1061 | /* |
| @@ -1117,11 +1135,11 @@ | |
| 1117 | int rc; |
| 1118 | |
| 1119 | if( argc!=3 ){ |
| 1120 | return Th_WrongNumArgs(interp, "array exists var"); |
| 1121 | } |
| 1122 | rc = Th_ExistsArrayVar(interp, argv[2], argl[2]); |
| 1123 | Th_SetResultInt(interp, rc); |
| 1124 | return TH_OK; |
| 1125 | } |
| 1126 | |
| 1127 | /* |
| @@ -1137,11 +1155,11 @@ | |
| 1137 | int nElem = 0; |
| 1138 | |
| 1139 | if( argc!=3 ){ |
| 1140 | return Th_WrongNumArgs(interp, "array names varname"); |
| 1141 | } |
| 1142 | rc = Th_ListAppendArray(interp, argv[2], argl[2], &zElem, &nElem); |
| 1143 | if( rc!=TH_OK ){ |
| 1144 | return rc; |
| 1145 | } |
| 1146 | Th_SetResult(interp, zElem, nElem); |
| 1147 | if( zElem ) Th_Free(interp, zElem); |
| @@ -1161,11 +1179,11 @@ | |
| 1161 | int *argl |
| 1162 | ){ |
| 1163 | if( argc!=2 ){ |
| 1164 | return Th_WrongNumArgs(interp, "unset var"); |
| 1165 | } |
| 1166 | return Th_UnsetVar(interp, argv[1], argl[1]); |
| 1167 | } |
| 1168 | |
| 1169 | int Th_CallSubCommand( |
| 1170 | Th_Interp *interp, |
| 1171 | void *ctx, |
| @@ -1176,19 +1194,22 @@ | |
| 1176 | ){ |
| 1177 | if( argc>1 ){ |
| 1178 | int i; |
| 1179 | for(i=0; aSub[i].zName; i++){ |
| 1180 | const char *zName = aSub[i].zName; |
| 1181 | if( th_strlen(zName)==argl[1] && 0==memcmp(zName, argv[1], argl[1]) ){ |
| 1182 | return aSub[i].xProc(interp, ctx, argc, argv, argl); |
| 1183 | } |
| 1184 | } |
| 1185 | } |
| 1186 | if(argc<2){ |
| 1187 | Th_ErrorMessage(interp, "Expected sub-command for", argv[0], argl[0]); |
| 1188 | }else{ |
| 1189 | Th_ErrorMessage(interp, "Expected sub-command, got:", argv[1], argl[1]); |
| 1190 | } |
| 1191 | return TH_ERROR; |
| 1192 | } |
| 1193 | |
| 1194 | /* |
| @@ -1319,11 +1340,11 @@ | |
| 1319 | int iFrame = -1; |
| 1320 | |
| 1321 | if( argc!=2 && argc!=3 ){ |
| 1322 | return Th_WrongNumArgs(interp, "uplevel ?level? script..."); |
| 1323 | } |
| 1324 | if( argc==3 && TH_OK!=thToFrame(interp, argv[1], argl[1], &iFrame) ){ |
| 1325 | return TH_ERROR; |
| 1326 | } |
| 1327 | return Th_Eval(interp, iFrame, argv[argc-1], -1); |
| 1328 | } |
| 1329 | |
| @@ -1342,19 +1363,20 @@ | |
| 1342 | int iVar = 1; |
| 1343 | int iFrame = -1; |
| 1344 | int rc = TH_OK; |
| 1345 | int i; |
| 1346 | |
| 1347 | if( TH_OK==thToFrame(0, argv[1], argl[1], &iFrame) ){ |
| 1348 | iVar++; |
| 1349 | } |
| 1350 | if( argc==iVar || (argc-iVar)%2 ){ |
| 1351 | return Th_WrongNumArgs(interp, |
| 1352 | "upvar frame othervar myvar ?othervar myvar...?"); |
| 1353 | } |
| 1354 | for(i=iVar; rc==TH_OK && i<argc; i=i+2){ |
| 1355 | rc = Th_LinkVar(interp, argv[i+1], argl[i+1], iFrame, argv[i], argl[i]); |
| 1356 | } |
| 1357 | return rc; |
| 1358 | } |
| 1359 | |
| 1360 | /* |
| 1361 |
| --- src/th_lang.c | |
| +++ src/th_lang.c | |
| @@ -39,11 +39,11 @@ | |
| 39 | |
| 40 | rc = Th_Eval(interp, 0, argv[1], -1); |
| 41 | if( argc==3 ){ |
| 42 | int nResult; |
| 43 | const char *zResult = Th_GetResult(interp, &nResult); |
| 44 | Th_SetVar(interp, argv[2], TH1_LEN(argl[2]), zResult, nResult); |
| 45 | } |
| 46 | |
| 47 | Th_SetResultInt(interp, rc); |
| 48 | return TH_OK; |
| 49 | } |
| @@ -215,15 +215,18 @@ | |
| 215 | int *argl |
| 216 | ){ |
| 217 | char *zList = 0; |
| 218 | int nList = 0; |
| 219 | int i; |
| 220 | int bTaint = 0; |
| 221 | |
| 222 | for(i=1; i<argc; i++){ |
| 223 | TH1_XFER_TAINT(bTaint,argl[i]); |
| 224 | Th_ListAppend(interp, &zList, &nList, argv[i], argl[i]); |
| 225 | } |
| 226 | |
| 227 | TH1_XFER_TAINT(nList, bTaint); |
| 228 | Th_SetResult(interp, zList, nList); |
| 229 | Th_Free(interp, zList); |
| 230 | |
| 231 | return TH_OK; |
| 232 | } |
| @@ -244,10 +247,11 @@ | |
| 247 | int *argl |
| 248 | ){ |
| 249 | char *zList = 0; |
| 250 | int nList = 0; |
| 251 | int i, rc; |
| 252 | int bTaint = 0; |
| 253 | |
| 254 | if( argc<2 ){ |
| 255 | return Th_WrongNumArgs(interp, "lappend var ..."); |
| 256 | } |
| 257 | rc = Th_GetVar(interp, argv[1], argl[1]); |
| @@ -254,13 +258,15 @@ | |
| 258 | if( rc==TH_OK ){ |
| 259 | zList = Th_TakeResult(interp, &nList); |
| 260 | } |
| 261 | |
| 262 | for(i=2; i<argc; i++){ |
| 263 | TH1_XFER_TAINT(bTaint, argl[i]); |
| 264 | Th_ListAppend(interp, &zList, &nList, argv[i], argl[i]); |
| 265 | } |
| 266 | |
| 267 | TH1_XFER_TAINT(nList, bTaint); |
| 268 | Th_SetVar(interp, argv[1], argl[1], zList, nList); |
| 269 | Th_SetResult(interp, zList, nList); |
| 270 | Th_Free(interp, zList); |
| 271 | |
| 272 | return TH_OK; |
| @@ -356,13 +362,14 @@ | |
| 362 | return Th_WrongNumArgs(interp, "lsearch list string"); |
| 363 | } |
| 364 | |
| 365 | rc = Th_SplitList(interp, argv[1], argl[1], &azElem, &anElem, &nCount); |
| 366 | if( rc==TH_OK ){ |
| 367 | int nn = TH1_LEN(argl[2]); |
| 368 | Th_SetResultInt(interp, -1); |
| 369 | for(i=0; i<nCount; i++){ |
| 370 | if( TH1_LEN(anElem[i])==nn && 0==memcmp(azElem[i], argv[2], nn) ){ |
| 371 | Th_SetResultInt(interp, i); |
| 372 | break; |
| 373 | } |
| 374 | } |
| 375 | Th_Free(interp, azElem); |
| @@ -561,20 +568,21 @@ | |
| 568 | int nUsage = 0; /* Number of bytes at zUsage */ |
| 569 | |
| 570 | if( argc!=4 ){ |
| 571 | return Th_WrongNumArgs(interp, "proc name arglist code"); |
| 572 | } |
| 573 | if( Th_SplitList(interp, argv[2], TH1_LEN(argl[2]), |
| 574 | &azParam, &anParam, &nParam) ){ |
| 575 | return TH_ERROR; |
| 576 | } |
| 577 | |
| 578 | /* Allocate the new ProcDefn structure. */ |
| 579 | nByte = sizeof(ProcDefn) + /* ProcDefn structure */ |
| 580 | (sizeof(char *) + sizeof(int)) * nParam + /* azParam, anParam */ |
| 581 | (sizeof(char *) + sizeof(int)) * nParam + /* azDefault, anDefault */ |
| 582 | TH1_LEN(argl[3]) + /* zProgram */ |
| 583 | TH1_LEN(argl[2]); /* Space for copies of param names and dflt values */ |
| 584 | p = (ProcDefn *)Th_Malloc(interp, nByte); |
| 585 | |
| 586 | /* If the last parameter in the parameter list is "args", then set the |
| 587 | ** ProcDefn.hasArgs flag. The "args" parameter does not require an |
| 588 | ** entry in the ProcDefn.azParam[] or ProcDefn.azDefault[] arrays. |
| @@ -590,12 +598,12 @@ | |
| 598 | p->azParam = (char **)&p[1]; |
| 599 | p->anParam = (int *)&p->azParam[nParam]; |
| 600 | p->azDefault = (char **)&p->anParam[nParam]; |
| 601 | p->anDefault = (int *)&p->azDefault[nParam]; |
| 602 | p->zProgram = (char *)&p->anDefault[nParam]; |
| 603 | memcpy(p->zProgram, argv[3], TH1_LEN(argl[3])); |
| 604 | p->nProgram = TH1_LEN(argl[3]); |
| 605 | zSpace = &p->zProgram[p->nProgram]; |
| 606 | |
| 607 | for(i=0; i<nParam; i++){ |
| 608 | char **az; |
| 609 | int *an; |
| @@ -672,11 +680,12 @@ | |
| 680 | int *argl |
| 681 | ){ |
| 682 | if( argc!=3 ){ |
| 683 | return Th_WrongNumArgs(interp, "rename oldcmd newcmd"); |
| 684 | } |
| 685 | return Th_RenameCommand(interp, argv[1], TH1_LEN(argl[1]), |
| 686 | argv[2], TH1_LEN(argl[2])); |
| 687 | } |
| 688 | |
| 689 | /* |
| 690 | ** TH Syntax: |
| 691 | ** |
| @@ -746,13 +755,13 @@ | |
| 755 | if( argc!=4 ){ |
| 756 | return Th_WrongNumArgs(interp, "string compare str1 str2"); |
| 757 | } |
| 758 | |
| 759 | zLeft = argv[2]; |
| 760 | nLeft = TH1_LEN(argl[2]); |
| 761 | zRight = argv[3]; |
| 762 | nRight = TH1_LEN(argl[3]); |
| 763 | |
| 764 | for(i=0; iRes==0 && i<nLeft && i<nRight; i++){ |
| 765 | iRes = zLeft[i]-zRight[i]; |
| 766 | } |
| 767 | if( iRes==0 ){ |
| @@ -779,12 +788,12 @@ | |
| 788 | |
| 789 | if( argc!=4 ){ |
| 790 | return Th_WrongNumArgs(interp, "string first needle haystack"); |
| 791 | } |
| 792 | |
| 793 | nNeedle = TH1_LEN(argl[2]); |
| 794 | nHaystack = TH1_LEN(argl[3]); |
| 795 | |
| 796 | if( nNeedle && nHaystack && nNeedle<=nHaystack ){ |
| 797 | const char *zNeedle = argv[2]; |
| 798 | const char *zHaystack = argv[3]; |
| 799 | int i; |
| @@ -812,19 +821,19 @@ | |
| 821 | |
| 822 | if( argc!=4 ){ |
| 823 | return Th_WrongNumArgs(interp, "string index string index"); |
| 824 | } |
| 825 | |
| 826 | if( TH1_LEN(argl[3])==3 && 0==memcmp("end", argv[3], 3) ){ |
| 827 | iIndex = TH1_LEN(argl[2])-1; |
| 828 | }else if( Th_ToInt(interp, argv[3], argl[3], &iIndex) ){ |
| 829 | Th_ErrorMessage( |
| 830 | interp, "Expected \"end\" or integer, got:", argv[3], argl[3]); |
| 831 | return TH_ERROR; |
| 832 | } |
| 833 | |
| 834 | if( iIndex>=0 && iIndex<TH1_LEN(argl[2]) ){ |
| 835 | return Th_SetResult(interp, &argv[2][iIndex], 1); |
| 836 | }else{ |
| 837 | return Th_SetResult(interp, 0, 0); |
| 838 | } |
| 839 | } |
| @@ -838,41 +847,44 @@ | |
| 847 | Th_Interp *interp, void *ctx, int argc, const char **argv, int *argl |
| 848 | ){ |
| 849 | if( argc!=4 ){ |
| 850 | return Th_WrongNumArgs(interp, "string is class string"); |
| 851 | } |
| 852 | if( TH1_LEN(argl[2])==5 && 0==memcmp(argv[2], "alnum", 5) ){ |
| 853 | int i; |
| 854 | int iRes = 1; |
| 855 | |
| 856 | for(i=0; i<TH1_LEN(argl[3]); i++){ |
| 857 | if( !th_isalnum(argv[3][i]) ){ |
| 858 | iRes = 0; |
| 859 | } |
| 860 | } |
| 861 | |
| 862 | return Th_SetResultInt(interp, iRes); |
| 863 | }else if( TH1_LEN(argl[2])==6 && 0==memcmp(argv[2], "double", 6) ){ |
| 864 | double fVal; |
| 865 | if( Th_ToDouble(interp, argv[3], argl[3], &fVal)==TH_OK ){ |
| 866 | return Th_SetResultInt(interp, 1); |
| 867 | } |
| 868 | return Th_SetResultInt(interp, 0); |
| 869 | }else if( TH1_LEN(argl[2])==7 && 0==memcmp(argv[2], "integer", 7) ){ |
| 870 | int iVal; |
| 871 | if( Th_ToInt(interp, argv[3], argl[3], &iVal)==TH_OK ){ |
| 872 | return Th_SetResultInt(interp, 1); |
| 873 | } |
| 874 | return Th_SetResultInt(interp, 0); |
| 875 | }else if( TH1_LEN(argl[2])==4 && 0==memcmp(argv[2], "list", 4) ){ |
| 876 | if( Th_SplitList(interp, argv[3], argl[3], 0, 0, 0)==TH_OK ){ |
| 877 | return Th_SetResultInt(interp, 1); |
| 878 | } |
| 879 | return Th_SetResultInt(interp, 0); |
| 880 | }else if( TH1_LEN(argl[2])==7 && 0==memcmp(argv[2], "tainted", 7) ){ |
| 881 | return Th_SetResultInt(interp, TH1_TAINTED(argl[3])); |
| 882 | }else{ |
| 883 | Th_ErrorMessage(interp, |
| 884 | "Expected alnum, double, integer, list, or tainted, got:", |
| 885 | argv[2], TH1_LEN(argl[2])); |
| 886 | return TH_ERROR; |
| 887 | } |
| 888 | } |
| 889 | |
| 890 | /* |
| @@ -889,12 +901,12 @@ | |
| 901 | |
| 902 | if( argc!=4 ){ |
| 903 | return Th_WrongNumArgs(interp, "string last needle haystack"); |
| 904 | } |
| 905 | |
| 906 | nNeedle = TH1_LEN(argl[2]); |
| 907 | nHaystack = TH1_LEN(argl[3]); |
| 908 | |
| 909 | if( nNeedle && nHaystack && nNeedle<=nHaystack ){ |
| 910 | const char *zNeedle = argv[2]; |
| 911 | const char *zHaystack = argv[3]; |
| 912 | int i; |
| @@ -919,11 +931,11 @@ | |
| 931 | Th_Interp *interp, void *ctx, int argc, const char **argv, int *argl |
| 932 | ){ |
| 933 | if( argc!=3 ){ |
| 934 | return Th_WrongNumArgs(interp, "string length string"); |
| 935 | } |
| 936 | return Th_SetResultInt(interp, TH1_LEN(argl[2])); |
| 937 | } |
| 938 | |
| 939 | /* |
| 940 | ** TH Syntax: |
| 941 | ** |
| @@ -938,12 +950,12 @@ | |
| 950 | char *zPat, *zStr; |
| 951 | int rc; |
| 952 | if( argc!=4 ){ |
| 953 | return Th_WrongNumArgs(interp, "string match pattern string"); |
| 954 | } |
| 955 | zPat = fossil_strndup(argv[2],TH1_LEN(argl[2])); |
| 956 | zStr = fossil_strndup(argv[3],TH1_LEN(argl[3])); |
| 957 | rc = sqlite3_strglob(zPat,zStr); |
| 958 | fossil_free(zPat); |
| 959 | fossil_free(zStr); |
| 960 | return Th_SetResultInt(interp, !rc); |
| 961 | } |
| @@ -961,23 +973,23 @@ | |
| 973 | |
| 974 | if( argc!=5 ){ |
| 975 | return Th_WrongNumArgs(interp, "string range string first last"); |
| 976 | } |
| 977 | |
| 978 | if( TH1_LEN(argl[4])==3 && 0==memcmp("end", argv[4], 3) ){ |
| 979 | iEnd = TH1_LEN(argl[2]); |
| 980 | }else if( Th_ToInt(interp, argv[4], argl[4], &iEnd) ){ |
| 981 | Th_ErrorMessage( |
| 982 | interp, "Expected \"end\" or integer, got:", argv[4], TH1_LEN(argl[4])); |
| 983 | return TH_ERROR; |
| 984 | } |
| 985 | if( Th_ToInt(interp, argv[3], argl[3], &iStart) ){ |
| 986 | return TH_ERROR; |
| 987 | } |
| 988 | |
| 989 | if( iStart<0 ) iStart = 0; |
| 990 | if( iEnd>=TH1_LEN(argl[2]) ) iEnd = TH1_LEN(argl[2])-1; |
| 991 | if( iStart>iEnd ) iEnd = iStart-1; |
| 992 | |
| 993 | return Th_SetResult(interp, &argv[2][iStart], iEnd-iStart+1); |
| 994 | } |
| 995 | |
| @@ -989,27 +1001,33 @@ | |
| 1001 | static int string_repeat_command( |
| 1002 | Th_Interp *interp, void *ctx, int argc, const char **argv, int *argl |
| 1003 | ){ |
| 1004 | int n; |
| 1005 | int i; |
| 1006 | int sz; |
| 1007 | long long int nByte; |
| 1008 | char *zByte; |
| 1009 | |
| 1010 | if( argc!=4 ){ |
| 1011 | return Th_WrongNumArgs(interp, "string repeat string n"); |
| 1012 | } |
| 1013 | if( Th_ToInt(interp, argv[3], argl[3], &n) ){ |
| 1014 | return TH_ERROR; |
| 1015 | } |
| 1016 | |
| 1017 | nByte = n; |
| 1018 | sz = TH1_LEN(argl[2]); |
| 1019 | nByte *= sz; |
| 1020 | TH1_SIZECHECK(nByte+1); |
| 1021 | zByte = Th_Malloc(interp, nByte+1); |
| 1022 | for(i=0; i<nByte; i+=sz){ |
| 1023 | memcpy(&zByte[i], argv[2], sz); |
| 1024 | } |
| 1025 | |
| 1026 | n = nByte; |
| 1027 | TH1_XFER_TAINT(n, argl[2]); |
| 1028 | Th_SetResult(interp, zByte, n); |
| 1029 | Th_Free(interp, zByte); |
| 1030 | return TH_OK; |
| 1031 | } |
| 1032 | |
| 1033 | /* |
| @@ -1027,15 +1045,15 @@ | |
| 1045 | |
| 1046 | if( argc!=3 ){ |
| 1047 | return Th_WrongNumArgs(interp, "string trim string"); |
| 1048 | } |
| 1049 | z = argv[2]; |
| 1050 | n = TH1_LEN(argl[2]); |
| 1051 | if( TH1_LEN(argl[1])<5 || argv[1][4]=='l' ){ |
| 1052 | while( n && th_isspace(z[0]) ){ z++; n--; } |
| 1053 | } |
| 1054 | if( TH1_LEN(argl[1])<5 || argv[1][4]=='r' ){ |
| 1055 | while( n && th_isspace(z[n-1]) ){ n--; } |
| 1056 | } |
| 1057 | Th_SetResult(interp, z, n); |
| 1058 | return TH_OK; |
| 1059 | } |
| @@ -1051,11 +1069,11 @@ | |
| 1069 | int rc; |
| 1070 | |
| 1071 | if( argc!=3 ){ |
| 1072 | return Th_WrongNumArgs(interp, "info exists var"); |
| 1073 | } |
| 1074 | rc = Th_ExistsVar(interp, argv[2], TH1_LEN(argl[2])); |
| 1075 | Th_SetResultInt(interp, rc); |
| 1076 | return TH_OK; |
| 1077 | } |
| 1078 | |
| 1079 | /* |
| @@ -1117,11 +1135,11 @@ | |
| 1135 | int rc; |
| 1136 | |
| 1137 | if( argc!=3 ){ |
| 1138 | return Th_WrongNumArgs(interp, "array exists var"); |
| 1139 | } |
| 1140 | rc = Th_ExistsArrayVar(interp, argv[2], TH1_LEN(argl[2])); |
| 1141 | Th_SetResultInt(interp, rc); |
| 1142 | return TH_OK; |
| 1143 | } |
| 1144 | |
| 1145 | /* |
| @@ -1137,11 +1155,11 @@ | |
| 1155 | int nElem = 0; |
| 1156 | |
| 1157 | if( argc!=3 ){ |
| 1158 | return Th_WrongNumArgs(interp, "array names varname"); |
| 1159 | } |
| 1160 | rc = Th_ListAppendArray(interp, argv[2], TH1_LEN(argl[2]), &zElem, &nElem); |
| 1161 | if( rc!=TH_OK ){ |
| 1162 | return rc; |
| 1163 | } |
| 1164 | Th_SetResult(interp, zElem, nElem); |
| 1165 | if( zElem ) Th_Free(interp, zElem); |
| @@ -1161,11 +1179,11 @@ | |
| 1179 | int *argl |
| 1180 | ){ |
| 1181 | if( argc!=2 ){ |
| 1182 | return Th_WrongNumArgs(interp, "unset var"); |
| 1183 | } |
| 1184 | return Th_UnsetVar(interp, argv[1], TH1_LEN(argl[1])); |
| 1185 | } |
| 1186 | |
| 1187 | int Th_CallSubCommand( |
| 1188 | Th_Interp *interp, |
| 1189 | void *ctx, |
| @@ -1176,19 +1194,22 @@ | |
| 1194 | ){ |
| 1195 | if( argc>1 ){ |
| 1196 | int i; |
| 1197 | for(i=0; aSub[i].zName; i++){ |
| 1198 | const char *zName = aSub[i].zName; |
| 1199 | if( th_strlen(zName)==TH1_LEN(argl[1]) |
| 1200 | && 0==memcmp(zName, argv[1], TH1_LEN(argl[1])) ){ |
| 1201 | return aSub[i].xProc(interp, ctx, argc, argv, argl); |
| 1202 | } |
| 1203 | } |
| 1204 | } |
| 1205 | if(argc<2){ |
| 1206 | Th_ErrorMessage(interp, "Expected sub-command for", |
| 1207 | argv[0], TH1_LEN(argl[0])); |
| 1208 | }else{ |
| 1209 | Th_ErrorMessage(interp, "Expected sub-command, got:", |
| 1210 | argv[1], TH1_LEN(argl[1])); |
| 1211 | } |
| 1212 | return TH_ERROR; |
| 1213 | } |
| 1214 | |
| 1215 | /* |
| @@ -1319,11 +1340,11 @@ | |
| 1340 | int iFrame = -1; |
| 1341 | |
| 1342 | if( argc!=2 && argc!=3 ){ |
| 1343 | return Th_WrongNumArgs(interp, "uplevel ?level? script..."); |
| 1344 | } |
| 1345 | if( argc==3 && TH_OK!=thToFrame(interp, argv[1], TH1_LEN(argl[1]), &iFrame) ){ |
| 1346 | return TH_ERROR; |
| 1347 | } |
| 1348 | return Th_Eval(interp, iFrame, argv[argc-1], -1); |
| 1349 | } |
| 1350 | |
| @@ -1342,19 +1363,20 @@ | |
| 1363 | int iVar = 1; |
| 1364 | int iFrame = -1; |
| 1365 | int rc = TH_OK; |
| 1366 | int i; |
| 1367 | |
| 1368 | if( TH_OK==thToFrame(0, argv[1], TH1_LEN(argl[1]), &iFrame) ){ |
| 1369 | iVar++; |
| 1370 | } |
| 1371 | if( argc==iVar || (argc-iVar)%2 ){ |
| 1372 | return Th_WrongNumArgs(interp, |
| 1373 | "upvar frame othervar myvar ?othervar myvar...?"); |
| 1374 | } |
| 1375 | for(i=iVar; rc==TH_OK && i<argc; i=i+2){ |
| 1376 | rc = Th_LinkVar(interp, argv[i+1], TH1_LEN(argl[i+1]), |
| 1377 | iFrame, argv[i], TH1_LEN(argl[i])); |
| 1378 | } |
| 1379 | return rc; |
| 1380 | } |
| 1381 | |
| 1382 | /* |
| 1383 |
+196
-37
| --- src/th_main.c | ||
| +++ src/th_main.c | ||
| @@ -262,11 +262,11 @@ | ||
| 262 | 262 | ){ |
| 263 | 263 | char *zOut; |
| 264 | 264 | if( argc!=2 ){ |
| 265 | 265 | return Th_WrongNumArgs(interp, "httpize STRING"); |
| 266 | 266 | } |
| 267 | - zOut = httpize((char*)argv[1], argl[1]); | |
| 267 | + zOut = httpize((char*)argv[1], TH1_LEN(argl[1])); | |
| 268 | 268 | Th_SetResult(interp, zOut, -1); |
| 269 | 269 | free(zOut); |
| 270 | 270 | return TH_OK; |
| 271 | 271 | } |
| 272 | 272 | |
| @@ -291,11 +291,12 @@ | ||
| 291 | 291 | if( argc<2 || argc>3 ){ |
| 292 | 292 | return Th_WrongNumArgs(interp, "enable_output [LABEL] BOOLEAN"); |
| 293 | 293 | } |
| 294 | 294 | rc = Th_ToInt(interp, argv[argc-1], argl[argc-1], &enableOutput); |
| 295 | 295 | if( g.thTrace ){ |
| 296 | - Th_Trace("enable_output {%.*s} -> %d<br>\n", argl[1],argv[1],enableOutput); | |
| 296 | + Th_Trace("enable_output {%.*s} -> %d<br>\n", | |
| 297 | + TH1_LEN(argl[1]),argv[1],enableOutput); | |
| 297 | 298 | } |
| 298 | 299 | return rc; |
| 299 | 300 | } |
| 300 | 301 | |
| 301 | 302 | /* |
| @@ -322,11 +323,11 @@ | ||
| 322 | 323 | buul = (TH_INIT_NO_ENCODE & g.th1Flags) ? 0 : 1; |
| 323 | 324 | Th_SetResultInt(g.interp, buul); |
| 324 | 325 | if(argc>1){ |
| 325 | 326 | if( g.thTrace ){ |
| 326 | 327 | Th_Trace("enable_htmlify {%.*s} -> %d<br>\n", |
| 327 | - argl[1],argv[1],buul); | |
| 328 | + TH1_LEN(argl[1]),argv[1],buul); | |
| 328 | 329 | } |
| 329 | 330 | rc = Th_ToInt(interp, argv[argc-1], argl[argc-1], &buul); |
| 330 | 331 | if(!rc){ |
| 331 | 332 | if(buul){ |
| 332 | 333 | g.th1Flags &= ~TH_INIT_NO_ENCODE; |
| @@ -381,19 +382,23 @@ | ||
| 381 | 382 | ** g.th1Flags has the TH_INIT_NO_ENCODE flag. |
| 382 | 383 | ** |
| 383 | 384 | ** If pOut is NULL and the global pThOut is not then that blob |
| 384 | 385 | ** is used for output. |
| 385 | 386 | */ |
| 386 | -static void sendText(Blob * pOut, const char *z, int n, int encode){ | |
| 387 | +static void sendText(Blob *pOut, const char *z, int n, int encode){ | |
| 387 | 388 | if(0==pOut && pThOut!=0){ |
| 388 | 389 | pOut = pThOut; |
| 389 | 390 | } |
| 390 | 391 | if(TH_INIT_NO_ENCODE & g.th1Flags){ |
| 391 | 392 | encode = 0; |
| 392 | 393 | } |
| 393 | 394 | if( enableOutput && n ){ |
| 394 | - if( n<0 ) n = strlen(z); | |
| 395 | + if( n<0 ){ | |
| 396 | + n = strlen(z); | |
| 397 | + }else{ | |
| 398 | + n = TH1_LEN(n); | |
| 399 | + } | |
| 395 | 400 | if( encode ){ |
| 396 | 401 | z = htmlize(z, n); |
| 397 | 402 | n = strlen(z); |
| 398 | 403 | } |
| 399 | 404 | if(pOut!=0){ |
| @@ -525,14 +530,23 @@ | ||
| 525 | 530 | void *pConvert, |
| 526 | 531 | int argc, |
| 527 | 532 | const char **argv, |
| 528 | 533 | int *argl |
| 529 | 534 | ){ |
| 535 | + int encode = *(unsigned int*)pConvert; | |
| 536 | + int n; | |
| 530 | 537 | if( argc!=2 ){ |
| 531 | 538 | return Th_WrongNumArgs(interp, "puts STRING"); |
| 532 | 539 | } |
| 533 | - sendText(0,(char*)argv[1], argl[1], *(unsigned int*)pConvert); | |
| 540 | + n = argl[1]; | |
| 541 | + if( encode==0 && n>0 && TH1_TAINTED(n) ){ | |
| 542 | + if( Th_ReportTaint(interp, "output string", argv[1], n) ){ | |
| 543 | + return TH_ERROR; | |
| 544 | + } | |
| 545 | + n = TH1_LEN(n); | |
| 546 | + } | |
| 547 | + sendText(0,(char*)argv[1], n, encode); | |
| 534 | 548 | return TH_OK; |
| 535 | 549 | } |
| 536 | 550 | |
| 537 | 551 | /* |
| 538 | 552 | ** TH1 command: redirect URL ?withMethod? |
| @@ -557,10 +571,15 @@ | ||
| 557 | 571 | } |
| 558 | 572 | if( argc==3 ){ |
| 559 | 573 | if( Th_ToInt(interp, argv[2], argl[2], &withMethod) ){ |
| 560 | 574 | return TH_ERROR; |
| 561 | 575 | } |
| 576 | + } | |
| 577 | + if( TH1_TAINTED(argl[1]) | |
| 578 | + && Th_ReportTaint(interp,"redirect URL",argv[1],argl[1]) | |
| 579 | + ){ | |
| 580 | + return TH_ERROR; | |
| 562 | 581 | } |
| 563 | 582 | if( withMethod ){ |
| 564 | 583 | cgi_redirect_with_method(argv[1]); |
| 565 | 584 | }else{ |
| 566 | 585 | cgi_redirect(argv[1]); |
| @@ -660,11 +679,11 @@ | ||
| 660 | 679 | int nValue = 0; |
| 661 | 680 | if( argc!=2 ){ |
| 662 | 681 | return Th_WrongNumArgs(interp, "markdown STRING"); |
| 663 | 682 | } |
| 664 | 683 | blob_zero(&src); |
| 665 | - blob_init(&src, (char*)argv[1], argl[1]); | |
| 684 | + blob_init(&src, (char*)argv[1], TH1_LEN(argl[1])); | |
| 666 | 685 | blob_zero(&title); blob_zero(&body); |
| 667 | 686 | markdown_to_html(&src, &title, &body); |
| 668 | 687 | Th_ListAppend(interp, &zValue, &nValue, blob_str(&title), blob_size(&title)); |
| 669 | 688 | Th_ListAppend(interp, &zValue, &nValue, blob_str(&body), blob_size(&body)); |
| 670 | 689 | Th_SetResult(interp, zValue, nValue); |
| @@ -690,11 +709,11 @@ | ||
| 690 | 709 | if( argc!=2 ){ |
| 691 | 710 | return Th_WrongNumArgs(interp, "wiki STRING"); |
| 692 | 711 | } |
| 693 | 712 | if( enableOutput ){ |
| 694 | 713 | Blob src; |
| 695 | - blob_init(&src, (char*)argv[1], argl[1]); | |
| 714 | + blob_init(&src, (char*)argv[1], TH1_LEN(argl[1])); | |
| 696 | 715 | wiki_convert(&src, 0, flags); |
| 697 | 716 | blob_reset(&src); |
| 698 | 717 | } |
| 699 | 718 | return TH_OK; |
| 700 | 719 | } |
| @@ -735,11 +754,11 @@ | ||
| 735 | 754 | ){ |
| 736 | 755 | char *zOut; |
| 737 | 756 | if( argc!=2 ){ |
| 738 | 757 | return Th_WrongNumArgs(interp, "htmlize STRING"); |
| 739 | 758 | } |
| 740 | - zOut = htmlize((char*)argv[1], argl[1]); | |
| 759 | + zOut = htmlize((char*)argv[1], TH1_LEN(argl[1])); | |
| 741 | 760 | Th_SetResult(interp, zOut, -1); |
| 742 | 761 | free(zOut); |
| 743 | 762 | return TH_OK; |
| 744 | 763 | } |
| 745 | 764 | |
| @@ -757,11 +776,11 @@ | ||
| 757 | 776 | ){ |
| 758 | 777 | char *zOut; |
| 759 | 778 | if( argc!=2 ){ |
| 760 | 779 | return Th_WrongNumArgs(interp, "encode64 STRING"); |
| 761 | 780 | } |
| 762 | - zOut = encode64((char*)argv[1], argl[1]); | |
| 781 | + zOut = encode64((char*)argv[1], TH1_LEN(argl[1])); | |
| 763 | 782 | Th_SetResult(interp, zOut, -1); |
| 764 | 783 | free(zOut); |
| 765 | 784 | return TH_OK; |
| 766 | 785 | } |
| 767 | 786 | |
| @@ -778,11 +797,11 @@ | ||
| 778 | 797 | int argc, |
| 779 | 798 | const char **argv, |
| 780 | 799 | int *argl |
| 781 | 800 | ){ |
| 782 | 801 | char *zOut; |
| 783 | - if( argc>=2 && argl[1]==6 && memcmp(argv[1],"-local",6)==0 ){ | |
| 802 | + if( argc>=2 && TH1_LEN(argl[1])==6 && memcmp(argv[1],"-local",6)==0 ){ | |
| 784 | 803 | zOut = db_text("??", "SELECT datetime('now',toLocal())"); |
| 785 | 804 | }else{ |
| 786 | 805 | zOut = db_text("??", "SELECT datetime('now')"); |
| 787 | 806 | } |
| 788 | 807 | Th_SetResult(interp, zOut, -1); |
| @@ -810,13 +829,13 @@ | ||
| 810 | 829 | if( argc<2 ){ |
| 811 | 830 | return Th_WrongNumArgs(interp, "hascap STRING ..."); |
| 812 | 831 | } |
| 813 | 832 | for(i=1; rc==1 && i<argc; i++){ |
| 814 | 833 | if( g.thTrace ){ |
| 815 | - Th_ListAppend(interp, &zCapList, &nCapList, argv[i], argl[i]); | |
| 834 | + Th_ListAppend(interp, &zCapList, &nCapList, argv[i], TH1_LEN(argl[i])); | |
| 816 | 835 | } |
| 817 | - rc = login_has_capability((char*)argv[i],argl[i],*(int*)p); | |
| 836 | + rc = login_has_capability((char*)argv[i],TH1_LEN(argl[i]),*(int*)p); | |
| 818 | 837 | } |
| 819 | 838 | if( g.thTrace ){ |
| 820 | 839 | Th_Trace("[%s %#h] => %d<br>\n", argv[0], nCapList, zCapList, rc); |
| 821 | 840 | Th_Free(interp, zCapList); |
| 822 | 841 | } |
| @@ -858,11 +877,11 @@ | ||
| 858 | 877 | int i; |
| 859 | 878 | |
| 860 | 879 | if( argc!=2 ){ |
| 861 | 880 | return Th_WrongNumArgs(interp, "capexpr EXPR"); |
| 862 | 881 | } |
| 863 | - rc = Th_SplitList(interp, argv[1], argl[1], &azCap, &anCap, &nCap); | |
| 882 | + rc = Th_SplitList(interp, argv[1], TH1_LEN(argl[1]), &azCap, &anCap, &nCap); | |
| 864 | 883 | if( rc ) return rc; |
| 865 | 884 | rc = 0; |
| 866 | 885 | for(i=0; i<nCap; i++){ |
| 867 | 886 | if( azCap[i][0]=='!' ){ |
| 868 | 887 | rc = !login_has_capability(azCap[i]+1, anCap[i]-1, 0); |
| @@ -921,11 +940,12 @@ | ||
| 921 | 940 | if( argc<2 ){ |
| 922 | 941 | return Th_WrongNumArgs(interp, "hascap STRING ..."); |
| 923 | 942 | } |
| 924 | 943 | for(i=1; i<argc && rc; i++){ |
| 925 | 944 | int match = 0; |
| 926 | - for(j=0; j<argl[i]; j++){ | |
| 945 | + int nn = TH1_LEN(argl[i]); | |
| 946 | + for(j=0; j<nn; j++){ | |
| 927 | 947 | switch( argv[i][j] ){ |
| 928 | 948 | case 'c': match |= searchCap & SRCH_CKIN; break; |
| 929 | 949 | case 'd': match |= searchCap & SRCH_DOC; break; |
| 930 | 950 | case 't': match |= searchCap & SRCH_TKT; break; |
| 931 | 951 | case 'w': match |= searchCap & SRCH_WIKI; break; |
| @@ -932,11 +952,11 @@ | ||
| 932 | 952 | } |
| 933 | 953 | } |
| 934 | 954 | if( !match ) rc = 0; |
| 935 | 955 | } |
| 936 | 956 | if( g.thTrace ){ |
| 937 | - Th_Trace("[searchable %#h] => %d<br>\n", argl[1], argv[1], rc); | |
| 957 | + Th_Trace("[searchable %#h] => %d<br>\n", TH1_LEN(argl[1]), argv[1], rc); | |
| 938 | 958 | } |
| 939 | 959 | Th_SetResultInt(interp, rc); |
| 940 | 960 | return TH_OK; |
| 941 | 961 | } |
| 942 | 962 | |
| @@ -1051,11 +1071,11 @@ | ||
| 1051 | 1071 | #endif |
| 1052 | 1072 | else if( 0 == fossil_strnicmp( zArg, "markdown\0", 9 ) ){ |
| 1053 | 1073 | rc = 1; |
| 1054 | 1074 | } |
| 1055 | 1075 | if( g.thTrace ){ |
| 1056 | - Th_Trace("[hasfeature %#h] => %d<br>\n", argl[1], zArg, rc); | |
| 1076 | + Th_Trace("[hasfeature %#h] => %d<br>\n", TH1_LEN(argl[1]), zArg, rc); | |
| 1057 | 1077 | } |
| 1058 | 1078 | Th_SetResultInt(interp, rc); |
| 1059 | 1079 | return TH_OK; |
| 1060 | 1080 | } |
| 1061 | 1081 | |
| @@ -1104,18 +1124,20 @@ | ||
| 1104 | 1124 | const char **argv, |
| 1105 | 1125 | int *argl |
| 1106 | 1126 | ){ |
| 1107 | 1127 | int rc = 0; |
| 1108 | 1128 | int i; |
| 1129 | + int nn; | |
| 1109 | 1130 | if( argc!=2 ){ |
| 1110 | 1131 | return Th_WrongNumArgs(interp, "anycap STRING"); |
| 1111 | 1132 | } |
| 1112 | - for(i=0; rc==0 && i<argl[1]; i++){ | |
| 1133 | + nn = TH1_LEN(argl[1]); | |
| 1134 | + for(i=0; rc==0 && i<nn; i++){ | |
| 1113 | 1135 | rc = login_has_capability((char*)&argv[1][i],1,0); |
| 1114 | 1136 | } |
| 1115 | 1137 | if( g.thTrace ){ |
| 1116 | - Th_Trace("[anycap %#h] => %d<br>\n", argl[1], argv[1], rc); | |
| 1138 | + Th_Trace("[anycap %#h] => %d<br>\n", TH1_LEN(argl[1]), argv[1], rc); | |
| 1117 | 1139 | } |
| 1118 | 1140 | Th_SetResultInt(interp, rc); |
| 1119 | 1141 | return TH_OK; |
| 1120 | 1142 | } |
| 1121 | 1143 | |
| @@ -1140,22 +1162,23 @@ | ||
| 1140 | 1162 | return Th_WrongNumArgs(interp, "combobox NAME TEXT-LIST NUMLINES"); |
| 1141 | 1163 | } |
| 1142 | 1164 | if( enableOutput ){ |
| 1143 | 1165 | int height; |
| 1144 | 1166 | Blob name; |
| 1145 | - int nValue; | |
| 1167 | + int nValue = 0; | |
| 1146 | 1168 | const char *zValue; |
| 1147 | 1169 | char *z, *zH; |
| 1148 | 1170 | int nElem; |
| 1149 | 1171 | int *aszElem; |
| 1150 | 1172 | char **azElem; |
| 1151 | 1173 | int i; |
| 1152 | 1174 | |
| 1153 | 1175 | if( Th_ToInt(interp, argv[3], argl[3], &height) ) return TH_ERROR; |
| 1154 | - Th_SplitList(interp, argv[2], argl[2], &azElem, &aszElem, &nElem); | |
| 1155 | - blob_init(&name, (char*)argv[1], argl[1]); | |
| 1176 | + Th_SplitList(interp, argv[2], TH1_LEN(argl[2]), &azElem, &aszElem, &nElem); | |
| 1177 | + blob_init(&name, (char*)argv[1], TH1_LEN(argl[1])); | |
| 1156 | 1178 | zValue = Th_Fetch(blob_str(&name), &nValue); |
| 1179 | + nValue = TH1_LEN(nValue); | |
| 1157 | 1180 | zH = htmlize(blob_buffer(&name), blob_size(&name)); |
| 1158 | 1181 | z = mprintf("<select id=\"%s\" name=\"%s\" size=\"%d\">", zH, zH, height); |
| 1159 | 1182 | free(zH); |
| 1160 | 1183 | sendText(0,z, -1, 0); |
| 1161 | 1184 | free(z); |
| @@ -1247,11 +1270,11 @@ | ||
| 1247 | 1270 | return Th_WrongNumArgs(interp, "linecount STRING MAX MIN"); |
| 1248 | 1271 | } |
| 1249 | 1272 | if( Th_ToInt(interp, argv[2], argl[2], &iMax) ) return TH_ERROR; |
| 1250 | 1273 | if( Th_ToInt(interp, argv[3], argl[3], &iMin) ) return TH_ERROR; |
| 1251 | 1274 | z = argv[1]; |
| 1252 | - size = argl[1]; | |
| 1275 | + size = TH1_LEN(argl[1]); | |
| 1253 | 1276 | for(n=1, i=0; i<size; i++){ |
| 1254 | 1277 | if( z[i]=='\n' ){ |
| 1255 | 1278 | n++; |
| 1256 | 1279 | if( n>=iMax ) break; |
| 1257 | 1280 | } |
| @@ -1407,11 +1430,12 @@ | ||
| 1407 | 1430 | return TH_OK; |
| 1408 | 1431 | }else if( fossil_strnicmp(argv[1], "vfs\0", 4)==0 ){ |
| 1409 | 1432 | Th_SetResult(interp, g.zVfsName ? g.zVfsName : zDefault, -1); |
| 1410 | 1433 | return TH_OK; |
| 1411 | 1434 | }else{ |
| 1412 | - Th_ErrorMessage(interp, "unsupported global state:", argv[1], argl[1]); | |
| 1435 | + Th_ErrorMessage(interp, "unsupported global state:", | |
| 1436 | + argv[1], TH1_LEN(argl[1])); | |
| 1413 | 1437 | return TH_ERROR; |
| 1414 | 1438 | } |
| 1415 | 1439 | } |
| 1416 | 1440 | |
| 1417 | 1441 | /* |
| @@ -1848,10 +1872,47 @@ | ||
| 1848 | 1872 | sqlite3_snprintf(sizeof(zUTime), zUTime, "%llu", x); |
| 1849 | 1873 | Th_SetResult(interp, zUTime, -1); |
| 1850 | 1874 | return TH_OK; |
| 1851 | 1875 | } |
| 1852 | 1876 | |
| 1877 | +/* | |
| 1878 | +** TH1 command: taint STRING | |
| 1879 | +** | |
| 1880 | +** Return a copy of STRING that is marked as tainted. | |
| 1881 | +*/ | |
| 1882 | +static int taintCmd( | |
| 1883 | + Th_Interp *interp, | |
| 1884 | + void *p, | |
| 1885 | + int argc, | |
| 1886 | + const char **argv, | |
| 1887 | + int *argl | |
| 1888 | +){ | |
| 1889 | + if( argc!=2 ){ | |
| 1890 | + return Th_WrongNumArgs(interp, "STRING"); | |
| 1891 | + } | |
| 1892 | + Th_SetResult(interp, argv[1], TH1_ADD_TAINT(argl[1])); | |
| 1893 | + return TH_OK; | |
| 1894 | +} | |
| 1895 | + | |
| 1896 | +/* | |
| 1897 | +** TH1 command: untaint STRING | |
| 1898 | +** | |
| 1899 | +** Return a copy of STRING that is marked as untainted. | |
| 1900 | +*/ | |
| 1901 | +static int untaintCmd( | |
| 1902 | + Th_Interp *interp, | |
| 1903 | + void *p, | |
| 1904 | + int argc, | |
| 1905 | + const char **argv, | |
| 1906 | + int *argl | |
| 1907 | +){ | |
| 1908 | + if( argc!=2 ){ | |
| 1909 | + return Th_WrongNumArgs(interp, "STRING"); | |
| 1910 | + } | |
| 1911 | + Th_SetResult(interp, argv[1], TH1_LEN(argl[1])); | |
| 1912 | + return TH_OK; | |
| 1913 | +} | |
| 1853 | 1914 | |
| 1854 | 1915 | /* |
| 1855 | 1916 | ** TH1 command: randhex N |
| 1856 | 1917 | ** |
| 1857 | 1918 | ** Return N*2 random hexadecimal digits with N<50. If N is omitted, |
| @@ -1923,11 +1984,13 @@ | ||
| 1923 | 1984 | int res = TH_OK; |
| 1924 | 1985 | int nVar; |
| 1925 | 1986 | char *zErr = 0; |
| 1926 | 1987 | int noComplain = 0; |
| 1927 | 1988 | |
| 1928 | - if( argc>3 && argl[1]==11 && strncmp(argv[1], "-nocomplain", 11)==0 ){ | |
| 1989 | + if( argc>3 && TH1_LEN(argl[1])==11 | |
| 1990 | + && strncmp(argv[1], "-nocomplain", 11)==0 | |
| 1991 | + ){ | |
| 1929 | 1992 | argc--; |
| 1930 | 1993 | argv++; |
| 1931 | 1994 | argl++; |
| 1932 | 1995 | noComplain = 1; |
| 1933 | 1996 | } |
| @@ -1939,15 +2002,22 @@ | ||
| 1939 | 2002 | Th_ErrorMessage(interp, "database is not open", 0, 0); |
| 1940 | 2003 | return TH_ERROR; |
| 1941 | 2004 | } |
| 1942 | 2005 | zSql = argv[1]; |
| 1943 | 2006 | nSql = argl[1]; |
| 2007 | + if( TH1_TAINTED(nSql) ){ | |
| 2008 | + if( Th_ReportTaint(interp,"query SQL",zSql,nSql) ){ | |
| 2009 | + return TH_ERROR; | |
| 2010 | + } | |
| 2011 | + nSql = TH1_LEN(nSql); | |
| 2012 | + } | |
| 2013 | + | |
| 1944 | 2014 | while( res==TH_OK && nSql>0 ){ |
| 1945 | 2015 | zErr = 0; |
| 1946 | 2016 | report_restrict_sql(&zErr); |
| 1947 | 2017 | g.dbIgnoreErrors++; |
| 1948 | - rc = sqlite3_prepare_v2(g.db, argv[1], argl[1], &pStmt, &zTail); | |
| 2018 | + rc = sqlite3_prepare_v2(g.db, argv[1], TH1_LEN(argl[1]), &pStmt, &zTail); | |
| 1949 | 2019 | g.dbIgnoreErrors--; |
| 1950 | 2020 | report_unrestrict_sql(); |
| 1951 | 2021 | if( rc!=0 || zErr!=0 ){ |
| 1952 | 2022 | if( noComplain ) return TH_OK; |
| 1953 | 2023 | Th_ErrorMessage(interp, "SQL error: ", |
| @@ -1964,31 +2034,31 @@ | ||
| 1964 | 2034 | int szVar = zVar ? th_strlen(zVar) : 0; |
| 1965 | 2035 | if( szVar>1 && zVar[0]=='$' |
| 1966 | 2036 | && Th_GetVar(interp, zVar+1, szVar-1)==TH_OK ){ |
| 1967 | 2037 | int nVal; |
| 1968 | 2038 | const char *zVal = Th_GetResult(interp, &nVal); |
| 1969 | - sqlite3_bind_text(pStmt, i, zVal, nVal, SQLITE_TRANSIENT); | |
| 2039 | + sqlite3_bind_text(pStmt, i, zVal, TH1_LEN(nVal), SQLITE_TRANSIENT); | |
| 1970 | 2040 | } |
| 1971 | 2041 | } |
| 1972 | 2042 | while( res==TH_OK && ignore_errors_step(pStmt)==SQLITE_ROW ){ |
| 1973 | 2043 | int nCol = sqlite3_column_count(pStmt); |
| 1974 | 2044 | for(i=0; i<nCol; i++){ |
| 1975 | 2045 | const char *zCol = sqlite3_column_name(pStmt, i); |
| 1976 | 2046 | int szCol = th_strlen(zCol); |
| 1977 | 2047 | const char *zVal = (const char*)sqlite3_column_text(pStmt, i); |
| 1978 | 2048 | int szVal = sqlite3_column_bytes(pStmt, i); |
| 1979 | - Th_SetVar(interp, zCol, szCol, zVal, szVal); | |
| 2049 | + Th_SetVar(interp, zCol, szCol, zVal, TH1_ADD_TAINT(szVal)); | |
| 1980 | 2050 | } |
| 1981 | 2051 | if( g.thTrace ){ |
| 1982 | - Th_Trace("query_eval {<pre>%#h</pre>}<br>\n", argl[2], argv[2]); | |
| 2052 | + Th_Trace("query_eval {<pre>%#h</pre>}<br>\n",TH1_LEN(argl[2]),argv[2]); | |
| 1983 | 2053 | } |
| 1984 | - res = Th_Eval(interp, 0, argv[2], argl[2]); | |
| 2054 | + res = Th_Eval(interp, 0, argv[2], TH1_LEN(argl[2])); | |
| 1985 | 2055 | if( g.thTrace ){ |
| 1986 | 2056 | int nTrRes; |
| 1987 | 2057 | char *zTrRes = (char*)Th_GetResult(g.interp, &nTrRes); |
| 1988 | 2058 | Th_Trace("[query_eval] => %h {%#h}<br>\n", |
| 1989 | - Th_ReturnCodeName(res, 0), nTrRes, zTrRes); | |
| 2059 | + Th_ReturnCodeName(res, 0), TH1_LEN(nTrRes), zTrRes); | |
| 1990 | 2060 | } |
| 1991 | 2061 | if( res==TH_BREAK || res==TH_CONTINUE ) res = TH_OK; |
| 1992 | 2062 | } |
| 1993 | 2063 | rc = sqlite3_finalize(pStmt); |
| 1994 | 2064 | if( rc!=SQLITE_OK ){ |
| @@ -2038,11 +2108,11 @@ | ||
| 2038 | 2108 | Th_SetResult(interp, 0, 0); |
| 2039 | 2109 | rc = TH_OK; |
| 2040 | 2110 | } |
| 2041 | 2111 | if( g.thTrace ){ |
| 2042 | 2112 | Th_Trace("[setting %s%#h] => %d<br>\n", strict ? "strict " : "", |
| 2043 | - argl[nArg], argv[nArg], rc); | |
| 2113 | + TH1_LEN(argl[nArg]), argv[nArg], rc); | |
| 2044 | 2114 | } |
| 2045 | 2115 | return rc; |
| 2046 | 2116 | } |
| 2047 | 2117 | |
| 2048 | 2118 | /* |
| @@ -2121,11 +2191,11 @@ | ||
| 2121 | 2191 | return Th_WrongNumArgs(interp, REGEXP_WRONGNUMARGS); |
| 2122 | 2192 | } |
| 2123 | 2193 | zErr = re_compile(&pRe, argv[nArg], noCase); |
| 2124 | 2194 | if( !zErr ){ |
| 2125 | 2195 | Th_SetResultInt(interp, re_match(pRe, |
| 2126 | - (const unsigned char *)argv[nArg+1], argl[nArg+1])); | |
| 2196 | + (const unsigned char *)argv[nArg+1], TH1_LEN(argl[nArg+1]))); | |
| 2127 | 2197 | rc = TH_OK; |
| 2128 | 2198 | }else{ |
| 2129 | 2199 | Th_SetResult(interp, zErr, -1); |
| 2130 | 2200 | rc = TH_ERROR; |
| 2131 | 2201 | } |
| @@ -2160,11 +2230,11 @@ | ||
| 2160 | 2230 | UrlData urlData; |
| 2161 | 2231 | |
| 2162 | 2232 | if( argc<2 || argc>5 ){ |
| 2163 | 2233 | return Th_WrongNumArgs(interp, HTTP_WRONGNUMARGS); |
| 2164 | 2234 | } |
| 2165 | - if( fossil_strnicmp(argv[nArg], "-asynchronous", argl[nArg])==0 ){ | |
| 2235 | + if( fossil_strnicmp(argv[nArg], "-asynchronous", TH1_LEN(argl[nArg]))==0 ){ | |
| 2166 | 2236 | fAsynchronous = 1; nArg++; |
| 2167 | 2237 | } |
| 2168 | 2238 | if( fossil_strcmp(argv[nArg], "--")==0 ) nArg++; |
| 2169 | 2239 | if( nArg+1!=argc && nArg+2!=argc ){ |
| 2170 | 2240 | return Th_WrongNumArgs(interp, REGEXP_WRONGNUMARGS); |
| @@ -2189,11 +2259,11 @@ | ||
| 2189 | 2259 | return TH_ERROR; |
| 2190 | 2260 | } |
| 2191 | 2261 | re_free(pRe); |
| 2192 | 2262 | blob_zero(&payload); |
| 2193 | 2263 | if( nArg+2==argc ){ |
| 2194 | - blob_append(&payload, argv[nArg+1], argl[nArg+1]); | |
| 2264 | + blob_append(&payload, argv[nArg+1], TH1_LEN(argl[nArg+1])); | |
| 2195 | 2265 | zType = "POST"; |
| 2196 | 2266 | }else{ |
| 2197 | 2267 | zType = "GET"; |
| 2198 | 2268 | } |
| 2199 | 2269 | if( fAsynchronous ){ |
| @@ -2268,11 +2338,11 @@ | ||
| 2268 | 2338 | if( argc!=2 ){ |
| 2269 | 2339 | return Th_WrongNumArgs(interp, "captureTh1 STRING"); |
| 2270 | 2340 | } |
| 2271 | 2341 | pOrig = Th_SetOutputBlob(&out); |
| 2272 | 2342 | zStr = argv[1]; |
| 2273 | - nStr = argl[1]; | |
| 2343 | + nStr = TH1_LEN(argl[1]); | |
| 2274 | 2344 | rc = Th_Eval(g.interp, 0, zStr, nStr); |
| 2275 | 2345 | Th_SetOutputBlob(pOrig); |
| 2276 | 2346 | if(0==rc){ |
| 2277 | 2347 | Th_SetResult(g.interp, blob_str(&out), blob_size(&out)); |
| 2278 | 2348 | } |
| @@ -2387,13 +2457,15 @@ | ||
| 2387 | 2457 | {"setting", settingCmd, 0}, |
| 2388 | 2458 | {"styleFooter", styleFooterCmd, 0}, |
| 2389 | 2459 | {"styleHeader", styleHeaderCmd, 0}, |
| 2390 | 2460 | {"styleScript", styleScriptCmd, 0}, |
| 2391 | 2461 | {"submenu", submenuCmd, 0}, |
| 2462 | + {"taint", taintCmd, 0}, | |
| 2392 | 2463 | {"tclReady", tclReadyCmd, 0}, |
| 2393 | 2464 | {"trace", traceCmd, 0}, |
| 2394 | 2465 | {"stime", stimeCmd, 0}, |
| 2466 | + {"untaint", untaintCmd, 0}, | |
| 2395 | 2467 | {"unversioned", unversionedCmd, 0}, |
| 2396 | 2468 | {"utime", utimeCmd, 0}, |
| 2397 | 2469 | {"verifyCsrf", verifyCsrfCmd, 0}, |
| 2398 | 2470 | {"verifyLogin", verifyLoginCmd, 0}, |
| 2399 | 2471 | {"wiki", wikiCmd, (void*)&aFlags[0]}, |
| @@ -2494,10 +2566,26 @@ | ||
| 2494 | 2566 | Th_Trace("set %h {%h}<br>\n", zName, zValue); |
| 2495 | 2567 | } |
| 2496 | 2568 | Th_SetVar(g.interp, zName, -1, zValue, strlen(zValue)); |
| 2497 | 2569 | } |
| 2498 | 2570 | } |
| 2571 | + | |
| 2572 | +/* | |
| 2573 | +** Store a string value in a variable in the interpreter | |
| 2574 | +** with the "taint" marking, so that TH1 knows that this | |
| 2575 | +** variable contains content under the control of the remote | |
| 2576 | +** user and presents a risk of XSS or SQL-injection attacks. | |
| 2577 | +*/ | |
| 2578 | +void Th_StoreUnsafe(const char *zName, const char *zValue){ | |
| 2579 | + Th_FossilInit(TH_INIT_DEFAULT); | |
| 2580 | + if( zValue ){ | |
| 2581 | + if( g.thTrace ){ | |
| 2582 | + Th_Trace("set %h [taint {%h}]<br>\n", zName, zValue); | |
| 2583 | + } | |
| 2584 | + Th_SetVar(g.interp, zName, -1, zValue, TH1_ADD_TAINT(strlen(zValue))); | |
| 2585 | + } | |
| 2586 | +} | |
| 2499 | 2587 | |
| 2500 | 2588 | /* |
| 2501 | 2589 | ** Appends an element to a TH1 list value. This function is called by the |
| 2502 | 2590 | ** transfer subsystem; therefore, it must be very careful to avoid doing |
| 2503 | 2591 | ** any unnecessary work. To that end, the TH1 subsystem will not be called |
| @@ -2680,10 +2768,11 @@ | ||
| 2680 | 2768 | char *zResult = (char*)Th_GetResult(g.interp, &nResult); |
| 2681 | 2769 | /* |
| 2682 | 2770 | ** Make sure that the TH1 script error was not caused by a "missing" |
| 2683 | 2771 | ** command hook handler as that is not actually an error condition. |
| 2684 | 2772 | */ |
| 2773 | + nResult = TH1_LEN(nResult); | |
| 2685 | 2774 | if( memcmp(zResult, NO_COMMAND_HOOK_ERROR, nResult)!=0 ){ |
| 2686 | 2775 | sendError(0,zResult, nResult, 0); |
| 2687 | 2776 | }else{ |
| 2688 | 2777 | /* |
| 2689 | 2778 | ** There is no command hook handler "installed". This situation |
| @@ -2767,10 +2856,11 @@ | ||
| 2767 | 2856 | char *zResult = (char*)Th_GetResult(g.interp, &nResult); |
| 2768 | 2857 | /* |
| 2769 | 2858 | ** Make sure that the TH1 script error was not caused by a "missing" |
| 2770 | 2859 | ** webpage hook handler as that is not actually an error condition. |
| 2771 | 2860 | */ |
| 2861 | + nResult = TH1_LEN(nResult); | |
| 2772 | 2862 | if( memcmp(zResult, NO_WEBPAGE_HOOK_ERROR, nResult)!=0 ){ |
| 2773 | 2863 | sendError(0,zResult, nResult, 1); |
| 2774 | 2864 | }else{ |
| 2775 | 2865 | /* |
| 2776 | 2866 | ** There is no webpage hook handler "installed". This situation |
| @@ -2907,11 +2997,11 @@ | ||
| 2907 | 2997 | rc = Th_Eval(g.interp, 0, (const char*)z, i); |
| 2908 | 2998 | if( g.thTrace ){ |
| 2909 | 2999 | int nTrRes; |
| 2910 | 3000 | char *zTrRes = (char*)Th_GetResult(g.interp, &nTrRes); |
| 2911 | 3001 | Th_Trace("[render_eval] => %h {%#h}<br>\n", |
| 2912 | - Th_ReturnCodeName(rc, 0), nTrRes, zTrRes); | |
| 3002 | + Th_ReturnCodeName(rc, 0), TH1_LEN(nTrRes), zTrRes); | |
| 2913 | 3003 | } |
| 2914 | 3004 | if( rc!=TH_OK ) break; |
| 2915 | 3005 | z += i; |
| 2916 | 3006 | if( z[0] ){ z += 6; } |
| 2917 | 3007 | i = 0; |
| @@ -2953,10 +3043,77 @@ | ||
| 2953 | 3043 | ** as appropriate. We need to pass on g.th1Flags for the case of |
| 2954 | 3044 | ** recursive calls, so that, e.g., TH_INIT_NO_ENCODE does not get |
| 2955 | 3045 | ** inadvertently toggled off by a recursive call. |
| 2956 | 3046 | */; |
| 2957 | 3047 | } |
| 3048 | + | |
| 3049 | +/* | |
| 3050 | +** SETTING: vuln-report width=8 default=log | |
| 3051 | +** | |
| 3052 | +** This setting controls Fossil's behavior when it encounters a potential | |
| 3053 | +** XSS or SQL-injection vulnerability due to misuse of TH1 configuration | |
| 3054 | +** scripts. Choices are: | |
| 3055 | +** | |
| 3056 | +** off Do nothing. Ignore the vulnerability. | |
| 3057 | +** | |
| 3058 | +** log Write a report of the problem into the error log. | |
| 3059 | +** | |
| 3060 | +** block Like "log" but also prevent the offending TH1 command | |
| 3061 | +** from running. | |
| 3062 | +** | |
| 3063 | +** fatal Render an error message page instead of the requested | |
| 3064 | +** page. | |
| 3065 | +*/ | |
| 3066 | + | |
| 3067 | +/* | |
| 3068 | +** Report misuse of a tainted string in TH1. | |
| 3069 | +** | |
| 3070 | +** The behavior depends on the vuln-report setting. If "off", this routine | |
| 3071 | +** is a no-op. Otherwise, right a message into the error log. If | |
| 3072 | +** vuln-report is "log", that is all that happens. But for any other | |
| 3073 | +** value of vuln-report, a fatal error is raised. | |
| 3074 | +*/ | |
| 3075 | +int Th_ReportTaint( | |
| 3076 | + Th_Interp *interp, /* Report error here, if an error is reported */ | |
| 3077 | + const char *zWhere, /* Where the tainted string appears */ | |
| 3078 | + const char *zStr, /* The tainted string */ | |
| 3079 | + int nStr /* Length of the tainted string */ | |
| 3080 | +){ | |
| 3081 | + char *zDisp; /* Dispensation */ | |
| 3082 | + const char *zVulnType; /* Type of vulnerability */ | |
| 3083 | + | |
| 3084 | + zDisp = db_get("vuln-report","log"); | |
| 3085 | + if( is_false(zDisp) ) return 0; | |
| 3086 | + if( strstr(zWhere,"SQL")!=0 ){ | |
| 3087 | + zVulnType = "SQL-injection"; | |
| 3088 | + }else{ | |
| 3089 | + zVulnType = "XSS"; | |
| 3090 | + } | |
| 3091 | + nStr = TH1_LEN(nStr); | |
| 3092 | + fossil_errorlog("possible %s vulnerability due to tainted TH1 %s: \"%.*s\"", | |
| 3093 | + zVulnType, zWhere, nStr, zStr); | |
| 3094 | + if( strcmp(zDisp,"log")==0 ){ | |
| 3095 | + return 0; | |
| 3096 | + } | |
| 3097 | + if( strcmp(zDisp,"block")==0 ){ | |
| 3098 | + char *z = mprintf("tainted %s: \"", zWhere); | |
| 3099 | + Th_ErrorMessage(interp, z, zStr, nStr); | |
| 3100 | + fossil_free(z); | |
| 3101 | + }else{ | |
| 3102 | + char *z = mprintf("%#h", nStr, zStr); | |
| 3103 | + cgi_reset_content(); | |
| 3104 | + style_submenu_enable(0); | |
| 3105 | + style_set_current_feature("error"); | |
| 3106 | + style_header("Configuration Error"); | |
| 3107 | + @ <p>Error in a TH1 configuration script: | |
| 3108 | + @ tainted %h(zWhere): "%z(z)" | |
| 3109 | + style_finish_page(); | |
| 3110 | + cgi_reply(); | |
| 3111 | + fossil_exit(1); | |
| 3112 | + } | |
| 3113 | + return 1; | |
| 3114 | +} | |
| 2958 | 3115 | |
| 2959 | 3116 | /* |
| 2960 | 3117 | ** COMMAND: test-th-render |
| 2961 | 3118 | ** |
| 2962 | 3119 | ** Usage: %fossil test-th-render FILE |
| @@ -2992,10 +3149,11 @@ | ||
| 2992 | 3149 | if( find_option("set-user-caps", 0, 0)!=0 ){ |
| 2993 | 3150 | const char *zCap = fossil_getenv("TH1_TEST_USER_CAPS"); |
| 2994 | 3151 | login_set_capabilities(zCap ? zCap : "sx", 0); |
| 2995 | 3152 | g.useLocalauth = 1; |
| 2996 | 3153 | } |
| 3154 | + db_find_and_open_repository(OPEN_OK_NOT_FOUND|OPEN_SUBSTITUTE,0); | |
| 2997 | 3155 | verify_all_options(); |
| 2998 | 3156 | if( g.argc<3 ){ |
| 2999 | 3157 | usage("FILE"); |
| 3000 | 3158 | } |
| 3001 | 3159 | blob_zero(&in); |
| @@ -3044,10 +3202,11 @@ | ||
| 3044 | 3202 | if( find_option("set-user-caps", 0, 0)!=0 ){ |
| 3045 | 3203 | const char *zCap = fossil_getenv("TH1_TEST_USER_CAPS"); |
| 3046 | 3204 | login_set_capabilities(zCap ? zCap : "sx", 0); |
| 3047 | 3205 | g.useLocalauth = 1; |
| 3048 | 3206 | } |
| 3207 | + db_find_and_open_repository(OPEN_OK_NOT_FOUND|OPEN_SUBSTITUTE,0); | |
| 3049 | 3208 | verify_all_options(); |
| 3050 | 3209 | if( g.argc!=3 ){ |
| 3051 | 3210 | usage("script"); |
| 3052 | 3211 | } |
| 3053 | 3212 | if(file_isfile(g.argv[2], ExtFILE)){ |
| 3054 | 3213 |
| --- src/th_main.c | |
| +++ src/th_main.c | |
| @@ -262,11 +262,11 @@ | |
| 262 | ){ |
| 263 | char *zOut; |
| 264 | if( argc!=2 ){ |
| 265 | return Th_WrongNumArgs(interp, "httpize STRING"); |
| 266 | } |
| 267 | zOut = httpize((char*)argv[1], argl[1]); |
| 268 | Th_SetResult(interp, zOut, -1); |
| 269 | free(zOut); |
| 270 | return TH_OK; |
| 271 | } |
| 272 | |
| @@ -291,11 +291,12 @@ | |
| 291 | if( argc<2 || argc>3 ){ |
| 292 | return Th_WrongNumArgs(interp, "enable_output [LABEL] BOOLEAN"); |
| 293 | } |
| 294 | rc = Th_ToInt(interp, argv[argc-1], argl[argc-1], &enableOutput); |
| 295 | if( g.thTrace ){ |
| 296 | Th_Trace("enable_output {%.*s} -> %d<br>\n", argl[1],argv[1],enableOutput); |
| 297 | } |
| 298 | return rc; |
| 299 | } |
| 300 | |
| 301 | /* |
| @@ -322,11 +323,11 @@ | |
| 322 | buul = (TH_INIT_NO_ENCODE & g.th1Flags) ? 0 : 1; |
| 323 | Th_SetResultInt(g.interp, buul); |
| 324 | if(argc>1){ |
| 325 | if( g.thTrace ){ |
| 326 | Th_Trace("enable_htmlify {%.*s} -> %d<br>\n", |
| 327 | argl[1],argv[1],buul); |
| 328 | } |
| 329 | rc = Th_ToInt(interp, argv[argc-1], argl[argc-1], &buul); |
| 330 | if(!rc){ |
| 331 | if(buul){ |
| 332 | g.th1Flags &= ~TH_INIT_NO_ENCODE; |
| @@ -381,19 +382,23 @@ | |
| 381 | ** g.th1Flags has the TH_INIT_NO_ENCODE flag. |
| 382 | ** |
| 383 | ** If pOut is NULL and the global pThOut is not then that blob |
| 384 | ** is used for output. |
| 385 | */ |
| 386 | static void sendText(Blob * pOut, const char *z, int n, int encode){ |
| 387 | if(0==pOut && pThOut!=0){ |
| 388 | pOut = pThOut; |
| 389 | } |
| 390 | if(TH_INIT_NO_ENCODE & g.th1Flags){ |
| 391 | encode = 0; |
| 392 | } |
| 393 | if( enableOutput && n ){ |
| 394 | if( n<0 ) n = strlen(z); |
| 395 | if( encode ){ |
| 396 | z = htmlize(z, n); |
| 397 | n = strlen(z); |
| 398 | } |
| 399 | if(pOut!=0){ |
| @@ -525,14 +530,23 @@ | |
| 525 | void *pConvert, |
| 526 | int argc, |
| 527 | const char **argv, |
| 528 | int *argl |
| 529 | ){ |
| 530 | if( argc!=2 ){ |
| 531 | return Th_WrongNumArgs(interp, "puts STRING"); |
| 532 | } |
| 533 | sendText(0,(char*)argv[1], argl[1], *(unsigned int*)pConvert); |
| 534 | return TH_OK; |
| 535 | } |
| 536 | |
| 537 | /* |
| 538 | ** TH1 command: redirect URL ?withMethod? |
| @@ -557,10 +571,15 @@ | |
| 557 | } |
| 558 | if( argc==3 ){ |
| 559 | if( Th_ToInt(interp, argv[2], argl[2], &withMethod) ){ |
| 560 | return TH_ERROR; |
| 561 | } |
| 562 | } |
| 563 | if( withMethod ){ |
| 564 | cgi_redirect_with_method(argv[1]); |
| 565 | }else{ |
| 566 | cgi_redirect(argv[1]); |
| @@ -660,11 +679,11 @@ | |
| 660 | int nValue = 0; |
| 661 | if( argc!=2 ){ |
| 662 | return Th_WrongNumArgs(interp, "markdown STRING"); |
| 663 | } |
| 664 | blob_zero(&src); |
| 665 | blob_init(&src, (char*)argv[1], argl[1]); |
| 666 | blob_zero(&title); blob_zero(&body); |
| 667 | markdown_to_html(&src, &title, &body); |
| 668 | Th_ListAppend(interp, &zValue, &nValue, blob_str(&title), blob_size(&title)); |
| 669 | Th_ListAppend(interp, &zValue, &nValue, blob_str(&body), blob_size(&body)); |
| 670 | Th_SetResult(interp, zValue, nValue); |
| @@ -690,11 +709,11 @@ | |
| 690 | if( argc!=2 ){ |
| 691 | return Th_WrongNumArgs(interp, "wiki STRING"); |
| 692 | } |
| 693 | if( enableOutput ){ |
| 694 | Blob src; |
| 695 | blob_init(&src, (char*)argv[1], argl[1]); |
| 696 | wiki_convert(&src, 0, flags); |
| 697 | blob_reset(&src); |
| 698 | } |
| 699 | return TH_OK; |
| 700 | } |
| @@ -735,11 +754,11 @@ | |
| 735 | ){ |
| 736 | char *zOut; |
| 737 | if( argc!=2 ){ |
| 738 | return Th_WrongNumArgs(interp, "htmlize STRING"); |
| 739 | } |
| 740 | zOut = htmlize((char*)argv[1], argl[1]); |
| 741 | Th_SetResult(interp, zOut, -1); |
| 742 | free(zOut); |
| 743 | return TH_OK; |
| 744 | } |
| 745 | |
| @@ -757,11 +776,11 @@ | |
| 757 | ){ |
| 758 | char *zOut; |
| 759 | if( argc!=2 ){ |
| 760 | return Th_WrongNumArgs(interp, "encode64 STRING"); |
| 761 | } |
| 762 | zOut = encode64((char*)argv[1], argl[1]); |
| 763 | Th_SetResult(interp, zOut, -1); |
| 764 | free(zOut); |
| 765 | return TH_OK; |
| 766 | } |
| 767 | |
| @@ -778,11 +797,11 @@ | |
| 778 | int argc, |
| 779 | const char **argv, |
| 780 | int *argl |
| 781 | ){ |
| 782 | char *zOut; |
| 783 | if( argc>=2 && argl[1]==6 && memcmp(argv[1],"-local",6)==0 ){ |
| 784 | zOut = db_text("??", "SELECT datetime('now',toLocal())"); |
| 785 | }else{ |
| 786 | zOut = db_text("??", "SELECT datetime('now')"); |
| 787 | } |
| 788 | Th_SetResult(interp, zOut, -1); |
| @@ -810,13 +829,13 @@ | |
| 810 | if( argc<2 ){ |
| 811 | return Th_WrongNumArgs(interp, "hascap STRING ..."); |
| 812 | } |
| 813 | for(i=1; rc==1 && i<argc; i++){ |
| 814 | if( g.thTrace ){ |
| 815 | Th_ListAppend(interp, &zCapList, &nCapList, argv[i], argl[i]); |
| 816 | } |
| 817 | rc = login_has_capability((char*)argv[i],argl[i],*(int*)p); |
| 818 | } |
| 819 | if( g.thTrace ){ |
| 820 | Th_Trace("[%s %#h] => %d<br>\n", argv[0], nCapList, zCapList, rc); |
| 821 | Th_Free(interp, zCapList); |
| 822 | } |
| @@ -858,11 +877,11 @@ | |
| 858 | int i; |
| 859 | |
| 860 | if( argc!=2 ){ |
| 861 | return Th_WrongNumArgs(interp, "capexpr EXPR"); |
| 862 | } |
| 863 | rc = Th_SplitList(interp, argv[1], argl[1], &azCap, &anCap, &nCap); |
| 864 | if( rc ) return rc; |
| 865 | rc = 0; |
| 866 | for(i=0; i<nCap; i++){ |
| 867 | if( azCap[i][0]=='!' ){ |
| 868 | rc = !login_has_capability(azCap[i]+1, anCap[i]-1, 0); |
| @@ -921,11 +940,12 @@ | |
| 921 | if( argc<2 ){ |
| 922 | return Th_WrongNumArgs(interp, "hascap STRING ..."); |
| 923 | } |
| 924 | for(i=1; i<argc && rc; i++){ |
| 925 | int match = 0; |
| 926 | for(j=0; j<argl[i]; j++){ |
| 927 | switch( argv[i][j] ){ |
| 928 | case 'c': match |= searchCap & SRCH_CKIN; break; |
| 929 | case 'd': match |= searchCap & SRCH_DOC; break; |
| 930 | case 't': match |= searchCap & SRCH_TKT; break; |
| 931 | case 'w': match |= searchCap & SRCH_WIKI; break; |
| @@ -932,11 +952,11 @@ | |
| 932 | } |
| 933 | } |
| 934 | if( !match ) rc = 0; |
| 935 | } |
| 936 | if( g.thTrace ){ |
| 937 | Th_Trace("[searchable %#h] => %d<br>\n", argl[1], argv[1], rc); |
| 938 | } |
| 939 | Th_SetResultInt(interp, rc); |
| 940 | return TH_OK; |
| 941 | } |
| 942 | |
| @@ -1051,11 +1071,11 @@ | |
| 1051 | #endif |
| 1052 | else if( 0 == fossil_strnicmp( zArg, "markdown\0", 9 ) ){ |
| 1053 | rc = 1; |
| 1054 | } |
| 1055 | if( g.thTrace ){ |
| 1056 | Th_Trace("[hasfeature %#h] => %d<br>\n", argl[1], zArg, rc); |
| 1057 | } |
| 1058 | Th_SetResultInt(interp, rc); |
| 1059 | return TH_OK; |
| 1060 | } |
| 1061 | |
| @@ -1104,18 +1124,20 @@ | |
| 1104 | const char **argv, |
| 1105 | int *argl |
| 1106 | ){ |
| 1107 | int rc = 0; |
| 1108 | int i; |
| 1109 | if( argc!=2 ){ |
| 1110 | return Th_WrongNumArgs(interp, "anycap STRING"); |
| 1111 | } |
| 1112 | for(i=0; rc==0 && i<argl[1]; i++){ |
| 1113 | rc = login_has_capability((char*)&argv[1][i],1,0); |
| 1114 | } |
| 1115 | if( g.thTrace ){ |
| 1116 | Th_Trace("[anycap %#h] => %d<br>\n", argl[1], argv[1], rc); |
| 1117 | } |
| 1118 | Th_SetResultInt(interp, rc); |
| 1119 | return TH_OK; |
| 1120 | } |
| 1121 | |
| @@ -1140,22 +1162,23 @@ | |
| 1140 | return Th_WrongNumArgs(interp, "combobox NAME TEXT-LIST NUMLINES"); |
| 1141 | } |
| 1142 | if( enableOutput ){ |
| 1143 | int height; |
| 1144 | Blob name; |
| 1145 | int nValue; |
| 1146 | const char *zValue; |
| 1147 | char *z, *zH; |
| 1148 | int nElem; |
| 1149 | int *aszElem; |
| 1150 | char **azElem; |
| 1151 | int i; |
| 1152 | |
| 1153 | if( Th_ToInt(interp, argv[3], argl[3], &height) ) return TH_ERROR; |
| 1154 | Th_SplitList(interp, argv[2], argl[2], &azElem, &aszElem, &nElem); |
| 1155 | blob_init(&name, (char*)argv[1], argl[1]); |
| 1156 | zValue = Th_Fetch(blob_str(&name), &nValue); |
| 1157 | zH = htmlize(blob_buffer(&name), blob_size(&name)); |
| 1158 | z = mprintf("<select id=\"%s\" name=\"%s\" size=\"%d\">", zH, zH, height); |
| 1159 | free(zH); |
| 1160 | sendText(0,z, -1, 0); |
| 1161 | free(z); |
| @@ -1247,11 +1270,11 @@ | |
| 1247 | return Th_WrongNumArgs(interp, "linecount STRING MAX MIN"); |
| 1248 | } |
| 1249 | if( Th_ToInt(interp, argv[2], argl[2], &iMax) ) return TH_ERROR; |
| 1250 | if( Th_ToInt(interp, argv[3], argl[3], &iMin) ) return TH_ERROR; |
| 1251 | z = argv[1]; |
| 1252 | size = argl[1]; |
| 1253 | for(n=1, i=0; i<size; i++){ |
| 1254 | if( z[i]=='\n' ){ |
| 1255 | n++; |
| 1256 | if( n>=iMax ) break; |
| 1257 | } |
| @@ -1407,11 +1430,12 @@ | |
| 1407 | return TH_OK; |
| 1408 | }else if( fossil_strnicmp(argv[1], "vfs\0", 4)==0 ){ |
| 1409 | Th_SetResult(interp, g.zVfsName ? g.zVfsName : zDefault, -1); |
| 1410 | return TH_OK; |
| 1411 | }else{ |
| 1412 | Th_ErrorMessage(interp, "unsupported global state:", argv[1], argl[1]); |
| 1413 | return TH_ERROR; |
| 1414 | } |
| 1415 | } |
| 1416 | |
| 1417 | /* |
| @@ -1848,10 +1872,47 @@ | |
| 1848 | sqlite3_snprintf(sizeof(zUTime), zUTime, "%llu", x); |
| 1849 | Th_SetResult(interp, zUTime, -1); |
| 1850 | return TH_OK; |
| 1851 | } |
| 1852 | |
| 1853 | |
| 1854 | /* |
| 1855 | ** TH1 command: randhex N |
| 1856 | ** |
| 1857 | ** Return N*2 random hexadecimal digits with N<50. If N is omitted, |
| @@ -1923,11 +1984,13 @@ | |
| 1923 | int res = TH_OK; |
| 1924 | int nVar; |
| 1925 | char *zErr = 0; |
| 1926 | int noComplain = 0; |
| 1927 | |
| 1928 | if( argc>3 && argl[1]==11 && strncmp(argv[1], "-nocomplain", 11)==0 ){ |
| 1929 | argc--; |
| 1930 | argv++; |
| 1931 | argl++; |
| 1932 | noComplain = 1; |
| 1933 | } |
| @@ -1939,15 +2002,22 @@ | |
| 1939 | Th_ErrorMessage(interp, "database is not open", 0, 0); |
| 1940 | return TH_ERROR; |
| 1941 | } |
| 1942 | zSql = argv[1]; |
| 1943 | nSql = argl[1]; |
| 1944 | while( res==TH_OK && nSql>0 ){ |
| 1945 | zErr = 0; |
| 1946 | report_restrict_sql(&zErr); |
| 1947 | g.dbIgnoreErrors++; |
| 1948 | rc = sqlite3_prepare_v2(g.db, argv[1], argl[1], &pStmt, &zTail); |
| 1949 | g.dbIgnoreErrors--; |
| 1950 | report_unrestrict_sql(); |
| 1951 | if( rc!=0 || zErr!=0 ){ |
| 1952 | if( noComplain ) return TH_OK; |
| 1953 | Th_ErrorMessage(interp, "SQL error: ", |
| @@ -1964,31 +2034,31 @@ | |
| 1964 | int szVar = zVar ? th_strlen(zVar) : 0; |
| 1965 | if( szVar>1 && zVar[0]=='$' |
| 1966 | && Th_GetVar(interp, zVar+1, szVar-1)==TH_OK ){ |
| 1967 | int nVal; |
| 1968 | const char *zVal = Th_GetResult(interp, &nVal); |
| 1969 | sqlite3_bind_text(pStmt, i, zVal, nVal, SQLITE_TRANSIENT); |
| 1970 | } |
| 1971 | } |
| 1972 | while( res==TH_OK && ignore_errors_step(pStmt)==SQLITE_ROW ){ |
| 1973 | int nCol = sqlite3_column_count(pStmt); |
| 1974 | for(i=0; i<nCol; i++){ |
| 1975 | const char *zCol = sqlite3_column_name(pStmt, i); |
| 1976 | int szCol = th_strlen(zCol); |
| 1977 | const char *zVal = (const char*)sqlite3_column_text(pStmt, i); |
| 1978 | int szVal = sqlite3_column_bytes(pStmt, i); |
| 1979 | Th_SetVar(interp, zCol, szCol, zVal, szVal); |
| 1980 | } |
| 1981 | if( g.thTrace ){ |
| 1982 | Th_Trace("query_eval {<pre>%#h</pre>}<br>\n", argl[2], argv[2]); |
| 1983 | } |
| 1984 | res = Th_Eval(interp, 0, argv[2], argl[2]); |
| 1985 | if( g.thTrace ){ |
| 1986 | int nTrRes; |
| 1987 | char *zTrRes = (char*)Th_GetResult(g.interp, &nTrRes); |
| 1988 | Th_Trace("[query_eval] => %h {%#h}<br>\n", |
| 1989 | Th_ReturnCodeName(res, 0), nTrRes, zTrRes); |
| 1990 | } |
| 1991 | if( res==TH_BREAK || res==TH_CONTINUE ) res = TH_OK; |
| 1992 | } |
| 1993 | rc = sqlite3_finalize(pStmt); |
| 1994 | if( rc!=SQLITE_OK ){ |
| @@ -2038,11 +2108,11 @@ | |
| 2038 | Th_SetResult(interp, 0, 0); |
| 2039 | rc = TH_OK; |
| 2040 | } |
| 2041 | if( g.thTrace ){ |
| 2042 | Th_Trace("[setting %s%#h] => %d<br>\n", strict ? "strict " : "", |
| 2043 | argl[nArg], argv[nArg], rc); |
| 2044 | } |
| 2045 | return rc; |
| 2046 | } |
| 2047 | |
| 2048 | /* |
| @@ -2121,11 +2191,11 @@ | |
| 2121 | return Th_WrongNumArgs(interp, REGEXP_WRONGNUMARGS); |
| 2122 | } |
| 2123 | zErr = re_compile(&pRe, argv[nArg], noCase); |
| 2124 | if( !zErr ){ |
| 2125 | Th_SetResultInt(interp, re_match(pRe, |
| 2126 | (const unsigned char *)argv[nArg+1], argl[nArg+1])); |
| 2127 | rc = TH_OK; |
| 2128 | }else{ |
| 2129 | Th_SetResult(interp, zErr, -1); |
| 2130 | rc = TH_ERROR; |
| 2131 | } |
| @@ -2160,11 +2230,11 @@ | |
| 2160 | UrlData urlData; |
| 2161 | |
| 2162 | if( argc<2 || argc>5 ){ |
| 2163 | return Th_WrongNumArgs(interp, HTTP_WRONGNUMARGS); |
| 2164 | } |
| 2165 | if( fossil_strnicmp(argv[nArg], "-asynchronous", argl[nArg])==0 ){ |
| 2166 | fAsynchronous = 1; nArg++; |
| 2167 | } |
| 2168 | if( fossil_strcmp(argv[nArg], "--")==0 ) nArg++; |
| 2169 | if( nArg+1!=argc && nArg+2!=argc ){ |
| 2170 | return Th_WrongNumArgs(interp, REGEXP_WRONGNUMARGS); |
| @@ -2189,11 +2259,11 @@ | |
| 2189 | return TH_ERROR; |
| 2190 | } |
| 2191 | re_free(pRe); |
| 2192 | blob_zero(&payload); |
| 2193 | if( nArg+2==argc ){ |
| 2194 | blob_append(&payload, argv[nArg+1], argl[nArg+1]); |
| 2195 | zType = "POST"; |
| 2196 | }else{ |
| 2197 | zType = "GET"; |
| 2198 | } |
| 2199 | if( fAsynchronous ){ |
| @@ -2268,11 +2338,11 @@ | |
| 2268 | if( argc!=2 ){ |
| 2269 | return Th_WrongNumArgs(interp, "captureTh1 STRING"); |
| 2270 | } |
| 2271 | pOrig = Th_SetOutputBlob(&out); |
| 2272 | zStr = argv[1]; |
| 2273 | nStr = argl[1]; |
| 2274 | rc = Th_Eval(g.interp, 0, zStr, nStr); |
| 2275 | Th_SetOutputBlob(pOrig); |
| 2276 | if(0==rc){ |
| 2277 | Th_SetResult(g.interp, blob_str(&out), blob_size(&out)); |
| 2278 | } |
| @@ -2387,13 +2457,15 @@ | |
| 2387 | {"setting", settingCmd, 0}, |
| 2388 | {"styleFooter", styleFooterCmd, 0}, |
| 2389 | {"styleHeader", styleHeaderCmd, 0}, |
| 2390 | {"styleScript", styleScriptCmd, 0}, |
| 2391 | {"submenu", submenuCmd, 0}, |
| 2392 | {"tclReady", tclReadyCmd, 0}, |
| 2393 | {"trace", traceCmd, 0}, |
| 2394 | {"stime", stimeCmd, 0}, |
| 2395 | {"unversioned", unversionedCmd, 0}, |
| 2396 | {"utime", utimeCmd, 0}, |
| 2397 | {"verifyCsrf", verifyCsrfCmd, 0}, |
| 2398 | {"verifyLogin", verifyLoginCmd, 0}, |
| 2399 | {"wiki", wikiCmd, (void*)&aFlags[0]}, |
| @@ -2494,10 +2566,26 @@ | |
| 2494 | Th_Trace("set %h {%h}<br>\n", zName, zValue); |
| 2495 | } |
| 2496 | Th_SetVar(g.interp, zName, -1, zValue, strlen(zValue)); |
| 2497 | } |
| 2498 | } |
| 2499 | |
| 2500 | /* |
| 2501 | ** Appends an element to a TH1 list value. This function is called by the |
| 2502 | ** transfer subsystem; therefore, it must be very careful to avoid doing |
| 2503 | ** any unnecessary work. To that end, the TH1 subsystem will not be called |
| @@ -2680,10 +2768,11 @@ | |
| 2680 | char *zResult = (char*)Th_GetResult(g.interp, &nResult); |
| 2681 | /* |
| 2682 | ** Make sure that the TH1 script error was not caused by a "missing" |
| 2683 | ** command hook handler as that is not actually an error condition. |
| 2684 | */ |
| 2685 | if( memcmp(zResult, NO_COMMAND_HOOK_ERROR, nResult)!=0 ){ |
| 2686 | sendError(0,zResult, nResult, 0); |
| 2687 | }else{ |
| 2688 | /* |
| 2689 | ** There is no command hook handler "installed". This situation |
| @@ -2767,10 +2856,11 @@ | |
| 2767 | char *zResult = (char*)Th_GetResult(g.interp, &nResult); |
| 2768 | /* |
| 2769 | ** Make sure that the TH1 script error was not caused by a "missing" |
| 2770 | ** webpage hook handler as that is not actually an error condition. |
| 2771 | */ |
| 2772 | if( memcmp(zResult, NO_WEBPAGE_HOOK_ERROR, nResult)!=0 ){ |
| 2773 | sendError(0,zResult, nResult, 1); |
| 2774 | }else{ |
| 2775 | /* |
| 2776 | ** There is no webpage hook handler "installed". This situation |
| @@ -2907,11 +2997,11 @@ | |
| 2907 | rc = Th_Eval(g.interp, 0, (const char*)z, i); |
| 2908 | if( g.thTrace ){ |
| 2909 | int nTrRes; |
| 2910 | char *zTrRes = (char*)Th_GetResult(g.interp, &nTrRes); |
| 2911 | Th_Trace("[render_eval] => %h {%#h}<br>\n", |
| 2912 | Th_ReturnCodeName(rc, 0), nTrRes, zTrRes); |
| 2913 | } |
| 2914 | if( rc!=TH_OK ) break; |
| 2915 | z += i; |
| 2916 | if( z[0] ){ z += 6; } |
| 2917 | i = 0; |
| @@ -2953,10 +3043,77 @@ | |
| 2953 | ** as appropriate. We need to pass on g.th1Flags for the case of |
| 2954 | ** recursive calls, so that, e.g., TH_INIT_NO_ENCODE does not get |
| 2955 | ** inadvertently toggled off by a recursive call. |
| 2956 | */; |
| 2957 | } |
| 2958 | |
| 2959 | /* |
| 2960 | ** COMMAND: test-th-render |
| 2961 | ** |
| 2962 | ** Usage: %fossil test-th-render FILE |
| @@ -2992,10 +3149,11 @@ | |
| 2992 | if( find_option("set-user-caps", 0, 0)!=0 ){ |
| 2993 | const char *zCap = fossil_getenv("TH1_TEST_USER_CAPS"); |
| 2994 | login_set_capabilities(zCap ? zCap : "sx", 0); |
| 2995 | g.useLocalauth = 1; |
| 2996 | } |
| 2997 | verify_all_options(); |
| 2998 | if( g.argc<3 ){ |
| 2999 | usage("FILE"); |
| 3000 | } |
| 3001 | blob_zero(&in); |
| @@ -3044,10 +3202,11 @@ | |
| 3044 | if( find_option("set-user-caps", 0, 0)!=0 ){ |
| 3045 | const char *zCap = fossil_getenv("TH1_TEST_USER_CAPS"); |
| 3046 | login_set_capabilities(zCap ? zCap : "sx", 0); |
| 3047 | g.useLocalauth = 1; |
| 3048 | } |
| 3049 | verify_all_options(); |
| 3050 | if( g.argc!=3 ){ |
| 3051 | usage("script"); |
| 3052 | } |
| 3053 | if(file_isfile(g.argv[2], ExtFILE)){ |
| 3054 |
| --- src/th_main.c | |
| +++ src/th_main.c | |
| @@ -262,11 +262,11 @@ | |
| 262 | ){ |
| 263 | char *zOut; |
| 264 | if( argc!=2 ){ |
| 265 | return Th_WrongNumArgs(interp, "httpize STRING"); |
| 266 | } |
| 267 | zOut = httpize((char*)argv[1], TH1_LEN(argl[1])); |
| 268 | Th_SetResult(interp, zOut, -1); |
| 269 | free(zOut); |
| 270 | return TH_OK; |
| 271 | } |
| 272 | |
| @@ -291,11 +291,12 @@ | |
| 291 | if( argc<2 || argc>3 ){ |
| 292 | return Th_WrongNumArgs(interp, "enable_output [LABEL] BOOLEAN"); |
| 293 | } |
| 294 | rc = Th_ToInt(interp, argv[argc-1], argl[argc-1], &enableOutput); |
| 295 | if( g.thTrace ){ |
| 296 | Th_Trace("enable_output {%.*s} -> %d<br>\n", |
| 297 | TH1_LEN(argl[1]),argv[1],enableOutput); |
| 298 | } |
| 299 | return rc; |
| 300 | } |
| 301 | |
| 302 | /* |
| @@ -322,11 +323,11 @@ | |
| 323 | buul = (TH_INIT_NO_ENCODE & g.th1Flags) ? 0 : 1; |
| 324 | Th_SetResultInt(g.interp, buul); |
| 325 | if(argc>1){ |
| 326 | if( g.thTrace ){ |
| 327 | Th_Trace("enable_htmlify {%.*s} -> %d<br>\n", |
| 328 | TH1_LEN(argl[1]),argv[1],buul); |
| 329 | } |
| 330 | rc = Th_ToInt(interp, argv[argc-1], argl[argc-1], &buul); |
| 331 | if(!rc){ |
| 332 | if(buul){ |
| 333 | g.th1Flags &= ~TH_INIT_NO_ENCODE; |
| @@ -381,19 +382,23 @@ | |
| 382 | ** g.th1Flags has the TH_INIT_NO_ENCODE flag. |
| 383 | ** |
| 384 | ** If pOut is NULL and the global pThOut is not then that blob |
| 385 | ** is used for output. |
| 386 | */ |
| 387 | static void sendText(Blob *pOut, const char *z, int n, int encode){ |
| 388 | if(0==pOut && pThOut!=0){ |
| 389 | pOut = pThOut; |
| 390 | } |
| 391 | if(TH_INIT_NO_ENCODE & g.th1Flags){ |
| 392 | encode = 0; |
| 393 | } |
| 394 | if( enableOutput && n ){ |
| 395 | if( n<0 ){ |
| 396 | n = strlen(z); |
| 397 | }else{ |
| 398 | n = TH1_LEN(n); |
| 399 | } |
| 400 | if( encode ){ |
| 401 | z = htmlize(z, n); |
| 402 | n = strlen(z); |
| 403 | } |
| 404 | if(pOut!=0){ |
| @@ -525,14 +530,23 @@ | |
| 530 | void *pConvert, |
| 531 | int argc, |
| 532 | const char **argv, |
| 533 | int *argl |
| 534 | ){ |
| 535 | int encode = *(unsigned int*)pConvert; |
| 536 | int n; |
| 537 | if( argc!=2 ){ |
| 538 | return Th_WrongNumArgs(interp, "puts STRING"); |
| 539 | } |
| 540 | n = argl[1]; |
| 541 | if( encode==0 && n>0 && TH1_TAINTED(n) ){ |
| 542 | if( Th_ReportTaint(interp, "output string", argv[1], n) ){ |
| 543 | return TH_ERROR; |
| 544 | } |
| 545 | n = TH1_LEN(n); |
| 546 | } |
| 547 | sendText(0,(char*)argv[1], n, encode); |
| 548 | return TH_OK; |
| 549 | } |
| 550 | |
| 551 | /* |
| 552 | ** TH1 command: redirect URL ?withMethod? |
| @@ -557,10 +571,15 @@ | |
| 571 | } |
| 572 | if( argc==3 ){ |
| 573 | if( Th_ToInt(interp, argv[2], argl[2], &withMethod) ){ |
| 574 | return TH_ERROR; |
| 575 | } |
| 576 | } |
| 577 | if( TH1_TAINTED(argl[1]) |
| 578 | && Th_ReportTaint(interp,"redirect URL",argv[1],argl[1]) |
| 579 | ){ |
| 580 | return TH_ERROR; |
| 581 | } |
| 582 | if( withMethod ){ |
| 583 | cgi_redirect_with_method(argv[1]); |
| 584 | }else{ |
| 585 | cgi_redirect(argv[1]); |
| @@ -660,11 +679,11 @@ | |
| 679 | int nValue = 0; |
| 680 | if( argc!=2 ){ |
| 681 | return Th_WrongNumArgs(interp, "markdown STRING"); |
| 682 | } |
| 683 | blob_zero(&src); |
| 684 | blob_init(&src, (char*)argv[1], TH1_LEN(argl[1])); |
| 685 | blob_zero(&title); blob_zero(&body); |
| 686 | markdown_to_html(&src, &title, &body); |
| 687 | Th_ListAppend(interp, &zValue, &nValue, blob_str(&title), blob_size(&title)); |
| 688 | Th_ListAppend(interp, &zValue, &nValue, blob_str(&body), blob_size(&body)); |
| 689 | Th_SetResult(interp, zValue, nValue); |
| @@ -690,11 +709,11 @@ | |
| 709 | if( argc!=2 ){ |
| 710 | return Th_WrongNumArgs(interp, "wiki STRING"); |
| 711 | } |
| 712 | if( enableOutput ){ |
| 713 | Blob src; |
| 714 | blob_init(&src, (char*)argv[1], TH1_LEN(argl[1])); |
| 715 | wiki_convert(&src, 0, flags); |
| 716 | blob_reset(&src); |
| 717 | } |
| 718 | return TH_OK; |
| 719 | } |
| @@ -735,11 +754,11 @@ | |
| 754 | ){ |
| 755 | char *zOut; |
| 756 | if( argc!=2 ){ |
| 757 | return Th_WrongNumArgs(interp, "htmlize STRING"); |
| 758 | } |
| 759 | zOut = htmlize((char*)argv[1], TH1_LEN(argl[1])); |
| 760 | Th_SetResult(interp, zOut, -1); |
| 761 | free(zOut); |
| 762 | return TH_OK; |
| 763 | } |
| 764 | |
| @@ -757,11 +776,11 @@ | |
| 776 | ){ |
| 777 | char *zOut; |
| 778 | if( argc!=2 ){ |
| 779 | return Th_WrongNumArgs(interp, "encode64 STRING"); |
| 780 | } |
| 781 | zOut = encode64((char*)argv[1], TH1_LEN(argl[1])); |
| 782 | Th_SetResult(interp, zOut, -1); |
| 783 | free(zOut); |
| 784 | return TH_OK; |
| 785 | } |
| 786 | |
| @@ -778,11 +797,11 @@ | |
| 797 | int argc, |
| 798 | const char **argv, |
| 799 | int *argl |
| 800 | ){ |
| 801 | char *zOut; |
| 802 | if( argc>=2 && TH1_LEN(argl[1])==6 && memcmp(argv[1],"-local",6)==0 ){ |
| 803 | zOut = db_text("??", "SELECT datetime('now',toLocal())"); |
| 804 | }else{ |
| 805 | zOut = db_text("??", "SELECT datetime('now')"); |
| 806 | } |
| 807 | Th_SetResult(interp, zOut, -1); |
| @@ -810,13 +829,13 @@ | |
| 829 | if( argc<2 ){ |
| 830 | return Th_WrongNumArgs(interp, "hascap STRING ..."); |
| 831 | } |
| 832 | for(i=1; rc==1 && i<argc; i++){ |
| 833 | if( g.thTrace ){ |
| 834 | Th_ListAppend(interp, &zCapList, &nCapList, argv[i], TH1_LEN(argl[i])); |
| 835 | } |
| 836 | rc = login_has_capability((char*)argv[i],TH1_LEN(argl[i]),*(int*)p); |
| 837 | } |
| 838 | if( g.thTrace ){ |
| 839 | Th_Trace("[%s %#h] => %d<br>\n", argv[0], nCapList, zCapList, rc); |
| 840 | Th_Free(interp, zCapList); |
| 841 | } |
| @@ -858,11 +877,11 @@ | |
| 877 | int i; |
| 878 | |
| 879 | if( argc!=2 ){ |
| 880 | return Th_WrongNumArgs(interp, "capexpr EXPR"); |
| 881 | } |
| 882 | rc = Th_SplitList(interp, argv[1], TH1_LEN(argl[1]), &azCap, &anCap, &nCap); |
| 883 | if( rc ) return rc; |
| 884 | rc = 0; |
| 885 | for(i=0; i<nCap; i++){ |
| 886 | if( azCap[i][0]=='!' ){ |
| 887 | rc = !login_has_capability(azCap[i]+1, anCap[i]-1, 0); |
| @@ -921,11 +940,12 @@ | |
| 940 | if( argc<2 ){ |
| 941 | return Th_WrongNumArgs(interp, "hascap STRING ..."); |
| 942 | } |
| 943 | for(i=1; i<argc && rc; i++){ |
| 944 | int match = 0; |
| 945 | int nn = TH1_LEN(argl[i]); |
| 946 | for(j=0; j<nn; j++){ |
| 947 | switch( argv[i][j] ){ |
| 948 | case 'c': match |= searchCap & SRCH_CKIN; break; |
| 949 | case 'd': match |= searchCap & SRCH_DOC; break; |
| 950 | case 't': match |= searchCap & SRCH_TKT; break; |
| 951 | case 'w': match |= searchCap & SRCH_WIKI; break; |
| @@ -932,11 +952,11 @@ | |
| 952 | } |
| 953 | } |
| 954 | if( !match ) rc = 0; |
| 955 | } |
| 956 | if( g.thTrace ){ |
| 957 | Th_Trace("[searchable %#h] => %d<br>\n", TH1_LEN(argl[1]), argv[1], rc); |
| 958 | } |
| 959 | Th_SetResultInt(interp, rc); |
| 960 | return TH_OK; |
| 961 | } |
| 962 | |
| @@ -1051,11 +1071,11 @@ | |
| 1071 | #endif |
| 1072 | else if( 0 == fossil_strnicmp( zArg, "markdown\0", 9 ) ){ |
| 1073 | rc = 1; |
| 1074 | } |
| 1075 | if( g.thTrace ){ |
| 1076 | Th_Trace("[hasfeature %#h] => %d<br>\n", TH1_LEN(argl[1]), zArg, rc); |
| 1077 | } |
| 1078 | Th_SetResultInt(interp, rc); |
| 1079 | return TH_OK; |
| 1080 | } |
| 1081 | |
| @@ -1104,18 +1124,20 @@ | |
| 1124 | const char **argv, |
| 1125 | int *argl |
| 1126 | ){ |
| 1127 | int rc = 0; |
| 1128 | int i; |
| 1129 | int nn; |
| 1130 | if( argc!=2 ){ |
| 1131 | return Th_WrongNumArgs(interp, "anycap STRING"); |
| 1132 | } |
| 1133 | nn = TH1_LEN(argl[1]); |
| 1134 | for(i=0; rc==0 && i<nn; i++){ |
| 1135 | rc = login_has_capability((char*)&argv[1][i],1,0); |
| 1136 | } |
| 1137 | if( g.thTrace ){ |
| 1138 | Th_Trace("[anycap %#h] => %d<br>\n", TH1_LEN(argl[1]), argv[1], rc); |
| 1139 | } |
| 1140 | Th_SetResultInt(interp, rc); |
| 1141 | return TH_OK; |
| 1142 | } |
| 1143 | |
| @@ -1140,22 +1162,23 @@ | |
| 1162 | return Th_WrongNumArgs(interp, "combobox NAME TEXT-LIST NUMLINES"); |
| 1163 | } |
| 1164 | if( enableOutput ){ |
| 1165 | int height; |
| 1166 | Blob name; |
| 1167 | int nValue = 0; |
| 1168 | const char *zValue; |
| 1169 | char *z, *zH; |
| 1170 | int nElem; |
| 1171 | int *aszElem; |
| 1172 | char **azElem; |
| 1173 | int i; |
| 1174 | |
| 1175 | if( Th_ToInt(interp, argv[3], argl[3], &height) ) return TH_ERROR; |
| 1176 | Th_SplitList(interp, argv[2], TH1_LEN(argl[2]), &azElem, &aszElem, &nElem); |
| 1177 | blob_init(&name, (char*)argv[1], TH1_LEN(argl[1])); |
| 1178 | zValue = Th_Fetch(blob_str(&name), &nValue); |
| 1179 | nValue = TH1_LEN(nValue); |
| 1180 | zH = htmlize(blob_buffer(&name), blob_size(&name)); |
| 1181 | z = mprintf("<select id=\"%s\" name=\"%s\" size=\"%d\">", zH, zH, height); |
| 1182 | free(zH); |
| 1183 | sendText(0,z, -1, 0); |
| 1184 | free(z); |
| @@ -1247,11 +1270,11 @@ | |
| 1270 | return Th_WrongNumArgs(interp, "linecount STRING MAX MIN"); |
| 1271 | } |
| 1272 | if( Th_ToInt(interp, argv[2], argl[2], &iMax) ) return TH_ERROR; |
| 1273 | if( Th_ToInt(interp, argv[3], argl[3], &iMin) ) return TH_ERROR; |
| 1274 | z = argv[1]; |
| 1275 | size = TH1_LEN(argl[1]); |
| 1276 | for(n=1, i=0; i<size; i++){ |
| 1277 | if( z[i]=='\n' ){ |
| 1278 | n++; |
| 1279 | if( n>=iMax ) break; |
| 1280 | } |
| @@ -1407,11 +1430,12 @@ | |
| 1430 | return TH_OK; |
| 1431 | }else if( fossil_strnicmp(argv[1], "vfs\0", 4)==0 ){ |
| 1432 | Th_SetResult(interp, g.zVfsName ? g.zVfsName : zDefault, -1); |
| 1433 | return TH_OK; |
| 1434 | }else{ |
| 1435 | Th_ErrorMessage(interp, "unsupported global state:", |
| 1436 | argv[1], TH1_LEN(argl[1])); |
| 1437 | return TH_ERROR; |
| 1438 | } |
| 1439 | } |
| 1440 | |
| 1441 | /* |
| @@ -1848,10 +1872,47 @@ | |
| 1872 | sqlite3_snprintf(sizeof(zUTime), zUTime, "%llu", x); |
| 1873 | Th_SetResult(interp, zUTime, -1); |
| 1874 | return TH_OK; |
| 1875 | } |
| 1876 | |
| 1877 | /* |
| 1878 | ** TH1 command: taint STRING |
| 1879 | ** |
| 1880 | ** Return a copy of STRING that is marked as tainted. |
| 1881 | */ |
| 1882 | static int taintCmd( |
| 1883 | Th_Interp *interp, |
| 1884 | void *p, |
| 1885 | int argc, |
| 1886 | const char **argv, |
| 1887 | int *argl |
| 1888 | ){ |
| 1889 | if( argc!=2 ){ |
| 1890 | return Th_WrongNumArgs(interp, "STRING"); |
| 1891 | } |
| 1892 | Th_SetResult(interp, argv[1], TH1_ADD_TAINT(argl[1])); |
| 1893 | return TH_OK; |
| 1894 | } |
| 1895 | |
| 1896 | /* |
| 1897 | ** TH1 command: untaint STRING |
| 1898 | ** |
| 1899 | ** Return a copy of STRING that is marked as untainted. |
| 1900 | */ |
| 1901 | static int untaintCmd( |
| 1902 | Th_Interp *interp, |
| 1903 | void *p, |
| 1904 | int argc, |
| 1905 | const char **argv, |
| 1906 | int *argl |
| 1907 | ){ |
| 1908 | if( argc!=2 ){ |
| 1909 | return Th_WrongNumArgs(interp, "STRING"); |
| 1910 | } |
| 1911 | Th_SetResult(interp, argv[1], TH1_LEN(argl[1])); |
| 1912 | return TH_OK; |
| 1913 | } |
| 1914 | |
| 1915 | /* |
| 1916 | ** TH1 command: randhex N |
| 1917 | ** |
| 1918 | ** Return N*2 random hexadecimal digits with N<50. If N is omitted, |
| @@ -1923,11 +1984,13 @@ | |
| 1984 | int res = TH_OK; |
| 1985 | int nVar; |
| 1986 | char *zErr = 0; |
| 1987 | int noComplain = 0; |
| 1988 | |
| 1989 | if( argc>3 && TH1_LEN(argl[1])==11 |
| 1990 | && strncmp(argv[1], "-nocomplain", 11)==0 |
| 1991 | ){ |
| 1992 | argc--; |
| 1993 | argv++; |
| 1994 | argl++; |
| 1995 | noComplain = 1; |
| 1996 | } |
| @@ -1939,15 +2002,22 @@ | |
| 2002 | Th_ErrorMessage(interp, "database is not open", 0, 0); |
| 2003 | return TH_ERROR; |
| 2004 | } |
| 2005 | zSql = argv[1]; |
| 2006 | nSql = argl[1]; |
| 2007 | if( TH1_TAINTED(nSql) ){ |
| 2008 | if( Th_ReportTaint(interp,"query SQL",zSql,nSql) ){ |
| 2009 | return TH_ERROR; |
| 2010 | } |
| 2011 | nSql = TH1_LEN(nSql); |
| 2012 | } |
| 2013 | |
| 2014 | while( res==TH_OK && nSql>0 ){ |
| 2015 | zErr = 0; |
| 2016 | report_restrict_sql(&zErr); |
| 2017 | g.dbIgnoreErrors++; |
| 2018 | rc = sqlite3_prepare_v2(g.db, argv[1], TH1_LEN(argl[1]), &pStmt, &zTail); |
| 2019 | g.dbIgnoreErrors--; |
| 2020 | report_unrestrict_sql(); |
| 2021 | if( rc!=0 || zErr!=0 ){ |
| 2022 | if( noComplain ) return TH_OK; |
| 2023 | Th_ErrorMessage(interp, "SQL error: ", |
| @@ -1964,31 +2034,31 @@ | |
| 2034 | int szVar = zVar ? th_strlen(zVar) : 0; |
| 2035 | if( szVar>1 && zVar[0]=='$' |
| 2036 | && Th_GetVar(interp, zVar+1, szVar-1)==TH_OK ){ |
| 2037 | int nVal; |
| 2038 | const char *zVal = Th_GetResult(interp, &nVal); |
| 2039 | sqlite3_bind_text(pStmt, i, zVal, TH1_LEN(nVal), SQLITE_TRANSIENT); |
| 2040 | } |
| 2041 | } |
| 2042 | while( res==TH_OK && ignore_errors_step(pStmt)==SQLITE_ROW ){ |
| 2043 | int nCol = sqlite3_column_count(pStmt); |
| 2044 | for(i=0; i<nCol; i++){ |
| 2045 | const char *zCol = sqlite3_column_name(pStmt, i); |
| 2046 | int szCol = th_strlen(zCol); |
| 2047 | const char *zVal = (const char*)sqlite3_column_text(pStmt, i); |
| 2048 | int szVal = sqlite3_column_bytes(pStmt, i); |
| 2049 | Th_SetVar(interp, zCol, szCol, zVal, TH1_ADD_TAINT(szVal)); |
| 2050 | } |
| 2051 | if( g.thTrace ){ |
| 2052 | Th_Trace("query_eval {<pre>%#h</pre>}<br>\n",TH1_LEN(argl[2]),argv[2]); |
| 2053 | } |
| 2054 | res = Th_Eval(interp, 0, argv[2], TH1_LEN(argl[2])); |
| 2055 | if( g.thTrace ){ |
| 2056 | int nTrRes; |
| 2057 | char *zTrRes = (char*)Th_GetResult(g.interp, &nTrRes); |
| 2058 | Th_Trace("[query_eval] => %h {%#h}<br>\n", |
| 2059 | Th_ReturnCodeName(res, 0), TH1_LEN(nTrRes), zTrRes); |
| 2060 | } |
| 2061 | if( res==TH_BREAK || res==TH_CONTINUE ) res = TH_OK; |
| 2062 | } |
| 2063 | rc = sqlite3_finalize(pStmt); |
| 2064 | if( rc!=SQLITE_OK ){ |
| @@ -2038,11 +2108,11 @@ | |
| 2108 | Th_SetResult(interp, 0, 0); |
| 2109 | rc = TH_OK; |
| 2110 | } |
| 2111 | if( g.thTrace ){ |
| 2112 | Th_Trace("[setting %s%#h] => %d<br>\n", strict ? "strict " : "", |
| 2113 | TH1_LEN(argl[nArg]), argv[nArg], rc); |
| 2114 | } |
| 2115 | return rc; |
| 2116 | } |
| 2117 | |
| 2118 | /* |
| @@ -2121,11 +2191,11 @@ | |
| 2191 | return Th_WrongNumArgs(interp, REGEXP_WRONGNUMARGS); |
| 2192 | } |
| 2193 | zErr = re_compile(&pRe, argv[nArg], noCase); |
| 2194 | if( !zErr ){ |
| 2195 | Th_SetResultInt(interp, re_match(pRe, |
| 2196 | (const unsigned char *)argv[nArg+1], TH1_LEN(argl[nArg+1]))); |
| 2197 | rc = TH_OK; |
| 2198 | }else{ |
| 2199 | Th_SetResult(interp, zErr, -1); |
| 2200 | rc = TH_ERROR; |
| 2201 | } |
| @@ -2160,11 +2230,11 @@ | |
| 2230 | UrlData urlData; |
| 2231 | |
| 2232 | if( argc<2 || argc>5 ){ |
| 2233 | return Th_WrongNumArgs(interp, HTTP_WRONGNUMARGS); |
| 2234 | } |
| 2235 | if( fossil_strnicmp(argv[nArg], "-asynchronous", TH1_LEN(argl[nArg]))==0 ){ |
| 2236 | fAsynchronous = 1; nArg++; |
| 2237 | } |
| 2238 | if( fossil_strcmp(argv[nArg], "--")==0 ) nArg++; |
| 2239 | if( nArg+1!=argc && nArg+2!=argc ){ |
| 2240 | return Th_WrongNumArgs(interp, REGEXP_WRONGNUMARGS); |
| @@ -2189,11 +2259,11 @@ | |
| 2259 | return TH_ERROR; |
| 2260 | } |
| 2261 | re_free(pRe); |
| 2262 | blob_zero(&payload); |
| 2263 | if( nArg+2==argc ){ |
| 2264 | blob_append(&payload, argv[nArg+1], TH1_LEN(argl[nArg+1])); |
| 2265 | zType = "POST"; |
| 2266 | }else{ |
| 2267 | zType = "GET"; |
| 2268 | } |
| 2269 | if( fAsynchronous ){ |
| @@ -2268,11 +2338,11 @@ | |
| 2338 | if( argc!=2 ){ |
| 2339 | return Th_WrongNumArgs(interp, "captureTh1 STRING"); |
| 2340 | } |
| 2341 | pOrig = Th_SetOutputBlob(&out); |
| 2342 | zStr = argv[1]; |
| 2343 | nStr = TH1_LEN(argl[1]); |
| 2344 | rc = Th_Eval(g.interp, 0, zStr, nStr); |
| 2345 | Th_SetOutputBlob(pOrig); |
| 2346 | if(0==rc){ |
| 2347 | Th_SetResult(g.interp, blob_str(&out), blob_size(&out)); |
| 2348 | } |
| @@ -2387,13 +2457,15 @@ | |
| 2457 | {"setting", settingCmd, 0}, |
| 2458 | {"styleFooter", styleFooterCmd, 0}, |
| 2459 | {"styleHeader", styleHeaderCmd, 0}, |
| 2460 | {"styleScript", styleScriptCmd, 0}, |
| 2461 | {"submenu", submenuCmd, 0}, |
| 2462 | {"taint", taintCmd, 0}, |
| 2463 | {"tclReady", tclReadyCmd, 0}, |
| 2464 | {"trace", traceCmd, 0}, |
| 2465 | {"stime", stimeCmd, 0}, |
| 2466 | {"untaint", untaintCmd, 0}, |
| 2467 | {"unversioned", unversionedCmd, 0}, |
| 2468 | {"utime", utimeCmd, 0}, |
| 2469 | {"verifyCsrf", verifyCsrfCmd, 0}, |
| 2470 | {"verifyLogin", verifyLoginCmd, 0}, |
| 2471 | {"wiki", wikiCmd, (void*)&aFlags[0]}, |
| @@ -2494,10 +2566,26 @@ | |
| 2566 | Th_Trace("set %h {%h}<br>\n", zName, zValue); |
| 2567 | } |
| 2568 | Th_SetVar(g.interp, zName, -1, zValue, strlen(zValue)); |
| 2569 | } |
| 2570 | } |
| 2571 | |
| 2572 | /* |
| 2573 | ** Store a string value in a variable in the interpreter |
| 2574 | ** with the "taint" marking, so that TH1 knows that this |
| 2575 | ** variable contains content under the control of the remote |
| 2576 | ** user and presents a risk of XSS or SQL-injection attacks. |
| 2577 | */ |
| 2578 | void Th_StoreUnsafe(const char *zName, const char *zValue){ |
| 2579 | Th_FossilInit(TH_INIT_DEFAULT); |
| 2580 | if( zValue ){ |
| 2581 | if( g.thTrace ){ |
| 2582 | Th_Trace("set %h [taint {%h}]<br>\n", zName, zValue); |
| 2583 | } |
| 2584 | Th_SetVar(g.interp, zName, -1, zValue, TH1_ADD_TAINT(strlen(zValue))); |
| 2585 | } |
| 2586 | } |
| 2587 | |
| 2588 | /* |
| 2589 | ** Appends an element to a TH1 list value. This function is called by the |
| 2590 | ** transfer subsystem; therefore, it must be very careful to avoid doing |
| 2591 | ** any unnecessary work. To that end, the TH1 subsystem will not be called |
| @@ -2680,10 +2768,11 @@ | |
| 2768 | char *zResult = (char*)Th_GetResult(g.interp, &nResult); |
| 2769 | /* |
| 2770 | ** Make sure that the TH1 script error was not caused by a "missing" |
| 2771 | ** command hook handler as that is not actually an error condition. |
| 2772 | */ |
| 2773 | nResult = TH1_LEN(nResult); |
| 2774 | if( memcmp(zResult, NO_COMMAND_HOOK_ERROR, nResult)!=0 ){ |
| 2775 | sendError(0,zResult, nResult, 0); |
| 2776 | }else{ |
| 2777 | /* |
| 2778 | ** There is no command hook handler "installed". This situation |
| @@ -2767,10 +2856,11 @@ | |
| 2856 | char *zResult = (char*)Th_GetResult(g.interp, &nResult); |
| 2857 | /* |
| 2858 | ** Make sure that the TH1 script error was not caused by a "missing" |
| 2859 | ** webpage hook handler as that is not actually an error condition. |
| 2860 | */ |
| 2861 | nResult = TH1_LEN(nResult); |
| 2862 | if( memcmp(zResult, NO_WEBPAGE_HOOK_ERROR, nResult)!=0 ){ |
| 2863 | sendError(0,zResult, nResult, 1); |
| 2864 | }else{ |
| 2865 | /* |
| 2866 | ** There is no webpage hook handler "installed". This situation |
| @@ -2907,11 +2997,11 @@ | |
| 2997 | rc = Th_Eval(g.interp, 0, (const char*)z, i); |
| 2998 | if( g.thTrace ){ |
| 2999 | int nTrRes; |
| 3000 | char *zTrRes = (char*)Th_GetResult(g.interp, &nTrRes); |
| 3001 | Th_Trace("[render_eval] => %h {%#h}<br>\n", |
| 3002 | Th_ReturnCodeName(rc, 0), TH1_LEN(nTrRes), zTrRes); |
| 3003 | } |
| 3004 | if( rc!=TH_OK ) break; |
| 3005 | z += i; |
| 3006 | if( z[0] ){ z += 6; } |
| 3007 | i = 0; |
| @@ -2953,10 +3043,77 @@ | |
| 3043 | ** as appropriate. We need to pass on g.th1Flags for the case of |
| 3044 | ** recursive calls, so that, e.g., TH_INIT_NO_ENCODE does not get |
| 3045 | ** inadvertently toggled off by a recursive call. |
| 3046 | */; |
| 3047 | } |
| 3048 | |
| 3049 | /* |
| 3050 | ** SETTING: vuln-report width=8 default=log |
| 3051 | ** |
| 3052 | ** This setting controls Fossil's behavior when it encounters a potential |
| 3053 | ** XSS or SQL-injection vulnerability due to misuse of TH1 configuration |
| 3054 | ** scripts. Choices are: |
| 3055 | ** |
| 3056 | ** off Do nothing. Ignore the vulnerability. |
| 3057 | ** |
| 3058 | ** log Write a report of the problem into the error log. |
| 3059 | ** |
| 3060 | ** block Like "log" but also prevent the offending TH1 command |
| 3061 | ** from running. |
| 3062 | ** |
| 3063 | ** fatal Render an error message page instead of the requested |
| 3064 | ** page. |
| 3065 | */ |
| 3066 | |
| 3067 | /* |
| 3068 | ** Report misuse of a tainted string in TH1. |
| 3069 | ** |
| 3070 | ** The behavior depends on the vuln-report setting. If "off", this routine |
| 3071 | ** is a no-op. Otherwise, right a message into the error log. If |
| 3072 | ** vuln-report is "log", that is all that happens. But for any other |
| 3073 | ** value of vuln-report, a fatal error is raised. |
| 3074 | */ |
| 3075 | int Th_ReportTaint( |
| 3076 | Th_Interp *interp, /* Report error here, if an error is reported */ |
| 3077 | const char *zWhere, /* Where the tainted string appears */ |
| 3078 | const char *zStr, /* The tainted string */ |
| 3079 | int nStr /* Length of the tainted string */ |
| 3080 | ){ |
| 3081 | char *zDisp; /* Dispensation */ |
| 3082 | const char *zVulnType; /* Type of vulnerability */ |
| 3083 | |
| 3084 | zDisp = db_get("vuln-report","log"); |
| 3085 | if( is_false(zDisp) ) return 0; |
| 3086 | if( strstr(zWhere,"SQL")!=0 ){ |
| 3087 | zVulnType = "SQL-injection"; |
| 3088 | }else{ |
| 3089 | zVulnType = "XSS"; |
| 3090 | } |
| 3091 | nStr = TH1_LEN(nStr); |
| 3092 | fossil_errorlog("possible %s vulnerability due to tainted TH1 %s: \"%.*s\"", |
| 3093 | zVulnType, zWhere, nStr, zStr); |
| 3094 | if( strcmp(zDisp,"log")==0 ){ |
| 3095 | return 0; |
| 3096 | } |
| 3097 | if( strcmp(zDisp,"block")==0 ){ |
| 3098 | char *z = mprintf("tainted %s: \"", zWhere); |
| 3099 | Th_ErrorMessage(interp, z, zStr, nStr); |
| 3100 | fossil_free(z); |
| 3101 | }else{ |
| 3102 | char *z = mprintf("%#h", nStr, zStr); |
| 3103 | cgi_reset_content(); |
| 3104 | style_submenu_enable(0); |
| 3105 | style_set_current_feature("error"); |
| 3106 | style_header("Configuration Error"); |
| 3107 | @ <p>Error in a TH1 configuration script: |
| 3108 | @ tainted %h(zWhere): "%z(z)" |
| 3109 | style_finish_page(); |
| 3110 | cgi_reply(); |
| 3111 | fossil_exit(1); |
| 3112 | } |
| 3113 | return 1; |
| 3114 | } |
| 3115 | |
| 3116 | /* |
| 3117 | ** COMMAND: test-th-render |
| 3118 | ** |
| 3119 | ** Usage: %fossil test-th-render FILE |
| @@ -2992,10 +3149,11 @@ | |
| 3149 | if( find_option("set-user-caps", 0, 0)!=0 ){ |
| 3150 | const char *zCap = fossil_getenv("TH1_TEST_USER_CAPS"); |
| 3151 | login_set_capabilities(zCap ? zCap : "sx", 0); |
| 3152 | g.useLocalauth = 1; |
| 3153 | } |
| 3154 | db_find_and_open_repository(OPEN_OK_NOT_FOUND|OPEN_SUBSTITUTE,0); |
| 3155 | verify_all_options(); |
| 3156 | if( g.argc<3 ){ |
| 3157 | usage("FILE"); |
| 3158 | } |
| 3159 | blob_zero(&in); |
| @@ -3044,10 +3202,11 @@ | |
| 3202 | if( find_option("set-user-caps", 0, 0)!=0 ){ |
| 3203 | const char *zCap = fossil_getenv("TH1_TEST_USER_CAPS"); |
| 3204 | login_set_capabilities(zCap ? zCap : "sx", 0); |
| 3205 | g.useLocalauth = 1; |
| 3206 | } |
| 3207 | db_find_and_open_repository(OPEN_OK_NOT_FOUND|OPEN_SUBSTITUTE,0); |
| 3208 | verify_all_options(); |
| 3209 | if( g.argc!=3 ){ |
| 3210 | usage("script"); |
| 3211 | } |
| 3212 | if(file_isfile(g.argv[2], ExtFILE)){ |
| 3213 |
+18
-18
| --- src/th_tcl.c | ||
| +++ src/th_tcl.c | ||
| @@ -41,16 +41,16 @@ | ||
| 41 | 41 | #define USE_ARGV_TO_OBJV() \ |
| 42 | 42 | int objc; \ |
| 43 | 43 | Tcl_Obj **objv; \ |
| 44 | 44 | int obji; |
| 45 | 45 | |
| 46 | -#define COPY_ARGV_TO_OBJV() \ | |
| 47 | - objc = argc-1; \ | |
| 48 | - objv = (Tcl_Obj **)ckalloc((unsigned)(objc * sizeof(Tcl_Obj *))); \ | |
| 49 | - for(obji=1; obji<argc; obji++){ \ | |
| 50 | - objv[obji-1] = Tcl_NewStringObj(argv[obji], argl[obji]); \ | |
| 51 | - Tcl_IncrRefCount(objv[obji-1]); \ | |
| 46 | +#define COPY_ARGV_TO_OBJV() \ | |
| 47 | + objc = argc-1; \ | |
| 48 | + objv = (Tcl_Obj **)ckalloc((unsigned)(objc * sizeof(Tcl_Obj *))); \ | |
| 49 | + for(obji=1; obji<argc; obji++){ \ | |
| 50 | + objv[obji-1] = Tcl_NewStringObj(argv[obji], TH1_LEN(argl[obji])); \ | |
| 51 | + Tcl_IncrRefCount(objv[obji-1]); \ | |
| 52 | 52 | } |
| 53 | 53 | |
| 54 | 54 | #define FREE_ARGV_TO_OBJV() \ |
| 55 | 55 | for(obji=1; obji<argc; obji++){ \ |
| 56 | 56 | Tcl_DecrRefCount(objv[obji-1]); \ |
| @@ -449,11 +449,11 @@ | ||
| 449 | 449 | } |
| 450 | 450 | xNotifyProc = bIsPost ? tclContext->xPostEval : tclContext->xPreEval; |
| 451 | 451 | if( xNotifyProc ){ |
| 452 | 452 | rc = xNotifyProc(bIsPost ? |
| 453 | 453 | tclContext->pPostContext : tclContext->pPreContext, |
| 454 | - interp, ctx, argc, argv, argl, rc); | |
| 454 | + interp, ctx, argc, argv, TH1_LEN(argl), rc); | |
| 455 | 455 | } |
| 456 | 456 | return rc; |
| 457 | 457 | } |
| 458 | 458 | |
| 459 | 459 | /* |
| @@ -485,17 +485,17 @@ | ||
| 485 | 485 | tclInterp = GET_CTX_TCL_INTERP(ctx); |
| 486 | 486 | if( !tclInterp || Tcl_InterpDeleted(tclInterp) ){ |
| 487 | 487 | Th_ErrorMessage(interp, "invalid Tcl interpreter", (const char *)"", 0); |
| 488 | 488 | return TH_ERROR; |
| 489 | 489 | } |
| 490 | - rc = notifyPreOrPostEval(0, interp, ctx, argc, argv, argl, rc); | |
| 490 | + rc = notifyPreOrPostEval(0, interp, ctx, argc, argv, TH1_LEN(argl), rc); | |
| 491 | 491 | if( rc!=TH_OK ){ |
| 492 | 492 | return rc; |
| 493 | 493 | } |
| 494 | 494 | Tcl_Preserve((ClientData)tclInterp); |
| 495 | 495 | if( argc==2 ){ |
| 496 | - objPtr = Tcl_NewStringObj(argv[1], argl[1]); | |
| 496 | + objPtr = Tcl_NewStringObj(argv[1], TH1_LEN(argl[1])); | |
| 497 | 497 | Tcl_IncrRefCount(objPtr); |
| 498 | 498 | rc = Tcl_EvalObjEx(tclInterp, objPtr, 0); |
| 499 | 499 | Tcl_DecrRefCount(objPtr); objPtr = 0; |
| 500 | 500 | }else{ |
| 501 | 501 | USE_ARGV_TO_OBJV(); |
| @@ -507,11 +507,11 @@ | ||
| 507 | 507 | FREE_ARGV_TO_OBJV(); |
| 508 | 508 | } |
| 509 | 509 | zResult = getTclResult(tclInterp, &nResult); |
| 510 | 510 | Th_SetResult(interp, zResult, nResult); |
| 511 | 511 | Tcl_Release((ClientData)tclInterp); |
| 512 | - rc = notifyPreOrPostEval(1, interp, ctx, argc, argv, argl, | |
| 512 | + rc = notifyPreOrPostEval(1, interp, ctx, argc, argv, TH1_LEN(argl), | |
| 513 | 513 | getTh1ReturnCode(rc)); |
| 514 | 514 | return rc; |
| 515 | 515 | } |
| 516 | 516 | |
| 517 | 517 | /* |
| @@ -545,17 +545,17 @@ | ||
| 545 | 545 | tclInterp = GET_CTX_TCL_INTERP(ctx); |
| 546 | 546 | if( !tclInterp || Tcl_InterpDeleted(tclInterp) ){ |
| 547 | 547 | Th_ErrorMessage(interp, "invalid Tcl interpreter", (const char *)"", 0); |
| 548 | 548 | return TH_ERROR; |
| 549 | 549 | } |
| 550 | - rc = notifyPreOrPostEval(0, interp, ctx, argc, argv, argl, rc); | |
| 550 | + rc = notifyPreOrPostEval(0, interp, ctx, argc, argv, TH1_LEN(argl), rc); | |
| 551 | 551 | if( rc!=TH_OK ){ |
| 552 | 552 | return rc; |
| 553 | 553 | } |
| 554 | 554 | Tcl_Preserve((ClientData)tclInterp); |
| 555 | 555 | if( argc==2 ){ |
| 556 | - objPtr = Tcl_NewStringObj(argv[1], argl[1]); | |
| 556 | + objPtr = Tcl_NewStringObj(argv[1], TH1_LEN(argl[1])); | |
| 557 | 557 | Tcl_IncrRefCount(objPtr); |
| 558 | 558 | rc = Tcl_ExprObj(tclInterp, objPtr, &resultObjPtr); |
| 559 | 559 | Tcl_DecrRefCount(objPtr); objPtr = 0; |
| 560 | 560 | }else{ |
| 561 | 561 | USE_ARGV_TO_OBJV(); |
| @@ -574,11 +574,11 @@ | ||
| 574 | 574 | Th_SetResult(interp, zResult, nResult); |
| 575 | 575 | if( rc==TCL_OK ){ |
| 576 | 576 | Tcl_DecrRefCount(resultObjPtr); resultObjPtr = 0; |
| 577 | 577 | } |
| 578 | 578 | Tcl_Release((ClientData)tclInterp); |
| 579 | - rc = notifyPreOrPostEval(1, interp, ctx, argc, argv, argl, | |
| 579 | + rc = notifyPreOrPostEval(1, interp, ctx, argc, argv, TH1_LEN(argl), | |
| 580 | 580 | getTh1ReturnCode(rc)); |
| 581 | 581 | return rc; |
| 582 | 582 | } |
| 583 | 583 | |
| 584 | 584 | /* |
| @@ -610,20 +610,20 @@ | ||
| 610 | 610 | tclInterp = GET_CTX_TCL_INTERP(ctx); |
| 611 | 611 | if( !tclInterp || Tcl_InterpDeleted(tclInterp) ){ |
| 612 | 612 | Th_ErrorMessage(interp, "invalid Tcl interpreter", (const char *)"", 0); |
| 613 | 613 | return TH_ERROR; |
| 614 | 614 | } |
| 615 | - rc = notifyPreOrPostEval(0, interp, ctx, argc, argv, argl, rc); | |
| 615 | + rc = notifyPreOrPostEval(0, interp, ctx, argc, argv, TH1_LEN(argl), rc); | |
| 616 | 616 | if( rc!=TH_OK ){ |
| 617 | 617 | return rc; |
| 618 | 618 | } |
| 619 | 619 | Tcl_Preserve((ClientData)tclInterp); |
| 620 | 620 | #if !defined(USE_TCL_EVALOBJV) || !USE_TCL_EVALOBJV |
| 621 | 621 | if( GET_CTX_TCL_USEOBJPROC(ctx) ){ |
| 622 | 622 | Tcl_Command command; |
| 623 | 623 | Tcl_CmdInfo cmdInfo; |
| 624 | - Tcl_Obj *objPtr = Tcl_NewStringObj(argv[1], argl[1]); | |
| 624 | + Tcl_Obj *objPtr = Tcl_NewStringObj(argv[1], TH1_LEN(argl[1])); | |
| 625 | 625 | Tcl_IncrRefCount(objPtr); |
| 626 | 626 | command = Tcl_GetCommandFromObj(tclInterp, objPtr); |
| 627 | 627 | if( !command || Tcl_GetCommandInfoFromToken(command, &cmdInfo)==0 ){ |
| 628 | 628 | Th_ErrorMessage(interp, "Tcl command not found:", argv[1], argl[1]); |
| 629 | 629 | Tcl_DecrRefCount(objPtr); objPtr = 0; |
| @@ -649,11 +649,11 @@ | ||
| 649 | 649 | FREE_ARGV_TO_OBJV(); |
| 650 | 650 | } |
| 651 | 651 | zResult = getTclResult(tclInterp, &nResult); |
| 652 | 652 | Th_SetResult(interp, zResult, nResult); |
| 653 | 653 | Tcl_Release((ClientData)tclInterp); |
| 654 | - rc = notifyPreOrPostEval(1, interp, ctx, argc, argv, argl, | |
| 654 | + rc = notifyPreOrPostEval(1, interp, ctx, argc, argv, TH1_LEN(argl), | |
| 655 | 655 | getTh1ReturnCode(rc)); |
| 656 | 656 | return rc; |
| 657 | 657 | } |
| 658 | 658 | |
| 659 | 659 | /* |
| @@ -782,11 +782,11 @@ | ||
| 782 | 782 | return TCL_ERROR; |
| 783 | 783 | } |
| 784 | 784 | arg = Tcl_GetStringFromObj(objv[1], &nArg); |
| 785 | 785 | rc = Th_Eval(th1Interp, 0, arg, nArg); |
| 786 | 786 | arg = Th_GetResult(th1Interp, &nArg); |
| 787 | - Tcl_SetObjResult(interp, Tcl_NewStringObj(arg, nArg)); | |
| 787 | + Tcl_SetObjResult(interp, Tcl_NewStringObj(arg, TH1_LEN(nArg))); | |
| 788 | 788 | return getTclReturnCode(rc); |
| 789 | 789 | } |
| 790 | 790 | |
| 791 | 791 | /* |
| 792 | 792 | ** Tcl command: th1Expr arg |
| @@ -815,11 +815,11 @@ | ||
| 815 | 815 | return TCL_ERROR; |
| 816 | 816 | } |
| 817 | 817 | arg = Tcl_GetStringFromObj(objv[1], &nArg); |
| 818 | 818 | rc = Th_Expr(th1Interp, arg, nArg); |
| 819 | 819 | arg = Th_GetResult(th1Interp, &nArg); |
| 820 | - Tcl_SetObjResult(interp, Tcl_NewStringObj(arg, nArg)); | |
| 820 | + Tcl_SetObjResult(interp, Tcl_NewStringObj(arg, TH1_LEN(nArg))); | |
| 821 | 821 | return getTclReturnCode(rc); |
| 822 | 822 | } |
| 823 | 823 | |
| 824 | 824 | /* |
| 825 | 825 | ** Array of Tcl integration commands. Used when adding or removing the Tcl |
| 826 | 826 |
| --- src/th_tcl.c | |
| +++ src/th_tcl.c | |
| @@ -41,16 +41,16 @@ | |
| 41 | #define USE_ARGV_TO_OBJV() \ |
| 42 | int objc; \ |
| 43 | Tcl_Obj **objv; \ |
| 44 | int obji; |
| 45 | |
| 46 | #define COPY_ARGV_TO_OBJV() \ |
| 47 | objc = argc-1; \ |
| 48 | objv = (Tcl_Obj **)ckalloc((unsigned)(objc * sizeof(Tcl_Obj *))); \ |
| 49 | for(obji=1; obji<argc; obji++){ \ |
| 50 | objv[obji-1] = Tcl_NewStringObj(argv[obji], argl[obji]); \ |
| 51 | Tcl_IncrRefCount(objv[obji-1]); \ |
| 52 | } |
| 53 | |
| 54 | #define FREE_ARGV_TO_OBJV() \ |
| 55 | for(obji=1; obji<argc; obji++){ \ |
| 56 | Tcl_DecrRefCount(objv[obji-1]); \ |
| @@ -449,11 +449,11 @@ | |
| 449 | } |
| 450 | xNotifyProc = bIsPost ? tclContext->xPostEval : tclContext->xPreEval; |
| 451 | if( xNotifyProc ){ |
| 452 | rc = xNotifyProc(bIsPost ? |
| 453 | tclContext->pPostContext : tclContext->pPreContext, |
| 454 | interp, ctx, argc, argv, argl, rc); |
| 455 | } |
| 456 | return rc; |
| 457 | } |
| 458 | |
| 459 | /* |
| @@ -485,17 +485,17 @@ | |
| 485 | tclInterp = GET_CTX_TCL_INTERP(ctx); |
| 486 | if( !tclInterp || Tcl_InterpDeleted(tclInterp) ){ |
| 487 | Th_ErrorMessage(interp, "invalid Tcl interpreter", (const char *)"", 0); |
| 488 | return TH_ERROR; |
| 489 | } |
| 490 | rc = notifyPreOrPostEval(0, interp, ctx, argc, argv, argl, rc); |
| 491 | if( rc!=TH_OK ){ |
| 492 | return rc; |
| 493 | } |
| 494 | Tcl_Preserve((ClientData)tclInterp); |
| 495 | if( argc==2 ){ |
| 496 | objPtr = Tcl_NewStringObj(argv[1], argl[1]); |
| 497 | Tcl_IncrRefCount(objPtr); |
| 498 | rc = Tcl_EvalObjEx(tclInterp, objPtr, 0); |
| 499 | Tcl_DecrRefCount(objPtr); objPtr = 0; |
| 500 | }else{ |
| 501 | USE_ARGV_TO_OBJV(); |
| @@ -507,11 +507,11 @@ | |
| 507 | FREE_ARGV_TO_OBJV(); |
| 508 | } |
| 509 | zResult = getTclResult(tclInterp, &nResult); |
| 510 | Th_SetResult(interp, zResult, nResult); |
| 511 | Tcl_Release((ClientData)tclInterp); |
| 512 | rc = notifyPreOrPostEval(1, interp, ctx, argc, argv, argl, |
| 513 | getTh1ReturnCode(rc)); |
| 514 | return rc; |
| 515 | } |
| 516 | |
| 517 | /* |
| @@ -545,17 +545,17 @@ | |
| 545 | tclInterp = GET_CTX_TCL_INTERP(ctx); |
| 546 | if( !tclInterp || Tcl_InterpDeleted(tclInterp) ){ |
| 547 | Th_ErrorMessage(interp, "invalid Tcl interpreter", (const char *)"", 0); |
| 548 | return TH_ERROR; |
| 549 | } |
| 550 | rc = notifyPreOrPostEval(0, interp, ctx, argc, argv, argl, rc); |
| 551 | if( rc!=TH_OK ){ |
| 552 | return rc; |
| 553 | } |
| 554 | Tcl_Preserve((ClientData)tclInterp); |
| 555 | if( argc==2 ){ |
| 556 | objPtr = Tcl_NewStringObj(argv[1], argl[1]); |
| 557 | Tcl_IncrRefCount(objPtr); |
| 558 | rc = Tcl_ExprObj(tclInterp, objPtr, &resultObjPtr); |
| 559 | Tcl_DecrRefCount(objPtr); objPtr = 0; |
| 560 | }else{ |
| 561 | USE_ARGV_TO_OBJV(); |
| @@ -574,11 +574,11 @@ | |
| 574 | Th_SetResult(interp, zResult, nResult); |
| 575 | if( rc==TCL_OK ){ |
| 576 | Tcl_DecrRefCount(resultObjPtr); resultObjPtr = 0; |
| 577 | } |
| 578 | Tcl_Release((ClientData)tclInterp); |
| 579 | rc = notifyPreOrPostEval(1, interp, ctx, argc, argv, argl, |
| 580 | getTh1ReturnCode(rc)); |
| 581 | return rc; |
| 582 | } |
| 583 | |
| 584 | /* |
| @@ -610,20 +610,20 @@ | |
| 610 | tclInterp = GET_CTX_TCL_INTERP(ctx); |
| 611 | if( !tclInterp || Tcl_InterpDeleted(tclInterp) ){ |
| 612 | Th_ErrorMessage(interp, "invalid Tcl interpreter", (const char *)"", 0); |
| 613 | return TH_ERROR; |
| 614 | } |
| 615 | rc = notifyPreOrPostEval(0, interp, ctx, argc, argv, argl, rc); |
| 616 | if( rc!=TH_OK ){ |
| 617 | return rc; |
| 618 | } |
| 619 | Tcl_Preserve((ClientData)tclInterp); |
| 620 | #if !defined(USE_TCL_EVALOBJV) || !USE_TCL_EVALOBJV |
| 621 | if( GET_CTX_TCL_USEOBJPROC(ctx) ){ |
| 622 | Tcl_Command command; |
| 623 | Tcl_CmdInfo cmdInfo; |
| 624 | Tcl_Obj *objPtr = Tcl_NewStringObj(argv[1], argl[1]); |
| 625 | Tcl_IncrRefCount(objPtr); |
| 626 | command = Tcl_GetCommandFromObj(tclInterp, objPtr); |
| 627 | if( !command || Tcl_GetCommandInfoFromToken(command, &cmdInfo)==0 ){ |
| 628 | Th_ErrorMessage(interp, "Tcl command not found:", argv[1], argl[1]); |
| 629 | Tcl_DecrRefCount(objPtr); objPtr = 0; |
| @@ -649,11 +649,11 @@ | |
| 649 | FREE_ARGV_TO_OBJV(); |
| 650 | } |
| 651 | zResult = getTclResult(tclInterp, &nResult); |
| 652 | Th_SetResult(interp, zResult, nResult); |
| 653 | Tcl_Release((ClientData)tclInterp); |
| 654 | rc = notifyPreOrPostEval(1, interp, ctx, argc, argv, argl, |
| 655 | getTh1ReturnCode(rc)); |
| 656 | return rc; |
| 657 | } |
| 658 | |
| 659 | /* |
| @@ -782,11 +782,11 @@ | |
| 782 | return TCL_ERROR; |
| 783 | } |
| 784 | arg = Tcl_GetStringFromObj(objv[1], &nArg); |
| 785 | rc = Th_Eval(th1Interp, 0, arg, nArg); |
| 786 | arg = Th_GetResult(th1Interp, &nArg); |
| 787 | Tcl_SetObjResult(interp, Tcl_NewStringObj(arg, nArg)); |
| 788 | return getTclReturnCode(rc); |
| 789 | } |
| 790 | |
| 791 | /* |
| 792 | ** Tcl command: th1Expr arg |
| @@ -815,11 +815,11 @@ | |
| 815 | return TCL_ERROR; |
| 816 | } |
| 817 | arg = Tcl_GetStringFromObj(objv[1], &nArg); |
| 818 | rc = Th_Expr(th1Interp, arg, nArg); |
| 819 | arg = Th_GetResult(th1Interp, &nArg); |
| 820 | Tcl_SetObjResult(interp, Tcl_NewStringObj(arg, nArg)); |
| 821 | return getTclReturnCode(rc); |
| 822 | } |
| 823 | |
| 824 | /* |
| 825 | ** Array of Tcl integration commands. Used when adding or removing the Tcl |
| 826 |
| --- src/th_tcl.c | |
| +++ src/th_tcl.c | |
| @@ -41,16 +41,16 @@ | |
| 41 | #define USE_ARGV_TO_OBJV() \ |
| 42 | int objc; \ |
| 43 | Tcl_Obj **objv; \ |
| 44 | int obji; |
| 45 | |
| 46 | #define COPY_ARGV_TO_OBJV() \ |
| 47 | objc = argc-1; \ |
| 48 | objv = (Tcl_Obj **)ckalloc((unsigned)(objc * sizeof(Tcl_Obj *))); \ |
| 49 | for(obji=1; obji<argc; obji++){ \ |
| 50 | objv[obji-1] = Tcl_NewStringObj(argv[obji], TH1_LEN(argl[obji])); \ |
| 51 | Tcl_IncrRefCount(objv[obji-1]); \ |
| 52 | } |
| 53 | |
| 54 | #define FREE_ARGV_TO_OBJV() \ |
| 55 | for(obji=1; obji<argc; obji++){ \ |
| 56 | Tcl_DecrRefCount(objv[obji-1]); \ |
| @@ -449,11 +449,11 @@ | |
| 449 | } |
| 450 | xNotifyProc = bIsPost ? tclContext->xPostEval : tclContext->xPreEval; |
| 451 | if( xNotifyProc ){ |
| 452 | rc = xNotifyProc(bIsPost ? |
| 453 | tclContext->pPostContext : tclContext->pPreContext, |
| 454 | interp, ctx, argc, argv, TH1_LEN(argl), rc); |
| 455 | } |
| 456 | return rc; |
| 457 | } |
| 458 | |
| 459 | /* |
| @@ -485,17 +485,17 @@ | |
| 485 | tclInterp = GET_CTX_TCL_INTERP(ctx); |
| 486 | if( !tclInterp || Tcl_InterpDeleted(tclInterp) ){ |
| 487 | Th_ErrorMessage(interp, "invalid Tcl interpreter", (const char *)"", 0); |
| 488 | return TH_ERROR; |
| 489 | } |
| 490 | rc = notifyPreOrPostEval(0, interp, ctx, argc, argv, TH1_LEN(argl), rc); |
| 491 | if( rc!=TH_OK ){ |
| 492 | return rc; |
| 493 | } |
| 494 | Tcl_Preserve((ClientData)tclInterp); |
| 495 | if( argc==2 ){ |
| 496 | objPtr = Tcl_NewStringObj(argv[1], TH1_LEN(argl[1])); |
| 497 | Tcl_IncrRefCount(objPtr); |
| 498 | rc = Tcl_EvalObjEx(tclInterp, objPtr, 0); |
| 499 | Tcl_DecrRefCount(objPtr); objPtr = 0; |
| 500 | }else{ |
| 501 | USE_ARGV_TO_OBJV(); |
| @@ -507,11 +507,11 @@ | |
| 507 | FREE_ARGV_TO_OBJV(); |
| 508 | } |
| 509 | zResult = getTclResult(tclInterp, &nResult); |
| 510 | Th_SetResult(interp, zResult, nResult); |
| 511 | Tcl_Release((ClientData)tclInterp); |
| 512 | rc = notifyPreOrPostEval(1, interp, ctx, argc, argv, TH1_LEN(argl), |
| 513 | getTh1ReturnCode(rc)); |
| 514 | return rc; |
| 515 | } |
| 516 | |
| 517 | /* |
| @@ -545,17 +545,17 @@ | |
| 545 | tclInterp = GET_CTX_TCL_INTERP(ctx); |
| 546 | if( !tclInterp || Tcl_InterpDeleted(tclInterp) ){ |
| 547 | Th_ErrorMessage(interp, "invalid Tcl interpreter", (const char *)"", 0); |
| 548 | return TH_ERROR; |
| 549 | } |
| 550 | rc = notifyPreOrPostEval(0, interp, ctx, argc, argv, TH1_LEN(argl), rc); |
| 551 | if( rc!=TH_OK ){ |
| 552 | return rc; |
| 553 | } |
| 554 | Tcl_Preserve((ClientData)tclInterp); |
| 555 | if( argc==2 ){ |
| 556 | objPtr = Tcl_NewStringObj(argv[1], TH1_LEN(argl[1])); |
| 557 | Tcl_IncrRefCount(objPtr); |
| 558 | rc = Tcl_ExprObj(tclInterp, objPtr, &resultObjPtr); |
| 559 | Tcl_DecrRefCount(objPtr); objPtr = 0; |
| 560 | }else{ |
| 561 | USE_ARGV_TO_OBJV(); |
| @@ -574,11 +574,11 @@ | |
| 574 | Th_SetResult(interp, zResult, nResult); |
| 575 | if( rc==TCL_OK ){ |
| 576 | Tcl_DecrRefCount(resultObjPtr); resultObjPtr = 0; |
| 577 | } |
| 578 | Tcl_Release((ClientData)tclInterp); |
| 579 | rc = notifyPreOrPostEval(1, interp, ctx, argc, argv, TH1_LEN(argl), |
| 580 | getTh1ReturnCode(rc)); |
| 581 | return rc; |
| 582 | } |
| 583 | |
| 584 | /* |
| @@ -610,20 +610,20 @@ | |
| 610 | tclInterp = GET_CTX_TCL_INTERP(ctx); |
| 611 | if( !tclInterp || Tcl_InterpDeleted(tclInterp) ){ |
| 612 | Th_ErrorMessage(interp, "invalid Tcl interpreter", (const char *)"", 0); |
| 613 | return TH_ERROR; |
| 614 | } |
| 615 | rc = notifyPreOrPostEval(0, interp, ctx, argc, argv, TH1_LEN(argl), rc); |
| 616 | if( rc!=TH_OK ){ |
| 617 | return rc; |
| 618 | } |
| 619 | Tcl_Preserve((ClientData)tclInterp); |
| 620 | #if !defined(USE_TCL_EVALOBJV) || !USE_TCL_EVALOBJV |
| 621 | if( GET_CTX_TCL_USEOBJPROC(ctx) ){ |
| 622 | Tcl_Command command; |
| 623 | Tcl_CmdInfo cmdInfo; |
| 624 | Tcl_Obj *objPtr = Tcl_NewStringObj(argv[1], TH1_LEN(argl[1])); |
| 625 | Tcl_IncrRefCount(objPtr); |
| 626 | command = Tcl_GetCommandFromObj(tclInterp, objPtr); |
| 627 | if( !command || Tcl_GetCommandInfoFromToken(command, &cmdInfo)==0 ){ |
| 628 | Th_ErrorMessage(interp, "Tcl command not found:", argv[1], argl[1]); |
| 629 | Tcl_DecrRefCount(objPtr); objPtr = 0; |
| @@ -649,11 +649,11 @@ | |
| 649 | FREE_ARGV_TO_OBJV(); |
| 650 | } |
| 651 | zResult = getTclResult(tclInterp, &nResult); |
| 652 | Th_SetResult(interp, zResult, nResult); |
| 653 | Tcl_Release((ClientData)tclInterp); |
| 654 | rc = notifyPreOrPostEval(1, interp, ctx, argc, argv, TH1_LEN(argl), |
| 655 | getTh1ReturnCode(rc)); |
| 656 | return rc; |
| 657 | } |
| 658 | |
| 659 | /* |
| @@ -782,11 +782,11 @@ | |
| 782 | return TCL_ERROR; |
| 783 | } |
| 784 | arg = Tcl_GetStringFromObj(objv[1], &nArg); |
| 785 | rc = Th_Eval(th1Interp, 0, arg, nArg); |
| 786 | arg = Th_GetResult(th1Interp, &nArg); |
| 787 | Tcl_SetObjResult(interp, Tcl_NewStringObj(arg, TH1_LEN(nArg))); |
| 788 | return getTclReturnCode(rc); |
| 789 | } |
| 790 | |
| 791 | /* |
| 792 | ** Tcl command: th1Expr arg |
| @@ -815,11 +815,11 @@ | |
| 815 | return TCL_ERROR; |
| 816 | } |
| 817 | arg = Tcl_GetStringFromObj(objv[1], &nArg); |
| 818 | rc = Th_Expr(th1Interp, arg, nArg); |
| 819 | arg = Th_GetResult(th1Interp, &nArg); |
| 820 | Tcl_SetObjResult(interp, Tcl_NewStringObj(arg, TH1_LEN(nArg))); |
| 821 | return getTclReturnCode(rc); |
| 822 | } |
| 823 | |
| 824 | /* |
| 825 | ** Array of Tcl integration commands. Used when adding or removing the Tcl |
| 826 |
+1
-1
| --- src/timeline.c | ||
| +++ src/timeline.c | ||
| @@ -1888,11 +1888,11 @@ | ||
| 1888 | 1888 | if( zTagName ){ |
| 1889 | 1889 | zType = "ci"; |
| 1890 | 1890 | if( matchStyle==MS_EXACT ){ |
| 1891 | 1891 | /* For exact maching, inhibit links to the selected tag. */ |
| 1892 | 1892 | zThisTag = zTagName; |
| 1893 | - Th_Store("current_checkin", zTagName); | |
| 1893 | + Th_StoreUnsafe("current_checkin", zTagName); | |
| 1894 | 1894 | } |
| 1895 | 1895 | |
| 1896 | 1896 | /* Display a checkbox to enable/disable display of related check-ins. */ |
| 1897 | 1897 | if( advancedMenu ){ |
| 1898 | 1898 | style_submenu_checkbox("rel", "Related", 0, 0); |
| 1899 | 1899 |
| --- src/timeline.c | |
| +++ src/timeline.c | |
| @@ -1888,11 +1888,11 @@ | |
| 1888 | if( zTagName ){ |
| 1889 | zType = "ci"; |
| 1890 | if( matchStyle==MS_EXACT ){ |
| 1891 | /* For exact maching, inhibit links to the selected tag. */ |
| 1892 | zThisTag = zTagName; |
| 1893 | Th_Store("current_checkin", zTagName); |
| 1894 | } |
| 1895 | |
| 1896 | /* Display a checkbox to enable/disable display of related check-ins. */ |
| 1897 | if( advancedMenu ){ |
| 1898 | style_submenu_checkbox("rel", "Related", 0, 0); |
| 1899 |
| --- src/timeline.c | |
| +++ src/timeline.c | |
| @@ -1888,11 +1888,11 @@ | |
| 1888 | if( zTagName ){ |
| 1889 | zType = "ci"; |
| 1890 | if( matchStyle==MS_EXACT ){ |
| 1891 | /* For exact maching, inhibit links to the selected tag. */ |
| 1892 | zThisTag = zTagName; |
| 1893 | Th_StoreUnsafe("current_checkin", zTagName); |
| 1894 | } |
| 1895 | |
| 1896 | /* Display a checkbox to enable/disable display of related check-ins. */ |
| 1897 | if( advancedMenu ){ |
| 1898 | style_submenu_checkbox("rel", "Related", 0, 0); |
| 1899 |
+10
-9
| --- src/tkt.c | ||
| +++ src/tkt.c | ||
| @@ -210,21 +210,21 @@ | ||
| 210 | 210 | zVal = zRevealed = db_reveal(zVal); |
| 211 | 211 | } |
| 212 | 212 | if( (j = fieldId(zName))>=0 ){ |
| 213 | 213 | aField[j].zValue = mprintf("%s", zVal); |
| 214 | 214 | }else if( memcmp(zName, "tkt_", 4)==0 && Th_Fetch(zName, &size)==0 ){ |
| 215 | - Th_Store(zName, zVal); | |
| 215 | + Th_StoreUnsafe(zName, zVal); | |
| 216 | 216 | } |
| 217 | 217 | free(zRevealed); |
| 218 | 218 | } |
| 219 | 219 | Th_Store("tkt_mage", human_readable_age(db_column_double(&q, 2))); |
| 220 | 220 | Th_Store("tkt_cage", human_readable_age(db_column_double(&q, 3))); |
| 221 | 221 | } |
| 222 | 222 | db_finalize(&q); |
| 223 | 223 | for(i=0; i<nField; i++){ |
| 224 | 224 | if( Th_Fetch(aField[i].zName, &size)==0 ){ |
| 225 | - Th_Store(aField[i].zName, aField[i].zValue); | |
| 225 | + Th_StoreUnsafe(aField[i].zName, aField[i].zValue); | |
| 226 | 226 | } |
| 227 | 227 | } |
| 228 | 228 | } |
| 229 | 229 | |
| 230 | 230 | /* |
| @@ -233,11 +233,11 @@ | ||
| 233 | 233 | static void initializeVariablesFromCGI(void){ |
| 234 | 234 | int i; |
| 235 | 235 | const char *z; |
| 236 | 236 | |
| 237 | 237 | for(i=0; (z = cgi_parameter_name(i))!=0; i++){ |
| 238 | - Th_Store(z, P(z)); | |
| 238 | + Th_StoreUnsafe(z, P(z)); | |
| 239 | 239 | } |
| 240 | 240 | } |
| 241 | 241 | |
| 242 | 242 | /* |
| 243 | 243 | ** Information about a single J-card |
| @@ -818,15 +818,15 @@ | ||
| 818 | 818 | if( argc!=3 ){ |
| 819 | 819 | return Th_WrongNumArgs(interp, "append_field FIELD STRING"); |
| 820 | 820 | } |
| 821 | 821 | if( g.thTrace ){ |
| 822 | 822 | Th_Trace("append_field %#h {%#h}<br>\n", |
| 823 | - argl[1], argv[1], argl[2], argv[2]); | |
| 823 | + TH1_LEN(argl[1]), argv[1], TH1_LEN(argl[2]), argv[2]); | |
| 824 | 824 | } |
| 825 | 825 | for(idx=0; idx<nField; idx++){ |
| 826 | - if( memcmp(aField[idx].zName, argv[1], argl[1])==0 | |
| 827 | - && aField[idx].zName[argl[1]]==0 ){ | |
| 826 | + if( memcmp(aField[idx].zName, argv[1], TH1_LEN(argl[1]))==0 | |
| 827 | + && aField[idx].zName[TH1_LEN(argl[1])]==0 ){ | |
| 828 | 828 | break; |
| 829 | 829 | } |
| 830 | 830 | } |
| 831 | 831 | if( idx>=nField ){ |
| 832 | 832 | Th_ErrorMessage(g.interp, "no such TICKET column: ", argv[1], argl[1]); |
| @@ -938,10 +938,11 @@ | ||
| 938 | 938 | const char *zValue; |
| 939 | 939 | int nValue; |
| 940 | 940 | if( aField[i].zAppend ) continue; |
| 941 | 941 | zValue = Th_Fetch(aField[i].zName, &nValue); |
| 942 | 942 | if( zValue ){ |
| 943 | + nValue = TH1_LEN(nValue); | |
| 943 | 944 | while( nValue>0 && fossil_isspace(zValue[nValue-1]) ){ nValue--; } |
| 944 | 945 | if( ((aField[i].mUsed & USEDBY_TICKETCHNG)!=0 && nValue>0) |
| 945 | 946 | || memcmp(zValue, aField[i].zValue, nValue)!=0 |
| 946 | 947 | ||(int)strlen(aField[i].zValue)!=nValue |
| 947 | 948 | ){ |
| @@ -1040,16 +1041,16 @@ | ||
| 1040 | 1041 | if( uid ){ |
| 1041 | 1042 | char * zEmail = |
| 1042 | 1043 | db_text(0, "SELECT find_emailaddr(info) FROM user WHERE uid=%d", |
| 1043 | 1044 | uid); |
| 1044 | 1045 | if( zEmail ){ |
| 1045 | - Th_Store("private_contact", zEmail); | |
| 1046 | + Th_StoreUnsafe("private_contact", zEmail); | |
| 1046 | 1047 | fossil_free(zEmail); |
| 1047 | 1048 | } |
| 1048 | 1049 | } |
| 1049 | 1050 | } |
| 1050 | - Th_Store("login", login_name()); | |
| 1051 | + Th_StoreUnsafe("login", login_name()); | |
| 1051 | 1052 | Th_Store("date", db_text(0, "SELECT datetime('now')")); |
| 1052 | 1053 | Th_CreateCommand(g.interp, "submit_ticket", submitTicketCmd, |
| 1053 | 1054 | (void*)&zNewUuid, 0); |
| 1054 | 1055 | if( g.thTrace ) Th_Trace("BEGIN_TKTNEW_SCRIPT<br>\n", -1); |
| 1055 | 1056 | if( Th_Render(zScript)==TH_RETURN && !g.thTrace && zNewUuid ){ |
| @@ -1120,11 +1121,11 @@ | ||
| 1120 | 1121 | initializeVariablesFromDb(); |
| 1121 | 1122 | if( g.zPath[0]=='d' ) showAllFields(); |
| 1122 | 1123 | form_begin(0, "%R/%s", g.zPath); |
| 1123 | 1124 | @ <input type="hidden" name="name" value="%s(zName)"> |
| 1124 | 1125 | zScript = ticket_editpage_code(); |
| 1125 | - Th_Store("login", login_name()); | |
| 1126 | + Th_StoreUnsafe("login", login_name()); | |
| 1126 | 1127 | Th_Store("date", db_text(0, "SELECT datetime('now')")); |
| 1127 | 1128 | Th_CreateCommand(g.interp, "append_field", appendRemarkCmd, 0, 0); |
| 1128 | 1129 | Th_CreateCommand(g.interp, "submit_ticket", submitTicketCmd, (void*)&zName,0); |
| 1129 | 1130 | if( g.thTrace ) Th_Trace("BEGIN_TKTEDIT_SCRIPT<br>\n", -1); |
| 1130 | 1131 | if( Th_Render(zScript)==TH_RETURN && !g.thTrace && zName ){ |
| 1131 | 1132 |
| --- src/tkt.c | |
| +++ src/tkt.c | |
| @@ -210,21 +210,21 @@ | |
| 210 | zVal = zRevealed = db_reveal(zVal); |
| 211 | } |
| 212 | if( (j = fieldId(zName))>=0 ){ |
| 213 | aField[j].zValue = mprintf("%s", zVal); |
| 214 | }else if( memcmp(zName, "tkt_", 4)==0 && Th_Fetch(zName, &size)==0 ){ |
| 215 | Th_Store(zName, zVal); |
| 216 | } |
| 217 | free(zRevealed); |
| 218 | } |
| 219 | Th_Store("tkt_mage", human_readable_age(db_column_double(&q, 2))); |
| 220 | Th_Store("tkt_cage", human_readable_age(db_column_double(&q, 3))); |
| 221 | } |
| 222 | db_finalize(&q); |
| 223 | for(i=0; i<nField; i++){ |
| 224 | if( Th_Fetch(aField[i].zName, &size)==0 ){ |
| 225 | Th_Store(aField[i].zName, aField[i].zValue); |
| 226 | } |
| 227 | } |
| 228 | } |
| 229 | |
| 230 | /* |
| @@ -233,11 +233,11 @@ | |
| 233 | static void initializeVariablesFromCGI(void){ |
| 234 | int i; |
| 235 | const char *z; |
| 236 | |
| 237 | for(i=0; (z = cgi_parameter_name(i))!=0; i++){ |
| 238 | Th_Store(z, P(z)); |
| 239 | } |
| 240 | } |
| 241 | |
| 242 | /* |
| 243 | ** Information about a single J-card |
| @@ -818,15 +818,15 @@ | |
| 818 | if( argc!=3 ){ |
| 819 | return Th_WrongNumArgs(interp, "append_field FIELD STRING"); |
| 820 | } |
| 821 | if( g.thTrace ){ |
| 822 | Th_Trace("append_field %#h {%#h}<br>\n", |
| 823 | argl[1], argv[1], argl[2], argv[2]); |
| 824 | } |
| 825 | for(idx=0; idx<nField; idx++){ |
| 826 | if( memcmp(aField[idx].zName, argv[1], argl[1])==0 |
| 827 | && aField[idx].zName[argl[1]]==0 ){ |
| 828 | break; |
| 829 | } |
| 830 | } |
| 831 | if( idx>=nField ){ |
| 832 | Th_ErrorMessage(g.interp, "no such TICKET column: ", argv[1], argl[1]); |
| @@ -938,10 +938,11 @@ | |
| 938 | const char *zValue; |
| 939 | int nValue; |
| 940 | if( aField[i].zAppend ) continue; |
| 941 | zValue = Th_Fetch(aField[i].zName, &nValue); |
| 942 | if( zValue ){ |
| 943 | while( nValue>0 && fossil_isspace(zValue[nValue-1]) ){ nValue--; } |
| 944 | if( ((aField[i].mUsed & USEDBY_TICKETCHNG)!=0 && nValue>0) |
| 945 | || memcmp(zValue, aField[i].zValue, nValue)!=0 |
| 946 | ||(int)strlen(aField[i].zValue)!=nValue |
| 947 | ){ |
| @@ -1040,16 +1041,16 @@ | |
| 1040 | if( uid ){ |
| 1041 | char * zEmail = |
| 1042 | db_text(0, "SELECT find_emailaddr(info) FROM user WHERE uid=%d", |
| 1043 | uid); |
| 1044 | if( zEmail ){ |
| 1045 | Th_Store("private_contact", zEmail); |
| 1046 | fossil_free(zEmail); |
| 1047 | } |
| 1048 | } |
| 1049 | } |
| 1050 | Th_Store("login", login_name()); |
| 1051 | Th_Store("date", db_text(0, "SELECT datetime('now')")); |
| 1052 | Th_CreateCommand(g.interp, "submit_ticket", submitTicketCmd, |
| 1053 | (void*)&zNewUuid, 0); |
| 1054 | if( g.thTrace ) Th_Trace("BEGIN_TKTNEW_SCRIPT<br>\n", -1); |
| 1055 | if( Th_Render(zScript)==TH_RETURN && !g.thTrace && zNewUuid ){ |
| @@ -1120,11 +1121,11 @@ | |
| 1120 | initializeVariablesFromDb(); |
| 1121 | if( g.zPath[0]=='d' ) showAllFields(); |
| 1122 | form_begin(0, "%R/%s", g.zPath); |
| 1123 | @ <input type="hidden" name="name" value="%s(zName)"> |
| 1124 | zScript = ticket_editpage_code(); |
| 1125 | Th_Store("login", login_name()); |
| 1126 | Th_Store("date", db_text(0, "SELECT datetime('now')")); |
| 1127 | Th_CreateCommand(g.interp, "append_field", appendRemarkCmd, 0, 0); |
| 1128 | Th_CreateCommand(g.interp, "submit_ticket", submitTicketCmd, (void*)&zName,0); |
| 1129 | if( g.thTrace ) Th_Trace("BEGIN_TKTEDIT_SCRIPT<br>\n", -1); |
| 1130 | if( Th_Render(zScript)==TH_RETURN && !g.thTrace && zName ){ |
| 1131 |
| --- src/tkt.c | |
| +++ src/tkt.c | |
| @@ -210,21 +210,21 @@ | |
| 210 | zVal = zRevealed = db_reveal(zVal); |
| 211 | } |
| 212 | if( (j = fieldId(zName))>=0 ){ |
| 213 | aField[j].zValue = mprintf("%s", zVal); |
| 214 | }else if( memcmp(zName, "tkt_", 4)==0 && Th_Fetch(zName, &size)==0 ){ |
| 215 | Th_StoreUnsafe(zName, zVal); |
| 216 | } |
| 217 | free(zRevealed); |
| 218 | } |
| 219 | Th_Store("tkt_mage", human_readable_age(db_column_double(&q, 2))); |
| 220 | Th_Store("tkt_cage", human_readable_age(db_column_double(&q, 3))); |
| 221 | } |
| 222 | db_finalize(&q); |
| 223 | for(i=0; i<nField; i++){ |
| 224 | if( Th_Fetch(aField[i].zName, &size)==0 ){ |
| 225 | Th_StoreUnsafe(aField[i].zName, aField[i].zValue); |
| 226 | } |
| 227 | } |
| 228 | } |
| 229 | |
| 230 | /* |
| @@ -233,11 +233,11 @@ | |
| 233 | static void initializeVariablesFromCGI(void){ |
| 234 | int i; |
| 235 | const char *z; |
| 236 | |
| 237 | for(i=0; (z = cgi_parameter_name(i))!=0; i++){ |
| 238 | Th_StoreUnsafe(z, P(z)); |
| 239 | } |
| 240 | } |
| 241 | |
| 242 | /* |
| 243 | ** Information about a single J-card |
| @@ -818,15 +818,15 @@ | |
| 818 | if( argc!=3 ){ |
| 819 | return Th_WrongNumArgs(interp, "append_field FIELD STRING"); |
| 820 | } |
| 821 | if( g.thTrace ){ |
| 822 | Th_Trace("append_field %#h {%#h}<br>\n", |
| 823 | TH1_LEN(argl[1]), argv[1], TH1_LEN(argl[2]), argv[2]); |
| 824 | } |
| 825 | for(idx=0; idx<nField; idx++){ |
| 826 | if( memcmp(aField[idx].zName, argv[1], TH1_LEN(argl[1]))==0 |
| 827 | && aField[idx].zName[TH1_LEN(argl[1])]==0 ){ |
| 828 | break; |
| 829 | } |
| 830 | } |
| 831 | if( idx>=nField ){ |
| 832 | Th_ErrorMessage(g.interp, "no such TICKET column: ", argv[1], argl[1]); |
| @@ -938,10 +938,11 @@ | |
| 938 | const char *zValue; |
| 939 | int nValue; |
| 940 | if( aField[i].zAppend ) continue; |
| 941 | zValue = Th_Fetch(aField[i].zName, &nValue); |
| 942 | if( zValue ){ |
| 943 | nValue = TH1_LEN(nValue); |
| 944 | while( nValue>0 && fossil_isspace(zValue[nValue-1]) ){ nValue--; } |
| 945 | if( ((aField[i].mUsed & USEDBY_TICKETCHNG)!=0 && nValue>0) |
| 946 | || memcmp(zValue, aField[i].zValue, nValue)!=0 |
| 947 | ||(int)strlen(aField[i].zValue)!=nValue |
| 948 | ){ |
| @@ -1040,16 +1041,16 @@ | |
| 1041 | if( uid ){ |
| 1042 | char * zEmail = |
| 1043 | db_text(0, "SELECT find_emailaddr(info) FROM user WHERE uid=%d", |
| 1044 | uid); |
| 1045 | if( zEmail ){ |
| 1046 | Th_StoreUnsafe("private_contact", zEmail); |
| 1047 | fossil_free(zEmail); |
| 1048 | } |
| 1049 | } |
| 1050 | } |
| 1051 | Th_StoreUnsafe("login", login_name()); |
| 1052 | Th_Store("date", db_text(0, "SELECT datetime('now')")); |
| 1053 | Th_CreateCommand(g.interp, "submit_ticket", submitTicketCmd, |
| 1054 | (void*)&zNewUuid, 0); |
| 1055 | if( g.thTrace ) Th_Trace("BEGIN_TKTNEW_SCRIPT<br>\n", -1); |
| 1056 | if( Th_Render(zScript)==TH_RETURN && !g.thTrace && zNewUuid ){ |
| @@ -1120,11 +1121,11 @@ | |
| 1121 | initializeVariablesFromDb(); |
| 1122 | if( g.zPath[0]=='d' ) showAllFields(); |
| 1123 | form_begin(0, "%R/%s", g.zPath); |
| 1124 | @ <input type="hidden" name="name" value="%s(zName)"> |
| 1125 | zScript = ticket_editpage_code(); |
| 1126 | Th_StoreUnsafe("login", login_name()); |
| 1127 | Th_Store("date", db_text(0, "SELECT datetime('now')")); |
| 1128 | Th_CreateCommand(g.interp, "append_field", appendRemarkCmd, 0, 0); |
| 1129 | Th_CreateCommand(g.interp, "submit_ticket", submitTicketCmd, (void*)&zName,0); |
| 1130 | if( g.thTrace ) Th_Trace("BEGIN_TKTEDIT_SCRIPT<br>\n", -1); |
| 1131 | if( Th_Render(zScript)==TH_RETURN && !g.thTrace && zName ){ |
| 1132 |
+13
-13
| --- src/tktsetup.c | ||
| +++ src/tktsetup.c | ||
| @@ -481,11 +481,11 @@ | ||
| 481 | 481 | @ <th1> |
| 482 | 482 | @ if {[info exists tkt_uuid]} { |
| 483 | 483 | @ html "<td class='tktDspValue' colspan='3'>" |
| 484 | 484 | @ copybtn hash-tk 0 $tkt_uuid 2 |
| 485 | 485 | @ if {[hascap s]} { |
| 486 | -@ html " ($tkt_id)" | |
| 486 | +@ puts " ($tkt_id)" | |
| 487 | 487 | @ } |
| 488 | 488 | @ html "</td></tr>\n" |
| 489 | 489 | @ } else { |
| 490 | 490 | @ if {[hascap s]} { |
| 491 | 491 | @ html "<td class='tktDspValue' colspan='3'>Deleted " |
| @@ -522,24 +522,24 @@ | ||
| 522 | 522 | @ $<resolution> |
| 523 | 523 | @ </td></tr> |
| 524 | 524 | @ <tr><td class="tktDspLabel">Last Modified:</td><td class="tktDspValue"> |
| 525 | 525 | @ <th1> |
| 526 | 526 | @ if {[info exists tkt_datetime]} { |
| 527 | -@ html $tkt_datetime | |
| 527 | +@ puts $tkt_datetime | |
| 528 | 528 | @ } |
| 529 | 529 | @ if {[info exists tkt_mage]} { |
| 530 | -@ html "<br>$tkt_mage" | |
| 530 | +@ html "<br>[htmlize $tkt_mage] ago" | |
| 531 | 531 | @ } |
| 532 | 532 | @ </th1> |
| 533 | 533 | @ </td> |
| 534 | 534 | @ <td class="tktDspLabel">Created:</td><td class="tktDspValue"> |
| 535 | 535 | @ <th1> |
| 536 | 536 | @ if {[info exists tkt_datetime_creation]} { |
| 537 | -@ html $tkt_datetime_creation | |
| 537 | +@ puts $tkt_datetime_creation | |
| 538 | 538 | @ } |
| 539 | 539 | @ if {[info exists tkt_cage]} { |
| 540 | -@ html "<br>$tkt_cage" | |
| 540 | +@ html "<br>[htmlize $tkt_cage] ago" | |
| 541 | 541 | @ } |
| 542 | 542 | @ </th1> |
| 543 | 543 | @ </td></tr> |
| 544 | 544 | @ <th1>enable_output [hascap e]</th1> |
| 545 | 545 | @ <tr> |
| @@ -614,19 +614,19 @@ | ||
| 614 | 614 | @ html "User Comments:</td></tr>\n" |
| 615 | 615 | @ html "<tr><td colspan='5' class='tktDspValue'>\n" |
| 616 | 616 | @ set seenRow 1 |
| 617 | 617 | @ } |
| 618 | 618 | @ html "<span class='tktDspCommenter'>" |
| 619 | -@ html "[htmlize $xlogin]" | |
| 619 | +@ puts $xlogin | |
| 620 | 620 | @ if {$xlogin ne $xusername && [string length $xusername]>0} { |
| 621 | -@ html " (claiming to be [htmlize $xusername])" | |
| 621 | +@ puts " (claiming to be $xusername)" | |
| 622 | 622 | @ } |
| 623 | -@ html " added on $xdate:" | |
| 623 | +@ puts " added on $xdate:" | |
| 624 | 624 | @ html "</span>\n" |
| 625 | 625 | @ if {$alwaysPlaintext || $xmimetype eq "text/plain"} { |
| 626 | 626 | @ set r [randhex] |
| 627 | -@ if {$xmimetype ne "text/plain"} {html "([htmlize $xmimetype])\n"} | |
| 627 | +@ if {$xmimetype ne "text/plain"} {puts "($xmimetype)\n"} | |
| 628 | 628 | @ wiki "<verbatim-$r>[string trimright $xcomment]</verbatim-$r>\n" |
| 629 | 629 | @ } elseif {$xmimetype eq "text/x-fossil-wiki"} { |
| 630 | 630 | @ wiki "<p>\n[string trimright $xcomment]\n</p>\n" |
| 631 | 631 | @ } elseif {$xmimetype eq "text/x-markdown"} { |
| 632 | 632 | @ html [lindex [markdown $xcomment] 1] |
| @@ -801,19 +801,19 @@ | ||
| 801 | 801 | @ html "Previous User Comments:</td></tr>\n" |
| 802 | 802 | @ html "<tr><td colspan='2' class='tktDspValue'>\n" |
| 803 | 803 | @ set seenRow 1 |
| 804 | 804 | @ } |
| 805 | 805 | @ html "<span class='tktDspCommenter'>" |
| 806 | -@ html "[htmlize $xlogin]" | |
| 806 | +@ puts $xlogin | |
| 807 | 807 | @ if {$xlogin ne $xusername && [string length $xusername]>0} { |
| 808 | -@ html " (claiming to be [htmlize $xusername])" | |
| 808 | +@ puts " (claiming to be $xusername)" | |
| 809 | 809 | @ } |
| 810 | -@ html " added on $xdate:" | |
| 810 | +@ puts " added on $xdate:" | |
| 811 | 811 | @ html "</span>\n" |
| 812 | 812 | @ if {$alwaysPlaintext || $xmimetype eq "text/plain"} { |
| 813 | 813 | @ set r [randhex] |
| 814 | -@ if {$xmimetype ne "text/plain"} {html "([htmlize $xmimetype])\n"} | |
| 814 | +@ if {$xmimetype ne "text/plain"} {puts "($xmimetype)\n"} | |
| 815 | 815 | @ wiki "<verbatim-$r>[string trimright $xcomment]</verbatim-$r>\n" |
| 816 | 816 | @ } elseif {$xmimetype eq "text/x-fossil-wiki"} { |
| 817 | 817 | @ wiki "<p>\n[string trimright $xcomment]\n</p>\n" |
| 818 | 818 | @ } elseif {$xmimetype eq "text/x-markdown"} { |
| 819 | 819 | @ html [lindex [markdown $xcomment] 1] |
| 820 | 820 |
| --- src/tktsetup.c | |
| +++ src/tktsetup.c | |
| @@ -481,11 +481,11 @@ | |
| 481 | @ <th1> |
| 482 | @ if {[info exists tkt_uuid]} { |
| 483 | @ html "<td class='tktDspValue' colspan='3'>" |
| 484 | @ copybtn hash-tk 0 $tkt_uuid 2 |
| 485 | @ if {[hascap s]} { |
| 486 | @ html " ($tkt_id)" |
| 487 | @ } |
| 488 | @ html "</td></tr>\n" |
| 489 | @ } else { |
| 490 | @ if {[hascap s]} { |
| 491 | @ html "<td class='tktDspValue' colspan='3'>Deleted " |
| @@ -522,24 +522,24 @@ | |
| 522 | @ $<resolution> |
| 523 | @ </td></tr> |
| 524 | @ <tr><td class="tktDspLabel">Last Modified:</td><td class="tktDspValue"> |
| 525 | @ <th1> |
| 526 | @ if {[info exists tkt_datetime]} { |
| 527 | @ html $tkt_datetime |
| 528 | @ } |
| 529 | @ if {[info exists tkt_mage]} { |
| 530 | @ html "<br>$tkt_mage" |
| 531 | @ } |
| 532 | @ </th1> |
| 533 | @ </td> |
| 534 | @ <td class="tktDspLabel">Created:</td><td class="tktDspValue"> |
| 535 | @ <th1> |
| 536 | @ if {[info exists tkt_datetime_creation]} { |
| 537 | @ html $tkt_datetime_creation |
| 538 | @ } |
| 539 | @ if {[info exists tkt_cage]} { |
| 540 | @ html "<br>$tkt_cage" |
| 541 | @ } |
| 542 | @ </th1> |
| 543 | @ </td></tr> |
| 544 | @ <th1>enable_output [hascap e]</th1> |
| 545 | @ <tr> |
| @@ -614,19 +614,19 @@ | |
| 614 | @ html "User Comments:</td></tr>\n" |
| 615 | @ html "<tr><td colspan='5' class='tktDspValue'>\n" |
| 616 | @ set seenRow 1 |
| 617 | @ } |
| 618 | @ html "<span class='tktDspCommenter'>" |
| 619 | @ html "[htmlize $xlogin]" |
| 620 | @ if {$xlogin ne $xusername && [string length $xusername]>0} { |
| 621 | @ html " (claiming to be [htmlize $xusername])" |
| 622 | @ } |
| 623 | @ html " added on $xdate:" |
| 624 | @ html "</span>\n" |
| 625 | @ if {$alwaysPlaintext || $xmimetype eq "text/plain"} { |
| 626 | @ set r [randhex] |
| 627 | @ if {$xmimetype ne "text/plain"} {html "([htmlize $xmimetype])\n"} |
| 628 | @ wiki "<verbatim-$r>[string trimright $xcomment]</verbatim-$r>\n" |
| 629 | @ } elseif {$xmimetype eq "text/x-fossil-wiki"} { |
| 630 | @ wiki "<p>\n[string trimright $xcomment]\n</p>\n" |
| 631 | @ } elseif {$xmimetype eq "text/x-markdown"} { |
| 632 | @ html [lindex [markdown $xcomment] 1] |
| @@ -801,19 +801,19 @@ | |
| 801 | @ html "Previous User Comments:</td></tr>\n" |
| 802 | @ html "<tr><td colspan='2' class='tktDspValue'>\n" |
| 803 | @ set seenRow 1 |
| 804 | @ } |
| 805 | @ html "<span class='tktDspCommenter'>" |
| 806 | @ html "[htmlize $xlogin]" |
| 807 | @ if {$xlogin ne $xusername && [string length $xusername]>0} { |
| 808 | @ html " (claiming to be [htmlize $xusername])" |
| 809 | @ } |
| 810 | @ html " added on $xdate:" |
| 811 | @ html "</span>\n" |
| 812 | @ if {$alwaysPlaintext || $xmimetype eq "text/plain"} { |
| 813 | @ set r [randhex] |
| 814 | @ if {$xmimetype ne "text/plain"} {html "([htmlize $xmimetype])\n"} |
| 815 | @ wiki "<verbatim-$r>[string trimright $xcomment]</verbatim-$r>\n" |
| 816 | @ } elseif {$xmimetype eq "text/x-fossil-wiki"} { |
| 817 | @ wiki "<p>\n[string trimright $xcomment]\n</p>\n" |
| 818 | @ } elseif {$xmimetype eq "text/x-markdown"} { |
| 819 | @ html [lindex [markdown $xcomment] 1] |
| 820 |
| --- src/tktsetup.c | |
| +++ src/tktsetup.c | |
| @@ -481,11 +481,11 @@ | |
| 481 | @ <th1> |
| 482 | @ if {[info exists tkt_uuid]} { |
| 483 | @ html "<td class='tktDspValue' colspan='3'>" |
| 484 | @ copybtn hash-tk 0 $tkt_uuid 2 |
| 485 | @ if {[hascap s]} { |
| 486 | @ puts " ($tkt_id)" |
| 487 | @ } |
| 488 | @ html "</td></tr>\n" |
| 489 | @ } else { |
| 490 | @ if {[hascap s]} { |
| 491 | @ html "<td class='tktDspValue' colspan='3'>Deleted " |
| @@ -522,24 +522,24 @@ | |
| 522 | @ $<resolution> |
| 523 | @ </td></tr> |
| 524 | @ <tr><td class="tktDspLabel">Last Modified:</td><td class="tktDspValue"> |
| 525 | @ <th1> |
| 526 | @ if {[info exists tkt_datetime]} { |
| 527 | @ puts $tkt_datetime |
| 528 | @ } |
| 529 | @ if {[info exists tkt_mage]} { |
| 530 | @ html "<br>[htmlize $tkt_mage] ago" |
| 531 | @ } |
| 532 | @ </th1> |
| 533 | @ </td> |
| 534 | @ <td class="tktDspLabel">Created:</td><td class="tktDspValue"> |
| 535 | @ <th1> |
| 536 | @ if {[info exists tkt_datetime_creation]} { |
| 537 | @ puts $tkt_datetime_creation |
| 538 | @ } |
| 539 | @ if {[info exists tkt_cage]} { |
| 540 | @ html "<br>[htmlize $tkt_cage] ago" |
| 541 | @ } |
| 542 | @ </th1> |
| 543 | @ </td></tr> |
| 544 | @ <th1>enable_output [hascap e]</th1> |
| 545 | @ <tr> |
| @@ -614,19 +614,19 @@ | |
| 614 | @ html "User Comments:</td></tr>\n" |
| 615 | @ html "<tr><td colspan='5' class='tktDspValue'>\n" |
| 616 | @ set seenRow 1 |
| 617 | @ } |
| 618 | @ html "<span class='tktDspCommenter'>" |
| 619 | @ puts $xlogin |
| 620 | @ if {$xlogin ne $xusername && [string length $xusername]>0} { |
| 621 | @ puts " (claiming to be $xusername)" |
| 622 | @ } |
| 623 | @ puts " added on $xdate:" |
| 624 | @ html "</span>\n" |
| 625 | @ if {$alwaysPlaintext || $xmimetype eq "text/plain"} { |
| 626 | @ set r [randhex] |
| 627 | @ if {$xmimetype ne "text/plain"} {puts "($xmimetype)\n"} |
| 628 | @ wiki "<verbatim-$r>[string trimright $xcomment]</verbatim-$r>\n" |
| 629 | @ } elseif {$xmimetype eq "text/x-fossil-wiki"} { |
| 630 | @ wiki "<p>\n[string trimright $xcomment]\n</p>\n" |
| 631 | @ } elseif {$xmimetype eq "text/x-markdown"} { |
| 632 | @ html [lindex [markdown $xcomment] 1] |
| @@ -801,19 +801,19 @@ | |
| 801 | @ html "Previous User Comments:</td></tr>\n" |
| 802 | @ html "<tr><td colspan='2' class='tktDspValue'>\n" |
| 803 | @ set seenRow 1 |
| 804 | @ } |
| 805 | @ html "<span class='tktDspCommenter'>" |
| 806 | @ puts $xlogin |
| 807 | @ if {$xlogin ne $xusername && [string length $xusername]>0} { |
| 808 | @ puts " (claiming to be $xusername)" |
| 809 | @ } |
| 810 | @ puts " added on $xdate:" |
| 811 | @ html "</span>\n" |
| 812 | @ if {$alwaysPlaintext || $xmimetype eq "text/plain"} { |
| 813 | @ set r [randhex] |
| 814 | @ if {$xmimetype ne "text/plain"} {puts "($xmimetype)\n"} |
| 815 | @ wiki "<verbatim-$r>[string trimright $xcomment]</verbatim-$r>\n" |
| 816 | @ } elseif {$xmimetype eq "text/x-fossil-wiki"} { |
| 817 | @ wiki "<p>\n[string trimright $xcomment]\n</p>\n" |
| 818 | @ } elseif {$xmimetype eq "text/x-markdown"} { |
| 819 | @ html [lindex [markdown $xcomment] 1] |
| 820 |